Summary
The frontend has ~19 TypeScript errors caused by inconsistent handling of the ApiResponse<T> wrapper type. The errors cluster into two categories:
- Half-built
compliance-automation feature — the service file (compliance-automation-service.ts) imports a non-existent api export and exports scheduleAPI/violationAPI/reportAPI while consumers (ScheduleManager.tsx, ViolationAlerts.tsx, compliance-automation/page.tsx) call methods on a complianceAutomationService object that doesn't exist. The whole feature needs to be wired up correctly.
- Admin pages and auth hook — call sites that forget to unwrap
ApiResponse<T> via .data (or that wrongly unwrap raw data as if it were an ApiResponse).
This is a great intermediate-level contribution: real bugs, well-scoped, no infra needed, and the fix teaches the codebase's API client conventions.
Background
Our apiClient returns a wrapped response shape:
// apps/frontend/src/lib/api/api-client.ts
export interface ApiResponse<T> {
success: boolean
data?: T
message?: string
error?: string
}
Every call site is supposed to check response.success first, then read response.data. About 19 sites get this wrong in two opposite ways.
The Two Patterns
Pattern A — Forgot to unwrap (admin pages, useAuthentik, compliance-automation/page.tsx)
// BEFORE - wrong: response is ApiResponse<{users: User[]}>, not {users: User[]}
const response = await apiClient.get<{ users: User[] }>(API_ENDPOINTS.admin.users)
setUsers(response.users) // TS2339: Property 'users' does not exist on type 'ApiResponse<...>'
// AFTER
const response = await apiClient.get<{ users: User[] }>(API_ENDPOINTS.admin.users)
if (response.success && response.data) {
setUsers(response.data.users)
} else {
setError(response.error ?? 'Failed to load users')
}
Pattern B — Treats raw data as if it were a wrapper (ScheduleManager, ViolationAlerts)
// BEFORE - wrong: complianceAutomationService.listSchedules() returns raw data, not ApiResponse
const res = await complianceAutomationService.listSchedules()
if (res.success && res.data) { // TS2339: Property 'success' does not exist
setSchedules(res.data)
}
// AFTER (option 1: change consumer to match service contract)
const res = await complianceAutomationService.listSchedules()
setSchedules(res.schedules) // res is { schedules: ComplianceSchedule[]; total; offset; limit }
// AFTER (option 2: change service to use apiClient's ApiResponse wrapper for consistency)
// - see "Recommended approach" below
Recommended Approach
Unify on apiClient + ApiResponse<T>. The codebase's other services use apiClient directly and expose typed methods that return ApiResponse<T>. The compliance-automation service is the odd one out:
- Rewrite
apps/frontend/src/lib/api/compliance-automation-service.ts:
- Replace
import { api } from '@/lib/api/api-client' with import { apiClient } from '@/lib/api/api-client'.
- Export a single
complianceAutomationService object with methods (listSchedules, createSchedule, listViolations, acknowledgeViolation, listReports, etc.) that all return ApiResponse<T>.
- The current
scheduleAPI/violationAPI/reportAPI/remediationAPI namespacing can be kept as a thin wrapper if you prefer, but consumers need a single import.
- Update consumers (
ScheduleManager.tsx, ViolationAlerts.tsx, compliance-automation/page.tsx) to use the new ApiResponse-shaped return - if (res.success && res.data) { ... }.
- Fix the admin pages and
useAuthentik.ts to properly unwrap response.data before reading inner properties.
Files to Fix
| # |
File |
Line |
Error |
| 1 |
apps/frontend/src/lib/api/compliance-automation-service.ts |
12 |
TS2305: 'api' not exported from api-client |
| 2 |
apps/frontend/src/components/compliance-automation/ScheduleManager.tsx |
30 |
TS2339: success does not exist on {data: ComplianceSchedule[]} |
| 3 |
apps/frontend/src/components/compliance-automation/ScheduleManager.tsx |
45 |
TS2322: string not assignable to 'daily' | 'weekly' | 'monthly' (state needs literal type) |
| 4 |
apps/frontend/src/components/compliance-automation/ScheduleManager.tsx |
49 |
TS2339: success does not exist on ComplianceSchedule |
| 5 |
apps/frontend/src/components/compliance-automation/ViolationAlerts.tsx |
22 |
TS2339: success/data do not exist on raw violations payload |
| 6 |
apps/frontend/src/components/compliance-automation/ViolationAlerts.tsx |
23 |
TS2339: same |
| 7 |
apps/frontend/src/app/(dashboard)/compliance-automation/page.tsx |
41 |
TS2345: ApiResponse<SystemStatus> not assignable to SystemStatus | null |
| 8 |
apps/frontend/src/app/(dashboard)/compliance-automation/page.tsx |
223 |
TS2339: schedules does not exist on ApiResponse<unknown> |
| 9 |
apps/frontend/src/app/(dashboard)/compliance-automation/page.tsx |
299 |
TS2339: violations does not exist on ApiResponse<unknown> |
| 10 |
apps/frontend/src/app/(dashboard)/compliance-automation/page.tsx |
384 |
TS2339: reports does not exist on ApiResponse<unknown> |
| 11 |
apps/frontend/src/app/(dashboard)/admin/audit-log/page.tsx |
56 |
TS2339: logs does not exist on ApiResponse<{logs: AuditLog[]}> |
| 12 |
apps/frontend/src/app/(dashboard)/admin/registrations/page.tsx |
46 |
TS2339: requests does not exist on ApiResponse<{requests: ...}> |
| 13 |
apps/frontend/src/app/(dashboard)/admin/users/page.tsx |
40 |
TS2339: users does not exist on ApiResponse<{users: User[]}> |
| 14 |
apps/frontend/src/lib/api/hooks/useAuthentik.ts |
181 |
TS2339: access_token does not exist on ApiResponse<AuthTokenResponse> |
| 15 |
apps/frontend/src/lib/api/hooks/useAuthentik.ts |
181 |
TS2339: refresh_token does not exist on ApiResponse<AuthTokenResponse> |
| 16 |
apps/frontend/src/lib/api/hooks/useAuthentik.ts |
181 |
TS2339: user does not exist on ApiResponse<AuthTokenResponse> |
| 17 |
apps/frontend/src/lib/api/hooks/useAuthentik.ts |
194 |
TS2345: ApiResponse<AuthTokenResponse> not assignable to AuthTokenResponse |
Acceptance Criteria
How to Reproduce
git clone https://github.com/adhit-r/fairmind.git
cd fairmind/apps/frontend
npm install
npx tsc --noEmit
You should see 19 errors in the files listed above.
How to Test
- Type check (the gate):
npx tsc --noEmit - must pass.
- Manual smoke (optional, needs a running backend): Log in, navigate to
/dashboard/admin/users, /dashboard/admin/audit-log, /dashboard/admin/registrations, and /dashboard/compliance-automation. Pages should render data when the backend responds, and show an error state (not a blank screen) when it doesn't.
- If you can't run the backend, just the type check is acceptable for review.
Estimated Effort
2-4 hours for someone comfortable with TypeScript generics and React state management. The compliance-automation rewrite is the largest piece (~1-2h); admin pages and useAuthentik are smaller mechanical fixes (~1h).
Why This Is a Good Contribution
- Real bugs, not busywork. Several of these would cause runtime crashes today if anyone tried to use the affected pages.
- Self-contained. Frontend only, no backend changes required.
- Teaches the codebase's conventions. You'll understand our API client pattern after fixing this.
- Maintainer support. Comment on this issue with questions and a maintainer will respond within 48 hours.
Help / Questions
Comment on this issue and a maintainer will respond. If you're new to the repo, check CONTRIBUTING.md first. Please claim the issue (comment "I'd like to take this") before starting so we don't have duplicate work.
Summary
The frontend has ~19 TypeScript errors caused by inconsistent handling of the
ApiResponse<T>wrapper type. The errors cluster into two categories:compliance-automationfeature — the service file (compliance-automation-service.ts) imports a non-existentapiexport and exportsscheduleAPI/violationAPI/reportAPIwhile consumers (ScheduleManager.tsx,ViolationAlerts.tsx,compliance-automation/page.tsx) call methods on acomplianceAutomationServiceobject that doesn't exist. The whole feature needs to be wired up correctly.ApiResponse<T>via.data(or that wrongly unwrap raw data as if it were anApiResponse).This is a great intermediate-level contribution: real bugs, well-scoped, no infra needed, and the fix teaches the codebase's API client conventions.
Background
Our
apiClientreturns a wrapped response shape:Every call site is supposed to check
response.successfirst, then readresponse.data. About 19 sites get this wrong in two opposite ways.The Two Patterns
Pattern A — Forgot to unwrap (admin pages, useAuthentik, compliance-automation/page.tsx)
Pattern B — Treats raw data as if it were a wrapper (ScheduleManager, ViolationAlerts)
Recommended Approach
Unify on
apiClient+ApiResponse<T>. The codebase's other services useapiClientdirectly and expose typed methods that returnApiResponse<T>. The compliance-automation service is the odd one out:apps/frontend/src/lib/api/compliance-automation-service.ts:import { api } from '@/lib/api/api-client'withimport { apiClient } from '@/lib/api/api-client'.complianceAutomationServiceobject with methods (listSchedules,createSchedule,listViolations,acknowledgeViolation,listReports, etc.) that all returnApiResponse<T>.scheduleAPI/violationAPI/reportAPI/remediationAPInamespacing can be kept as a thin wrapper if you prefer, but consumers need a single import.ScheduleManager.tsx,ViolationAlerts.tsx,compliance-automation/page.tsx) to use the newApiResponse-shaped return -if (res.success && res.data) { ... }.useAuthentik.tsto properly unwrapresponse.databefore reading inner properties.Files to Fix
apps/frontend/src/lib/api/compliance-automation-service.ts'api'not exported from api-clientapps/frontend/src/components/compliance-automation/ScheduleManager.tsxsuccessdoes not exist on{data: ComplianceSchedule[]}apps/frontend/src/components/compliance-automation/ScheduleManager.tsxstringnot assignable to'daily' | 'weekly' | 'monthly'(state needs literal type)apps/frontend/src/components/compliance-automation/ScheduleManager.tsxsuccessdoes not exist onComplianceScheduleapps/frontend/src/components/compliance-automation/ViolationAlerts.tsxsuccess/datado not exist on raw violations payloadapps/frontend/src/components/compliance-automation/ViolationAlerts.tsxapps/frontend/src/app/(dashboard)/compliance-automation/page.tsxApiResponse<SystemStatus>not assignable toSystemStatus | nullapps/frontend/src/app/(dashboard)/compliance-automation/page.tsxschedulesdoes not exist onApiResponse<unknown>apps/frontend/src/app/(dashboard)/compliance-automation/page.tsxviolationsdoes not exist onApiResponse<unknown>apps/frontend/src/app/(dashboard)/compliance-automation/page.tsxreportsdoes not exist onApiResponse<unknown>apps/frontend/src/app/(dashboard)/admin/audit-log/page.tsxlogsdoes not exist onApiResponse<{logs: AuditLog[]}>apps/frontend/src/app/(dashboard)/admin/registrations/page.tsxrequestsdoes not exist onApiResponse<{requests: ...}>apps/frontend/src/app/(dashboard)/admin/users/page.tsxusersdoes not exist onApiResponse<{users: User[]}>apps/frontend/src/lib/api/hooks/useAuthentik.tsaccess_tokendoes not exist onApiResponse<AuthTokenResponse>apps/frontend/src/lib/api/hooks/useAuthentik.tsrefresh_tokendoes not exist onApiResponse<AuthTokenResponse>apps/frontend/src/lib/api/hooks/useAuthentik.tsuserdoes not exist onApiResponse<AuthTokenResponse>apps/frontend/src/lib/api/hooks/useAuthentik.tsApiResponse<AuthTokenResponse>not assignable toAuthTokenResponseAcceptance Criteria
cd apps/frontend && npx tsc --noEmitreports zero errors in any file listed abovecompliance-automation-service.tsusesapiClient(not the non-existentapi)response.success === false(show an error state, not crash)as any/as ApiResponse<T>casts used to silence errorsScheduleManager.tsxandViolationAlerts.tsxto matchHow to Reproduce
git clone https://github.com/adhit-r/fairmind.git cd fairmind/apps/frontend npm install npx tsc --noEmitYou should see 19 errors in the files listed above.
How to Test
npx tsc --noEmit- must pass./dashboard/admin/users,/dashboard/admin/audit-log,/dashboard/admin/registrations, and/dashboard/compliance-automation. Pages should render data when the backend responds, and show an error state (not a blank screen) when it doesn't.Estimated Effort
2-4 hours for someone comfortable with TypeScript generics and React state management. The compliance-automation rewrite is the largest piece (~1-2h); admin pages and useAuthentik are smaller mechanical fixes (~1h).
Why This Is a Good Contribution
Help / Questions
Comment on this issue and a maintainer will respond. If you're new to the repo, check
CONTRIBUTING.mdfirst. Please claim the issue (comment "I'd like to take this") before starting so we don't have duplicate work.