Index: src/app/page.tsx
===================================================================
--- src/app/page.tsx	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ src/app/page.tsx	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -11,9 +11,12 @@
 } from '@fortawesome/free-solid-svg-icons';
 import { useState } from 'react';
+import { login } from '@/lib/auth';
 
 export default function LoginPage() {
   const [showPassword, setShowPassword] = useState(false);
+  const [isSubmitting, setIsSubmitting] = useState(false);
+  const [errorMessage, setErrorMessage] = useState<string | null>(null);
   const [formData, setFormData] = useState({
-    username: '',
+    email: '',
     password: ''
   });
@@ -27,8 +30,16 @@
   };
 
-  const handleSubmit = (e: React.FormEvent) => {
+  const handleSubmit = async (e: React.FormEvent) => {
     e.preventDefault();
-    // Handle login logic here
-    console.log('Login attempt:', formData);
+    setErrorMessage(null);
+    setIsSubmitting(true);
+    try {
+      await login({ email: formData.email, password: formData.password });
+      window.location.href = '/students/profile';
+    } catch (err) {
+      setErrorMessage(err instanceof Error ? err.message : 'Login failed.');
+    } finally {
+      setIsSubmitting(false);
+    }
   };
 
@@ -57,8 +68,8 @@
             <div className="p-8">
               <form onSubmit={handleSubmit} className="space-y-6">
-                {/* Username Field */}
+                {/* Email Field */}
                 <div>
-                  <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-2">
-                    Корисничко име
+                  <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
+                    Внесете е-пошта
                   </label>
                   <div className="relative">
@@ -67,12 +78,12 @@
                     </div>
                     <input
-                      id="username"
-                      name="username"
-                      type="text"
+                      id="email"
+                      name="email"
+                      type="email"
                       required
-                      value={formData.username}
+                      value={formData.email}
                       onChange={handleInputChange}
                       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"
-                      placeholder="Внесете корисничко име"
+                      placeholder="Внесете е-пошта"
                     />
                   </div>
@@ -132,11 +143,18 @@
                 </div>
 
+                {errorMessage && (
+                  <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+                    {errorMessage}
+                  </div>
+                )}
+
                 {/* Submit Button */}
                 <button
                   type="submit"
-                  className="w-full bg-primary hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 shadow-lg hover:shadow-xl"
+                  disabled={isSubmitting}
+                  className="w-full bg-primary hover:bg-blue-700 disabled:opacity-60 disabled:hover:bg-primary text-white font-medium py-3 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 shadow-lg hover:shadow-xl"
                 >
                   <FontAwesomeIcon icon={faSignInAlt} className="h-4 w-4" />
-                  Најави се
+                  {isSubmitting ? 'Најави се...' : 'Најави се'}
                 </button>
               </form>
Index: src/app/students/profile/page.tsx
===================================================================
--- src/app/students/profile/page.tsx	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ src/app/students/profile/page.tsx	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -17,7 +17,74 @@
   faPassport
 } from '@fortawesome/free-solid-svg-icons';
-import studentData from '@/data/student-profile.json';
-
+import { useEffect, useState } from 'react';
+import { getAccessToken } from '@/lib/auth';
 import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
+
+type PersonalInfo = {
+  firstName: string;
+  middleName: string;
+  lastName: string;
+  maidenName: string;
+  dateOfBirth: string;
+  gender: string;
+  nationality: string;
+  citizenship: string;
+  scholarship: string;
+  currentPlan: string;
+  registryNumber: string;
+  studyGroup: string;
+  notes?: string;
+  index: string;
+  embg: string;
+};
+
+type BirthInfo = {
+  placeOfBirth: string;
+  municipalityOfBirth: string;
+  country: string;
+};
+
+type PreviousEducation = {
+  type: string;
+  profession: string;
+  average: string | number;
+  language: string;
+  country: string;
+  previousUniversity: string;
+  previousFaculty: string;
+  previousStudyMode: string;
+};
+
+type EnrollmentInfo = {
+  enrollmentYear: string | number;
+  status: string;
+  cycle: string;
+  program: string;
+  quota: string;
+  secondaryEducationNumber: string;
+  previousEducationCredits: string | number;
+};
+
+type Contact = {
+  placeOfResidence: string;
+  municipalityOfResidence: string;
+  country: string;
+  address: string;
+  temporaryAddress: string;
+  phone: string;
+  mobilePhone: string;
+  passportNumber: string;
+  passportExpiryDate: string;
+  email: string;
+  microsoftEmail: string;
+};
+
+type StudentProfile = {
+  personalInfo: PersonalInfo;
+  birthInfo: BirthInfo;
+  previousEducation: PreviousEducation;
+  enrollmentInfo: EnrollmentInfo;
+  contact: Contact;
+};
 
 interface InfoRowProps {
@@ -60,4 +127,73 @@
 
 export default function ProfilePage() {
+  const [studentData, setStudentData] = useState<StudentProfile | null>(null);
+  const [isLoading, setIsLoading] = useState(true);
+  const [errorMessage, setErrorMessage] = useState<string | null>(null);
+
+  useEffect(() => {
+    let cancelled = false;
+
+    async function load() {
+      setIsLoading(true);
+      setErrorMessage(null);
+
+      const token = getAccessToken();
+      if (!token) {
+        setErrorMessage('Not authenticated. Please login again.');
+        setIsLoading(false);
+        return;
+      }
+
+      try {
+        const response = await fetch('http://localhost:5147/api/user/getUser', {
+          method: 'GET',
+          headers: {
+            Authorization: `Bearer ${token}`,
+          },
+        });
+
+        if (!response.ok) {
+          const text = await response.text().catch(() => '');
+          throw new Error(text || `Failed to load profile (${response.status})`);
+        }
+
+        const data = (await response.json()) as StudentProfile;
+        if (!cancelled) setStudentData(data);
+      } catch (err) {
+        if (!cancelled) {
+          setErrorMessage(err instanceof Error ? err.message : 'Failed to load profile.');
+        }
+      } finally {
+        if (!cancelled) setIsLoading(false);
+      }
+    }
+
+    void load();
+
+    return () => {
+      cancelled = true;
+    };
+  }, []);
+
+  if (isLoading) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
+          Loading...
+        </div>
+      </div>
+    );
+  }
+
+  if (errorMessage || !studentData) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+          {errorMessage ?? 'Failed to load profile.'}
+        </div>
+      </div>
+    );
+  }
+
   const { personalInfo, birthInfo, previousEducation, enrollmentInfo, contact } = studentData;
 
