diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..23f3eb29
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,42 @@
+name: Test
+
+on:
+ pull_request:
+ branches: [dev, prod]
+ push:
+ branches: [dev]
+
+concurrency:
+ group: test-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: 'npm'
+ cache-dependency-path: '**/package-lock.json'
+
+ - name: Build Client
+ working-directory: ./client
+ run: |
+ npm install
+ npm run build
+
+ - name: Build Admin
+ working-directory: ./admin
+ run: |
+ npm install
+ npm run build
+
+ - name: Verify Server Dependencies
+ working-directory: ./server
+ run: |
+ npm install
diff --git a/client/src/data/examScheduleData.js b/client/src/data/examScheduleData.js
new file mode 100644
index 00000000..c34858ca
--- /dev/null
+++ b/client/src/data/examScheduleData.js
@@ -0,0 +1,767 @@
+const examScheduleData = {
+ "courseSlotMap": {
+ "BM5103H": "B",
+ "BM5105H": "D",
+ "BM5181H": "A1",
+ "BM6046H": "D1",
+ "BM5104H": "C",
+ "BM5101H": "A",
+ "BM5102H": "E",
+ "BM6014H": "B1",
+ "BM6043H": "C1",
+ "BM6003H": "F1",
+ "BM6047H": "E1",
+ "BT3001": "A1",
+ "CL2101": "E",
+ "CL2103": "F1",
+ "CL2091M": "F",
+ "BT3002": "B1",
+ "BT3003": "C1",
+ "BT3004": "D1",
+ "BT6005": "B1",
+ "BT6034": "C1",
+ "BT6045": "C1",
+ "CL3001": "C1",
+ "CL3002": "D1",
+ "CL3003": "A1",
+ "BT6004": "B1",
+ "BT6051": "E1",
+ "BT6037": "D1",
+ "BT6019": "A1",
+ "BT6041": "D1",
+ "BT2191M": "F",
+ "BT5101": "A",
+ "BT5102": "C",
+ "BT5103": "D",
+ "BT5104": "E",
+ "CL2102": "C",
+ "CL2104": "C",
+ "CL1101": "A1",
+ "CL1102": "A1",
+ "CL1103": "A1",
+ "CL5101": "C",
+ "CL5102": "B",
+ "CL6045": "A",
+ "PH0001": "A",
+ "BM5121H": "C",
+ "BM5122H": "D",
+ "BM5125H": "E",
+ "CL5201": "D",
+ "CL5202": "A",
+ "CL5203": "A",
+ "CL6014": "C1",
+ "CL6023": "F1",
+ "CL6043": "F",
+ "CL6013": "F",
+ "CL6029": "G",
+ "CL6030": "A1",
+ "CL6031": "D1",
+ "CL6040": "E1",
+ "CL6046": "G1",
+ "CL6049": "G1",
+ "CL6055": "B1",
+ "NT6002": "A",
+ "CH1101H": "C1",
+ "CH1102H": "C1",
+ "CH1103H": "C1",
+ "CH1105H": "C1",
+ "CH1104H": "C1",
+ "CH1106H": "C1",
+ "CH1107H": "C1",
+ "CH1108H": "C1",
+ "CH1112": "C1",
+ "CH2102": "E",
+ "CH2103H": "D",
+ "CH2101": "B",
+ "CH3121": "B1",
+ "CH3122": "C1",
+ "CH3123": "A1",
+ "CH4120": "B",
+ "CH4122": "C",
+ "CH4123": "D",
+ "CH4124": "D",
+ "CH4151": "A",
+ "CH4152": "B",
+ "CH4153": "C",
+ "CH4154": "D",
+ "CH5151": "A",
+ "CH5152": "B",
+ "CH5153": "C",
+ "CH5154": "D",
+ "CH7151": "A1",
+ "CH7152": "B1",
+ "CH7153": "C1",
+ "CH7154": "F",
+ "CH7155": "D1",
+ "CH7157": "G",
+ "BM5123H": "B",
+ "BM6006H": "A1",
+ "BM6001H": "B1",
+ "BM6048H": "C1",
+ "EE2101H": "B",
+ "EE2102H": "B",
+ "EE2103": "B",
+ "EE2100": "C",
+ "EE2104": "D",
+ "BM5124H": "A",
+ "EE3000": "A1",
+ "EE3001": "B1",
+ "EE3002": "C1",
+ "EE5000": "A",
+ "EE5001": "B",
+ "EE5002": "C",
+ "EE5102": "B",
+ "EE5103": "C",
+ "EE5100": "A",
+ "PH1110H": "E1",
+ "PH1112H": "E1",
+ "PH1113H": "E1",
+ "PH1111H": "E1",
+ "PH1114H": "E1",
+ "PH1115H": "E1",
+ "PH1116H": "E1",
+ "PH1310H": "E1",
+ "PH1312H": "E1",
+ "PH1311H": "E1",
+ "PH1313H": "E1",
+ "PH1314H": "E1",
+ "PH1315H": "E1",
+ "PH1130": "E1",
+ "PH2091M": "F",
+ "PH2510": "A",
+ "PH2520": "B",
+ "PH2530": "C",
+ "PH2540": "D",
+ "PH2110H": "E",
+ "PH3010": "A1",
+ "PH3030": "B1",
+ "PH3050": "C1",
+ "FT6001": "C",
+ "PH3070": "D1",
+ "FT6005": "E1",
+ "MA1221": "B",
+ "MA2091M": "F",
+ "MA2121": "C",
+ "MA2171": "D",
+ "MA2172": "E",
+ "MA4121": "A",
+ "FT6003": "B",
+ "EE5201": "C",
+ "EE5202": "B",
+ "EE5300": "A",
+ "EE5301": "B",
+ "EE5304": "D",
+ "EE5400": "A",
+ "EE5401": "B",
+ "EE5402": "C",
+ "EE5500": "D",
+ "EE5503": "C",
+ "FT6011": "F1",
+ "EE5501": "B",
+ "FT6007": "G",
+ "PH4150": "B",
+ "PH4130": "C",
+ "PH4010": "E",
+ "PH4030": "A",
+ "PH4050": "B",
+ "PH4070": "D",
+ "PH4090": "C",
+ "PH5350": "E1",
+ "PH5430": "G1",
+ "PH5220": "F1",
+ "MA4122": "B",
+ "MA4123": "C",
+ "MA4124": "D",
+ "MA4125": "A1",
+ "MA4126": "B1",
+ "MA4127": "C1",
+ "PH7010": "A",
+ "MA4128": "D1",
+ "PH7030": "B",
+ "PH7050": "D",
+ "PH7070": "C",
+ "MA4171": "E",
+ "MA4172": "C1",
+ "MA6001": "A1",
+ "MA6002": "B1",
+ "MA6003": "C1",
+ "MA6005": "B1",
+ "MA6004": "A1",
+ "BT2101": "B",
+ "BT2102": "C",
+ "BT2103": "D",
+ "BT2151H": "A",
+ "BT2161H": "A",
+ "BT4408": "A",
+ "BT4412": "C",
+ "BT6021": "B",
+ "BT4418": "C",
+ "BT4413": "D",
+ "BT4421": "E",
+ "BT6020": "F",
+ "BT6030": "B",
+ "BT6044": "E1",
+ "EE4032": "B",
+ "MA3911M": "E1",
+ "MA3921": "D1",
+ "MA3972": "A1",
+ "MA4923": "B",
+ "MA4973": "C",
+ "MA5022": "D",
+ "MA5055": "F",
+ "MA5074": "E1",
+ "MA6056": "E",
+ "MA6072": "D1",
+ "MA6026": "F1",
+ "MA2302H": "A",
+ "MA2306H": "A",
+ "MA2312H": "A",
+ "MA2310H": "A",
+ "MA2305H": "A",
+ "MA2307H": "A",
+ "MA2304H": "A",
+ "MA2309H": "A",
+ "MA2311H": "A",
+ "MA2502H": "A",
+ "MA2506H": "A",
+ "MA2512H": "A",
+ "MA2510H": "A",
+ "MA2505H": "A",
+ "MA2507H": "A",
+ "MA2504H": "A",
+ "MA2509H": "A",
+ "MA2511H": "A",
+ "MA5102": "A",
+ "MA5301H": "B",
+ "MA5501H": "B",
+ "EE3004": "D1",
+ "EE3005": "B1",
+ "EE5042": "F1",
+ "EE2091M": "F",
+ "EE3091M": "E1",
+ "EE3092M": "A",
+ "EE5003": "E",
+ "EE5031": "A1",
+ "EE5134": "A1",
+ "EE5235": "A1",
+ "EE5237": "E1",
+ "EE5334": "B1",
+ "EE5336": "C1",
+ "EE5340": "D1",
+ "EE5432": "D1",
+ "EE5434": "B1",
+ "EE5440": "A1",
+ "EE5536": "E1",
+ "EE5538": "C1",
+ "EE4033": "D",
+ "DA1102": "C1",
+ "DA1103": "D1",
+ "DA2141": "C",
+ "DA2121": "E",
+ "DA2151": "B",
+ "DA3021": "A1",
+ "DA3022": "B1",
+ "DA3031": "C1",
+ "DA3041": "D1",
+ "DA3051": "F1",
+ "DA3091M": "E1",
+ "DA4021": "E",
+ "DA4061": "B",
+ "DA4062": "C",
+ "DA6024": "D",
+ "DA6075": "D",
+ "DA6071": "D",
+ "DA4091M": "A",
+ "DA5011H": "C",
+ "DA5012H": "C",
+ "HS7005": "B",
+ "HS7045": "C",
+ "HS7060": "E",
+ "HS4002": "B",
+ "HS4003": "C",
+ "HS4004": "D",
+ "HS4005": "E",
+ "HS5206": "A1",
+ "HS5207": "B1",
+ "HS5208": "C1",
+ "HS6208": "D1",
+ "HS6224": "E1",
+ "HS6225": "F1",
+ "HS4011": "B",
+ "HS4012": "C",
+ "HS5213": "E",
+ "HS5215": "F",
+ "HS5001": "A1",
+ "HS5002": "D1",
+ "HS5203": "E1",
+ "CS1101": "A1",
+ "CS1109": "A1",
+ "CS1110": "A1",
+ "CS1102": "A1",
+ "CS1103": "A1",
+ "CS1104": "A1",
+ "CS1115": "A1",
+ "CS1116H": "C1",
+ "CS1113": "D1",
+ "CS2001": "A",
+ "CS2002": "D",
+ "CS2003": "E",
+ "CS2201": "C",
+ "CS3103": "B1",
+ "CS3105": "C1",
+ "CS3201M": "E1",
+ "CS4302M": "A",
+ "CS5001": "B",
+ "CS5003": "A",
+ "CS5005": "A",
+ "CS5006": "B",
+ "CS5201": "E1",
+ "CS5399": "F1",
+ "CS5396": "G1",
+ "CS6399": "G",
+ "CS5099": "F1",
+ "CS5097": "G",
+ "CS5191": "D1",
+ "DD1101S": "A",
+ "DD1103S": "B",
+ "DD1104": "C",
+ "DD1105": "D",
+ "DD2091M": "F",
+ "DD3301": "A",
+ "DD3302": "B",
+ "DD3304S": "C",
+ "DD3101M": "E1",
+ "DD4301": "A",
+ "DD4302S": "B",
+ "DD4101M": "A",
+ "DD5101": "A",
+ "DD5103": "B",
+ "DD5104": "C",
+ "DD5501": "A1",
+ "DD5503S": "B1",
+ "DD5510": "C1",
+ "DD5511": "D",
+ "DD5526": "E",
+ "BM6025H": "G1",
+ "DD5557": "E1",
+ "DD5514": "D1",
+ "DD5559": "F1",
+ "ME1101": "D1",
+ "ME1102": "D1",
+ "ME1103": "D1",
+ "ME1104": "D1",
+ "ME1105": "D1",
+ "ME1106": "D1",
+ "ME1107": "D1",
+ "ME1112": "C1",
+ "ME2091M": "F",
+ "ME2101": "B",
+ "ME2102": "C",
+ "ME2103": "D",
+ "ME2104": "E",
+ "ME2105": "D",
+ "ME3301M": "E1",
+ "ME3101": "A1",
+ "ME3312": "C1",
+ "ME3103": "D1",
+ "ME3104": "F1",
+ "ME4401M": "A",
+ "ME5501": "A",
+ "ME5510": "D",
+ "ME5512": "B1",
+ "ME5520": "A1",
+ "ME5521": "E1",
+ "ME5523": "D",
+ "ME5530": "D",
+ "ME5531": "A1",
+ "ME5532": "C",
+ "ME5543": "B1",
+ "ME5550": "D1",
+ "ME5551": "A1",
+ "ME6608": "E1",
+ "ME6609": "B",
+ "ME6644": "E1",
+ "ME6659": "B1",
+ "ME6660": "D1",
+ "ME6674": "F",
+ "ME6680": "C",
+ "ME6689": "E1",
+ "ME6690": "C",
+ "ME6692": "D1",
+ "ME6703": "D1",
+ "CE2101": "C",
+ "CE2103": "B",
+ "CE2105": "D",
+ "CE3102": "A1",
+ "CE3105": "B1",
+ "CE3116": "D1",
+ "CE3903": "C1",
+ "CE3904": "F1",
+ "CE4015": "B",
+ "CE4045": "D",
+ "CE5001": "A",
+ "CE5002": "C",
+ "CE5003": "D",
+ "CE5021": "A1",
+ "CE5022": "C1",
+ "CE5023": "B1",
+ "CE5031": "F",
+ "CE5032": "A1",
+ "CE5041": "A",
+ "CE5042": "E",
+ "CE5051": "A1",
+ "CE5053": "B1",
+ "CE5054": "E",
+ "CE5067": "C1",
+ "CE5081": "B1",
+ "CE5082": "A1",
+ "CE5087": "E",
+ "CE5091": "B",
+ "CE5092": "B1",
+ "CE5093": "C1",
+ "CE6001": "D1",
+ "CE6026": "B",
+ "CE6041": "D",
+ "CE6042": "C",
+ "CE6048": "B",
+ "CE6051": "D",
+ "CE6052": "C",
+ "CE6060": "E",
+ "CS5187": "A1",
+ "CS5393": "E",
+ "CS6398": "B1",
+ "CS6198": "A1",
+ "DD6501S": "G",
+ "DD6509": "G1",
+ "DD7501": "A1",
+ "DD2103": "A",
+ "DD2104": "B",
+ "DD2105": "C",
+ "HS4001": "A",
+ "HS4121": "D",
+ "DA2091M": "F",
+ "PH4101": "A1",
+ "PH4103": "B1",
+ "PH4105": "C1",
+ "PH4107": "D1",
+ "PH3091M": "E1",
+ "HT1101": "A1",
+ "HT1102": "D1",
+ "HT1103": "C1",
+ "HT1104": "B1",
+ "HT1105": "E1",
+ "HT1106": "F1",
+ "HT2301H": "A1",
+ "HT2501H": "A1",
+ "HT2101": "B1",
+ "HT2102": "F1",
+ "HT2103": "E1",
+ "HT2104": "D1",
+ "HT5001": "A",
+ "HT5002": "B",
+ "HT5003": "C",
+ "HT5004": "D",
+ "HT5005": "E",
+ "HS2008": "G",
+ "HS2011": "G",
+ "HS2018": "G",
+ "HS2022.": "G",
+ "HS2027": "G",
+ "HS2031": "G",
+ "HS2040": "G",
+ "HS2013": "G1",
+ "HS2022": "G1",
+ "HS2034": "G1",
+ "HS3004": "G1",
+ "HS2037": "G1",
+ "HS3006": "G1",
+ "HS3042": "G",
+ "HS3045": "G",
+ "HS3011": "G",
+ "HS3012": "G",
+ "HS3013": "G",
+ "HS3014": "G",
+ "HS3019": "G",
+ "HS3030": "G",
+ "EN2101": "C",
+ "EN2103": "B",
+ "EN2104": "D",
+ "EN3101": "A1",
+ "EN3102": "D1",
+ "EN3103": "B1",
+ "EN3104": "C1",
+ "EN5105": "D",
+ "EN5204": "E",
+ "EN5101": "B",
+ "EN5102": "C",
+ "EN5104": "C1",
+ "PH4190": "D",
+ "MA4911M": "A",
+ "DD5550": "F",
+ "BT4422": "D",
+ "DA2101H": "B",
+ "CS5291": "F1",
+ "EE5239": "C1",
+ "EE5341": "E1",
+ "EE5447": "B1",
+ "EE5540": "F",
+ "DA2142": "D",
+ "EE5303": "C",
+ "CS5290": "A1",
+ "HS5216": "F1",
+ "RT5001": "A",
+ "RT5002": "B",
+ "RT5003": "C",
+ "RT5004": "D",
+ "RT5020": "E",
+ "RT5021": "F",
+ "HS6205": "G1",
+ "HS7071": "C",
+ "HS7032": "A",
+ "HS7034": "B",
+ "HS7035": "C",
+ "HS7042": "D",
+ "HS7025": "A",
+ "HS7023": "B",
+ "HS5111": "B",
+ "HS7033": "A1",
+ "HS7036": "B1",
+ "SS5102": "B",
+ "SS5301": "E",
+ "SS5201": "C",
+ "SS5202": "D",
+ "LS5003": "A",
+ "LS5004": "B",
+ "LS5006": "C",
+ "HS7047": "E1",
+ "HS7029": "D1",
+ "HS6230": "D",
+ "HS4918M": "A",
+ "DA4022": "F",
+ "DA6021": "F",
+ "CS2091M": "F",
+ "EN1101": "A1",
+ "MA1301H": "B1",
+ "DA1101H": "G1",
+ "MA1302H": "B1",
+ "MA1303H": "B1",
+ "MA1304H": "B1",
+ "MA1305H": "B1",
+ "MA1306H": "B1",
+ "MA1307H": "B1",
+ "MA1308H": "B1",
+ "MA1309H": "B1",
+ "MA1310H": "B1",
+ "MA1311H": "B1",
+ "MA1312H": "B1",
+ "MA1501H": "B1",
+ "MA1502H": "B1",
+ "MA1503H": "B1",
+ "MA1504H": "B1",
+ "MA1505H": "B1",
+ "MA1506H": "B1",
+ "MA1507H": "B1",
+ "MA1508H": "B1",
+ "MA1509H": "B1",
+ "MA1510H": "B1",
+ "MA1511H": "B1",
+ "MA1512H": "B1",
+ "RT5022": "G",
+ "BT1101": "F1",
+ "EE1101H": "F1",
+ "EE1105H": "F1",
+ "EE1113": "C1",
+ "EE1115": "C1",
+ "EE1102H": "F1",
+ "EE1106H": "F1",
+ "EE1103H": "F1",
+ "EE1104H": "F1",
+ "EE1107H": "F1",
+ "EE1108H": "F1",
+ "CL6019": "B1",
+ "HS7048": "D1",
+ "PH3810M": "E1",
+ "CE6012": "C",
+ "CE4011": "F",
+ "CH4125": "F",
+ "CS4101": "F",
+ "MA4024": "F",
+ "PH4650": "F",
+ "CS3001": "F1",
+ "HS7046": "D",
+ "FT6016": "A",
+ "HS3041": "G1"
+ },
+ "slotSchedule": {
+ "G": {
+ "midSem": {
+ "date": "13-09-2026",
+ "time": "9:00-11:00",
+ "raw": "13-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "14-11-2026",
+ "time": "9:00-12:00",
+ "raw": "14-11-2026 (9:00-12:00)"
+ }
+ },
+ "G1": {
+ "midSem": {
+ "date": "13-09-2026",
+ "time": "14:00-16:00",
+ "raw": "13-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "14-11-2026",
+ "time": "14:00-17:00",
+ "raw": "14-11-2026 (14:00-17:00)"
+ }
+ },
+ "A": {
+ "midSem": {
+ "date": "14-09-2026",
+ "time": "9:00-11:00",
+ "raw": "14-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "15-11-2026",
+ "time": "9:00-12:00",
+ "raw": "15-11-2026 (9:00-12:00)"
+ }
+ },
+ "A1": {
+ "midSem": {
+ "date": "14-09-2026",
+ "time": "14:00-16:00",
+ "raw": "14-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "15-11-2026",
+ "time": "14:00-17:00",
+ "raw": "15-11-2026 (14:00-17:00)"
+ }
+ },
+ "B": {
+ "midSem": {
+ "date": "15-09-2026",
+ "time": "9:00-11:00",
+ "raw": "15-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "16-11-2026",
+ "time": "9:00-12:00",
+ "raw": "16-11-2026 (9:00-12:00)"
+ }
+ },
+ "B1": {
+ "midSem": {
+ "date": "15-09-2026",
+ "time": "14:00-16:00",
+ "raw": "15-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "16-11-2026",
+ "time": "14:00-17:00",
+ "raw": "16-11-2026 (14:00-17:00)"
+ }
+ },
+ "C": {
+ "midSem": {
+ "date": "16-09-2026",
+ "time": "9:00-11:00",
+ "raw": "16-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "17-11-2026",
+ "time": "9:00-12:00",
+ "raw": "17-11-2026 (9:00-12:00)"
+ }
+ },
+ "C1": {
+ "midSem": {
+ "date": "16-09-2026",
+ "time": "14:00-16:00",
+ "raw": "16-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "17-11-2026",
+ "time": "14:00-17:00",
+ "raw": "17-11-2026 (14:00-17:00)"
+ }
+ },
+ "D": {
+ "midSem": {
+ "date": "18-09-2026",
+ "time": "9:00-11:00",
+ "raw": "18-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "19-11-2026",
+ "time": "9:00-12:00",
+ "raw": "19-11-2026 (9:00-12:00)"
+ }
+ },
+ "D1": {
+ "midSem": {
+ "date": "18-09-2026",
+ "time": "14:00-16:00",
+ "raw": "18-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "19-11-2026",
+ "time": "14:00-17:00",
+ "raw": "19-11-2026 (14:00-17:00)"
+ }
+ },
+ "E": {
+ "midSem": {
+ "date": "19-09-2026",
+ "time": "9:00-11:00",
+ "raw": "19-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "20-11-2026",
+ "time": "9:00-12:00",
+ "raw": "20-11-2026 (9:00-12:00)"
+ }
+ },
+ "E1": {
+ "midSem": {
+ "date": "19-09-2026",
+ "time": "14:00-16:00",
+ "raw": "19-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "20-11-2026",
+ "time": "14:00-17:00",
+ "raw": "20-11-2026 (14:00-17:00)"
+ }
+ },
+ "F": {
+ "midSem": {
+ "date": "20-09-2026",
+ "time": "9:00-11:00",
+ "raw": "20-09-2026 (9:00-11:00)"
+ },
+ "endSem": {
+ "date": "21-11-2026",
+ "time": "9:00-12:00",
+ "raw": "21-11-2026 (9:00-12:00)"
+ }
+ },
+ "F1": {
+ "midSem": {
+ "date": "20-09-2026",
+ "time": "14:00-16:00",
+ "raw": "20-09-2026 (14:00-16:00)"
+ },
+ "endSem": {
+ "date": "21-11-2026",
+ "time": "14:00-17:00",
+ "raw": "21-11-2026 (14:00-17:00)"
+ }
+ }
+ }
+};
+
+export default examScheduleData;
diff --git a/client/src/screens/dashboard/components/examschedule/index.jsx b/client/src/screens/dashboard/components/examschedule/index.jsx
new file mode 100644
index 00000000..069f27a5
--- /dev/null
+++ b/client/src/screens/dashboard/components/examschedule/index.jsx
@@ -0,0 +1,152 @@
+import React, { useState, useEffect, useMemo } from "react";
+import { useSelector } from "react-redux";
+import Container from "../../../../components/container";
+import SubHeading from "../../../../components/subheading";
+import Space from "../../../../components/space";
+import { isUserExcluded, getExamScheduleForCourses } from "../../../../utils/examSchedule";
+import { getColors } from "../../../../utils/colors";
+import { capitalise } from "../../../../utils/capitalise";
+import formatLongText from "../../../../utils/formatLongText";
+import "./styles.scss";
+
+/**
+ * Format a DD-MM-YYYY string into a clean readable format, e.g. "14 Sep 2026"
+ */
+function formatDisplayDate(dateStr) {
+ if (!dateStr) return "";
+ const parts = dateStr.split("-").map(Number);
+ if (parts.length !== 3) return dateStr;
+ const [day, month, year] = parts;
+ const dateObj = new Date(year, month - 1, day);
+ if (isNaN(dateObj.getTime())) return dateStr;
+ return dateObj.toLocaleDateString("en-GB", {
+ day: "numeric",
+ month: "short",
+ year: "numeric",
+ });
+}
+
+const ExamScheduleWidget = () => {
+ const userState = useSelector((state) => state.user);
+ const currentUser = userState?.user;
+
+ const [activeTab, setActiveTab] = useState("midSem"); // "midSem" | "endSem"
+ const [isLoading, setIsLoading] = useState(true);
+ const [hasError, setHasError] = useState(false);
+
+ // Business Rule: Exclude B.Tech Semester 1 and Semester 2 users completely (return null)
+ const isExcluded = useMemo(() => isUserExcluded(currentUser), [currentUser]);
+
+ useEffect(() => {
+ if (isExcluded) {
+ setIsLoading(false);
+ return;
+ }
+
+ try {
+ setIsLoading(false);
+ setHasError(false);
+ } catch (err) {
+ setHasError(true);
+ setIsLoading(false);
+ }
+ }, [currentUser, isExcluded]);
+
+ // Cross-reference user's registered courses with exam schedule
+ const scheduledExams = useMemo(() => {
+ if (!currentUser || isExcluded) return [];
+ const registeredCourses = Array.isArray(currentUser.courses) ? currentUser.courses : [];
+ return getExamScheduleForCourses(registeredCourses, undefined, activeTab);
+ }, [currentUser, isExcluded, activeTab]);
+
+ // If user is B.Tech Sem 1 or 2, return null per specification (not an empty state)
+ if (isExcluded) {
+ return null;
+ }
+
+ return (
+ Loading exam schedule... Failed to load exam schedule. No {activeTab === "midSem" ? "Mid-Sem" : "End-Sem"} exams scheduled for your courses.
+ {formatLongText(capitalise(exam.name || exam.code), 36)}
+