feat(v3): restructure project + add complete frontend

- Restructure: move backend from new_project/ to backend/
- Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks)
- Add docs/ with SRS specs, user stories, and workflow documentation
- Update .gitignore for new directory layout

Workflows implemented:
  WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration,
  WF4 General English Exam, WF5 Course Generation,
  WF6 Entity Student Onboarding, AI Course Generation,
  Adaptive Learning Engine UI, White-Label Branding, Score Release

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-10 17:26:42 +04:00
commit 11a7265460
392 changed files with 62287 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
import { api } from "@/lib/api-client";
import type { Subject, Domain, Topic, LearningObjective, TaxonomyTree, PaginatedResponse, PaginationParams, ApiSuccessResponse } from "@/types";
export const taxonomyService = {
async listSubjects(): Promise<Subject[]> {
const res = await api.get<{ data: Subject[] } | Subject[]>("/subjects");
return Array.isArray(res) ? res : (res as { data: Subject[] }).data ?? [];
},
async getSubject(id: number): Promise<Subject> {
const res = await api.get<{ data: Subject } | Subject>(`/subjects/${id}`);
return (res as { data: Subject }).data ?? res;
},
async createSubject(data: Partial<Subject>): Promise<Subject> {
return api.post<Subject>("/subjects", data);
},
async updateSubject(id: number, data: Partial<Subject>): Promise<Subject> {
return api.patch<Subject>(`/subjects/${id}`, data);
},
async deleteSubject(id: number): Promise<ApiSuccessResponse> {
return api.delete<ApiSuccessResponse>(`/subjects/${id}`);
},
async getTaxonomyTree(subjectId: number): Promise<TaxonomyTree> {
const res = await api.get<{ data: TaxonomyTree } | TaxonomyTree>(`/subjects/${subjectId}/taxonomy`);
return (res as { data: TaxonomyTree }).data ?? res;
},
async importTaxonomy(subjectId: number, data: FormData): Promise<ApiSuccessResponse> {
return api.upload<ApiSuccessResponse>(`/subjects/${subjectId}/taxonomy/import`, data);
},
async listDomains(params?: { subject_id?: number }): Promise<Domain[]> {
const res = await api.get<{ data: Domain[] } | Domain[]>("/domains", params as Record<string, string | number | boolean | undefined>);
return Array.isArray(res) ? res : (res as { data: Domain[] }).data ?? [];
},
async createDomain(data: Partial<Domain>): Promise<Domain> {
return api.post<Domain>("/domains", data);
},
async updateDomain(id: number, data: Partial<Domain>): Promise<Domain> {
return api.patch<Domain>(`/domains/${id}`, data);
},
async deleteDomain(id: number): Promise<ApiSuccessResponse> {
return api.delete<ApiSuccessResponse>(`/domains/${id}`);
},
async aiSuggestTopics(domainId: number): Promise<{ suggestions: Partial<Topic>[] }> {
return api.post(`/domains/${domainId}/ai-suggest`);
},
async listTopics(params?: { domain_id?: number; subject_id?: number }): Promise<Topic[]> {
const res = await api.get<{ data: Topic[] } | Topic[]>("/topics", params as Record<string, string | number | boolean | undefined>);
return Array.isArray(res) ? res : (res as { data: Topic[] }).data ?? [];
},
async createTopic(data: Partial<Topic>): Promise<Topic> {
return api.post<Topic>("/topics", data);
},
async updateTopic(id: number, data: Partial<Topic>): Promise<Topic> {
return api.patch<Topic>(`/topics/${id}`, data);
},
async deleteTopic(id: number): Promise<ApiSuccessResponse> {
return api.delete<ApiSuccessResponse>(`/topics/${id}`);
},
};