Changeset 50f2fb4 for src/app


Ignore:
Timestamp:
01/30/26 01:40:27 (8 months ago)
Author:
Stefan-Saveski <stefansaveski19@…>
Branches:
master
Children:
ba52069
Parents:
298f9b3
Message:

Added dual language feature.

Location:
src/app
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • src/app/layout.tsx

    r298f9b3 r50f2fb4  
    1 import type { Metadata } from "next";
     1"use client";
    22import { Geist, Geist_Mono } from "next/font/google";
    33import "./globals.css";
    44import '@fortawesome/fontawesome-svg-core/styles.css';
    55import { config } from '@fortawesome/fontawesome-svg-core';
     6import { I18nextProvider } from 'react-i18next';
     7import i18n from '../i18n';
    68
    7 // Prevent FontAwesome from adding CSS automatically
    89config.autoAddCss = false;
    910
     
    1819});
    1920
    20 export const metadata: Metadata = {
    21   title: "IKnow - Student Portal",
    22   description: "Student portal for Ss. Cyril and Methodius University",
    23 };
    24 
    2521export default function RootLayout({
    2622  children,
     
    3026  return (
    3127    <html lang="en">
    32       <body
    33         className={`${geistSans.variable} ${geistMono.variable} antialiased`}
    34       >
    35         <div className="mx-auto max-w-6xl px-4">{children}</div>
     28      <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
     29        <I18nextProvider i18n={i18n}>
     30          <div className="mx-auto max-w-6xl px-4">{children}</div>
     31        </I18nextProvider>
    3632      </body>
    3733    </html>
  • src/app/page.tsx

    r298f9b3 r50f2fb4  
    1212import { useState } from 'react';
    1313import { login } from '@/lib/auth';
     14import { useTranslation } from 'react-i18next';
     15import LanguageSwitcher from '@/components/LanguageSwitcher';
    1416
    1517export default function LoginPage() {
     18  const { t } = useTranslation();
    1619  const [showPassword, setShowPassword] = useState(false);
    1720  const [isSubmitting, setIsSubmitting] = useState(false);
     
    5457              <div className="w-full flex flex-col gap-4">
    5558                <div className="bg-yellow-100 border border-yellow-200 rounded-xl shadow-sm p-5">
    56                   <div className="text-sm font-bold text-gray-900 mb-2">Login for Student</div>
     59                  <div className="text-sm font-bold text-gray-900 mb-2">{t('login_student_title')}</div>
    5760                  <div className="text-xs text-gray-800 whitespace-pre-wrap font-mono">
    58                     {`"email":"boris@example.com",
    59 "password":"your-password-here",`}
     61                    {t('login_student_example')}
    6062                  </div>
    6163                  <div className="mt-3 text-xs text-gray-700">
    62                     may take up to 30 seconds afte long inactivity
     64                    {t('login_note_slow')}
    6365                  </div>
    6466                </div>
    6567
    6668                <div className="bg-yellow-100 border border-yellow-200 rounded-xl shadow-sm p-5">
    67                   <div className="text-sm font-bold text-gray-900 mb-2">Login for Professor</div>
     69                  <div className="text-sm font-bold text-gray-900 mb-2">{t('login_professor_title')}</div>
    6870                  <div className="text-xs text-gray-800 whitespace-pre-wrap font-mono">
    69                     {`"email":"andonov@gmail.com",
    70 "password":"123lol456",`}
     71                    {t('login_professor_example')}
    7172                  </div>
    7273                </div>
     
    8485                </div>
    8586              </div>
    86               <h1 className="text-2xl font-bold mb-2">Добредојде</h1>
     87              <h1 className="text-2xl font-bold mb-2">{t('login_welcome')}</h1>
    8788              <p className="text-blue-100 text-sm">
    88                 Најавете се во вашиот IKnow акаунт
     89                {t('login_subtitle')}
    8990              </p>
    9091            </div>
     
    9293            {/* Form */}
    9394            <div className="p-8">
     95              {/* Language Switcher above email field, centered */}
     96              <div className="flex justify-center mb-6">
     97                <LanguageSwitcher />
     98              </div>
    9499              <form onSubmit={handleSubmit} className="space-y-6">
    95100                {/* Email Field */}
    96101                <div>
    97102                  <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
    98                     Внесете е-пошта
     103                    {t('login_email_label')}
    99104                  </label>
    100105                  <div className="relative">
     
    110115                      onChange={handleInputChange}
    111116                      className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-colors"
    112                       placeholder="Внесете е-пошта"
     117                      placeholder={t('login_email_placeholder')}
    113118                    />
    114119                  </div>
     
    118123                <div>
    119124                  <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
    120                     Лозинка
     125                    {t('login_password_label')}
    121126                  </label>
    122127                  <div className="relative">
     
    132137                      onChange={handleInputChange}
    133138                      className="w-full pl-10 pr-12 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-colors"
    134                       placeholder="Внесете лозинка"
     139                      placeholder={t('login_password_placeholder')}
    135140                    />
    136141                    <button
     
    157162                    />
    158163                    <label htmlFor="remember" className="ml-2 block text-sm text-gray-700">
    159                       Запомни ме
     164                      {t('login_remember_me')}
    160165                    </label>
    161166                  </div>
     
    164169                    className="text-sm text-primary hover:text-blue-700 font-medium transition-colors"
    165170                  >
    166                     Заборавена лозинка?
     171                    {t('login_forgot_password')}
    167172                  </button>
    168173                </div>
     
    170175                {errorMessage && (
    171176                  <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
    172                     {errorMessage}
     177                    {t(errorMessage) || errorMessage}
    173178                  </div>
    174179                )}
     
    181186                >
    182187                  <FontAwesomeIcon icon={faSignInAlt} className="h-4 w-4" />
    183                   {isSubmitting ? 'Најави се...' : 'Најави се'}
     188                  {isSubmitting ? t('login_button_loading') : t('login_button')}
    184189                </button>
    185190              </form>
     
    189194            <div className="bg-gray-50 px-8 py-4 border-t border-gray-100">
    190195              <p className="text-center text-sm text-gray-600">
    191                 Немате акаунт?{' '}
     196                {t('login_no_account')}{' '}
    192197                <button className="text-primary hover:text-blue-700 font-medium transition-colors">
    193                   Контактирајте ја администрацијата
     198                  {t('login_contact_admin')}
    194199                </button>
    195200              </p>
     
    202207                    <FontAwesomeIcon icon={faGraduationCap} className="text-primary" />
    203208                    <span className="text-sm text-gray-600">
    204                       Студентски информационен систем
     209                      {t('login_footer')}
    205210                    </span>
    206211                  </div>
  • src/app/professor/profile/page.tsx

    r298f9b3 r50f2fb4  
    1818import { getAccessToken } from "@/lib/auth";
    1919import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
     20import { useTranslation } from 'react-i18next';
    2021
    2122type PersonalInfo = {
     
    9495}
    9596
    96 const InfoRow = ({ label, value, icon }: InfoRowProps) => (
    97   <div className="flex justify-between items-center py-3 border-b border-gray-100 last:border-b-0">
    98     <div className="flex items-center gap-2 text-gray-600 font-medium">
    99       {icon && <FontAwesomeIcon icon={icon} className="w-4 h-4" />}
    100       <span>{label}:</span>
     97const InfoRow = ({ label, value, icon }: InfoRowProps) => {
     98  const { t } = useTranslation();
     99  return (
     100    <div className="flex justify-between items-center py-3 border-b border-gray-100 last:border-b-0">
     101      <div className="flex items-center gap-2 text-gray-600 font-medium">
     102        {icon && <FontAwesomeIcon icon={icon} className="w-4 h-4" />}
     103        <span>{t(label)}:</span>
     104      </div>
     105      <div className="text-gray-900 font-semibold text-right max-w-xs break-words">
     106        {value || t('n_a')}
     107      </div>
    101108    </div>
    102     <div className="text-gray-900 font-semibold text-right max-w-xs break-words">
    103       {value || "N/A"}
    104     </div>
    105   </div>
    106 );
     109  );
     110};
    107111
    108112interface SectionProps {
     
    112116}
    113117
    114 const Section = ({ title, icon, children }: SectionProps) => (
    115   <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    116     <div className="bg-primary text-white px-6 py-4">
    117       <div className="flex items-center gap-3">
    118         <FontAwesomeIcon icon={icon} className="text-xl" />
    119         <h2 className="text-xl font-bold">{title}</h2>
    120       </div>
     118const Section = ({ title, icon, children }: SectionProps) => {
     119  const { t } = useTranslation();
     120  return (
     121    <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
     122      <div className="bg-primary text-white px-6 py-4">
     123        <div className="flex items-center gap-3">
     124          <FontAwesomeIcon icon={icon} className="text-xl" />
     125          <h2 className="text-xl font-bold">{t(title)}</h2>
     126        </div>
     127      </div>
     128      <div className="p-6">{children}</div>
    121129    </div>
    122     <div className="p-6">{children}</div>
    123   </div>
    124 );
     130  );
     131};
    125132
    126133export default function ProfessorProfilePage() {
     
    128135  const [isLoading, setIsLoading] = useState(true);
    129136  const [errorMessage, setErrorMessage] = useState<string | null>(null);
     137  const { t } = useTranslation();
    130138
    131139  useEffect(() => {
     
    177185    return (
    178186      <div className="min-h-screen pb-8">
    179         <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">Loading...</div>
     187        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">{t('loading')}</div>
    180188      </div>
    181189    );
     
    186194      <div className="min-h-screen pb-8">
    187195        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
    188           {errorMessage ?? "Failed to load profile."}
     196          {errorMessage ?? t('failed_to_load_profile')}
    189197        </div>
    190198      </div>
     
    210218            </h1>
    211219            <div className="text-lg opacity-90">
    212               Индекс: {personalInfo.index} | ЕМБГ: {personalInfo.embg}
     220              {t('index')}: {personalInfo.index} | {t('embg')}: {personalInfo.embg}
    213221            </div>
    214222          </div>
     
    219227      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
    220228        {/* Personal Information */}
    221         <Section title="Лични податоци" icon={faIdCard}>
    222           <InfoRow label="Име" value={personalInfo.firstName} />
    223           <InfoRow label="Средно име" value={personalInfo.middleName} />
    224           <InfoRow label="Презиме" value={personalInfo.lastName} />
    225           <InfoRow label="Моминско презиме" value={personalInfo.maidenName} />
    226           <InfoRow label="Датум на раѓање" value={personalInfo.dateOfBirth} icon={faCalendarAlt} />
     229        <Section title="personal_info" icon={faIdCard}>
     230          <InfoRow label="first_name" value={personalInfo.firstName} />
     231          <InfoRow label="middle_name" value={personalInfo.middleName} />
     232          <InfoRow label="last_name" value={personalInfo.lastName} />
     233          <InfoRow label="maiden_name" value={personalInfo.maidenName} />
     234          <InfoRow label="date_of_birth" value={personalInfo.dateOfBirth} icon={faCalendarAlt} />
    227235          <InfoRow
    228             label="Пол"
     236            label="gender"
    229237            value={personalInfo.gender}
    230             icon={personalInfo.gender === "машки" ? faMars : faVenus}
     238            icon={personalInfo.gender === t('male') ? faMars : faVenus}
    231239          />
    232           <InfoRow label="Националност" value={personalInfo.nationality} icon={faFlag} />
    233           <InfoRow label="Државјанство" value={personalInfo.citizenship} />
    234           <InfoRow label="Стипендија" value={personalInfo.scholarship} />
    235           <InfoRow label="Тековен план" value={personalInfo.currentPlan} />
    236           <InfoRow label="Бр. во матична книга" value={personalInfo.registryNumber} />
    237           <InfoRow label="Група на студирање" value={personalInfo.studyGroup} />
     240          <InfoRow label="nationality" value={personalInfo.nationality} icon={faFlag} />
     241          <InfoRow label="citizenship" value={personalInfo.citizenship} />
     242          <InfoRow label="scholarship" value={personalInfo.scholarship} />
     243          <InfoRow label="current_plan" value={personalInfo.currentPlan} />
     244          <InfoRow label="registry_number" value={personalInfo.registryNumber} />
     245          <InfoRow label="study_group" value={personalInfo.studyGroup} />
    238246          {personalInfo.notes && (
    239247            <div className="mt-4 p-4 bg-blue-50 rounded-lg">
    240               <div className="text-sm font-medium text-blue-800 mb-1">Забелешка:</div>
     248              <div className="text-sm font-medium text-blue-800 mb-1">{t('note')}:</div>
    241249              <div className="text-sm text-blue-700">{personalInfo.notes}</div>
    242250            </div>
     
    245253
    246254        {/* Birth Information */}
    247         <Section title="Податоци за раѓање" icon={faMapMarkerAlt}>
    248           <InfoRow label="Место на раѓање" value={birthInfo.placeOfBirth} />
    249           <InfoRow label="Општина на раѓање" value={birthInfo.municipalityOfBirth} />
    250           <InfoRow label="Земја" value={birthInfo.country} />
     255        <Section title="birth_info" icon={faMapMarkerAlt}>
     256          <InfoRow label="place_of_birth" value={birthInfo.placeOfBirth} />
     257          <InfoRow label="municipality_of_birth" value={birthInfo.municipalityOfBirth} />
     258          <InfoRow label="country" value={birthInfo.country} />
    251259        </Section>
    252260
    253261        {/* Contact Information */}
    254262        <div className="lg:col-span-2">
    255           <Section title="Контакт" icon={faAddressCard}>
     263          <Section title="contact" icon={faAddressCard}>
    256264            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
    257265              <div>
    258                 <InfoRow label="Место на живеење" value={contact.placeOfResidence} icon={faMapMarkerAlt} />
    259                 <InfoRow label="Општина на живеење" value={contact.municipalityOfResidence} />
    260                 <InfoRow label="Држава" value={contact.country} />
    261                 <InfoRow label="Адреса" value={contact.address} />
    262                 <InfoRow label="Адреса на престој" value={contact.temporaryAddress} />
     266                <InfoRow label="place_of_residence" value={contact.placeOfResidence} icon={faMapMarkerAlt} />
     267                <InfoRow label="municipality_of_residence" value={contact.municipalityOfResidence} />
     268                <InfoRow label="country" value={contact.country} />
     269                <InfoRow label="address" value={contact.address} />
     270                <InfoRow label="temporary_address" value={contact.temporaryAddress} />
    263271              </div>
    264272              <div>
    265                 <InfoRow label="Телефон" value={contact.phone} icon={faPhone} />
    266                 <InfoRow label="Моб. телефон" value={contact.mobilePhone} icon={faPhone} />
    267                 <InfoRow label="Број на пасош" value={contact.passportNumber} icon={faPassport} />
    268                 <InfoRow label="Датум на истекување на пасошот" value={contact.passportExpiryDate} />
    269                 <InfoRow label="Е-пошта" value={contact.email} icon={faEnvelope} />
    270                 <InfoRow label="Microsoft email" value={contact.microsoftEmail} icon={faEnvelope} />
     273                <InfoRow label="phone" value={contact.phone} icon={faPhone} />
     274                <InfoRow label="mobile_phone" value={contact.mobilePhone} icon={faPhone} />
     275                <InfoRow label="passport_number" value={contact.passportNumber} icon={faPassport} />
     276                <InfoRow label="passport_expiry_date" value={contact.passportExpiryDate} />
     277                <InfoRow label="email" value={contact.email} icon={faEnvelope} />
     278                <InfoRow label="microsoft_email" value={contact.microsoftEmail} icon={faEnvelope} />
    271279              </div>
    272280            </div>
  • src/app/professor/students/page.tsx

    r298f9b3 r50f2fb4  
    22
    33import { useEffect, useMemo, useState } from "react";
     4import { useTranslation } from 'react-i18next';
    45
    56type UsersBySubject = {
     
    3637
    3738export default function ProfessorStudentsPage() {
     39  const { t } = useTranslation();
    3840  const [subjects, setSubjects] = useState<SubjectsAndUsers[]>([]);
    3941  const [loading, setLoading] = useState(true);
     
    129131    <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
    130132      <div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
     133
    131134        <div>
    132           <h1 className="text-2xl font-semibold text-gray-900">Professor • Students</h1>
     135          <h1 className="text-2xl font-semibold text-gray-900">{t('prof_students_title')}</h1>
    133136          <p className="text-gray-700 mt-1">
    134             Data comes from <span className="font-mono">/api/prof/students</span> (demo store).
     137            {t('prof_students_data_note')} <span className="font-mono">/api/prof/students</span> (demo store).
    135138          </p>
    136139        </div>
     
    138141        <div className="flex flex-col sm:flex-row gap-3">
    139142          <div className="flex flex-col">
    140             <label className="text-sm text-gray-700">Filter by subject</label>
     143            <label className="text-sm text-gray-700">{t('filter_by_subject')}</label>
    141144            <select
    142145              className="border border-gray-300 rounded-lg px-3 py-2"
     
    144147              onChange={(e) => setSubjectFilter(e.target.value)}
    145148            >
    146               <option value="all">All subjects</option>
     149              <option value="all">{t('all_subjects')}</option>
    147150              {subjects
    148151                .filter((s) => typeof s.Id === "number")
    149152                .map((s) => (
    150153                  <option key={String(s.Id)} value={String(s.Id)}>
    151                     {s.Name ?? `Subject ${s.Id}`}
     154                    {s.Name ?? `${t('subject')} ${s.Id}`}
    152155                  </option>
    153156                ))}
     
    156159
    157160          <div className="flex flex-col">
    158             <label className="text-sm text-gray-700">Find student by index / ID</label>
     161            <label className="text-sm text-gray-700">{t('find_student_by_id')}</label>
    159162            <input
    160163              className="border border-gray-300 rounded-lg px-3 py-2"
    161               placeholder="e.g. 20001"
     164              placeholder={t('student_id_placeholder')}
    162165              value={studentIdQuery}
    163166              onChange={(e) => setStudentIdQuery(e.target.value)}
     
    177180          <thead className="bg-gray-50">
    178181            <tr>
    179               <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">Student</th>
    180               <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">ID</th>
    181               <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">Subject</th>
    182               <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">Grade</th>
    183               <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">Actions</th>
     182              <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">{t('student')}</th>
     183              <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">{t('id')}</th>
     184              <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">{t('subject')}</th>
     185              <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">{t('grade')}</th>
     186              <th className="text-left text-sm font-semibold text-gray-700 px-4 py-3 border-b">{t('actions')}</th>
    184187            </tr>
    185188          </thead>
     
    188191              <tr>
    189192                <td className="px-4 py-4 text-gray-700" colSpan={5}>
    190                   Loading...
     193                  {t('loading')}
    191194                </td>
    192195              </tr>
     
    194197              <tr>
    195198                <td className="px-4 py-4 text-gray-700" colSpan={5}>
    196                   No students found.
     199                  {t('no_students_found')}
    197200                </td>
    198201              </tr>
     
    227230                        </select>
    228231                        <span className="text-sm text-gray-600">
    229                           Current: {r.grade > 0 ? r.grade : "—"}
     232                          {t('current')}: {r.grade > 0 ? r.grade : t('none')}
    230233                        </span>
    231234                      </div>
     
    245248                          }}
    246249                        >
    247                           Add grade
     250                          {t('add_grade')}
    248251                        </button>
    249252
     
    260263                          }}
    261264                        >
    262                           Edit grade
     265                          {t('edit_grade')}
    263266                        </button>
    264267
     
    275278                          }}
    276279                        >
    277                           Remove grade
     280                          {t('remove_grade')}
    278281                        </button>
    279282                      </div>
  • src/app/students/applications/page.tsx

    r298f9b3 r50f2fb4  
    1414} from '@fortawesome/free-solid-svg-icons';
    1515import { useState } from 'react';
     16import { useTranslation } from 'react-i18next';
    1617import applicationsData from '@/data/applications.json';
    1718
     
    2829
    2930const 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">
     31  const { t } = useTranslation();
     32  if (completed === t('yes', 'Да')) {
     33    return (
     34      <span className="inline-flex items-center justify-center w-6 h-6 bg-green-100 text-green-600 rounded-full" title={t('completed')}>
    3335        <FontAwesomeIcon icon={faCheckCircle} className="w-4 h-4" />
    3436      </span>
     
    3638  } else {
    3739    return (
    38       <span className="inline-flex items-center justify-center w-6 h-6 bg-red-100 text-red-600 rounded-full">
     40      <span className="inline-flex items-center justify-center w-6 h-6 bg-red-100 text-red-600 rounded-full" title={t('not_completed')}>
    3941        <FontAwesomeIcon icon={faTimesCircle} className="w-4 h-4" />
    4042      </span>
     
    6466
    6567export default function ApplicationsPage() {
     68  const { t } = useTranslation();
    6669  const [selectedSession, setSelectedSession] = useState(applicationsData.currentSession.id);
    6770  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
    68  
     71
    6972  const currentSessionData = applicationsData.examSessions.find(s => s.id === selectedSession) || applicationsData.currentSession;
    7073  const { applications } = applicationsData;
     
    7982          </div>
    8083          <div>
    81             <h1 className="text-3xl font-bold mb-2">Пријави</h1>
     84            <h1 className="text-3xl font-bold mb-2">{t('applications_header', 'Пријави')}</h1>
    8285            <p className="text-lg opacity-90">
    83               Електронски пријави за испити
     86              {t('applications_subheader', 'Електронски пријави за испити')}
    8487            </p>
    8588          </div>
     
    9295          <h2 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
    9396            <FontAwesomeIcon icon={faCalendarAlt} className="text-primary" />
    94             Избери испитна сесија:
     97            {t('select_exam_session', 'Избери испитна сесија:')}
    9598          </h2>
    96          
     99
    97100          <div className="relative">
    98101            <button
     
    102105              <div className="flex items-center justify-between">
    103106                <span className="text-sm font-medium text-primary">
    104                   {currentSessionData.name}
     107                  {t(currentSessionData.id, currentSessionData.name)}
    105108                </span>
    106                 <FontAwesomeIcon 
    107                   icon={faChevronDown} 
     109                <FontAwesomeIcon
     110                  icon={faChevronDown}
    108111                  className={`w-4 h-4 text-gray-400 transition-transform ml-4 ${isDropdownOpen ? 'rotate-180' : ''}`}
    109112                />
    110113              </div>
    111114            </button>
    112            
     115
    113116            {isDropdownOpen && (
    114117              <div className="absolute z-10 right-0 mt-1 w-80 bg-white border border-gray-200 rounded-lg shadow-lg">
     
    122125                    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"
    123126                  >
    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>
     127                    <div className="font-medium text-gray-900">{t(session.id, session.name)}</div>
     128                    <div className="text-xs text-gray-500">{session.year} - {t(session.semester, session.semester)} - {t(session.session, session.session)}</div>
    126129                  </button>
    127130                ))}
     
    135138      <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    136139        <div className="bg-primary text-white px-6 py-4">
    137           <h2 className="text-xl font-bold">Пријавени испити</h2>
    138         </div>
    139        
     140          <h2 className="text-xl font-bold">{t('registered_exams', 'Пријавени испити')}</h2>
     141        </div>
     142
    140143        <div className="overflow-x-auto">
    141144          <table className="w-full">
     
    143146              <tr>
    144147                <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>
     148                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('serial_number', 'Сериски број')}</th>
     149                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('code', 'Код')}</th>
     150                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('subject', 'Предмет')}</th>
     151                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('completed', 'Завршена')}</th>
     152                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('fee', 'Таксени')}</th>
     153                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('date', 'Датум')}</th>
     154                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('instructor', 'Наставник')}</th>
     155                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('decade', 'Декада')}</th>
    153156              </tr>
    154157            </thead>
     
    166169                    <div className="flex items-center gap-2">
    167170                      <FontAwesomeIcon icon={faFileText} className="w-4 h-4 text-primary" />
    168                       {application.subject}
     171                      {t(application.subject, application.subject)}
    169172                    </div>
    170173                  </TableCell>
     
    179182                    <div className="flex items-center gap-2">
    180183                      <FontAwesomeIcon icon={faUser} className="w-4 h-4 text-gray-400" />
    181                       <span className="font-medium text-gray-900">{application.instructor}</span>
     184                      <span className="font-medium text-gray-900">{t(application.instructor, application.instructor)}</span>
    182185                    </div>
    183186                  </TableCell>
     
    194197            <FontAwesomeIcon icon={faInfoCircle} className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
    195198            <div>
    196               <h3 className="font-medium text-blue-900 mb-1">Важна забелешка</h3>
     199              <h3 className="font-medium text-blue-900 mb-1">{t('important_note', 'Важна забелешка')}</h3>
    197200              <p className="text-sm text-blue-800 leading-relaxed">
    198                 {applicationsData.note}
     201                {t('applications_important_note_text')}
    199202              </p>
    200203            </div>
     
    211214            </div>
    212215            <div>
    213               <h3 className="text-lg font-bold text-gray-900">Пријавени испити</h3>
     216              <h3 className="text-lg font-bold text-gray-900">{t('registered_exams', 'Пријавени испити')}</h3>
    214217              <p className="text-2xl font-bold text-blue-600">
    215218                {applications.length}
     
    225228            </div>
    226229            <div>
    227               <h3 className="text-lg font-bold text-gray-900">Завршени</h3>
     230              <h3 className="text-lg font-bold text-gray-900">{t('completed', 'Завршени')}</h3>
    228231              <p className="text-2xl font-bold text-green-600">
    229                 {applications.filter(app => app.completed === "Да").length}
     232                {applications.filter(app => app.completed === t('yes', 'Да')).length}
    230233              </p>
    231234            </div>
     
    239242            </div>
    240243            <div>
    241               <h3 className="text-lg font-bold text-gray-900">Вкупна такса</h3>
     244              <h3 className="text-lg font-bold text-gray-900">{t('total_fee', 'Вкупна такса')}</h3>
    242245              <p className="text-2xl font-bold text-yellow-600">
    243246                {applications.reduce((sum, app) => sum + parseFloat(app.fee.replace(',', '.')), 0).toFixed(2)}
  • src/app/students/documents/page.tsx

    r298f9b3 r50f2fb4  
    1515import { useState } from 'react';
    1616import documentsData from '@/data/documents.json';
     17import { useTranslation } from 'react-i18next';
    1718
    1819interface TableCellProps {
     
    2829
    2930const StatusBadge = ({ status }: { status: string }) => {
    30   if (status === "approved") {
     31  const { t } = useTranslation();
     32  if (status === t('approved', 'Одобрено')) {
    3133    return (
    3234      <span className="inline-flex items-center justify-center w-8 h-8 bg-green-100 text-green-600 rounded-full">
     
    4345
    4446const PriceBadge = ({ price }: { price: number }) => {
     47  const { t } = useTranslation();
    4548  if (price === 0) {
    46     return <span className="font-medium text-green-600">0,00</span>;
     49    return <span className="font-medium text-green-600">{t('free', '0,00')}</span>;
    4750  }
    4851  return <span className="font-medium text-blue-600">{price.toFixed(2)}</span>;
     
    5558  const [currentPage, setCurrentPage] = useState(1);
    5659  const [recordsPerPage, setRecordsPerPage] = useState(15);
     60  const { t } = useTranslation();
    5761
    5862  const selectedDocument = documentsData.documentTypes.find(d => d.id === selectedDocumentType);
     
    7377          </div>
    7478          <div>
    75             <h1 className="text-3xl font-bold mb-2">Документи</h1>
     79            <h1 className="text-3xl font-bold mb-2">{t('documents')}</h1>
    7680            <p className="text-lg opacity-90">
    77               Барање и преглед на документи
     81              {t('documents_overview', 'Преглед на вашите документи и нивниот статус.')}
    7882            </p>
    7983          </div>
     
    8589        <h2 className="text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2">
    8690          <FontAwesomeIcon icon={faFileText} className="text-primary" />
    87           Ново барање за документ
     91          {t('new_document_request', 'Ново барање за документ')}
    8892        </h2>
    8993       
     
    9296          <div>
    9397            <label className="block text-sm font-medium text-gray-700 mb-2">
    94               Изберете документ:
     98              {t('select_document', 'Избери документ')}:
    9599            </label>
    96100            <div className="relative">
     
    101105                <div className="flex items-center justify-between">
    102106                  <span className="text-sm text-gray-900 truncate">
    103                     {selectedDocument?.name || "Изберете документ"}
     107                    {selectedDocument ? t(selectedDocument.id) : t('select_document', 'Избери документ')}
    104108                  </span>
    105109                  <FontAwesomeIcon
     
    121125                      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"
    122126                    >
    123                       <div className="font-medium text-gray-900">{docType.name}</div>
     127                      <div className="font-medium text-gray-900">{t(docType.id)}</div>
    124128                      {docType.price > 0 && (
    125                         <div className="text-xs text-blue-600 mt-1">Цена: {docType.price} мкд</div>
     129                        <div className="text-xs text-blue-600 mt-1">{t('price', 'Цена')}: {docType.price} мкд</div>
    126130                      )}
    127131                    </button>
     
    135139          <div>
    136140            <label className="block text-sm font-medium text-gray-700 mb-2">
    137               Коментар:
     141              {t('comment', 'Коментар')}:
    138142            </label>
    139143            <textarea
     
    142146              rows={4}
    143147              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="Додајте коментар..."
     148              placeholder={t('add_comment', 'Додај коментар')}
    145149            />
    146150          </div>
     
    153157          >
    154158            <FontAwesomeIcon icon={faFileText} className="w-4 h-4" />
    155             Внеси
     159            {t('submit', 'Поднеси')}
    156160          </button>
    157161        </div>
     
    161165      <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    162166        <div className="bg-primary text-white px-6 py-4">
    163           <h2 className="text-xl font-bold">Мои документи</h2>
     167          <h2 className="text-xl font-bold">{t('my_documents', 'Мои документи')}</h2>
    164168        </div>
    165169       
     
    169173              <tr>
    170174                <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>
     175                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('archive', 'Архива')}</th>
     176                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('date', 'Датум')}</th>
     177                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('request', 'Барање')}</th>
     178                <th className="px-4 py-4 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">{t('price', 'Цена')}</th>
     179                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('paid', 'Платено')}</th>
     180                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('document', 'Документ')}</th>
     181                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('pay_online', 'Плати онлајн')}</th>
     182                <th className="px-4 py-4 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">{t('status', 'Статус')}</th>
     183                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('comment', 'Коментар')}</th>
    180184              </tr>
    181185            </thead>
     
    187191                  <TableCell className="text-gray-600">{document.date}</TableCell>
    188192                  <TableCell className="font-medium text-gray-900 max-w-xs">
    189                     {document.request}
     193                    {t(document.request)}
    190194                  </TableCell>
    191195                  <TableCell className="text-right">
     
    206210                      <FontAwesomeIcon icon={faCheckCircle} className="w-5 h-5 text-green-600" />
    207211                    ) : (
    208                       <span className="text-gray-400"></span>
     212                      <span className="text-gray-400">{t('none', '—')}</span>
    209213                    )}
    210214                  </TableCell>
     
    214218                  <TableCell>
    215219                    {document.comment || (
    216                       <span className="text-gray-400"></span>
     220                      <span className="text-gray-400">{t('none', '—')}</span>
    217221                    )}
    218222                  </TableCell>
     
    227231          <div className="flex items-center gap-4 text-sm text-gray-700">
    228232            <div className="flex items-center gap-2">
    229               <span>Прикажи редови:</span>
     233              <span>{t('show_rows', 'Прикажи редови')}:</span>
    230234              <select
    231235                value={recordsPerPage}
     
    239243            </div>
    240244            <div>
    241               Страна <input
     245              {t('page', 'Страница')} <input
    242246                type="number"
    243247                min="1"
     
    246250                onChange={(e) => setCurrentPage(Number(e.target.value))}
    247251                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}
     252              /> {t('of', 'од')} {totalPages}
    249253            </div>
    250254          </div>
     
    266270            </button>
    267271            <span className="px-4 py-2 bg-primary text-white rounded text-sm font-medium">
    268               Прва
     272              {t('first', 'Прва')}
    269273            </span>
    270274            <button
     
    283287            </button>
    284288            <span className="ml-4 text-sm text-gray-700">
    285               Последна
     289              {t('last', 'Последна')}
    286290            </span>
    287291          </div>
    288292
    289293          <div className="text-sm text-gray-700">
    290             Вкупно: {documents.length}
     294            {t('total', 'Вкупно')}: {documents.length}
    291295          </div>
    292296        </div>
     
    297301            <FontAwesomeIcon icon={faMoneyBillWave} className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
    298302            <p className="text-sm text-blue-800 leading-relaxed">
    299               {paymentInfo}
     303              {t('documents_payment_info', paymentInfo)}
    300304            </p>
    301305          </div>
     
    311315            </div>
    312316            <div>
    313               <h3 className="text-lg font-bold text-gray-900">Вкупно документи</h3>
     317              <h3 className="text-lg font-bold text-gray-900">{t('total_documents', 'Вкупно документи')}</h3>
    314318              <p className="text-2xl font-bold text-blue-600">
    315319                {documents.length}
     
    325329            </div>
    326330            <div>
    327               <h3 className="text-lg font-bold text-gray-900">Одобрени</h3>
     331              <h3 className="text-lg font-bold text-gray-900">{t('approved', 'Одобрени')}</h3>
    328332              <p className="text-2xl font-bold text-green-600">
    329333                {documents.filter(doc => doc.status === "approved").length}
     
    339343            </div>
    340344            <div>
    341               <h3 className="text-lg font-bold text-gray-900">Вкупна цена</h3>
     345              <h3 className="text-lg font-bold text-gray-900">{t('total_price', 'Вкупна цена')}</h3>
    342346              <p className="text-2xl font-bold text-yellow-600">
    343                 {documents.reduce((sum, doc) => sum + doc.price, 0).toFixed(2)} мкд
     347                {documents.reduce((sum, doc) => sum + doc.price, 0).toFixed(2)} {t('mkd', 'мкд')}
    344348              </p>
    345349            </div>
  • src/app/students/profile/page.tsx

    r298f9b3 r50f2fb4  
    2020import { getAccessToken } from '@/lib/auth';
    2121import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
     22import { useTranslation } from 'react-i18next';
    2223
    2324type PersonalInfo = {
     
    9495}
    9596
    96 const InfoRow = ({ label, value, icon }: InfoRowProps) => (
    97   <div className="flex justify-between items-center py-3 border-b border-gray-100 last:border-b-0">
    98     <div className="flex items-center gap-2 text-gray-600 font-medium">
    99       {icon && <FontAwesomeIcon icon={icon} className="w-4 h-4" />}
    100       <span>{label}:</span>
     97const InfoRow = ({ label, value, icon }: InfoRowProps) => {
     98  const { t } = useTranslation();
     99  return (
     100    <div className="flex justify-between items-center py-3 border-b border-gray-100 last:border-b-0">
     101      <div className="flex items-center gap-2 text-gray-600 font-medium">
     102        {icon && <FontAwesomeIcon icon={icon} className="w-4 h-4" />}
     103        <span>{t(label)}:</span>
     104      </div>
     105      <div className="text-gray-900 font-semibold text-right max-w-xs break-words">
     106        {value || t('n_a')}
     107      </div>
    101108    </div>
    102     <div className="text-gray-900 font-semibold text-right max-w-xs break-words">
    103       {value || "N/A"}
    104     </div>
    105   </div>
    106 );
     109  );
     110};
    107111
    108112interface SectionProps {
     
    112116}
    113117
    114 const Section = ({ title, icon, children }: SectionProps) => (
    115   <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    116     <div className="bg-primary text-white px-6 py-4">
    117       <div className="flex items-center gap-3">
    118         <FontAwesomeIcon icon={icon} className="text-xl" />
    119         <h2 className="text-xl font-bold">{title}</h2>
     118const Section = ({ title, icon, children }: SectionProps) => {
     119  const { t } = useTranslation();
     120  return (
     121    <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
     122      <div className="bg-primary text-white px-6 py-4">
     123        <div className="flex items-center gap-3">
     124          <FontAwesomeIcon icon={icon} className="text-xl" />
     125          <h2 className="text-xl font-bold">{t(title)}</h2>
     126        </div>
     127      </div>
     128      <div className="p-6">
     129        {children}
    120130      </div>
    121131    </div>
    122     <div className="p-6">
    123       {children}
    124     </div>
    125   </div>
    126 );
     132  );
     133};
    127134
    128135export default function ProfilePage() {
     
    130137  const [isLoading, setIsLoading] = useState(true);
    131138  const [errorMessage, setErrorMessage] = useState<string | null>(null);
     139  const { t } = useTranslation();
    132140
    133141  useEffect(() => {
     
    180188      <div className="min-h-screen pb-8">
    181189        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
    182           Loading...
     190          {t('loading')}
    183191        </div>
    184192      </div>
     
    190198      <div className="min-h-screen pb-8">
    191199        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
    192           {errorMessage ?? 'Failed to load profile.'}
     200          {errorMessage ?? t('failed_to_load_profile')}
    193201        </div>
    194202      </div>
     
    214222            </h1>
    215223            <div className="text-lg opacity-90">
    216               Индекс: {personalInfo.index} | ЕМБГ: {personalInfo.embg}
     224              {t('index')}: {personalInfo.index} | {t('embg')}: {personalInfo.embg}
    217225            </div>
    218226            <div className="text-base opacity-80 mt-1">
     
    227235       
    228236        {/* Personal Information */}
    229         <Section title="Лични податоци" icon={faIdCard}>
    230           <InfoRow label="Име" value={personalInfo.firstName} />
    231           <InfoRow label="Средно име" value={personalInfo.middleName} />
    232           <InfoRow label="Презиме" value={personalInfo.lastName} />
    233           <InfoRow label="Моминско презиме" value={personalInfo.maidenName} />
     237        <Section title="personal_info" icon={faIdCard}>
     238          <InfoRow label="first_name" value={personalInfo.firstName} />
     239          <InfoRow label="middle_name" value={personalInfo.middleName} />
     240          <InfoRow label="last_name" value={personalInfo.lastName} />
     241          <InfoRow label="maiden_name" value={personalInfo.maidenName} />
    234242          <InfoRow
    235             label="Датум на раѓање"
     243            label="date_of_birth"
    236244            value={personalInfo.dateOfBirth}
    237245            icon={faCalendarAlt}
    238246          />
    239247          <InfoRow
    240             label="Пол"
     248            label="gender"
    241249            value={personalInfo.gender}
    242             icon={personalInfo.gender === 'машки' ? faMars : faVenus}
     250            icon={personalInfo.gender === t('male') ? faMars : faVenus}
    243251          />
    244252          <InfoRow
    245             label="Националност"
     253            label="nationality"
    246254            value={personalInfo.nationality}
    247255            icon={faFlag}
    248256          />
    249           <InfoRow label="Државјанство" value={personalInfo.citizenship} />
    250           <InfoRow label="Стипендија" value={personalInfo.scholarship} />
    251           <InfoRow label="Тековен план" value={personalInfo.currentPlan} />
    252           <InfoRow label="Бр. во матична книга" value={personalInfo.registryNumber} />
    253           <InfoRow label="Група на студирање" value={personalInfo.studyGroup} />
     257          <InfoRow label="citizenship" value={personalInfo.citizenship} />
     258          <InfoRow label="scholarship" value={personalInfo.scholarship} />
     259          <InfoRow label="current_plan" value={personalInfo.currentPlan} />
     260          <InfoRow label="registry_number" value={personalInfo.registryNumber} />
     261          <InfoRow label="study_group" value={personalInfo.studyGroup} />
    254262          {personalInfo.notes && (
    255263            <div className="mt-4 p-4 bg-blue-50 rounded-lg">
    256               <div className="text-sm font-medium text-blue-800 mb-1">Забелешка:</div>
     264              <div className="text-sm font-medium text-blue-800 mb-1">{t('note')}:</div>
    257265              <div className="text-sm text-blue-700">{personalInfo.notes}</div>
    258266            </div>
     
    261269
    262270        {/* Birth Information */}
    263         <Section title="Податоци за раѓање" icon={faMapMarkerAlt}>
    264           <InfoRow label="Место на раѓање" value={birthInfo.placeOfBirth} />
    265           <InfoRow label="Општина на раѓање" value={birthInfo.municipalityOfBirth} />
    266           <InfoRow label="Земја" value={birthInfo.country} />
     271        <Section title="birth_info" icon={faMapMarkerAlt}>
     272          <InfoRow label="place_of_birth" value={birthInfo.placeOfBirth} />
     273          <InfoRow label="municipality_of_birth" value={birthInfo.municipalityOfBirth} />
     274          <InfoRow label="country" value={birthInfo.country} />
    267275        </Section>
    268276
    269277        {/* Previous Education */}
    270         <Section title="Претходно образование" icon={faSchool}>
    271           <InfoRow label="Тип" value={previousEducation.type} />
    272           <InfoRow label="Професија" value={previousEducation.profession} />
    273           <InfoRow label="Просек" value={previousEducation.average} />
    274           <InfoRow label="Јазик" value={previousEducation.language} />
    275           <InfoRow label="Земја" value={previousEducation.country} />
    276           <InfoRow label="Претходен универзитет" value={previousEducation.previousUniversity} />
    277           <InfoRow label="Претходен факултет" value={previousEducation.previousFaculty} />
    278           <InfoRow label="Режим на претходни студии" value={previousEducation.previousStudyMode} />
     278        <Section title="previous_education" icon={faSchool}>
     279          <InfoRow label="type" value={previousEducation.type} />
     280          <InfoRow label="profession" value={previousEducation.profession} />
     281          <InfoRow label="average" value={previousEducation.average} />
     282          <InfoRow label="language" value={previousEducation.language} />
     283          <InfoRow label="country" value={previousEducation.country} />
     284          <InfoRow label="previous_university" value={previousEducation.previousUniversity} />
     285          <InfoRow label="previous_faculty" value={previousEducation.previousFaculty} />
     286          <InfoRow label="previous_study_mode" value={previousEducation.previousStudyMode} />
    279287        </Section>
    280288
    281289        {/* Enrollment Information */}
    282         <Section title="Податоци за упис" icon={faGraduationCap}>
    283           <InfoRow label="Година на упис" value={enrollmentInfo.enrollmentYear} />
    284           <InfoRow label="Статус" value={enrollmentInfo.status} />
    285           <InfoRow label="Циклус" value={enrollmentInfo.cycle} />
    286           <InfoRow label="Запишана програма" value={enrollmentInfo.program} />
    287           <InfoRow label="Квота на прв упис" value={enrollmentInfo.quota} />
    288           <InfoRow label="Бр. дипл. ср. образование" value={enrollmentInfo.secondaryEducationNumber} />
    289           <InfoRow label="Кред. пр. образование" value={enrollmentInfo.previousEducationCredits} />
     290        <Section title="enrollment_info" icon={faGraduationCap}>
     291          <InfoRow label="enrollment_year" value={enrollmentInfo.enrollmentYear} />
     292          <InfoRow label="status" value={enrollmentInfo.status} />
     293          <InfoRow label="cycle" value={enrollmentInfo.cycle} />
     294          <InfoRow label="program" value={enrollmentInfo.program} />
     295          <InfoRow label="quota" value={enrollmentInfo.quota} />
     296          <InfoRow label="secondary_education_number" value={enrollmentInfo.secondaryEducationNumber} />
     297          <InfoRow label="previous_education_credits" value={enrollmentInfo.previousEducationCredits} />
    290298        </Section>
    291299
    292300        {/* Contact Information */}
    293301        <div className="lg:col-span-2">
    294           <Section title="Контакт" icon={faAddressCard}>
     302          <Section title="contact" icon={faAddressCard}>
    295303            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
    296304              <div>
    297305                <InfoRow
    298                   label="Место на живеење"
     306                  label="place_of_residence"
    299307                  value={contact.placeOfResidence}
    300308                  icon={faMapMarkerAlt}
    301309                />
    302                 <InfoRow label="Општина на живеење" value={contact.municipalityOfResidence} />
    303                 <InfoRow label="Држава" value={contact.country} />
    304                 <InfoRow label="Адреса" value={contact.address} />
    305                 <InfoRow label="Адреса на престој" value={contact.temporaryAddress} />
     310                <InfoRow label="municipality_of_residence" value={contact.municipalityOfResidence} />
     311                <InfoRow label="country" value={contact.country} />
     312                <InfoRow label="address" value={contact.address} />
     313                <InfoRow label="temporary_address" value={contact.temporaryAddress} />
    306314              </div>
    307315              <div>
    308                 <InfoRow label="Телефон" value={contact.phone} icon={faPhone} />
    309                 <InfoRow label="Моб. телефон" value={contact.mobilePhone} icon={faPhone} />
    310                 <InfoRow label="Број на пасош" value={contact.passportNumber} icon={faPassport} />
    311                 <InfoRow label="Датум на истекување на пасошот" value={contact.passportExpiryDate} />
     316                <InfoRow label="phone" value={contact.phone} icon={faPhone} />
     317                <InfoRow label="mobile_phone" value={contact.mobilePhone} icon={faPhone} />
     318                <InfoRow label="passport_number" value={contact.passportNumber} icon={faPassport} />
     319                <InfoRow label="passport_expiry_date" value={contact.passportExpiryDate} />
    312320                <InfoRow
    313                   label="Е-пошта"
     321                  label="email"
    314322                  value={contact.email}
    315323                  icon={faEnvelope}
    316324                />
    317325                <InfoRow
    318                   label="Microsoft email"
     326                  label="microsoft_email"
    319327                  value={contact.microsoftEmail}
    320328                  icon={faEnvelope}
  • src/app/students/semesters/page.tsx

    r298f9b3 r50f2fb4  
    1414import { useEffect, useState } from 'react';
    1515import { getAccessToken } from '@/lib/auth';
     16import { useTranslation } from 'react-i18next';
    1617
    1718type Semester = {
     
    5455
    5556const StatusBadge = ({ status }: { status: string }) => {
     57  const { t } = useTranslation();
    5658  const baseClasses = "px-2 py-1 rounded-full text-xs font-medium";
    57  
    58   if (status === "валиден") {
     59  if (status === t('valid')) {
    5960    return (
    6061      <span className={`${baseClasses} bg-green-100 text-green-800 flex items-center gap-1`}>
    6162        <FontAwesomeIcon icon={faCheckCircle} className="w-3 h-3" />
    62         {status}
     63        {t('valid')}
    6364      </span>
    6465    );
    6566  }
    66  
    6767  return (
    6868    <span className={`${baseClasses} bg-gray-100 text-gray-800`}>
    69       {status}
     69      {t(status) || status}
    7070    </span>
    7171  );
     
    7373
    7474const YesNoBadge = ({ value }: { value: string }) => {
    75   if (value === "Да") {
     75  const { t } = useTranslation();
     76  if (value === t('yes')) {
    7677    return (
    7778      <span className="inline-flex items-center justify-center w-6 h-6 bg-green-100 text-green-600 rounded-full">
     
    7980      </span>
    8081    );
    81   } else if (value === "Не") {
     82  } else if (value === t('no')) {
    8283    return (
    8384      <span className="inline-flex items-center justify-center w-6 h-6 bg-red-100 text-red-600 rounded-full">
     
    112113  const [isLoading, setIsLoading] = useState(true);
    113114  const [errorMessage, setErrorMessage] = useState<string | null>(null);
     115  const { t } = useTranslation();
    114116
    115117  useEffect(() => {
     
    162164      <div className="min-h-screen pb-8">
    163165        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
    164           Loading...
     166          {t('loading')}
    165167        </div>
    166168      </div>
     
    187189          </div>
    188190          <div>
    189             <h1 className="text-3xl font-bold mb-2">Семестри</h1>
     191            <h1 className="text-3xl font-bold mb-2">{t('semesters')}</h1>
    190192            <p className="text-lg opacity-90">
    191               Преглед на сите запишани семестри и нивниот статус
     193              {t('semesters_overview')}
    192194            </p>
    193195          </div>
     
    198200      <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    199201        <div className="bg-primary text-white px-6 py-4">
    200           <h2 className="text-xl font-bold">Листа на семестри</h2>
     202          <h2 className="text-xl font-bold">{t('semesters_list')}</h2>
    201203        </div>
    202204       
     
    206208              <tr>
    207209                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">#</th>
    208                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Семестар</th>
    209                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Насока</th>
    210                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Квота</th>
    211                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Забелешка</th>
    212                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Студ.Ком.</th>
    213                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Сума</th>
    214                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Платено</th>
    215                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">УКИМ</th>
    216                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Креирано на</th>
    217                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Датум промена</th>
    218                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ц.Кр</th>
    219                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
    220                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Док.</th>
    221                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Док1.</th>
    222                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Вериф.</th>
    223                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Таксени</th>
    224                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Потписи</th>
    225                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
    226                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Зав.</th>
     210                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('semester')}</th>
     211                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('direction')}</th>
     212                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('quota')}</th>
     213                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('note')}</th>
     214                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('student_com')}</th>
     215                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('sum')}</th>
     216                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('paid')}</th>
     217                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('ukim')}</th>
     218                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('created_on')}</th>
     219                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('date_changed')}</th>
     220                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('credits')}</th>
     221                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('type')}</th>
     222                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('doc')}</th>
     223                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('doc1')}</th>
     224                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('verified')}</th>
     225                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('taxes')}</th>
     226                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('signatures')}</th>
     227                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('status')}</th>
     228                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('completed')}</th>
    227229              </tr>
    228230            </thead>
     
    335337            </div>
    336338            <div>
    337               <h3 className="text-lg font-bold text-gray-900">Завршени семестри</h3>
     339              <h3 className="text-lg font-bold text-gray-900">{t('completed_semesters')}</h3>
    338340              <p className="text-2xl font-bold text-green-600">
    339341                {semesters.filter(s => s.completed !== "Не").length}
     
    349351            </div>
    350352            <div>
    351               <h3 className="text-lg font-bold text-gray-900">Верифицирани</h3>
     353              <h3 className="text-lg font-bold text-gray-900">{t('verified_semesters')}</h3>
    352354              <p className="text-2xl font-bold text-blue-600">
    353355                {semesters.filter(s => s.verified === "Да").length}
     
    363365            </div>
    364366            <div>
    365               <h3 className="text-lg font-bold text-gray-900">Вкупно семестри</h3>
     367              <h3 className="text-lg font-bold text-gray-900">{t('total_semesters')}</h3>
    366368              <p className="text-2xl font-bold text-primary">
    367369                {semesters.length}
  • src/app/students/subjects/page.tsx

    r298f9b3 r50f2fb4  
    99import { useEffect, useState } from 'react';
    1010import { getAccessToken } from '@/lib/auth';
     11import { useTranslation } from 'react-i18next';
    1112
    1213interface Subject {
     
    147148
    148149const StatusBadge = ({ status }: { status: string }) => {
     150  const { t } = useTranslation();
    149151  const baseClasses = "px-3 py-1 rounded-md text-xs font-medium";
    150  
    151   if (status === "Зад.") {
     152  if (status === t('mandatory_short')) {
    152153    return (
    153154      <span className={`${baseClasses} bg-blue-100 text-blue-800`}>
    154         {status}
     155        {t('mandatory_short')}
    155156      </span>
    156157    );
    157   } else if (status === "Изб.") {
     158  } else if (status === t('elective_short')) {
    158159    return (
    159160      <span className={`${baseClasses} bg-green-100 text-green-800`}>
    160         {status}
     161        {t('elective_short')}
    161162      </span>
    162163    );
    163164  }
    164  
    165165  return (
    166166    <span className={`${baseClasses} bg-gray-100 text-gray-800`}>
    167       {status}
     167      {t(status) || status}
    168168    </span>
    169169  );
     
    176176  const [isLoading, setIsLoading] = useState(true);
    177177  const [errorMessage, setErrorMessage] = useState<string | null>(null);
     178  const { t } = useTranslation();
    178179
    179180  useEffect(() => {
     
    275276      <div className="min-h-screen pb-8">
    276277        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
    277           Loading...
     278          {t('loading')}
    278279        </div>
    279280      </div>
     
    285286      <div className="min-h-screen pb-8">
    286287        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
    287           {errorMessage ?? 'Failed to load subjects.'}
     288          {errorMessage ?? t('failed_to_load_subjects')}
    288289        </div>
    289290      </div>
     
    308309          </div>
    309310          <div>
    310             <h1 className="text-3xl font-bold mb-2">Предмети</h1>
     311            <h1 className="text-3xl font-bold mb-2">{t('subjects')}</h1>
    311312            <p className="text-lg opacity-90">
    312               Преглед на запишани предмети по семестри
     313              {t('subjects_overview')}
    313314            </p>
    314315          </div>
     
    325326          <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
    326327            <div className="text-sm text-gray-600 mb-2">
    327               Статус: <span className="text-primary font-semibold">Запишан од студент</span>
     328              {t('status')}: <span className="text-primary font-semibold">{t('enrolled_by_student')}</span>
    328329            </div>
    329330            <div className="text-sm text-gray-600">
    330               Број на тикет: <span className="font-semibold">{currentSemester.ticketNumber}</span>
     331              {t('ticket_number')}: <span className="font-semibold">{currentSemester.ticketNumber}</span>
    331332            </div>
    332333          </div>
     
    335336          <div className="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
    336337            <div className="text-sm text-gray-600 mb-2">
    337               Долг од документи: <span className="font-semibold">{currentSemester.debt}</span>
     338              {t('debt_from_documents')}: <span className="font-semibold">{currentSemester.debt}</span>
    338339            </div>
    339340           
    340341            <div className="relative mt-4">
    341342              <label className="block text-sm font-medium text-gray-700 mb-2">
    342                 Избери семестар:
     343                {t('select_semester')}:
    343344              </label>
    344345              <div className="relative">
     
    370371                      >
    371372                        <div className="font-medium text-gray-900">{semester.name}</div>
    372                         <div className="text-xs text-gray-500">Статус: {semester.status}</div>
     373                        <div className="text-xs text-gray-500">{t('status')}: {semester.status}</div>
    373374                      </button>
    374375                    ))}
     
    380381            <div className="mt-4 text-sm">
    381382              <div className="text-primary font-medium">
    382                 Сериски број: {currentSemesterData.serviceNumber}
     383                {t('serial_number')}: {currentSemesterData.serviceNumber}
    383384              </div>
    384385            </div>
     
    389390            <h3 className="font-semibold text-gray-900 mb-3 flex items-center gap-2">
    390391              <FontAwesomeIcon icon={faBook} className="w-4 h-4 text-primary" />
    391               Запишани предмети
     392              {t('enrolled_subjects')}
    392393            </h3>
    393394            <div className="text-3xl font-bold text-primary">
     
    403404              <h2 className="text-xl font-bold flex items-center gap-2">
    404405                <FontAwesomeIcon icon={faFileInvoice} />
    405                 Финансиски информации
     406                {t('financial_info')}
    406407              </h2>
    407408            </div>
     
    413414                <div className="space-y-4">
    414415                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    415                     <span className="text-gray-600">Сума:</span>
     416                    <span className="text-gray-600">{t('sum')}:</span>
    416417                    <span className="font-semibold text-primary">{currentSemester.financialInfo.sum}</span>
    417418                  </div>
    418419                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    419                     <span className="text-gray-600">Платено:</span>
     420                    <span className="text-gray-600">{t('paid')}:</span>
    420421                    <span className="font-semibold text-green-600">{currentSemester.financialInfo.paid}</span>
    421422                  </div>
    422423                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    423                     <span className="text-gray-600">Должи:</span>
     424                    <span className="text-gray-600">{t('due')}:</span>
    424425                    <span className="font-semibold text-red-600">{currentSemester.financialInfo.due}</span>
    425426                  </div>
    426427                  <div className="mt-4 p-4 bg-blue-50 rounded-lg">
    427                     <div className="text-sm font-medium text-blue-800 mb-1">Материјални трошоци:</div>
    428                     <div className="text-sm text-blue-700">{currentSemester.financialInfo.materialCosts}</div>
     428                    <div className="text-sm font-medium text-blue-800 mb-1">{t('material_costs')}:</div>
     429                    <div className="text-sm text-blue-700">{t('material_costs_info')}</div>
    429430                  </div>
    430431                </div>
     
    433434                <div className="space-y-4">
    434435                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    435                     <span className="text-gray-600">Кредити:</span>
     436                    <span className="text-gray-600">{t('credits')}:</span>
    436437                    <span className="font-semibold text-primary">{currentSemester.financialInfo.credits}</span>
    437438                  </div>
    438439                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    439                     <span className="text-gray-600">МКСА:</span>
     440                    <span className="text-gray-600">{t('mksa')}:</span>
    440441                    <span className="font-semibold">{currentSemester.financialInfo.MKSA}</span>
    441442                  </div>
    442443                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    443                     <span className="text-gray-600">Електронско запишување:</span>
     444                    <span className="text-gray-600">{t('electronic_registration')}:</span>
    444445                    <span className="font-semibold">{currentSemester.financialInfo.electronicRegistration}</span>
    445446                  </div>
    446447                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    447                     <span className="text-gray-600">Е-УКИМ:</span>
     448                    <span className="text-gray-600">{t('eukim')}:</span>
    448449                    <span className="font-semibold">{currentSemester.financialInfo.eUKIM}</span>
    449450                  </div>
    450451                  <div className="flex justify-between items-center py-2 border-b border-gray-100">
    451                     <span className="text-gray-600">Банкарска провизија:</span>
     452                    <span className="text-gray-600">{t('bank_provision')}:</span>
    452453                    <span className="font-semibold">{currentSemester.financialInfo.bankProvision}</span>
    453454                  </div>
    454455                  <div className="flex justify-between items-center py-3 border-t-2 border-primary bg-primary bg-opacity-5 rounded-lg px-4">
    455                     <span className="font-bold text-white">Тотал:</span>
     456                    <span className="font-bold text-white">{t('total')}:</span>
    456457                    <span className="font-bold text-xl text-white">{currentSemester.financialInfo.total}</span>
    457458                  </div>
     
    466467      <div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
    467468        <div className="bg-primary text-white px-6 py-4">
    468           <h2 className="text-xl font-bold">Листа на предмети</h2>
     469          <h2 className="text-xl font-bold">{t('subjects_list')}</h2>
    469470        </div>
    470471       
     
    474475              <tr>
    475476                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">#</th>
    476                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Код</th>
    477                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Часови</th>
    478                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Кој пат</th>
    479                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Предмет</th>
    480                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Семестар</th>
    481                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
    482                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Потпис</th>
    483                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Група</th>
    484                 <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Професор</th>
     477                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('code')}</th>
     478                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('hours')}</th>
     479                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('which_time')}</th>
     480                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('subject')}</th>
     481                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('semester')}</th>
     482                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('status')}</th>
     483                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('signature')}</th>
     484                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('group')}</th>
     485                <th className="px-4 py-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{t('professor')}</th>
    485486              </tr>
    486487            </thead>
     
    495496                    <div className="flex items-center gap-2">
    496497                      <FontAwesomeIcon icon={faBook} className="w-4 h-4 text-primary" />
    497                       {subject.name}
     498                      {t(subject.name, subject.name)}
    498499                    </div>
    499500                  </TableCell>
    500                   <TableCell className="text-center font-medium text-primary">{subject.semester}</TableCell>
     501                  <TableCell className="text-center font-medium text-primary">{t(subject.semester.toString(), subject.semester.toString())}</TableCell>
    501502                  <TableCell>
    502503                    <StatusBadge status={subject.status} />
    503504                  </TableCell>
    504505                  <TableCell className="text-center">
    505                     {subject.signature || (
     506                    {subject.signature ? t(subject.signature, subject.signature) : (
    506507                      <span className="text-gray-400">—</span>
    507508                    )}
    508509                  </TableCell>
    509510                  <TableCell className="text-center">
    510                     {subject.group || (
     511                    {subject.group ? t(subject.group, subject.group) : (
    511512                      <span className="text-gray-400">—</span>
    512513                    )}
    513514                  </TableCell>
    514515                  <TableCell>
    515                     {subject.professor || (
     516                    {subject.professor ? t(subject.professor, subject.professor) : (
    516517                      <span className="text-gray-400">—</span>
    517518                    )}
Note: See TracChangeset for help on using the changeset viewer.