Index: src/app/students/semesters/page.tsx
===================================================================
--- src/app/students/semesters/page.tsx	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ src/app/students/semesters/page.tsx	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -12,5 +12,33 @@
   faTimesCircle
 } from '@fortawesome/free-solid-svg-icons';
-import semestersData from '@/data/semesters.json';
+import { useEffect, useState } from 'react';
+import { getAccessToken } from '@/lib/auth';
+
+type Semester = {
+  id: number | string;
+  semester: string;
+  direction: string;
+  quota: string;
+  note: string;
+  studentCom: string;
+  sum: string;
+  paid: string;
+  ukim: string;
+  createdOn: string;
+  dateChanged: string;
+  credits: string;
+  type: string;
+  doc: string;
+  doc1: string;
+  verified: string;
+  taxes: string;
+  signatures: string;
+  status: string;
+  completed: string;
+};
+
+type SemestersResponse = {
+  semesters: Semester[];
+};
 
 interface TableCellProps {
@@ -81,5 +109,72 @@
 
 export default function SemestersPage() {
-  const { semesters } = semestersData;
+  const [semesters, setSemesters] = useState<Semester[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [errorMessage, setErrorMessage] = useState<string | null>(null);
+
+  useEffect(() => {
+    let cancelled = false;
+
+    async function load() {
+      setIsLoading(true);
+      setErrorMessage(null);
+
+      const token = getAccessToken();
+      if (!token) {
+        setErrorMessage('Not authenticated. Please login again.');
+        setIsLoading(false);
+        return;
+      }
+
+      try {
+        const response = await fetch('http://localhost:5147/api/user/getSemesters', {
+          method: 'GET',
+          headers: {
+            Authorization: `Bearer ${token}`,
+          },
+        });
+
+        if (!response.ok) {
+          const text = await response.text().catch(() => '');
+          throw new Error(text || `Failed to load semesters (${response.status})`);
+        }
+
+        const data = (await response.json()) as SemestersResponse;
+        if (!cancelled) setSemesters(Array.isArray(data?.semesters) ? data.semesters : []);
+      } catch (err) {
+        if (!cancelled) {
+          setErrorMessage(err instanceof Error ? err.message : 'Failed to load semesters.');
+        }
+      } finally {
+        if (!cancelled) setIsLoading(false);
+      }
+    }
+
+    void load();
+
+    return () => {
+      cancelled = true;
+    };
+  }, []);
+
+  if (isLoading) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
+          Loading...
+        </div>
+      </div>
+    );
+  }
+
+  if (errorMessage) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+          {errorMessage}
+        </div>
+      </div>
+    );
+  }
 
   return (
Index: src/app/students/subjects/page.tsx
===================================================================
--- src/app/students/subjects/page.tsx	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ src/app/students/subjects/page.tsx	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -7,6 +7,6 @@
   faFileInvoice
 } from '@fortawesome/free-solid-svg-icons';
-import { useState } from 'react';
-import subjectsData from '@/data/subjects.json';
+import { useEffect, useState } from 'react';
+import { getAccessToken } from '@/lib/auth';
 
 interface Subject {
@@ -21,4 +21,116 @@
   group: string;
   professor: string;
+}
+
+type SemesterInfo = {
+  id: number;
+  name: string;
+  status: string;
+  serviceNumber: string | number;
+};
+
+type FinancialInfo = {
+  sum: string | number;
+  paid: string;
+  due: string;
+  materialCosts: string;
+  credits: string;
+  MKSA: string;
+  electronicRegistration: string;
+  eUKIM: string;
+  bankProvision: string;
+  total: string;
+};
+
+type CurrentSemester = {
+  id: string;
+  name: string;
+  status: string;
+  serviceNumber: string | number;
+  ticketNumber: string;
+  debt: string;
+  financialInfo: FinancialInfo;
+};
+
+type SubjectsResponse = {
+  currentSemester: CurrentSemester;
+  semesters: SemesterInfo[];
+  subjectsBySemester: Record<string, Subject[]>;
+  semesterKeyById: Record<number, string>;
+};
+
+type ApiCurrentSemester = {
+  id: string;
+  name: string;
+  status: string;
+  serviceNumber: string | number;
+  ticketNumber: string;
+  debt: string;
+  financialInfo: {
+    sum: string | number;
+    paid: string;
+    due: string;
+    materialCosts: string;
+    credits: string;
+    totalCredits?: string;
+    mksa?: string;
+    MKSA?: string;
+    electronicRegistration: string;
+    eUKIM: string;
+    bankProvision: string;
+    total: string;
+  };
+};
+
+type ApiSubjectsResponse = {
+  semesters: SemesterInfo[];
+  currentSemester?: ApiCurrentSemester;
+  currentSemestar?: ApiCurrentSemester;
+  subjectsBySemester: Record<string, Subject[]>;
+};
+
+function normalizeKey(input: string) {
+  return input.toLowerCase().replace(/\s|\(|\)|\.|,/g, '');
+}
+
+function detectSeasonFromName(name: string): 'summer' | 'winter' | null {
+  const n = name.toLowerCase();
+  if (n.includes('летен')) return 'summer';
+  if (n.includes('зимски')) return 'winter';
+  return null;
+}
+
+function buildSemesterKeyById(semesters: SemesterInfo[], keys: string[]) {
+  const keyBySeason: Partial<Record<'summer' | 'winter', string>> = {};
+  for (const key of keys) {
+    const k = key.toLowerCase();
+    if (k.includes('summer')) keyBySeason.summer = key;
+    if (k.includes('winter')) keyBySeason.winter = key;
+  }
+
+  const mapping: Record<number, string> = {};
+  for (const s of semesters) {
+    const season = detectSeasonFromName(s.name);
+    const mapped = season ? keyBySeason[season] : undefined;
+    if (mapped) mapping[s.id] = mapped;
+  }
+
+  // Fallback: if we couldn't infer seasons, try matching by normalized names.
+  if (Object.keys(mapping).length === 0) {
+    for (const s of semesters) {
+      const ns = normalizeKey(s.name);
+      const match = keys.find((k) => normalizeKey(k).includes(ns) || ns.includes(normalizeKey(k)));
+      if (match) mapping[s.id] = match;
+    }
+  }
+
+  // Last resort: map in order.
+  if (Object.keys(mapping).length === 0) {
+    semesters.forEach((s, idx) => {
+      if (keys[idx]) mapping[s.id] = keys[idx];
+    });
+  }
+
+  return mapping;
 }
 
