From 0cf34552ed60493a73a84f8b1088eaf5c7a2505e Mon Sep 17 00:00:00 2001 From: Justin McLellan Date: Wed, 16 Sep 2026 00:30:23 -0500 Subject: [PATCH 1/2] Migrate authored JavaScript to TypeScript --- .eslintrc.json | 11 + .gitignore | 3 + Makefile | 9 +- README.md | 30 +- ...tor.jsx => AccessibleSelectCellEditor.tsx} | 19 +- components/{Button.jsx => Button.tsx} | 43 +- ...rmationModal.jsx => ConfirmationModal.tsx} | 0 ...gsPanel.jsx => DashboardSettingsPanel.tsx} | 0 components/{DataGrid.jsx => DataGrid.tsx} | 24 +- .../{DropdownMenuPm.js => DropdownMenuPm.tsx} | 24 +- ...downMenuStaff.js => DropdownMenuStaff.tsx} | 32 +- ...{GlobalSnackbar.jsx => GlobalSnackbar.tsx} | 15 +- components/{Layout.jsx => Layout.tsx} | 0 components/{NavItem.jsx => NavItem.tsx} | 0 components/{Navbar.jsx => Navbar.tsx} | 57 +- ...ink.jsx => PageTitleWithUserGuideLink.tsx} | 0 ...ksModal.jsx => TelecallerRemarksModal.tsx} | 23 +- ...ransferButtons.jsx => TransferButtons.tsx} | 0 ...eStudentForm.jsx => UpdateStudentForm.tsx} | 0 ...ActionsCell.jsx => BatchesActionsCell.tsx} | 0 ...esDeleteCell.jsx => BatchesDeleteCell.tsx} | 0 ...esRosterCell.jsx => BatchesRosterCell.tsx} | 0 ...{RemarksActions.jsx => RemarksActions.tsx} | 0 .../{RemarksModal.jsx => RemarksModal.tsx} | 19 +- ...llRenderer.jsx => RemarksCellRenderer.tsx} | 0 ...ntActionCell.jsx => StudentActionCell.tsx} | 3 +- ... => StudentActionCellBulkRegistration.tsx} | 3 +- ...ntDeleteCell.jsx => StudentDeleteCell.tsx} | 0 ....jsx => TelecallerRemarksCellRenderer.tsx} | 0 jest.config.js => jest.config.ts | 8 +- jest.setup.js => jest.setup.ts | 4 +- jsconfig.json | 9 - ...client-compat.js => auth-client-compat.ts} | 22 +- lib/auth.ts | 9 +- lib/{db.js => db.ts} | 6 +- next.config.js | 2 +- openspec/changes/archive/.gitkeep | 0 .../.openspec.yaml | 3 + .../baseline.md | 13 + .../design.md | 86 ++ .../migration-inventory.md | 19 + .../proposal.md | 31 + .../migrate-javascript-to-typescript/tasks.md | 55 + .../validation.md | 28 + openspec/config.yaml | 31 + openspec/specs/.gitkeep | 0 package.json | 1 + pages/{404.jsx => 404.tsx} | 0 pages/{_app.jsx => _app.tsx} | 2 +- pages/{_document.js => _document.tsx} | 4 +- pages/account/{security.jsx => security.tsx} | 10 +- .../{addassignment.js => addassignment.ts} | 0 pages/api/{addremark.js => addremark.ts} | 0 ...studenttobatch.js => addstudenttobatch.ts} | 2 +- pages/api/audit/{create.js => create.ts} | 0 pages/api/audit/{get.js => get.ts} | 2 +- pages/api/{batchcreate.js => batchcreate.ts} | 2 +- pages/api/{batches.js => batches.ts} | 2 +- ...es.js => bulkregcheckstudentduplicates.ts} | 6 +- ...plication.js => bulkstudentapplication.ts} | 6 +- ...DropdownOption.js => addDropdownOption.ts} | 6 +- ...atchStatusRules.js => batchStatusRules.ts} | 6 +- ...pdownOption.js => deleteDropdownOption.ts} | 6 +- ...{dropdownOptions.js => dropdownOptions.ts} | 0 pages/api/{countbatch.js => countbatch.ts} | 2 +- ...ntbatchesmonth.js => countbatchesmonth.ts} | 0 pages/api/{countcourse.js => countcourse.ts} | 2 +- ...ntcoursesmonth.js => countcoursesmonth.ts} | 0 ...ntenrollsmonth.js => countenrollsmonth.ts} | 0 ...ctorsmonth.js => countinstructorsmonth.ts} | 0 ...{countleadsmonth.js => countleadsmonth.ts} | 0 .../api/{countstudent.js => countstudent.ts} | 2 +- .../{counttasmonth.js => counttasmonth.ts} | 0 .../api/{coursecreate.js => coursecreate.ts} | 2 +- ...eleteassignment.js => deleteassignment.ts} | 0 pages/api/{deletebatch.js => deletebatch.ts} | 2 +- .../api/{deletecourse.js => deletecourse.ts} | 2 +- .../{deletestudent.js => deletestudent.ts} | 0 ...frombatch.js => deletestudentfrombatch.ts} | 0 pages/api/{deleteuser.js => deleteuser.ts} | 0 pages/api/{diag-host.js => diag-host.ts} | 0 .../{dropdownDataPm.js => dropdownDataPm.ts} | 8 +- ...pdownDataStaff.js => dropdownDataStaff.ts} | 8 +- ...lCount.js => getBatchesWithEnrollCount.ts} | 0 ...lCount.js => getCoursesWithEnrollCount.ts} | 0 ...ntTrend.js => getYearlyEnrollmentTrend.ts} | 0 ...{getbatchdetails.js => getbatchdetails.ts} | 4 +- .../{getbatchesdata.js => getbatchesdata.ts} | 4 +- ...etbatchesreport.js => getbatchesreport.ts} | 0 ...sesdata-test.js => getcoursesdata-test.ts} | 2 +- .../{getcoursesdata.js => getcoursesdata.ts} | 2 +- ...{getdocumentsfee.js => getdocumentsfee.ts} | 0 pages/api/{getstudent.js => getstudent.ts} | 4 +- ...studentdetails.js => getstudentdetails.ts} | 0 ...{getstudentsdata.js => getstudentsdata.ts} | 2 +- ...edstudents.js => getunassignedstudents.ts} | 0 pages/api/{getuserdata.js => getuserdata.ts} | 0 .../api/{getusersdata.js => getusersdata.ts} | 0 pages/api/{health.js => health.ts} | 0 pages/api/patchattendance.ts | 79 +- ...ntapplication.js => studentapplication.ts} | 2 +- .../telecaller-remarks/{[id].js => [id].ts} | 2 +- .../telecaller-remarks/{index.js => index.ts} | 2 +- ...pdateassignment.js => updateassignment.ts} | 0 ...attendance.js => updatebatchattendance.ts} | 0 .../{updatebatches.js => updatebatches.ts} | 0 ...atebatchstatus.js => updatebatchstatus.ts} | 0 .../api/{updatecourse.js => updatecourse.ts} | 0 ...edocumentsfee.js => updatedocumentsfee.ts} | 0 pages/api/{updategrade.js => updategrade.ts} | 0 .../{updatestudents.js => updatestudents.ts} | 6 +- pages/api/{updateusers.js => updateusers.ts} | 4 +- pages/api/{usercreate.js => usercreate.ts} | 4 +- pages/api/{users.js => users.ts} | 2 +- pages/api/va-remarks/{[id].js => [id].ts} | 0 pages/api/va-remarks/{index.js => index.ts} | 0 ...orgot-password.jsx => forgot-password.tsx} | 0 ...{reset-password.jsx => reset-password.tsx} | 0 pages/auth/{sign-in.jsx => sign-in.tsx} | 0 pages/batch/{[id].jsx => [id].tsx} | 235 ++-- pages/{batches.jsx => batches.tsx} | 70 +- ...ration.jsx => bulkstudentregistration.tsx} | 39 +- ...{configurations.jsx => configurations.tsx} | 15 +- .../{coursedetails.jsx => coursedetails.tsx} | 0 pages/{courses.jsx => courses.tsx} | 48 +- ...tions.jsx => dashboard-configurations.tsx} | 0 pages/{default.jsx => default.tsx} | 26 +- pages/{demo.jsx => demo.tsx} | 0 pages/{index.jsx => index.tsx} | 0 pages/{logs.js => logs.tsx} | 36 +- pages/{reports.jsx => reports.tsx} | 25 +- pages/student/{[id].jsx => [id].tsx} | 16 +- ...gistration.jsx => studentregistration.tsx} | 313 ++--- pages/{students.jsx => students.tsx} | 35 +- pages/users.jsx | 1112 ----------------- pages/users.tsx | 1045 ++++++++++++++++ scripts/check-authored-javascript.sh | 50 + styles/globals.css | 5 +- tailwind.config.js => tailwind.config.ts | 6 +- tsconfig.json | 15 +- ...{sort-table.test.js => sort-table.test.ts} | 0 ...-helpers.js => ag-grid-column-helpers.tsx} | 26 +- utils/{auditLogger.js => auditLogger.ts} | 8 +- ...ch-data-helper.js => batch-data-helper.ts} | 0 ...tus-derived.js => batch-status-derived.ts} | 4 +- ...config.js => batch-status-rules-config.ts} | 4 +- ...ns-defs.js => get-batches-columns-defs.ts} | 2 +- ...ons-access.js => configurations-access.ts} | 0 utils/course-days.ts | 6 +- ...ion-helper.js => csv-generation-helper.ts} | 13 +- ...ate-normalizers.js => date-normalizers.ts} | 4 +- ...{dropdown-config.js => dropdown-config.ts} | 4 +- utils/{export-to-csv.js => export-to-csv.ts} | 10 +- ...ort.js => generate-batch-status-report.ts} | 2 +- ...mn-defs.js => get-courses-column-defs.tsx} | 4 +- ...et.js => get-date-with-timezone-offset.ts} | 0 utils/get-row-style.ts | 12 +- ...umns-defs.js => get-users-columns-defs.ts} | 2 +- ...rid-comparators.js => grid-comparators.ts} | 0 utils/map-attendance-data-to-csv.ts | 6 +- ...{session-helpers.js => session-helpers.ts} | 0 utils/{sort-table.js => sort-table.ts} | 0 ...a.js => search-and-update-student-data.ts} | 52 +- ...js => use-get-student-column-defs-bulk.ts} | 13 +- ...defs.js => use-get-student-column-defs.ts} | 6 +- utils/types/global.d.ts | 34 +- 166 files changed, 2319 insertions(+), 1827 deletions(-) rename components/{AccessibleSelectCellEditor.jsx => AccessibleSelectCellEditor.tsx} (84%) rename components/{Button.jsx => Button.tsx} (68%) rename components/{ConfirmationModal.jsx => ConfirmationModal.tsx} (100%) rename components/{DashboardSettingsPanel.jsx => DashboardSettingsPanel.tsx} (100%) rename components/{DataGrid.jsx => DataGrid.tsx} (81%) rename components/{DropdownMenuPm.js => DropdownMenuPm.tsx} (67%) rename components/{DropdownMenuStaff.js => DropdownMenuStaff.tsx} (50%) rename components/{GlobalSnackbar.jsx => GlobalSnackbar.tsx} (74%) rename components/{Layout.jsx => Layout.tsx} (100%) rename components/{NavItem.jsx => NavItem.tsx} (100%) rename components/{Navbar.jsx => Navbar.tsx} (91%) rename components/{PageTitleWithUserGuideLink.jsx => PageTitleWithUserGuideLink.tsx} (100%) rename components/{TelecallerRemarksModal.jsx => TelecallerRemarksModal.tsx} (95%) rename components/{TransferButtons.jsx => TransferButtons.tsx} (100%) rename components/{UpdateStudentForm.jsx => UpdateStudentForm.tsx} (100%) rename components/batches/{BatchesActionsCell.jsx => BatchesActionsCell.tsx} (100%) rename components/batches/{BatchesDeleteCell.jsx => BatchesDeleteCell.tsx} (100%) rename components/batches/{BatchesRosterCell.jsx => BatchesRosterCell.tsx} (100%) rename components/batches/{RemarksActions.jsx => RemarksActions.tsx} (100%) rename components/batches/{RemarksModal.jsx => RemarksModal.tsx} (95%) rename components/students/{RemarksCellRenderer.jsx => RemarksCellRenderer.tsx} (100%) rename components/students/{StudentActionCell.jsx => StudentActionCell.tsx} (97%) rename components/students/{StudentActionCellBulkRegistration.jsx => StudentActionCellBulkRegistration.tsx} (96%) rename components/students/{StudentDeleteCell.jsx => StudentDeleteCell.tsx} (100%) rename components/students/{TelecallerRemarksCellRenderer.jsx => TelecallerRemarksCellRenderer.tsx} (100%) rename jest.config.js => jest.config.ts (84%) rename jest.setup.js => jest.setup.ts (96%) delete mode 100644 jsconfig.json rename lib/{auth-client-compat.js => auth-client-compat.ts} (58%) rename lib/{db.js => db.ts} (64%) create mode 100644 openspec/changes/archive/.gitkeep create mode 100644 openspec/changes/migrate-javascript-to-typescript/.openspec.yaml create mode 100644 openspec/changes/migrate-javascript-to-typescript/baseline.md create mode 100644 openspec/changes/migrate-javascript-to-typescript/design.md create mode 100644 openspec/changes/migrate-javascript-to-typescript/migration-inventory.md create mode 100644 openspec/changes/migrate-javascript-to-typescript/proposal.md create mode 100644 openspec/changes/migrate-javascript-to-typescript/tasks.md create mode 100644 openspec/changes/migrate-javascript-to-typescript/validation.md create mode 100644 openspec/config.yaml create mode 100644 openspec/specs/.gitkeep rename pages/{404.jsx => 404.tsx} (100%) rename pages/{_app.jsx => _app.tsx} (97%) rename pages/{_document.js => _document.tsx} (95%) rename pages/account/{security.jsx => security.tsx} (95%) rename pages/api/{addassignment.js => addassignment.ts} (100%) rename pages/api/{addremark.js => addremark.ts} (100%) rename pages/api/{addstudenttobatch.js => addstudenttobatch.ts} (99%) rename pages/api/audit/{create.js => create.ts} (100%) rename pages/api/audit/{get.js => get.ts} (98%) rename pages/api/{batchcreate.js => batchcreate.ts} (97%) rename pages/api/{batches.js => batches.ts} (91%) rename pages/api/{bulkregcheckstudentduplicates.js => bulkregcheckstudentduplicates.ts} (94%) rename pages/api/{bulkstudentapplication.js => bulkstudentapplication.ts} (96%) rename pages/api/configurations/{addDropdownOption.js => addDropdownOption.ts} (78%) rename pages/api/configurations/{batchStatusRules.js => batchStatusRules.ts} (88%) rename pages/api/configurations/{deleteDropdownOption.js => deleteDropdownOption.ts} (80%) rename pages/api/configurations/{dropdownOptions.js => dropdownOptions.ts} (100%) rename pages/api/{countbatch.js => countbatch.ts} (84%) rename pages/api/{countbatchesmonth.js => countbatchesmonth.ts} (100%) rename pages/api/{countcourse.js => countcourse.ts} (86%) rename pages/api/{countcoursesmonth.js => countcoursesmonth.ts} (100%) rename pages/api/{countenrollsmonth.js => countenrollsmonth.ts} (100%) rename pages/api/{countinstructorsmonth.js => countinstructorsmonth.ts} (100%) rename pages/api/{countleadsmonth.js => countleadsmonth.ts} (100%) rename pages/api/{countstudent.js => countstudent.ts} (86%) rename pages/api/{counttasmonth.js => counttasmonth.ts} (100%) rename pages/api/{coursecreate.js => coursecreate.ts} (96%) rename pages/api/{deleteassignment.js => deleteassignment.ts} (100%) rename pages/api/{deletebatch.js => deletebatch.ts} (93%) rename pages/api/{deletecourse.js => deletecourse.ts} (94%) rename pages/api/{deletestudent.js => deletestudent.ts} (100%) rename pages/api/{deletestudentfrombatch.js => deletestudentfrombatch.ts} (100%) rename pages/api/{deleteuser.js => deleteuser.ts} (100%) rename pages/api/{diag-host.js => diag-host.ts} (100%) rename pages/api/{dropdownDataPm.js => dropdownDataPm.ts} (88%) rename pages/api/{dropdownDataStaff.js => dropdownDataStaff.ts} (87%) rename pages/api/{getBatchesWithEnrollCount.js => getBatchesWithEnrollCount.ts} (100%) rename pages/api/{getCoursesWithEnrollCount.js => getCoursesWithEnrollCount.ts} (100%) rename pages/api/{getYearlyEnrollmentTrend.js => getYearlyEnrollmentTrend.ts} (100%) rename pages/api/{getbatchdetails.js => getbatchdetails.ts} (98%) rename pages/api/{getbatchesdata.js => getbatchesdata.ts} (98%) rename pages/api/{getbatchesreport.js => getbatchesreport.ts} (100%) rename pages/api/{getcoursesdata-test.js => getcoursesdata-test.ts} (95%) rename pages/api/{getcoursesdata.js => getcoursesdata.ts} (94%) rename pages/api/{getdocumentsfee.js => getdocumentsfee.ts} (100%) rename pages/api/{getstudent.js => getstudent.ts} (98%) rename pages/api/{getstudentdetails.js => getstudentdetails.ts} (100%) rename pages/api/{getstudentsdata.js => getstudentsdata.ts} (98%) rename pages/api/{getunassignedstudents.js => getunassignedstudents.ts} (100%) rename pages/api/{getuserdata.js => getuserdata.ts} (100%) rename pages/api/{getusersdata.js => getusersdata.ts} (100%) rename pages/api/{health.js => health.ts} (100%) rename pages/api/{studentapplication.js => studentapplication.ts} (98%) rename pages/api/telecaller-remarks/{[id].js => [id].ts} (98%) rename pages/api/telecaller-remarks/{index.js => index.ts} (97%) rename pages/api/{updateassignment.js => updateassignment.ts} (100%) rename pages/api/{updatebatchattendance.js => updatebatchattendance.ts} (100%) rename pages/api/{updatebatches.js => updatebatches.ts} (100%) rename pages/api/{updatebatchstatus.js => updatebatchstatus.ts} (100%) rename pages/api/{updatecourse.js => updatecourse.ts} (100%) rename pages/api/{updatedocumentsfee.js => updatedocumentsfee.ts} (100%) rename pages/api/{updategrade.js => updategrade.ts} (100%) rename pages/api/{updatestudents.js => updatestudents.ts} (97%) rename pages/api/{updateusers.js => updateusers.ts} (98%) rename pages/api/{usercreate.js => usercreate.ts} (96%) rename pages/api/{users.js => users.ts} (91%) rename pages/api/va-remarks/{[id].js => [id].ts} (100%) rename pages/api/va-remarks/{index.js => index.ts} (100%) rename pages/auth/{forgot-password.jsx => forgot-password.tsx} (100%) rename pages/auth/{reset-password.jsx => reset-password.tsx} (100%) rename pages/auth/{sign-in.jsx => sign-in.tsx} (100%) rename pages/batch/{[id].jsx => [id].tsx} (94%) rename pages/{batches.jsx => batches.tsx} (96%) rename pages/{bulkstudentregistration.jsx => bulkstudentregistration.tsx} (95%) rename pages/{configurations.jsx => configurations.tsx} (98%) rename pages/{coursedetails.jsx => coursedetails.tsx} (100%) rename pages/{courses.jsx => courses.tsx} (94%) rename pages/{dashboard-configurations.jsx => dashboard-configurations.tsx} (100%) rename pages/{default.jsx => default.tsx} (97%) rename pages/{demo.jsx => demo.tsx} (100%) rename pages/{index.jsx => index.tsx} (100%) rename pages/{logs.js => logs.tsx} (88%) rename pages/{reports.jsx => reports.tsx} (97%) rename pages/student/{[id].jsx => [id].tsx} (97%) rename pages/{studentregistration.jsx => studentregistration.tsx} (90%) rename pages/{students.jsx => students.tsx} (94%) delete mode 100644 pages/users.jsx create mode 100644 pages/users.tsx create mode 100755 scripts/check-authored-javascript.sh rename tailwind.config.js => tailwind.config.ts (52%) rename utils/__tests__/{sort-table.test.js => sort-table.test.ts} (100%) rename utils/{ag-grid-column-helpers.js => ag-grid-column-helpers.tsx} (88%) rename utils/{auditLogger.js => auditLogger.ts} (91%) rename utils/{batch-data-helper.js => batch-data-helper.ts} (100%) rename utils/{batch-status-derived.js => batch-status-derived.ts} (97%) rename utils/{batch-status-rules-config.js => batch-status-rules-config.ts} (97%) rename utils/batches/{get-batches-columns-defs.js => get-batches-columns-defs.ts} (99%) rename utils/{configurations-access.js => configurations-access.ts} (100%) rename utils/{csv-generation-helper.js => csv-generation-helper.ts} (95%) rename utils/{date-normalizers.js => date-normalizers.ts} (96%) rename utils/{dropdown-config.js => dropdown-config.ts} (96%) rename utils/{export-to-csv.js => export-to-csv.ts} (92%) rename utils/{generate-batch-status-report.js => generate-batch-status-report.ts} (99%) rename utils/{get-courses-column-defs.js => get-courses-column-defs.tsx} (97%) rename utils/{get-date-with-timezone-offset.js => get-date-with-timezone-offset.ts} (100%) rename utils/{get-users-columns-defs.js => get-users-columns-defs.ts} (98%) rename utils/{grid-comparators.js => grid-comparators.ts} (100%) rename utils/{session-helpers.js => session-helpers.ts} (100%) rename utils/{sort-table.js => sort-table.ts} (100%) rename utils/students/{search-and-update-student-data.js => search-and-update-student-data.ts} (54%) rename utils/students/{use-get-student-column-defs-bulk.js => use-get-student-column-defs-bulk.ts} (97%) rename utils/students/{use-get-student-column-defs.js => use-get-student-column-defs.ts} (98%) diff --git a/.eslintrc.json b/.eslintrc.json index 921c7998..bc81a2df 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -15,6 +15,17 @@ "argsIgnorePattern": "^_", "ignoreRestSiblings": true } + ], + "no-restricted-syntax": [ + "error", + { + "selector": "TSAsExpression", + "message": "Do not use TypeScript type assertions; use declarations, generics, unions, or narrowing." + }, + { + "selector": "TSTypeAssertion", + "message": "Do not use TypeScript type assertions; use declarations, generics, unions, or narrowing." + } ] } } diff --git a/.gitignore b/.gitignore index 66ca24f4..51afb61b 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,6 @@ tsconfig.tsbuildinfo # Claude Code local settings .claude/ + +# Codex/agent-local skills +/.agents/ diff --git a/Makefile b/Makefile index 09cbb9ad..45d0c3e3 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SHELL := /bin/sh NPM ?= npm -.PHONY: help install dev build start lint format format-fix type-check check test docker-up docker-down docker-logs ci +.PHONY: help install dev build start lint format format-fix type-check check check-javascript test docker-up docker-down docker-logs ci help: @echo "Available targets:" @@ -15,6 +15,7 @@ help: @echo " format - prettier --check" @echo " format-fix - prettier --write" @echo " type-check - tsc --noEmit" + @echo " check-javascript - reject authored JavaScript/JSX" @echo " check - lint + type-check + format + build" @echo " docker-up - docker compose up -d" @echo " docker-down - docker compose down" @@ -45,7 +46,10 @@ format-fix: type-check: $(NPM) run type-check -check: format-fix lint type-check build +check-javascript: + $(NPM) run check:javascript + +check: format-fix lint type-check check-javascript build ci: check @@ -57,4 +61,3 @@ docker-down: docker-logs: docker compose logs -f - diff --git a/README.md b/README.md index 6b82355b..0f35274e 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ pnpm dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. +The `pages/api` directory is mapped to `/api/*`. TypeScript files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. @@ -101,7 +101,7 @@ Next only substitutes `NEXT_PUBLIC_*` values that exist at build time, so `process.env.NEXT_PUBLIC_BASE_URL` survives in the compiled server output as a real runtime lookup and each environment supplies its own value through Coolify. -This works only because the value is read server-side (`utils/auditLogger.js`, +This works only because the value is read server-side (`utils/auditLogger.ts`, imported solely by `pages/api/*`). Reading it from client code would yield `undefined` in the browser — see the comment in the Dockerfile before changing this. @@ -153,12 +153,12 @@ must not be committed or uploaded. PAGES FOLDER: files requiring mods to API routes: -- batch > [id].jsx -- student > [id].jsx -- batches.jsx -- courses.jsx -- students.jsx -- users.jsx +- batch > [id].tsx +- student > [id].tsx +- batches.tsx +- courses.tsx +- students.tsx +- users.tsx .env file: @@ -166,19 +166,19 @@ PAGES FOLDER: files requiring mods to API routes: NOTE REGARDING BATCH ATTENDANCE DROPDOWN: -- This can be edited in /utils/tableHelper.js +- This can be edited in `/utils` TypeScript helpers. ## CSV download - related modifications - Components to modify: - Vercel - - /pages/users.jsx\*: name, accessor, htmlFor, id (input attribute) - - /pages/api/getuserdata.js (if necessary) + - /pages/users.tsx\*: name, accessor, htmlFor, id (input attribute) + - /pages/api/getuserdata.ts (if necessary) - DreamHost - [https://visionaid.dreamhosters.com/csv/staff.php](https://visionaid.dreamhosters.com/csv/staff.php): th tag list - /csv/csvfunctions.php: fputcsv(), while($row = mysqli_fetch_assoc($result)), get_all_user_records() -\*users.jsx file (staff) example: database and UI column names: +\*users.tsx file (staff) example: database and UI column names: id Id email Email @@ -203,7 +203,7 @@ action Action ## Errors - Can't log out? If you get an error preventing you from logging out: - please modify the /components/Navbar.jsx file per comments + please modify the `/components/Navbar.tsx` file per comments at the top of the file. ## Dreamhost-mtl folder (DB hosting) diff --git a/components/AccessibleSelectCellEditor.jsx b/components/AccessibleSelectCellEditor.tsx similarity index 84% rename from components/AccessibleSelectCellEditor.jsx rename to components/AccessibleSelectCellEditor.tsx index f510c2a4..25db8ade 100644 --- a/components/AccessibleSelectCellEditor.jsx +++ b/components/AccessibleSelectCellEditor.tsx @@ -1,13 +1,24 @@ import React, { forwardRef, useRef, useState, useEffect } from "react"; -const AccessibleSelectCellEditor = forwardRef((props) => { +const AccessibleSelectCellEditor = forwardRef< + HTMLSelectElement, + { + onValueChange: (_value: string) => void; + value?: string | number; + values?: Array; + stopEditing?: (_cancel?: boolean) => void; + colDef?: { headerName?: string; field?: string }; + data?: { name?: string }; + formatter?: (_value: string | number | undefined) => string; + } +>((props, _ref) => { const { onValueChange, value, values, stopEditing, colDef, data, formatter } = props; // If a formatter is provided, use it to format the initial value for display const formattedValue = formatter ? formatter(value) : value; const [selectedValue, setSelectedValue] = useState(formattedValue || ""); - const selectRef = useRef(null); + const selectRef = useRef(null); const valueRef = useRef(formattedValue || ""); // Auto-focus and open the select element when the editor mounts @@ -35,13 +46,13 @@ const AccessibleSelectCellEditor = forwardRef((props) => { setSelectedValue(newValue); onValueChange(newValue); - stopEditing(); + stopEditing?.(); }; const handleKeyDown = (e) => { if (e.key === "Escape") { e.stopPropagation(); - const resetValue = formatter ? formatter(value) : value; + const resetValue = formatter ? formatter(value) : (value ?? ""); valueRef.current = resetValue; setSelectedValue(resetValue); if (stopEditing) { diff --git a/components/Button.jsx b/components/Button.tsx similarity index 68% rename from components/Button.jsx rename to components/Button.tsx index a1ea3151..f410b1e8 100644 --- a/components/Button.jsx +++ b/components/Button.tsx @@ -1,6 +1,26 @@ import Image from "next/image"; -import PropTypes from "prop-types"; import styles from "@/styles/Button.module.css"; +import type { CSSProperties, KeyboardEventHandler, MouseEventHandler } from "react"; +import type { StaticImport } from "next/dist/shared/lib/get-img-props"; + +interface ButtonProps { + text?: string; + onClick?: MouseEventHandler; + onKeyDown?: KeyboardEventHandler; + iconSrc?: string | StaticImport; + iconAlt?: string; + isLight?: boolean; + style?: CSSProperties; + className?: string; + disabled?: boolean; + ariaControls?: string; + ariaExpanded?: boolean | "true" | "false"; + ariaLabel?: string; + courseName?: string; + batch?: string | number; + tabIndex?: number; + "aria-label"?: string; +} export default function Button({ text, @@ -17,7 +37,7 @@ export default function Button({ ariaLabel, // if needed, explicitly specify courseName, // if needed, pass only as data-attr batch, // if needed, pass only as data-attr -}) { +}: ButtonProps) { const buttonClassName = [isLight ? styles.genericButtonLight : styles.genericButtonDark, className] .filter(Boolean) .join(" "); @@ -40,7 +60,7 @@ export default function Button({ aria-controls={ariaControls || undefined} aria-label={computedAriaLabel} data-course-name={courseName ?? undefined} - data-batch={batch ?? undefined} + data-batch={batch == null ? undefined : String(batch)} > {iconSrc ? ( ); } - -Button.propTypes = { - text: PropTypes.string, - onClick: PropTypes.func, - onKeyDown: PropTypes.func, - iconSrc: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // next/image StaticImport allow (next/image StaticImport allow) - iconAlt: PropTypes.string, - isLight: PropTypes.bool, - style: PropTypes.object, - className: PropTypes.string, - disabled: PropTypes.bool, - ariaControls: PropTypes.string, - ariaExpanded: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), - ariaLabel: PropTypes.string, - courseName: PropTypes.string, - batch: PropTypes.string, -}; diff --git a/components/ConfirmationModal.jsx b/components/ConfirmationModal.tsx similarity index 100% rename from components/ConfirmationModal.jsx rename to components/ConfirmationModal.tsx diff --git a/components/DashboardSettingsPanel.jsx b/components/DashboardSettingsPanel.tsx similarity index 100% rename from components/DashboardSettingsPanel.jsx rename to components/DashboardSettingsPanel.tsx diff --git a/components/DataGrid.jsx b/components/DataGrid.tsx similarity index 81% rename from components/DataGrid.jsx rename to components/DataGrid.tsx index 340d7ed5..acf9bf61 100644 --- a/components/DataGrid.jsx +++ b/components/DataGrid.tsx @@ -3,7 +3,17 @@ import { smartComparator } from "@/utils/grid-comparators"; import { AgGridReact } from "ag-grid-react"; import React from "react"; -export function DataGrid({ title, rowData, onGridReady, onCellKeyDown, exitTargetId }) { +type RowData = Record; + +interface DataGridProps { + title: string; + rowData: RowData[]; + onGridReady?: (_event: unknown) => void; + onCellKeyDown?: (_event: unknown) => void; + exitTargetId: string; +} + +export function DataGrid({ title, rowData, onGridReady, onCellKeyDown, exitTargetId }: DataGridProps) { const columnDefs = [ { field: "id", @@ -39,7 +49,7 @@ export function DataGrid({ title, rowData, onGridReady, onCellKeyDown, exitTarge comparator: smartComparator, }; - const tabToNextCell = (params) => { + const tabToNextCell = (params: any) => { const { previousCellPosition, backwards, api } = params; if (!previousCellPosition) return null; @@ -59,13 +69,17 @@ export function DataGrid({ title, rowData, onGridReady, onCellKeyDown, exitTarge return null; } + const firstColumn = api.getAllDisplayedColumns()[0]; + if (!firstColumn) return null; + return { rowIndex: nextRowIndex, - column: api.getAllDisplayedColumns()[0], + rowPinned: null, + column: firstColumn, }; }; - const rowSelection = { + const rowSelection: { mode: "multiRow" } = { mode: "multiRow", }; @@ -83,7 +97,7 @@ export function DataGrid({ title, rowData, onGridReady, onCellKeyDown, exitTarge Use arrow keys to navigate rows. Press space to select students.

