source: src/app/students/documents/page.tsx@ 2d635ae

Last change on this file since 2d635ae was 6b8332f, checked in by stefansaveski <stefansaveski19@…>, 11 months ago

feat: Add student exams, profile, semesters, and subjects pages with respective components and data integration

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