@@ -59,9 +171,130 @@
 
 export default function SubjectsPage() {
-  const [selectedSemester, setSelectedSemester] = useState(subjectsData.currentSemester.id);
+  const [subjectsData, setSubjectsData] = useState<SubjectsResponse | null>(null);
+  const [selectedSemester, setSelectedSemester] = useState<number | null>(null);
   const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+  const [isLoading, setIsLoading] = useState(true);
+  const [errorMessage, setErrorMessage] = useState<string | null>(null);
+
+  useEffect(() => {
+    let cancelled = false;
+
+    async function load() {
+      setIsLoading(true);
+      setErrorMessage(null);
+
+      const token = getAccessToken();
+      if (!token) {
+        setErrorMessage('Not authenticated. Please login again.');
+        setIsLoading(false);
+        return;
+      }
+
+      try {
+        const response = await fetch('http://localhost:5147/api/user/getSubjects', {
+          method: 'GET',
+          headers: {
+            Authorization: `Bearer ${token}`,
+          },
+        });
+
+        if (!response.ok) {
+          const text = await response.text().catch(() => '');
+          throw new Error(text || `Failed to load subjects (${response.status})`);
+        }
+
+        const apiData = (await response.json()) as ApiSubjectsResponse;
+        if (cancelled) return;
+
+        const current = apiData.currentSemester ?? apiData.currentSemestar;
+        if (!current) {
+          throw new Error('Response missing currentSemestar/currentSemester');
+        }
+
+        const keys = Object.keys(apiData.subjectsBySemester ?? {});
+        const semesterKeyById = buildSemesterKeyById(apiData.semesters ?? [], keys);
+
+        const normalized: SubjectsResponse = {
+          semesters: apiData.semesters ?? [],
+          subjectsBySemester: apiData.subjectsBySemester ?? {},
+          semesterKeyById,
+          currentSemester: {
+            id: current.id,
+            name: current.name,
+            status: current.status,
+            serviceNumber: current.serviceNumber,
+            ticketNumber: current.ticketNumber,
+            debt: current.debt,
+            financialInfo: {
+              sum: current.financialInfo.sum,
+              paid: current.financialInfo.paid,
+              due: current.financialInfo.due,
+              materialCosts: current.financialInfo.materialCosts,
+              credits: current.financialInfo.credits,
+              MKSA: current.financialInfo.MKSA ?? current.financialInfo.mksa ?? '',
+              electronicRegistration: current.financialInfo.electronicRegistration,
+              eUKIM: current.financialInfo.eUKIM,
+              bankProvision: current.financialInfo.bankProvision,
+              total: current.financialInfo.total,
+            },
+          },
+        };
+
+        setSubjectsData(normalized);
+
+        setSelectedSemester((prev) => {
+          if (prev !== null) return prev;
+          const season = detectSeasonFromName(current.name);
+          if (season) {
+            const key = keys.find((k) => k.toLowerCase().includes(season));
+            if (key) {
+              const matchId = normalized.semesters.find((s) => normalized.semesterKeyById[s.id] === key)?.id;
+              if (typeof matchId === 'number') return matchId;
+            }
+          }
+          return normalized.semesters[0]?.id ?? null;
+        });
+      } catch (err) {
+        if (!cancelled) {
+          setErrorMessage(err instanceof Error ? err.message : 'Failed to load subjects.');
+        }
+      } finally {
+        if (!cancelled) setIsLoading(false);
+      }
+    }
+
+    void load();
+
+    return () => {
+      cancelled = true;
+    };
+  }, []);
+
+  if (isLoading) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
+          Loading...
+        </div>
+      </div>
+    );
+  }
+
+  if (errorMessage || !subjectsData || selectedSemester === null) {
+    return (
+      <div className="min-h-screen pb-8">
+        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+          {errorMessage ?? 'Failed to load subjects.'}
+        </div>
+      </div>
+    );
+  }
   
-  const currentSemesterData = subjectsData.semesters.find(s => s.id === selectedSemester) || subjectsData.currentSemester;
-  const currentSubjects: Subject[] = subjectsData.subjectsBySemester[selectedSemester as keyof typeof subjectsData.subjectsBySemester] || [];
+  const currentSemesterData =
+    subjectsData.semesters.find((s) => s.id === selectedSemester) ?? subjectsData.semesters[0];
+  const semesterKey =
+    subjectsData.semesterKeyById[selectedSemester] ??
+    Object.keys(subjectsData.subjectsBySemester)[0];
+  const currentSubjects: Subject[] = semesterKey ? subjectsData.subjectsBySemester[semesterKey] || [] : [];
   const { currentSemester } = subjectsData;
 