- enableCellTextSelection={true} rowData={rowData} columnDefs={columnDefs} diff --git a/components/DropdownMenuPm.js b/components/DropdownMenuPm.tsx similarity index 67% rename from components/DropdownMenuPm.js rename to components/DropdownMenuPm.tsx index 507ec08b..db96f553 100644 --- a/components/DropdownMenuPm.js +++ b/components/DropdownMenuPm.tsx @@ -1,16 +1,28 @@ // Part of page group that populate a dynamic dropdown from MySQL: -// /db.js -// /pages/api/dropdownDataPm.js -// /pages/api/dropdownDataStaff.js -// /components/DropdownMenuStaff.js +// /lib/db.ts +// /pages/api/dropdownDataPm.ts +// /pages/api/dropdownDataStaff.ts +// /components/DropdownMenuStaff.tsx // Author: Dante Ciolfi // Last update: 5/4/2024 import React, { useState, useEffect } from "react"; import styles from "../styles/Home.module.css"; -const DropdownMenuPm = ({ id = "PM", name = "PM", required = false, className = "", selectedValue = "" }) => { - const [options, setOptions] = useState([]); +const DropdownMenuPm = ({ + id = "PM", + name = "PM", + required = false, + className = "", + selectedValue = "", +}: { + id?: string; + name?: string; + required?: boolean; + className?: string; + selectedValue?: string; +}) => { + const [options, setOptions] = useState>([]); useEffect(() => { const fetchData = async () => { diff --git a/components/DropdownMenuStaff.js b/components/DropdownMenuStaff.tsx similarity index 50% rename from components/DropdownMenuStaff.js rename to components/DropdownMenuStaff.tsx index 401e5f40..bc9ba1d3 100644 --- a/components/DropdownMenuStaff.js +++ b/components/DropdownMenuStaff.tsx @@ -1,16 +1,28 @@ // Part of page group that populate a dynamic dropdown from MySQL: -// /db.js -// /pages/api/dropdownDataPm.js -// /pages/api/dropdownDataStaff.js -// /components/DropdownMenuStaff.js +// /lib/db.ts +// /pages/api/dropdownDataPm.ts +// /pages/api/dropdownDataStaff.ts +// /components/DropdownMenuStaff.tsx // Author: Dante Ciolfi // Last update: 5/4/2024 import React, { useState, useEffect } from "react"; import styles from "../styles/Home.module.css"; -const DropdownMenuStaff = ({ id, name }) => { - const [options, setOptions] = useState([]); +const DropdownMenuStaff = ({ + id, + name, + className = "", + selectedValue = "", + required = false, +}: { + id?: string; + name?: string; + className?: string; + selectedValue?: string; + required?: boolean; +}) => { + const [options, setOptions] = useState>([]); useEffect(() => { const fetchData = async () => { const response = await fetch("/api/dropdownDataStaff"); @@ -21,7 +33,13 @@ const DropdownMenuStaff = ({ id, name }) => { }, []); return ( - {options.map((option) => (