source: src/app/students/applications/page.tsx@ 997d094

Last change on this file since 997d094 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: 10.8 KB
Line 
1"use client"
2
3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4import {
5 faPenToSquare,
6 faChevronDown,
7 faCalendarAlt,
8 faFileText,
9 faMoneyBillWave,
10 faUser,
11 faInfoCircle,
12 faCheckCircle,
13 faTimesCircle
14} from '@fortawesome/free-solid-svg-icons';
15import { useState } from 'react';
16import applicationsData from '@/data/applications.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 CompletedBadge = ({ completed }: { completed: string }) => {
30 if (completed === "Да") {
31 return (
32 <span className="inline-flex items-center justify-center w-6 h-6 bg-green-100 text-green-600 rounded-full">
33 <FontAwesomeIcon icon={faCheckCircle} className="w-4 h-4" />
34 </span>
35 );
36 } else {
37 return (
38 <span className="inline-flex items-center justify-center w-6 h-6 bg-red-100 text-red-600 rounded-full">
39 <FontAwesomeIcon icon={faTimesCircle} className="w-4 h-4" />
40 </span>
41 );
42 }
43};
44
45const FeeBadge = ({ fee }: { fee: string }) => {
46 const feeValue = parseFloat(fee.replace(',', '.'));
47
48 if (feeValue === 0) {
49 return (
50 <span className="inline-flex items-center gap-1 px-2 py-1 bg-green-100 text-green-800 rounded-md text-xs font-medium">
51 <FontAwesomeIcon icon={faMoneyBillWave} className="w-3 h-3" />
52 {fee}
53 </span>
54 );
55 } else {
56 return (
57 <span className="inline-flex items-center gap-1 px-2 py-1 bg-yellow-100 text-yellow-800 rounded-md text-xs font-medium">
58 <FontAwesomeIcon icon={faMoneyBillWave} className="w-3 h-3" />
59 {fee}
60 </span>
61 );
62 }
63};
64
65export default function ApplicationsPage() {
66 const [selectedSession, setSelectedSession] = useState(applicationsData.currentSession.id);
67 const [isDropdownOpen, setIsDropdownOpen] = useState(false);
68
69 const currentSessionData = applicationsData.examSessions.find(s => s.id === selectedSession) || applicationsData.currentSession;
70 const { applications } = applicationsData;
71
72 return (
73 <div className="min-h-screen pb-8">
74 {/* Header */}
75 <div className="bg-primary text-white rounded-xl p-8 mb-8">
76 <div className="flex items-center gap-4">
77 <div className="bg-white bg-opacity-20 rounded-full p-4">
78 <FontAwesomeIcon icon={faPenToSquare} className="text-3xl text-primary" />
79 </div>
80 <div>
81 <h1 className="text-3xl font-bold mb-2">Пријави</h1>
82 <p className="text-lg opacity-90">
83 Електронски пријави за испити
84 </p>
85 </div>
86 </div>
87 </div>
88
89 {/* Session Selection */}
90 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100 mb-8">
91 <div className="flex items-center justify-between">
92 <h2 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
93 <FontAwesomeIcon icon={faCalendarAlt} className="text-primary" />
94 Избери испитна сесија:
95 </h2>
96
97 <div className="relative">
98 <button
99 onClick={() => setIsDropdownOpen(!isDropdownOpen)}
100 className="bg-white border border-gray-300 rounded-lg px-4 py-2 text-left focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary min-w-80"
101 >
102 <div className="flex items-center justify-between">
103 <span className="text-sm font-medium text-primary">
104 {currentSessionData.name}
105 </span>
106 <FontAwesomeIcon
107 icon={faChevronDown}
108 className={`w-4 h-4 text-gray-400 transition-transform ml-4 ${isDropdownOpen ? 'rotate-180' : ''}`}
109 />
110 </div>
111 </button>
112
113 {isDropdownOpen && (
114 <div className="absolute z-10 right-0 mt-1 w-80 bg-white border border-gray-200 rounded-lg shadow-lg">
115 {applicationsData.examSessions.map((session) => (
116 <button
117 key={session.id}
118 onClick={() => {
119 setSelectedSession(session.id);
120 setIsDropdownOpen(false);
121 }}
122 className="w-full px-4 py-3 text-left text-sm hover:bg-gray-50 focus:outline-none focus:bg-gray-50 first:rounded-t-lg last:rounded-b-lg"
123 >
124 <div className="font-medium text-gray-900">{session.name}</div>
125 <div className="text-xs text-gray-500">{session.year} - {session.semester} - {session.session}</div>
126 </button>
127 ))}
128 </div>
129 )}
130 </div>
131 </div>
132 </div>
133
134 {/* Applications Section */}
135 <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
136 <div className="bg-primary text-white px-6 py-4">
137 <h2 className="text-xl font-bold">Пријавени испити</h2>
138 </div>
139
140 <div className="overflow-x-auto">
141 <table className="w-full">
142 <thead className="bg-gray-50">
143 <tr>
144 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">#</th>
145 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Сериски број</th>
146 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Код</th>
147 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Предмет</th>
148 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Завршена</th>
149 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Таксени</th>
150 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Датум</th>
151 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Наставник</th>
152 <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Декада</th>
153 </tr>
154 </thead>
155 <tbody className="divide-y divide-gray-100">
156 {applications.map((application) => (
157 <tr key={application.id} className="hover:bg-gray-50 transition-colors">
158 <TableCell className="font-medium text-gray-900">{application.id}</TableCell>
159 <TableCell>
160 <span className="font-mono text-sm text-primary font-medium hover:underline cursor-pointer">
161 {application.serviceNumber}
162 </span>
163 </TableCell>
164 <TableCell className="font-mono text-sm font-medium">{application.code}</TableCell>
165 <TableCell className="font-medium text-gray-900 max-w-xs">
166 <div className="flex items-center gap-2">
167 <FontAwesomeIcon icon={faFileText} className="w-4 h-4 text-primary" />
168 {application.subject}
169 </div>
170 </TableCell>
171 <TableCell className="text-center">
172 <CompletedBadge completed={application.completed} />
173 </TableCell>
174 <TableCell className="text-center">
175 <FeeBadge fee={application.fee} />
176 </TableCell>
177 <TableCell className="text-gray-600 font-medium">{application.date}</TableCell>
178 <TableCell>
179 <div className="flex items-center gap-2">
180 <FontAwesomeIcon icon={faUser} className="w-4 h-4 text-gray-400" />
181 <span className="font-medium text-gray-900">{application.instructor}</span>
182 </div>
183 </TableCell>
184 <TableCell className="text-center font-medium text-primary">{application.decade}</TableCell>
185 </tr>
186 ))}
187 </tbody>
188 </table>
189 </div>
190
191 {/* Important Note */}
192 <div className="bg-blue-50 border-t border-blue-100 p-6">
193 <div className="flex items-start gap-3">
194 <FontAwesomeIcon icon={faInfoCircle} className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
195 <div>
196 <h3 className="font-medium text-blue-900 mb-1">Важна забелешка</h3>
197 <p className="text-sm text-blue-800 leading-relaxed">
198 {applicationsData.note}
199 </p>
200 </div>
201 </div>
202 </div>
203 </div>
204
205 {/* Statistics Cards */}
206 <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
207 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
208 <div className="flex items-center gap-4">
209 <div className="bg-blue-100 text-blue-600 rounded-full p-3">
210 <FontAwesomeIcon icon={faFileText} className="text-xl" />
211 </div>
212 <div>
213 <h3 className="text-lg font-bold text-gray-900">Пријавени испити</h3>
214 <p className="text-2xl font-bold text-blue-600">
215 {applications.length}
216 </p>
217 </div>
218 </div>
219 </div>
220
221 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
222 <div className="flex items-center gap-4">
223 <div className="bg-green-100 text-green-600 rounded-full p-3">
224 <FontAwesomeIcon icon={faCheckCircle} className="text-xl" />
225 </div>
226 <div>
227 <h3 className="text-lg font-bold text-gray-900">Завршени</h3>
228 <p className="text-2xl font-bold text-green-600">
229 {applications.filter(app => app.completed === "Да").length}
230 </p>
231 </div>
232 </div>
233 </div>
234
235 <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
236 <div className="flex items-center gap-4">
237 <div className="bg-yellow-100 text-yellow-600 rounded-full p-3">
238 <FontAwesomeIcon icon={faMoneyBillWave} className="text-xl" />
239 </div>
240 <div>
241 <h3 className="text-lg font-bold text-gray-900">Вкупна такса</h3>
242 <p className="text-2xl font-bold text-yellow-600">
243 {applications.reduce((sum, app) => sum + parseFloat(app.fee.replace(',', '.')), 0).toFixed(2)}
244 </p>
245 </div>
246 </div>
247 </div>
248 </div>
249 </div>
250 );
251}
Note: See TracBrowser for help on using the repository browser.