Index: src/components/exams.tsx
===================================================================
--- src/components/exams.tsx	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ src/components/exams.tsx	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -1,239 +1,121 @@
 "use client"
-import { useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons';
+import { getAccessToken } from '@/lib/auth';
+
+type PassedSubject = {
+  id: number;
+  subjectId: number;
+  code: string;
+  subject: string;
+  credits: number;
+  grade: number;
+  gradeText: string;
+  date: string; // DD.MM.YYYY
+  semester: string;
+  professor: string;
+};
+
+type PassedSubjectsResponse = {
+  passedSubjects: PassedSubject[];
+};
+
+type ExamRow = {
+  id: number;
+  code: string;
+  subject: string;
+  date: string;
+  semester: string;
+  credits: number;
+  grade: number;
+};
 
 const Exams = () => {
   const [sortField, setSortField] = useState<'grade' | 'date' | null>(null);
   const [sortOrder, setSortOrder] = useState<'asc' | 'desc' | null>(null);
-  // Real exam data
-  const stats = {
-    average: 8.95,
-    credits: { current: 108, total: 240 },
-    passed: 19,
-    remaining: 22
-  };
-
-  const exams = [
-    {
-      id: 1,
-      code: "F23L1W005",
-      subject: "Бизнис и менаџмент",
-      session: "2024 (Зимска) Прва испитна сесија",
-      date: "21.02.2024",
-      semester: 1,
-      credits: 6.00,
-      type: "З",
-      grade: 7
-    },
-    {
-      id: 2,
-      code: "F23L1W007",
-      subject: "Вовед во компјутерските науки",
-      session: "2024 (Зимска) Прва испитна сесија",
-      date: "06.02.2024",
-      semester: 1,
-      credits: 6.00,
-      type: "З",
-      grade: 10
-    },
-    {
-      id: 3,
-      code: "F23L2W003",
-      subject: "Избрани теми од математика",
-      session: "2024 (Зимска) Прва испитна сесија",
-      date: "12.02.2024",
-      semester: 1,
-      credits: 6.00,
-      type: "З",
-      grade: 9
-    },
-    {
-      id: 4,
-      code: "F23L1W018",
-      subject: "Професионални вештини",
-      session: "2024 (Зимска) Прва испитна сесија",
-      date: "01.02.2024",
-      semester: 1,
-      credits: 4.00,
-      type: "З",
-      grade: 9
-    },
-    {
-      id: 5,
-      code: "F23L1W004",
-      subject: "Спорт и здравје",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "15.07.2024",
-      semester: 1,
-      credits: 2.00,
-      type: "З",
-      grade: "Реализиран"
-    },
-    {
-      id: 6,
-      code: "F23L1W020",
-      subject: "Структурно програмирање",
-      session: "2024 (Зимска) Прва испитна сесија",
-      date: "12.02.2024",
-      semester: 1,
-      credits: 6.00,
-      type: "З",
-      grade: 10
-    },
-    {
-      id: 7,
-      code: "F23L1S003",
-      subject: "Архитектура и организација на компјутери",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "03.07.2024",
-      semester: 2,
-      credits: 6.00,
-      type: "З",
-      grade: 9
-    },
-    {
-      id: 8,
-      code: "F23L1S023",
-      subject: "Бизнис статистика",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "02.07.2024",
-      semester: 2,
-      credits: 6.00,
-      type: "З",
-      grade: 8
-    },
-    {
-      id: 9,
-      code: "F23L1S120",
-      subject: "Креативни вештини за решавање проблеми",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "25.06.2024",
-      semester: 2,
-      credits: 6.00,
-      type: "И",
-      grade: 10
-    },
-    {
-      id: 10,
-      code: "F23L1S016",
-      subject: "Објектно-ориентирано програмирање",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "13.07.2024",
-      semester: 2,
-      credits: 6.00,
-      type: "З",
-      grade: 10
-    },
-    {
-      id: 11,
-      code: "F23L1S146",
-      subject: "Основи на Веб дизајн",
-      session: "2024 (Летна) Втора испитна сесија",
-      date: "24.06.2024",
-      semester: 2,
-      credits: 6.00,
-      type: "З",
-      grade: 10
-    },
-    {
-      id: 12,
-      code: "F23L2W100",
-      subject: "Економија за ИКТ инженери",
-      session: "2025 (Зимска) Прва испитна сесија",
-      date: "03.02.2025",
-      semester: 3,
-      credits: 6.00,
-      type: "З",
-      grade: 8
-    },
-    {
-      id: 13,
-      code: "F23L2W109",
-      subject: "Интернет програмирање на клиентска страна",
-      session: "2025 (Зимска) Прва испитна сесија",
-      date: "30.01.2025",
-      semester: 3,
-      credits: 6.00,
-      type: "И",
-      grade: 10
-    },
-    {
-      id: 14,
-      code: "F23L2W014",
-      subject: "Компјутерски мрежи и безбедност",
-      session: "2025 (Зимска) Прва испитна сесија",
-      date: "10.02.2025",
-      semester: 3,
-      credits: 6.00,
-      type: "З",
-      grade: 7
-    },
-    {
-      id: 15,
-      code: "F23L2W201",
-      subject: "Примена на алгоритми и податочни структури",
-      session: "2025 (Зимска) Прва испитна сесија",
-      date: "13.02.2025",
-      semester: 3,
-      credits: 6.00,
-      type: "З",
-      grade: 10
-    },
-    {
-      id: 16,
-      code: "F23L2W167",
-      subject: "Шаблони за дизајн на кориснички интерфејси",
-      session: "2025 (Зимска) Прва испитна сесија",
-      date: "06.02.2025",
-      semester: 3,
-      credits: 6.00,
-      type: "И",
-      grade: 10
-    },
-    {
-      id: 17,
-      code: "F23L2S026",
-      subject: "Маркетинг",
-      session: "2025 (Летна) Втора испитна сесија",
-      date: "12.06.2025",
-      semester: 4,
-      credits: 6.00,
-      type: "З",
-      grade: 8
-    },
-    {
-      id: 18,
-      code: "F23L2S017",
-      subject: "Оперативни системи",
-      session: "2025 (Летна) Втора испитна сесија",
-      date: "19.06.2025",
-      semester: 4,
-      credits: 6.00,
-      type: "З",
-      grade: 7
-    },
-    {
-      id: 19,
-      code: "F23L2S029",
-      subject: "Софтверско инженерство",
-      session: "2025 (Летна) Втора испитна сесија",
-      date: "23.06.2025",
-      semester: 4,
-      credits: 6.00,
-      type: "З",
-      grade: 8
+  const [exams, setExams] = useState<ExamRow[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [errorMessage, setErrorMessage] = useState<string | null>(null);
+
+  useEffect(() => {
+    let cancelled = false;
+
+    async function load() {
+      setIsLoading(true);
+      setErrorMessage(null);
+
+      const token = getAccessToken();
+      if (!token) {
+        setErrorMessage('Not authenticated. Please login again.');
+        setIsLoading(false);
+        return;
+      }
+
+      try {
+        const response = await fetch('http://localhost:5147/api/user/getPassedSubjects', {
+          method: 'GET',
+          headers: {
+            Authorization: `Bearer ${token}`,
+          },
+        });
+
+        if (!response.ok) {
+          const text = await response.text().catch(() => '');
+          throw new Error(text || `Failed to load passed subjects (${response.status})`);
+        }
+
+        const data = (await response.json()) as PassedSubjectsResponse;
+        const rows: ExamRow[] = (data?.passedSubjects ?? []).map((s) => ({
+          id: s.id,
+          code: s.code,
+          subject: s.subject,
+          date: s.date,
+          semester: s.semester,
+          credits: s.credits,
+          grade: s.grade,
+        }));
+
+        if (!cancelled) setExams(rows);
+      } catch (err) {
+        if (!cancelled) {
+          setErrorMessage(err instanceof Error ? err.message : 'Failed to load passed subjects.');
+        }
+      } finally {
+        if (!cancelled) setIsLoading(false);
+      }
     }
-  ];
-
-  const getGradeColor = (grade: number | string) => {
-    if (grade === "Реализиран") return "bg-blue-100 text-blue-800 border-blue-200";
-    if (typeof grade === "number") {
-      if (grade >= 9) return "bg-green-100 text-green-800 border-green-200";
-      if (grade >= 8) return "bg-yellow-100 text-yellow-800 border-yellow-200";
-      if (grade >= 6) return "bg-orange-100 text-orange-800 border-orange-200";
-      return "bg-red-100 text-red-800 border-red-200";
-    }
-    return "bg-gray-100 text-gray-800 border-gray-200";
+
+    void load();
+
+    return () => {
+      cancelled = true;
+    };
+  }, []);
+
+  const stats = useMemo(() => {
+    const passed = exams.length;
+    const creditsCurrent = exams.reduce((sum, s) => sum + (typeof s.credits === 'number' ? s.credits : 0), 0);
+    const numericGrades = exams.map((e) => (typeof e.grade === 'number' ? e.grade : null)).filter((g): g is number => g !== null);
+    const average =
+      numericGrades.length > 0
+        ? numericGrades.reduce((sum, g) => sum + g, 0) / numericGrades.length
+        : 0;
+
+    const totalCredits = 240;
+    const totalExams = 40;
+    return {
+      average: Number.isFinite(average) ? Number(average.toFixed(2)) : 0,
+      credits: { current: creditsCurrent, total: totalCredits },
+      passed,
+      remaining: Math.max(0, totalExams - passed),
+    };
+  }, [exams]);
+
+  const getGradeColor = (grade: number) => {
+    if (grade >= 9) return "bg-green-100 text-green-800 border-green-200";
+    if (grade >= 8) return "bg-yellow-100 text-yellow-800 border-yellow-200";
+    if (grade >= 6) return "bg-orange-100 text-orange-800 border-orange-200";
+    return "bg-red-100 text-red-800 border-red-200";
   };
 
@@ -275,14 +157,5 @@
     return [...exams].sort((a, b) => {
       if (sortField === 'grade') {
-        // Handle "Реализиран" grade separately
-        if (a.grade === "Реализиран" && b.grade === "Реализиран") return 0;
-        if (a.grade === "Реализиран") return sortOrder === 'asc' ? -1 : 1;
-        if (b.grade === "Реализиран") return sortOrder === 'asc' ? 1 : -1;
-        
-        // Handle numeric grades
-        const gradeA = typeof a.grade === 'number' ? a.grade : 0;
-        const gradeB = typeof b.grade === 'number' ? b.grade : 0;
-        
-        return sortOrder === 'asc' ? gradeA - gradeB : gradeB - gradeA;
+        return sortOrder === 'asc' ? a.grade - b.grade : b.grade - a.grade;
       }
       
@@ -307,5 +180,23 @@
   };
 
-  const creditsPercentage = (stats.credits.current / stats.credits.total) * 100;
+  const creditsPercentage = stats.credits.total > 0 ? (stats.credits.current / stats.credits.total) * 100 : 0;
+
+  if (isLoading) {
+    return (
+      <div className="bg-white rounded-lg shadow-sm p-6">
+        Loading...
+      </div>
+    );
+  }
+
+  if (errorMessage) {
+    return (
+      <div className="bg-white rounded-lg shadow-sm p-6">
+        <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
+          {errorMessage}
+        </div>
+      </div>
+    );
+  }
 
   return (
Index: src/data/semesters.json
===================================================================
--- src/data/semesters.json	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ 	(revision )
@@ -1,114 +1,0 @@
-{
-  "semesters": [
-    {
-      "id": 1,
-      "semester": "Зимски(2025/2026)",
-      "direction": "PIT23(2023)",
-      "quota": "Не плаќа-Редовен(2023)",
-      "note": "",
-      "studentCom": "Ги имам си...",
-      "sum": "0,30",
-      "paid": "0,00",
-      "ukim": "",
-      "createdOn": "22.09.2025",
-      "dateChanged": "22.09.2025",
-      "credits": "0,01",
-      "type": "Ред.",
-      "doc": "Не",
-      "doc1": "Не",
-      "verified": "Не",
-      "taxes": "0,00",
-      "signatures": "0/5",
-      "status": "валиден",
-      "completed": "Не"
-    },
-    {
-      "id": 2,
-      "semester": "Летен(2024/2025)",
-      "direction": "PIT23(2023)",
-      "quota": "Не плаќа-Редовен(2023)",
-      "note": "",
-      "studentCom": "Ги имам си...",
-      "sum": "0,30",
-      "paid": "1.001,00",
-      "ukim": "450,00",
-      "createdOn": "07.02.2025",
-      "dateChanged": "20.03.2025",
-      "credits": "0,01",
-      "type": "Ред.",
-      "doc": "Да",
-      "doc1": "Да",
-      "verified": "Да",
-      "taxes": "0,00",
-      "signatures": "0/5",
-      "status": "валиден",
-      "completed": "Не"
-    },
-    {
-      "id": 3,
-      "semester": "Зимски(2024/2025)",
-      "direction": "PIT23(2023)",
-      "quota": "Не плаќа-Редовен(2023)",
-      "note": "",
-      "studentCom": "",
-      "sum": "0,30",
-      "paid": "",
-      "ukim": "",
-      "createdOn": "25.09.2024",
-      "dateChanged": "11.02.2025",
-      "credits": "0,01",
-      "type": "Ред.",
-      "doc": "Да",
-      "doc1": "Да",
-      "verified": "Да",
-      "taxes": "0,00",
-      "signatures": "5/5",
-      "status": "валиден",
-      "completed": "11.02.2025"
-    },
-    {
-      "id": 4,
-      "semester": "Летен(2023/2024)",
-      "direction": "PIT23(2023)",
-      "quota": "Кофинансирање-Редовен(2023)",
-      "note": "",
-      "studentCom": "",
-      "sum": "12.300,00",
-      "paid": "",
-      "ukim": "",
-      "createdOn": "25.09.2024",
-      "dateChanged": "01.10.2024",
-      "credits": "410,00",
-      "type": "Ред.",
-      "doc": "Да",
-      "doc1": "Да",
-      "verified": "Да",
-      "taxes": "0,00",
-      "signatures": "4/5",
-      "status": "валиден",
-      "completed": "01.10.2024"
-    },
-    {
-      "id": 5,
-      "semester": "Зимски(2023/2024)",
-      "direction": "PIT23(2023)",
-      "quota": "Кофинансирање-Редовен(2023)",
-      "note": "стип. ФИНК...",
-      "studentCom": "",
-      "sum": "0,00",
-      "paid": "0,00",
-      "ukim": "",
-      "createdOn": "25.09.2024",
-      "dateChanged": "12.01.2025",
-      "credits": "410,00",
-      "type": "Ред.",
-      "doc": "Да",
-      "doc1": "Да",
-      "verified": "Не",
-      "taxes": "0,00",
-      "signatures": "2/6",
-      "status": "валиден",
-      "completed": "15.02.2024"
-    }
-  ]
-}
Index: src/data/subjects.json
===================================================================
--- src/data/subjects.json	(revision 82c9c5395fa3ee03a1695f6a09655d574ffbc478)
+++ 	(revision )
@@ -1,441 +1,0 @@
-{
-  "semesters": [
-    {
-      "id": "winter_2025_2026",
-      "name": "Зимски (2025/2026)",
-      "status": "валиден",
-      "serviceNumber": "1029816"
-    },
-    {
-      "id": "summer_2024_2025",
-      "name": "Летен (2024/2025)",
-      "status": "валиден",
-      "serviceNumber": "1028745"
-    },
-    {
-      "id": "winter_2024_2025",
-      "name": "Зимски (2024/2025)",
-      "status": "валиден",
-      "serviceNumber": "1027634"
-    },
-    {
-      "id": "summer_2023_2024",
-      "name": "Летен (2023/2024)",
-      "status": "валиден",
-      "serviceNumber": "1026523"
-    },
-    {
-      "id": "winter_2023_2024",
-      "name": "Зимски (2023/2024)",
-      "status": "валиден",
-      "serviceNumber": "1025412"
-    }
-  ],
-  "currentSemester": {
-    "id": "winter_2025_2026",
-    "name": "Зимски (2025/2026)",
-    "status": "валиден",
-    "serviceNumber": "1029816",
-    "ticketNumber": "427306",
-    "debt": "0,00",
-    "financialInfo": {
-      "sum": 1,
-      "paid": "0,00",
-      "due": "0,00",
-      "materialCosts": "Осигурување, Административна такса, Тетратки (испити), зимски семестар: 1000,00",
-      "credits": "30,00",
-      "totalCredits": "30,00",
-      "MKSA": "750,00",
-      "electronicRegistration": "100,00",
-      "eUKIM": "350,00",
-      "bankProvision": "25,00",
-      "total": "2226,00"
-    }
-  },
-  "subjectsBySemester": {
-    "winter_2025_2026": [
-      {
-        "id": 1,
-        "code": "F23L3W004",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Бази на податоци",
-        "semester": 5,
-        "status": "Зад.",
-        "signature": "",
-        "group": "",
-        "professor": ""
-      },
-      {
-        "id": 2,
-        "code": "F23L3W008",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Вовед во науката за податоци",
-        "semester": 5,
-        "status": "Зад.",
-        "signature": "",
-        "group": "",
-        "professor": ""
-      },
-      {
-        "id": 3,
-        "code": "F23L3W024",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Веб програмирање",
-        "semester": 5,
-        "status": "Зад.",
-        "signature": "",
-        "group": "",
-        "professor": ""
-      },
-      {
-        "id": 4,
-        "code": "F23L3W136",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Напреден веб дизајн",
-        "semester": 5,
-        "status": "Зад.",
-        "signature": "",
-        "group": "",
-        "professor": ""
-      },
-      {
-        "id": 5,
-        "code": "F23L3W140",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Напредно програмирање",
-        "semester": 5,
-        "status": "Изб.",
-        "signature": "",
-        "group": "",
-        "professor": ""
-      }
-    ],
-    "summer_2024_2025": [
-      {
-        "id": 1,
-        "code": "F23L3S100",
-        "hours": "0+0",
-        "kojPat": 1,
-        "name": "Деловна пракса",
-        "semester": 4,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Стојменски",
-        "professor": "Стојменски Александар"
-      },
-      {
-        "id": 2,
-        "code": "F23L2S026",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Маркетинг",
-        "semester": 4,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Стојкоска",
-        "professor": "Стојкоска Билјана"
-      },
-      {
-        "id": 3,
-        "code": "F23L2S017",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Оперативни системи",
-        "semester": 4,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Ацковска",
-        "professor": "Ацковска Невена"
-      },
-      {
-        "id": 4,
-        "code": "F23L2S029",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Софтверско инженерство",
-        "semester": 4,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Маџаров",
-        "professor": "Маџаров Ѓорѓи"
-      },
-      {
-        "id": 5,
-        "code": "F23L2S030",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Вештачка интелигенција",
-        "semester": 4,
-        "status": "Изб.",
-        "signature": "",
-        "group": "проф. Гиевска",
-        "professor": "Гиевска Соња"
-      }
-    ],
-    "winter_2024_2025": [
-      {
-        "id": 1,
-        "code": "F23L2W100",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Економија за ИКТ инженери",
-        "semester": 3,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Саркањац",
-        "professor": "Јанеска Саркањац Смилка"
-      },
-      {
-        "id": 2,
-        "code": "F23L2W014",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Компјутерски мрежи и безбедност",
-        "semester": 3,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Граматиков",
-        "professor": "Граматиков Сашо"
-      },
-      {
-        "id": 3,
-        "code": "F23L2W201",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Примена на алгоритми и податочни структури",
-        "semester": 3,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Котеска",
-        "professor": "Котеска Бојана"
-      },
-      {
-        "id": 4,
-        "code": "F23L2W109",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Интернет програмирање на клиентска страна",
-        "semester": 3,
-        "status": "Изб.",
-        "signature": "Добива",
-        "group": "проф. Лошковска",
-        "professor": "Лошковска Сузана"
-      },
-      {
-        "id": 5,
-        "code": "F23L2W167",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Шаблони за дизајн на кориснички интерфејси",
-        "semester": 3,
-        "status": "Изб.",
-        "signature": "Добива",
-        "group": "проф. Лошковска",
-        "professor": "Лошковска Сузана"
-      }
-    ],
-    "summer_2023_2024": [
-      {
-        "id": 1,
-        "code": "F23L1S003",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Архитектура и организација на компјутери",
-        "semester": 2,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Антовски",
-        "professor": "Антовски Љупчо"
-      },
-      {
-        "id": 2,
-        "code": "F23L1S023",
-        "hours": "3+3",
-        "kojPat": 1,
-        "name": "Бизнис статистика",
-        "semester": 2,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Јанчески",
-        "professor": "Јанчески Методија"
-      },
-      {
-        "id": 3,
-        "code": "F23L1S016",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Објектно-ориентирано програмирање",
-        "semester": 2,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Ѓорѓевиќ",
-        "professor": "Ѓорѓевиќ Дејан"
-      },
-      {
-        "id": 4,
-        "code": "F23L1S146",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Основи на Веб дизајн",
-        "semester": 2,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Стојменски",
-        "professor": "Стојменски Александар"
-      },
-      {
-        "id": 5,
-        "code": "F23L1S120",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Креативни вештини за решавање проблеми",
-        "semester": 2,
-        "status": "Изб.",
-        "signature": "Добива",
-        "group": "проф. Филипоска",
-        "professor": "Филипоска Соња"
-      }
-    ],
-    "winter_2023_2024": [
-      {
-        "id": 1,
-        "code": "F23L1W004",
-        "hours": "0+2",
-        "kojPat": 1,
-        "name": "Спорт и здравје",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "",
-        "group": "",
-        "professor": "Новачевска Славица"
-      },
-      {
-        "id": 2,
-        "code": "F23L1W005",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Бизнис и менаџмент",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Здравески Владимир",
-        "professor": "Здравески Владимир"
-      },
-      {
-        "id": 3,
-        "code": "F23L1W007",
-        "hours": "2+3",
-        "kojPat": 1,
-        "name": "Вовед во компјутерските науки",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Китановски",
-        "professor": "Китановски Иван"
-      },
-      {
-        "id": 4,
-        "code": "F23L1W018",
-        "hours": "4+0",
-        "kojPat": 1,
-        "name": "Професионални вештини",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Мирчев",
-        "professor": "Мирчев Мирослав"
-      },
-      {
-        "id": 5,
-        "code": "F23L1W020",
-        "hours": "2+4",
-        "kojPat": 1,
-        "name": "Структурно програмирање",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "",
-        "group": "проф. Маџаров",
-        "professor": "Маџаров Ѓорѓи"
-      },
-      {
-        "id": 6,
-        "code": "F23L2W003",
-        "hours": "3+3",
-        "kojPat": 1,
-        "name": "Избрани теми од математика",
-        "semester": 1,
-        "status": "Зад.",
-        "signature": "Добива",
-        "group": "проф. Јанчески",
-        "professor": "Јанчески Методија"
-      }
-    ]
-  },
-  "subjects": [
-    {
-      "id": 1,
-      "code": "F23L3W004",
-      "hours": "2+4",
-      "kojPat": 1,
-      "name": "Бази на податоци",
-      "semester": 5,
-      "status": "Зад.",
-      "signature": "",
-      "group": "",
-      "professor": ""
-    },
-    {
-      "id": 2,
-      "code": "F23L3W008",
-      "hours": "2+3",
-      "kojPat": 1,
-      "name": "Вовед во науката за податоци",
-      "semester": 5,
-      "status": "Зад.",
-      "signature": "",
-      "group": "",
-      "professor": ""
-    },
-    {
-      "id": 3,
-      "code": "F23L3W024",
-      "hours": "2+3",
-      "kojPat": 1,
-      "name": "Веб програмирање",
-      "semester": 5,
-      "status": "Зад.",
-      "signature": "",
-      "group": "",
-      "professor": ""
-    },
-    {
-      "id": 4,
-      "code": "F23L3W136",
-      "hours": "2+3",
-      "kojPat": 1,
-      "name": "Напреден веб дизајн",
-      "semester": 5,
-      "status": "Зад.",
-      "signature": "",
-      "group": "",
-      "professor": ""
-    },
-    {
-      "id": 5,
-      "code": "F23L3W140",
-      "hours": "2+4",
-      "kojPat": 1,
-      "name": "Напредно програмирање",
-      "semester": 5,
-      "status": "Изб.",
-      "signature": "",
-      "group": "",
-      "professor": ""
-    }
-  ]
-}
Index: src/lib/auth.ts
===================================================================
--- src/lib/auth.ts	(revision b67f274e88a37e13671a714acf2c630e077f8758)
+++ src/lib/auth.ts	(revision b67f274e88a37e13671a714acf2c630e077f8758)
@@ -0,0 +1,115 @@
+export type AuthTokens = {
+  accessToken: string;
+  refreshToken: string;
+};
+
+type StoredToken = {
+  value: string;
+  expiresAt: number; // epoch ms
+};
+
+const STORAGE_KEYS = {
+  access: 'iknow.auth.access',
+  refresh: 'iknow.auth.refresh',
+} as const;
+
+const ACCESS_TOKEN_TTL_MS = 60 * 60 * 1000; // 1 hour
+const REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000; // ~1 month (30 days)
+
+function isBrowser() {
+  return typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
+}
+
+function readStoredToken(key: string): StoredToken | null {
+  if (!isBrowser()) return null;
+  const raw = window.localStorage.getItem(key);
+  if (!raw) return null;
+
+  try {
+    const parsed = JSON.parse(raw) as StoredToken;
+    if (!parsed?.value || typeof parsed.expiresAt !== 'number') {
+      window.localStorage.removeItem(key);
+      return null;
+    }
+
+    if (Date.now() >= parsed.expiresAt) {
+      window.localStorage.removeItem(key);
+      return null;
+    }
+
+    return parsed;
+  } catch {
+    window.localStorage.removeItem(key);
+    return null;
+  }
+}
+
+function writeStoredToken(key: string, value: string, ttlMs: number) {
+  if (!isBrowser()) return;
+  const payload: StoredToken = {
+    value,
+    expiresAt: Date.now() + ttlMs,
+  };
+  window.localStorage.setItem(key, JSON.stringify(payload));
+}
+
+export function setAuthTokens(tokens: AuthTokens) {
+  writeStoredToken(STORAGE_KEYS.access, tokens.accessToken, ACCESS_TOKEN_TTL_MS);
+  writeStoredToken(STORAGE_KEYS.refresh, tokens.refreshToken, REFRESH_TOKEN_TTL_MS);
+}
+
+export function getAccessToken(): string | null {
+  return readStoredToken(STORAGE_KEYS.access)?.value ?? null;
+}
+
+export function getRefreshToken(): string | null {
+  return readStoredToken(STORAGE_KEYS.refresh)?.value ?? null;
+}
+
+export function clearAuthTokens() {
+  if (!isBrowser()) return;
+  window.localStorage.removeItem(STORAGE_KEYS.access);
+  window.localStorage.removeItem(STORAGE_KEYS.refresh);
+}
+
+export async function login(params: { email: string; password: string }) {
+  const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? 'http://localhost:5147';
+
+  const response = await fetch(`${baseUrl}/api/auth/login`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify({
+      email: params.email,
+      password: params.password,
+      GenerateRefreshToken: true,
+    }),
+  });
+
+  if (!response.ok) {
+    let message = `Login failed (${response.status})`;
+    try {
+      const text = await response.text();
+      if (text) message = text;
+    } catch {
+      // ignore
+    }
+    throw new Error(message);
+  }
+
+  const data = (await response.json()) as {
+    token?: { accessToken?: string; refreshToken?: string };
+  };
+
+  const accessToken = data?.token?.accessToken;
+  const refreshToken = data?.token?.refreshToken;
+
+  if (!accessToken || !refreshToken) {
+    throw new Error('Login response did not contain tokens.');
+  }
+
+  const tokens: AuthTokens = { accessToken, refreshToken };
+  setAuthTokens(tokens);
+  return tokens;
+}
