source: src/app/students/documents/page.tsx@ 298f9b3

Last change on this file since 298f9b3 was 82c9c53, checked in by stefansaveski <stefansaveski19@…>, 11 months ago

refactor: Remove unused imports and optimize component code across multiple files

  • Property mode set to 100644
File size: 15.3 KB
Line 
1"use client"
2
3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4import {
5 faFilePdf,
6 faChevronDown,
7 faCheckCircle,
8 faMoneyBillWave,
9 faFileText,
10 faChevronLeft,
11 faChevronRight,
12 faAngleDoubleLeft,
13 faAngleDoubleRight
14} from '@fortawesome/free-solid-svg-icons';
15import { useState } from 'react';
16import documentsData from '@/data/documents.json';
17
18interface TableCellProps {
19 children: React.ReactNode;
20 className?: string;
21}
22
23const TableCell = ({ children, className = "" }: TableCellProps) => (
24 <td className={`px-4 py-3 text-sm border-b border-gray-200 ${className}`}>
25 {children}
26 </td>
27);
28
29const StatusBadge = ({ status }: { status: string }) => {
30 if (status === "approved") {
31 return (
32 <span className="inline-flex items-center justify-center w-8 h-8 bg-green-100 text-green-600 rounded-full">
33 <FontAwesomeIcon icon={faCheckCircle} className="w-5 h-5" />
34 </span>
35 );
36 }
37 return (
38 <span className="inline-flex items-center justify-center w-8 h-8 bg-gray-100 text-gray-600 rounded-full">
39 <FontAwesomeIcon icon={faFileText} className="w-4 h-4" />
40 </span>
41 );
42};
43
44const PriceBadge = ({ price }: { price: number }) => {
45 if (price === 0) {
46 return <span className="font-medium text-green-600">0,00</span>;
47 }
48 return <span className="font-medium text-blue-600">{price.toFixed(2)}</span>;
49};
50
51export default function DocumentsPage() {
52 const [selectedDocumentType, setSelectedDocumentType] = useState("select_document");
53 const [isDropdownOpen, setIsDropdownOpen] = useState(false);
54 const [comment, setComment] = useState("");
55 const [currentPage, setCurrentPage] = useState(1);
56 const [recordsPerPage, setRecordsPerPage] = useState(15);
57
58 const selectedDocument = documentsData.documentTypes.find(d => d.id === selectedDocumentType);
59 const { documents, paymentInfo } = documentsData;
60
61 const totalPages = Math.ceil(documents.length / recordsPerPage);
62 const startIndex = (currentPage - 1) * recordsPerPage;
63 const endIndex = startIndex + recordsPerPage;
64 const currentDocuments = documents.slice(startIndex, endIndex);
65
66 return (
67 <div className="min-h-screen pb-8">
68 {/* Header */}
69 <div className="bg-primary text-white rounded-xl p-8 mb-8">
70 <div className="flex items-center gap-4">
71 <div className="bg-white bg-opacity-20 rounded-full p-4">
72 <FontAwesomeIcon icon={faFilePdf} className="text-3xl text-primary" />
73 </div>
74 <div>
75 <h1 className="text-3xl font-bold mb-2">Документи</h1>
76 <p className="text-lg opacity-90">
77 Барање и преглед на документи
78 </p>
79 </div>
80 </div>
81 </div>
82
83 {/* Document Request Form */}
84 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100 mb-8">
85 <h2 className="text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2">
86 <FontAwesomeIcon icon={faFileText} className="text-primary" />
87 Ново барање за документ
88 </h2>
89
90 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
91 {/* Document Type Selection */}
92 <div>
93 <label className="block text-sm font-medium text-gray-700 mb-2">
94 Изберете документ:
95 </label>
96 <div className="relative">
97 <button
98 onClick={() => setIsDropdownOpen(!isDropdownOpen)}
99 className="w-full bg-white border border-gray-300 rounded-lg px-4 py-3 text-left focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary"
100 >
101 <div className="flex items-center justify-between">
102 <span className="text-sm text-gray-900 truncate">
103 {selectedDocument?.name || "Изберете документ"}
104 </span>
105 <FontAwesomeIcon
106 icon={faChevronDown}
107 className={`w-4 h-4 text-gray-400 transition-transform ml-2 flex-shrink-0 ${isDropdownOpen ? 'rotate-180' : ''}`}
108 />
109 </div>
110 </button>
111
112 {isDropdownOpen && (
113 <div className="absolute z-10 w-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg max-h-80 overflow-y-auto">
114 {documentsData.documentTypes.map((docType) => (
115 <button
116 key={docType.id}
117 onClick={() => {
118 setSelectedDocumentType(docType.id);
119 setIsDropdownOpen(false);
120 }}
121 className="w-full px-4 py-3 text-left text-sm hover:bg-gray-50 focus:outline-none focus:bg-gray-50 border-b border-gray-100 last:border-b-0"
122 >
123 <div className="font-medium text-gray-900">{docType.name}</div>
124 {docType.price > 0 && (
125 <div className="text-xs text-blue-600 mt-1">Цена: {docType.price} мкд</div>
126 )}
127 </button>
128 ))}
129 </div>
130 )}
131 </div>
132 </div>
133
134 {/* Comment Section */}
135 <div>
136 <label className="block text-sm font-medium text-gray-700 mb-2">
137 Коментар:
138 </label>
139 <textarea
140 value={comment}
141 onChange={(e) => setComment(e.target.value)}
142 rows={4}
143 className="w-full border border-gray-300 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary resize-none"
144 placeholder="Додајте коментар..."
145 />
146 </div>
147 </div>
148
149 <div className="flex justify-end mt-6">
150 <button
151 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"
152 disabled={selectedDocumentType === "select_document"}
153 >
154 <FontAwesomeIcon icon={faFileText} className="w-4 h-4" />
155 Внеси
156 </button>
157 </div>
158 </div>
159
160 {/* Documents Table */}
161 <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
162 <div className="bg-primary text-white px-6 py-4">
163 <h2 className="text-xl font-bold">Мои документи</h2>
164 </div>
165
166 <div className="overflow-x-auto">
167 <table className="w-full">
168 <thead className="bg-gray-50">
169 <tr>
170 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">#</th>
171 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Архива</th>
172 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Датум</th>
173 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Барање</th>
174 <th className="px-4 py-4 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Цена</th>
175 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Платено</th>
176 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Документ</th>
177 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Плати онлајн</th>
178 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
179 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Коментар</th>
180 </tr>
181 </thead>
182 <tbody className="divide-y divide-gray-100">
183 {currentDocuments.map((document) => (
184 <tr key={document.id} className="hover:bg-gray-50 transition-colors">
185 <TableCell className="font-medium text-gray-900">{document.id}</TableCell>
186 <TableCell className="font-mono text-sm text-primary font-medium">{document.archive}</TableCell>
187 <TableCell className="text-gray-600">{document.date}</TableCell>
188 <TableCell className="font-medium text-gray-900 max-w-xs">
189 {document.request}
190 </TableCell>
191 <TableCell className="text-right">
192 <PriceBadge price={document.price} />
193 </TableCell>
194 <TableCell className="text-center">
195 <span className="text-sm font-medium text-green-600">
196 {document.paid}
197 </span>
198 </TableCell>
199 <TableCell className="text-center">
200 <button className="text-primary hover:text-blue-700 font-medium text-sm underline">
201 {document.document}
202 </button>
203 </TableCell>
204 <TableCell className="text-center">
205 {document.payOnline ? (
206 <FontAwesomeIcon icon={faCheckCircle} className="w-5 h-5 text-green-600" />
207 ) : (
208 <span className="text-gray-400">—</span>
209 )}
210 </TableCell>
211 <TableCell className="text-center">
212 <StatusBadge status={document.status} />
213 </TableCell>
214 <TableCell>
215 {document.comment || (
216 <span className="text-gray-400">—</span>
217 )}
218 </TableCell>
219 </tr>
220 ))}
221 </tbody>
222 </table>
223 </div>
224
225 {/* Pagination */}
226 <div className="bg-gray-50 px-6 py-4 flex items-center justify-between border-t border-gray-200">
227 <div className="flex items-center gap-4 text-sm text-gray-700">
228 <div className="flex items-center gap-2">
229 <span>Прикажи редови:</span>
230 <select
231 value={recordsPerPage}
232 onChange={(e) => setRecordsPerPage(Number(e.target.value))}
233 className="border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
234 >
235 <option value={15}>15</option>
236 <option value={25}>25</option>
237 <option value={50}>50</option>
238 </select>
239 </div>
240 <div>
241 Страна <input
242 type="number"
243 min="1"
244 max={totalPages}
245 value={currentPage}
246 onChange={(e) => setCurrentPage(Number(e.target.value))}
247 className="w-12 border border-gray-300 rounded px-2 py-1 text-sm text-center focus:outline-none focus:ring-1 focus:ring-primary"
248 /> од {totalPages}
249 </div>
250 </div>
251
252 <div className="flex items-center gap-2">
253 <button
254 onClick={() => setCurrentPage(1)}
255 disabled={currentPage === 1}
256 className="p-2 text-gray-500 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
257 >
258 <FontAwesomeIcon icon={faAngleDoubleLeft} className="w-4 h-4" />
259 </button>
260 <button
261 onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
262 disabled={currentPage === 1}
263 className="p-2 text-gray-500 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
264 >
265 <FontAwesomeIcon icon={faChevronLeft} className="w-4 h-4" />
266 </button>
267 <span className="px-4 py-2 bg-primary text-white rounded text-sm font-medium">
268 Прва
269 </span>
270 <button
271 onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
272 disabled={currentPage === totalPages}
273 className="p-2 text-gray-500 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
274 >
275 <FontAwesomeIcon icon={faChevronRight} className="w-4 h-4" />
276 </button>
277 <button
278 onClick={() => setCurrentPage(totalPages)}
279 disabled={currentPage === totalPages}
280 className="p-2 text-gray-500 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
281 >
282 <FontAwesomeIcon icon={faAngleDoubleRight} className="w-4 h-4" />
283 </button>
284 <span className="ml-4 text-sm text-gray-700">
285 Последна
286 </span>
287 </div>
288
289 <div className="text-sm text-gray-700">
290 Вкупно: {documents.length}
291 </div>
292 </div>
293
294 {/* Payment Info */}
295 <div className="bg-blue-50 border-t border-blue-100 p-4">
296 <div className="flex items-start gap-3">
297 <FontAwesomeIcon icon={faMoneyBillWave} className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
298 <p className="text-sm text-blue-800 leading-relaxed">
299 {paymentInfo}
300 </p>
301 </div>
302 </div>
303 </div>
304
305 {/* Statistics Cards */}
306 <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
307 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
308 <div className="flex items-center gap-4">
309 <div className="bg-blue-100 text-blue-600 rounded-full p-3">
310 <FontAwesomeIcon icon={faFileText} className="text-xl" />
311 </div>
312 <div>
313 <h3 className="text-lg font-bold text-gray-900">Вкупно документи</h3>
314 <p className="text-2xl font-bold text-blue-600">
315 {documents.length}
316 </p>
317 </div>
318 </div>
319 </div>
320
321 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
322 <div className="flex items-center gap-4">
323 <div className="bg-green-100 text-green-600 rounded-full p-3">
324 <FontAwesomeIcon icon={faCheckCircle} className="text-xl" />
325 </div>
326 <div>
327 <h3 className="text-lg font-bold text-gray-900">Одобрени</h3>
328 <p className="text-2xl font-bold text-green-600">
329 {documents.filter(doc => doc.status === "approved").length}
330 </p>
331 </div>
332 </div>
333 </div>
334
335 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
336 <div className="flex items-center gap-4">
337 <div className="bg-yellow-100 text-yellow-600 rounded-full p-3">
338 <FontAwesomeIcon icon={faMoneyBillWave} className="text-xl" />
339 </div>
340 <div>
341 <h3 className="text-lg font-bold text-gray-900">Вкупна цена</h3>
342 <p className="text-2xl font-bold text-yellow-600">
343 {documents.reduce((sum, doc) => sum + doc.price, 0).toFixed(2)} мкд
344 </p>
345 </div>
346 </div>
347 </div>
348 </div>
349 </div>
350 );
351}
Note: See TracBrowser for help on using the repository browser.