Ignore:
Timestamp:
02/16/26 17:38:16 (7 months ago)
Author:
Stefan-Saveski <stefansaveski19@…>
Branches:
master
Children:
8496f3c
Parents:
d7deae5
Message:

Refactor code structure for improved readability and maintainability

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/app/students/documents/page.tsx

    rd7deae5 r4fe4582  
    66  faChevronDown,
    77  faCheckCircle,
     8  faClock,
    89  faMoneyBillWave,
    910  faFileText,
     
    1112  faChevronRight,
    1213  faAngleDoubleLeft,
    13   faAngleDoubleRight
     14  faAngleDoubleRight,
     15  faSpinner
    1416} from '@fortawesome/free-solid-svg-icons';
    1517import { useState } from 'react';
    1618import documentsData from '@/data/documents.json';
    1719import { useTranslation } from 'react-i18next';
     20import { getAccessToken } from '@/lib/auth';
     21import { downloadDocumentPDF } from '@/lib/pdf-generators';
    1822
    1923interface TableCellProps {
     
    3034const StatusBadge = ({ status }: { status: string }) => {
    3135  const { t } = useTranslation();
    32   if (status === t('approved', 'Одобрено')) {
     36  if (status === 'approved' || status === t('approved', 'Одобрено')) {
    3337    return (
    3438      <span className="inline-flex items-center justify-center w-8 h-8 bg-green-100 text-green-600 rounded-full">
    3539        <FontAwesomeIcon icon={faCheckCircle} className="w-5 h-5" />
     40      </span>
     41    );
     42  }
     43  if (status === 'pending') {
     44    return (
     45      <span className="inline-flex items-center gap-1 text-xs font-medium text-yellow-700 bg-yellow-100 px-2 py-1 rounded-full">
     46        <FontAwesomeIcon icon={faClock} className="w-3 h-3" />
     47        {t('pending', 'Во обработка')}
    3648      </span>
    3749    );
     
    5264};
    5365
     66interface DocumentRecord {
     67  id: number;
     68  archive: string;
     69  date: string;
     70  request: string;
     71  price: number;
     72  paid: string;
     73  document: string;
     74  payOnline: boolean;
     75  status: string;
     76  comment: string;
     77}
     78
    5479export default function DocumentsPage() {
    5580  const [selectedDocumentType, setSelectedDocumentType] = useState("select_document");
     
    5883  const [currentPage, setCurrentPage] = useState(1);
    5984  const [recordsPerPage, setRecordsPerPage] = useState(15);
     85  const [downloadingId, setDownloadingId] = useState<number | null>(null);
     86  const [documents, setDocuments] = useState<DocumentRecord[]>(documentsData.documents as DocumentRecord[]);
     87  const [isSubmitting, setIsSubmitting] = useState(false);
    6088  const { t } = useTranslation();
    6189
     90  const handleDownload = async (docId: number, request: string, archive: string, date: string) => {
     91    const token = getAccessToken();
     92    if (!token) {
     93      alert(t('not_authenticated', 'Не сте најавени. Ве молиме најавете се повторно.'));
     94      return;
     95    }
     96    setDownloadingId(docId);
     97    try {
     98      await downloadDocumentPDF(request, archive, date, token);
     99    } catch (err) {
     100      console.error('PDF generation error:', err);
     101      alert(t('pdf_error', 'Грешка при генерирање на документот. Обидете се повторно.'));
     102    } finally {
     103      setDownloadingId(null);
     104    }
     105  };
     106
     107  const handleSubmit = () => {
     108    if (selectedDocumentType === "select_document") return;
     109
     110    const docType = documentsData.documentTypes.find(d => d.id === selectedDocumentType);
     111    if (!docType) return;
     112
     113    setIsSubmitting(true);
     114
     115    // Generate archive number (random 5-digit)
     116    const archiveNumber = String(90000 + Math.floor(Math.random() * 10000));
     117
     118    // Current date in DD.MM.YYYY format
     119    const now = new Date();
     120    const dateStr = `${String(now.getDate()).padStart(2, '0')}.${String(now.getMonth() + 1).padStart(2, '0')}.${now.getFullYear()}`;
     121
     122    const newDoc: DocumentRecord = {
     123      id: documents.length > 0 ? Math.max(...documents.map(d => d.id)) + 1 : 1,
     124      archive: archiveNumber,
     125      date: dateStr,
     126      request: docType.name,
     127      price: docType.price,
     128      paid: "Не",
     129      document: "Преземи",
     130      payOnline: docType.price > 0,
     131      status: "pending",
     132      comment: comment,
     133    };
     134
     135    setDocuments(prev => [newDoc, ...prev]);
     136    setSelectedDocumentType("select_document");
     137    setComment("");
     138    setIsSubmitting(false);
     139    alert(t('document_submitted', 'Барањето за документ е успешно поднесено!'));
     140  };
     141
    62142  const selectedDocument = documentsData.documentTypes.find(d => d.id === selectedDocumentType);
    63   const { documents, paymentInfo } = documentsData;
     143  const { paymentInfo } = documentsData;
    64144
    65145  const totalPages = Math.ceil(documents.length / recordsPerPage);
     
    153233        <div className="flex justify-end mt-6">
    154234          <button
    155             className="bg-primary hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-lg transition-colors duration-200 flex items-center gap-2"
    156             disabled={selectedDocumentType === "select_document"}
     235            onClick={handleSubmit}
     236            className="bg-primary hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-lg transition-colors duration-200 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
     237            disabled={selectedDocumentType === "select_document" || isSubmitting}
    157238          >
    158             <FontAwesomeIcon icon={faFileText} className="w-4 h-4" />
    159             {t('submit', 'Поднеси')}
     239            <FontAwesomeIcon icon={isSubmitting ? faSpinner : faFileText} className={`w-4 h-4 ${isSubmitting ? 'animate-spin' : ''}`} />
     240            {isSubmitting ? t('submitting', 'Се поднесува...') : t('submit', 'Поднеси')}
    160241          </button>
    161242        </div>
     
    197278                  </TableCell>
    198279                  <TableCell className="text-center">
    199                     <span className="text-sm font-medium text-green-600">
     280                    <span className={`text-sm font-medium ${document.paid === 'ДА' ? 'text-green-600' : 'text-red-500'}`}>
    200281                      {document.paid}
    201282                    </span>
    202283                  </TableCell>
    203284                  <TableCell className="text-center">
    204                     <button className="text-primary hover:text-blue-700 font-medium text-sm underline">
    205                       {document.document}
     285                    <button
     286                      onClick={() => handleDownload(document.id, document.request, document.archive, document.date)}
     287                      disabled={downloadingId === document.id}
     288                      className="text-primary hover:text-blue-700 font-medium text-sm underline disabled:opacity-50 disabled:cursor-wait inline-flex items-center gap-1"
     289                    >
     290                      {downloadingId === document.id ? (
     291                        <>
     292                          <FontAwesomeIcon icon={faSpinner} className="w-3 h-3 animate-spin" />
     293                          {t('generating', 'Генерира...')}
     294                        </>
     295                      ) : (
     296                        t('download', document.document)
     297                      )}
    206298                    </button>
    207299                  </TableCell>
Note: See TracChangeset for help on using the changeset viewer.