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:
28
src/hooks/queries/index.ts
Normal file
28
src/hooks/queries/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export * from "./keys";
|
||||
export * from "./useCourseware";
|
||||
export * from "./useCommunication";
|
||||
export * from "./useNotifications";
|
||||
export * from "./useFaq";
|
||||
export * from "./useLms";
|
||||
export * from "./useUsers";
|
||||
export * from "./useExams";
|
||||
export * from "./useAssignments";
|
||||
export * from "./useEntities";
|
||||
export * from "./useAdaptive";
|
||||
export * from "./useAcademic";
|
||||
export * from "./useAdmissions";
|
||||
export * from "./useInstitutionalExams";
|
||||
export * from "./useStudentLeave";
|
||||
export * from "./useFees";
|
||||
export * from "./useLessons";
|
||||
export * from "./useGradebook";
|
||||
export * from "./useStudentProgress";
|
||||
export * from "./useLibrary";
|
||||
export * from "./useActivities";
|
||||
export * from "./useFacilities";
|
||||
export * from "./useExamTemplates";
|
||||
export * from "./useEntityOnboarding";
|
||||
export * from "./useAiCourse";
|
||||
export * from "./useAdaptiveEngine";
|
||||
export * from "./useScoreRelease";
|
||||
export { usePermissions } from "../usePermissions";
|
||||
304
src/hooks/queries/keys.ts
Normal file
304
src/hooks/queries/keys.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
export const queryKeys = {
|
||||
auth: {
|
||||
user: ["auth", "user"] as const,
|
||||
},
|
||||
users: {
|
||||
all: ["users"] as const,
|
||||
list: (params: Record<string, unknown>) => ["users", "list", params] as const,
|
||||
detail: (id: number) => ["users", id] as const,
|
||||
},
|
||||
entities: {
|
||||
all: ["entities"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["entities", "list", params] as const,
|
||||
detail: (id: number) => ["entities", id] as const,
|
||||
roles: (entityId: number) => ["entities", entityId, "roles"] as const,
|
||||
permissions: (entityId: number) => ["entities", entityId, "permissions"] as const,
|
||||
},
|
||||
exams: {
|
||||
all: ["exams"] as const,
|
||||
list: (module: string, params?: Record<string, unknown>) => ["exams", module, params] as const,
|
||||
detail: (module: string, id: number) => ["exams", module, id] as const,
|
||||
rubrics: (params?: Record<string, unknown>) => ["exams", "rubrics", params] as const,
|
||||
rubricGroups: (params?: Record<string, unknown>) => ["exams", "rubric-groups", params] as const,
|
||||
structures: (params?: Record<string, unknown>) => ["exams", "structures", params] as const,
|
||||
avatars: ["exams", "avatars"] as const,
|
||||
},
|
||||
assignments: {
|
||||
all: ["assignments"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["assignments", "list", params] as const,
|
||||
detail: (id: number) => ["assignments", id] as const,
|
||||
},
|
||||
classrooms: {
|
||||
all: ["classrooms"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["classrooms", "list", params] as const,
|
||||
detail: (id: number) => ["classrooms", id] as const,
|
||||
},
|
||||
stats: {
|
||||
sessions: (params?: Record<string, unknown>) => ["stats", "sessions", params] as const,
|
||||
stats: (params?: Record<string, unknown>) => ["stats", "stats", params] as const,
|
||||
statistical: (params?: Record<string, unknown>) => ["stats", "statistical", params] as const,
|
||||
performance: (params?: Record<string, unknown>) => ["stats", "performance", params] as const,
|
||||
},
|
||||
training: {
|
||||
all: ["training"] as const,
|
||||
tips: (params?: Record<string, unknown>) => ["training", "tips", params] as const,
|
||||
walkthroughs: (module?: string) => ["training", "walkthroughs", module] as const,
|
||||
},
|
||||
subscriptions: {
|
||||
packages: ["subscriptions", "packages"] as const,
|
||||
payments: (params?: Record<string, unknown>) => ["subscriptions", "payments", params] as const,
|
||||
discounts: (params?: Record<string, unknown>) => ["subscriptions", "discounts", params] as const,
|
||||
},
|
||||
tickets: {
|
||||
all: ["tickets"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["tickets", "list", params] as const,
|
||||
assigned: ["tickets", "assigned"] as const,
|
||||
},
|
||||
approvals: {
|
||||
all: ["approvals"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["approvals", "list", params] as const,
|
||||
},
|
||||
taxonomy: {
|
||||
subjects: ["taxonomy", "subjects"] as const,
|
||||
subject: (id: number) => ["taxonomy", "subjects", id] as const,
|
||||
tree: (subjectId: number) => ["taxonomy", "tree", subjectId] as const,
|
||||
domains: (params?: Record<string, unknown>) => ["taxonomy", "domains", params] as const,
|
||||
topics: (params?: Record<string, unknown>) => ["taxonomy", "topics", params] as const,
|
||||
},
|
||||
adaptive: {
|
||||
proficiency: (params?: Record<string, unknown>) => ["adaptive", "proficiency", params] as const,
|
||||
summary: ["adaptive", "proficiency", "summary"] as const,
|
||||
plan: (params?: Record<string, unknown>) => ["adaptive", "plan", params] as const,
|
||||
topicContent: (topicId: number) => ["adaptive", "topic", topicId, "content"] as const,
|
||||
diagnostic: (sessionId: number) => ["adaptive", "diagnostic", sessionId] as const,
|
||||
},
|
||||
resources: {
|
||||
all: ["resources"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["resources", "list", params] as const,
|
||||
},
|
||||
lms: {
|
||||
courses: (params?: Record<string, unknown>) => ["lms", "courses", params] as const,
|
||||
course: (id: number) => ["lms", "courses", id] as const,
|
||||
students: (params?: Record<string, unknown>) => ["lms", "students", params] as const,
|
||||
teachers: (params?: Record<string, unknown>) => ["lms", "teachers", params] as const,
|
||||
batches: (params?: Record<string, unknown>) => ["lms", "batches", params] as const,
|
||||
batch: (id: number) => ["lms", "batches", id] as const,
|
||||
timetable: (params?: Record<string, unknown>) => ["lms", "timetable", params] as const,
|
||||
attendance: (params?: Record<string, unknown>) => ["lms", "attendance", params] as const,
|
||||
grades: (params?: Record<string, unknown>) => ["lms", "grades", params] as const,
|
||||
},
|
||||
analytics: {
|
||||
student: (params?: Record<string, unknown>) => ["analytics", "student", params] as const,
|
||||
class: (params?: Record<string, unknown>) => ["analytics", "class", params] as const,
|
||||
subject: (params?: Record<string, unknown>) => ["analytics", "subject", params] as const,
|
||||
contentGaps: (params?: Record<string, unknown>) => ["analytics", "content-gaps", params] as const,
|
||||
alerts: ["analytics", "alerts"] as const,
|
||||
},
|
||||
academicYears: {
|
||||
all: ["academic-years"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["academic-years", "list", params] as const,
|
||||
detail: (id: number) => ["academic-years", id] as const,
|
||||
},
|
||||
academicTerms: {
|
||||
all: ["academic-terms"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["academic-terms", "list", params] as const,
|
||||
detail: (id: number) => ["academic-terms", id] as const,
|
||||
},
|
||||
departments: {
|
||||
all: ["departments"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["departments", "list", params] as const,
|
||||
detail: (id: number) => ["departments", id] as const,
|
||||
},
|
||||
admissionRegisters: {
|
||||
all: ["admission-registers"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["admission-registers", "list", params] as const,
|
||||
detail: (id: number) => ["admission-registers", id] as const,
|
||||
},
|
||||
admissions: {
|
||||
all: ["admissions"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["admissions", "list", params] as const,
|
||||
detail: (id: number) => ["admissions", id] as const,
|
||||
},
|
||||
instExamSessions: {
|
||||
all: ["inst-exam-sessions"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["inst-exam-sessions", "list", params] as const,
|
||||
detail: (id: number) => ["inst-exam-sessions", id] as const,
|
||||
},
|
||||
examTypes: {
|
||||
all: ["exam-types"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["exam-types", "list", params] as const,
|
||||
},
|
||||
gradeConfigs: {
|
||||
all: ["grade-configs"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["grade-configs", "list", params] as const,
|
||||
},
|
||||
resultTemplates: {
|
||||
all: ["result-templates"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["result-templates", "list", params] as const,
|
||||
},
|
||||
marksheets: {
|
||||
all: ["marksheets"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["marksheets", "list", params] as const,
|
||||
detail: (id: number) => ["marksheets", id] as const,
|
||||
},
|
||||
courseAssignments: {
|
||||
all: ["course-assignments"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["course-assignments", "list", params] as const,
|
||||
detail: (id: number) => ["course-assignments", id] as const,
|
||||
},
|
||||
assignmentTypes: {
|
||||
all: ["assignment-types"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["assignment-types", "list", params] as const,
|
||||
},
|
||||
subjectRegistrations: {
|
||||
all: ["subject-registrations"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["subject-registrations", "list", params] as const,
|
||||
available: (params?: Record<string, unknown>) => ["subject-registrations", "available", params] as const,
|
||||
},
|
||||
chapters: (courseId: number) => ["chapters", "list", courseId] as const,
|
||||
chapter: (id: number) => ["chapters", "detail", id] as const,
|
||||
chapterMaterials: (chapterId: number) => ["chapter-materials", chapterId] as const,
|
||||
chapterProgress: (chapterId: number) => ["chapter-progress", chapterId] as const,
|
||||
discussionBoards: (params?: Record<string, unknown>) => ["discussion-boards", params] as const,
|
||||
posts: (boardId: number, params?: Record<string, unknown>) => ["posts", boardId, params] as const,
|
||||
announcements: (params?: Record<string, unknown>) => ["announcements", params] as const,
|
||||
messages: (params?: Record<string, unknown>) => ["messages", params] as const,
|
||||
sentMessages: (params?: Record<string, unknown>) => ["messages", "sent", params] as const,
|
||||
unreadMessages: ["messages", "unread-count"] as const,
|
||||
notifications: (params?: Record<string, unknown>) => ["notifications", params] as const,
|
||||
unreadNotifications: ["notifications", "unread-count"] as const,
|
||||
notificationRules: ["notification-rules"] as const,
|
||||
notificationPreferences: ["notification-preferences"] as const,
|
||||
faqCategories: (audience?: string) => ["faq", "categories", audience] as const,
|
||||
faqItems: (params?: Record<string, unknown>) => ["faq", "items", params] as const,
|
||||
studentLeaves: {
|
||||
all: ["student-leaves"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["student-leaves", "list", params] as const,
|
||||
},
|
||||
studentLeaveTypes: {
|
||||
all: ["student-leave-types"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["student-leave-types", "list", params] as const,
|
||||
},
|
||||
feesPlans: {
|
||||
all: ["fees-plans"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["fees-plans", "list", params] as const,
|
||||
detail: (id: number) => ["fees-plans", id] as const,
|
||||
},
|
||||
studentFees: {
|
||||
all: ["student-fees"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["student-fees", "list", params] as const,
|
||||
},
|
||||
feesTerms: {
|
||||
all: ["fees-terms"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["fees-terms", "list", params] as const,
|
||||
},
|
||||
lessons: {
|
||||
all: ["lessons"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["lessons", "list", params] as const,
|
||||
},
|
||||
gradebooks: {
|
||||
all: ["gradebooks"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["gradebooks", "list", params] as const,
|
||||
},
|
||||
gradebookLines: {
|
||||
all: ["gradebook-lines"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["gradebook-lines", "list", params] as const,
|
||||
},
|
||||
gradingAssignments: {
|
||||
all: ["grading-assignments"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["grading-assignments", "list", params] as const,
|
||||
},
|
||||
studentProgress: {
|
||||
all: ["student-progress"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["student-progress", "list", params] as const,
|
||||
},
|
||||
libraryMedia: {
|
||||
all: ["library-media"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["library-media", "list", params] as const,
|
||||
},
|
||||
libraryMovements: {
|
||||
all: ["library-movements"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["library-movements", "list", params] as const,
|
||||
},
|
||||
libraryCards: {
|
||||
all: ["library-cards"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["library-cards", "list", params] as const,
|
||||
},
|
||||
activities: {
|
||||
all: ["activities"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["activities", "list", params] as const,
|
||||
},
|
||||
activityTypes: {
|
||||
all: ["activity-types"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["activity-types", "list", params] as const,
|
||||
},
|
||||
facilities: {
|
||||
all: ["facilities"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["facilities", "list", params] as const,
|
||||
},
|
||||
assets: {
|
||||
all: ["assets"] as const,
|
||||
list: (params?: Record<string, unknown>) => ["assets", "list", params] as const,
|
||||
},
|
||||
signup: {
|
||||
goals: ["signup", "goals"] as const,
|
||||
},
|
||||
placement: {
|
||||
results: (sessionId: string) => ["placement", "results", sessionId] as const,
|
||||
speakingStatus: (sessionId: string) => ["placement", "speaking-status", sessionId] as const,
|
||||
learningPath: (sessionId: string) => ["placement", "learning-path", sessionId] as const,
|
||||
},
|
||||
examTemplates: {
|
||||
international: ["exam-templates", "international"] as const,
|
||||
custom: ["exam-templates", "custom"] as const,
|
||||
detail: (id: number) => ["exam-templates", id] as const,
|
||||
},
|
||||
ieltsExam: {
|
||||
skills: (examId: number) => ["ielts-exam", examId, "skills"] as const,
|
||||
contentPool: (examId: number, filters?: Record<string, unknown>) => ["ielts-exam", examId, "content-pool", filters] as const,
|
||||
validation: (examId: number) => ["ielts-exam", examId, "validation"] as const,
|
||||
},
|
||||
examSession: {
|
||||
session: (examId: number) => ["exam-session", examId] as const,
|
||||
status: (examId: number) => ["exam-session", examId, "status"] as const,
|
||||
},
|
||||
grading: {
|
||||
queue: (params?: Record<string, unknown>) => ["grading", "queue", params] as const,
|
||||
response: (attemptId: number, skill: string) => ["grading", attemptId, "response", skill] as const,
|
||||
rubric: (attemptId: number, skill: string) => ["grading", attemptId, "rubric", skill] as const,
|
||||
},
|
||||
courseGeneration: {
|
||||
gapAnalysis: (source: string, sourceId: number) => ["course-gen", "gap", source, sourceId] as const,
|
||||
course: (id: number) => ["course-gen", "course", id] as const,
|
||||
},
|
||||
aiCourse: {
|
||||
course: (id: number) => ["ai-course", id] as const,
|
||||
tracks: (id: number) => ["ai-course", id, "tracks"] as const,
|
||||
quality: (id: number) => ["ai-course", id, "quality"] as const,
|
||||
ieltsValidation: (id: number) => ["ai-course", id, "ielts-validation"] as const,
|
||||
englishTaxonomy: ["ai-course", "english", "taxonomy"] as const,
|
||||
},
|
||||
entityOnboarding: {
|
||||
credentials: (params?: Record<string, unknown>) => ["entity-onboarding", "credentials", params] as const,
|
||||
},
|
||||
adaptiveEngine: {
|
||||
dashboard: ["adaptive-engine", "dashboard"] as const,
|
||||
students: (params?: Record<string, unknown>) => ["adaptive-engine", "students", params] as const,
|
||||
signals: (studentId: number) => ["adaptive-engine", "signals", studentId] as const,
|
||||
ability: (studentId: number) => ["adaptive-engine", "ability", studentId] as const,
|
||||
settings: ["adaptive-engine", "settings"] as const,
|
||||
},
|
||||
levelMapping: {
|
||||
get: (entityId: number) => ["level-mapping", entityId] as const,
|
||||
},
|
||||
branding: {
|
||||
get: (entityId: number) => ["branding", entityId] as const,
|
||||
current: ["branding", "current"] as const,
|
||||
},
|
||||
scoreRelease: {
|
||||
pending: (params?: Record<string, unknown>) => ["score-release", "pending", params] as const,
|
||||
},
|
||||
verification: {
|
||||
verify: (hash: string) => ["verification", hash] as const,
|
||||
},
|
||||
};
|
||||
104
src/hooks/queries/useAcademic.ts
Normal file
104
src/hooks/queries/useAcademic.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { academicService } from "@/services/academic.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type {
|
||||
AcademicYearCreateRequest,
|
||||
AcademicTermCreateRequest,
|
||||
DepartmentCreateRequest,
|
||||
} from "@/types/academic";
|
||||
|
||||
export function useAcademicYears(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.academicYears.list(params),
|
||||
queryFn: () => academicService.listAcademicYears(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAcademicYear(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.academicYears.detail(id),
|
||||
queryFn: () => academicService.getAcademicYear(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAcademicTerms(yearId?: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.academicTerms.list({ year_id: yearId }),
|
||||
queryFn: () => academicService.listTerms({ year_id: yearId }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDepartments(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.departments.list(params),
|
||||
queryFn: () => academicService.listDepartments(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAcademicYear() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AcademicYearCreateRequest) => academicService.createAcademicYear(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.academicYears.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateAcademicYear() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<AcademicYearCreateRequest> }) =>
|
||||
academicService.updateAcademicYear(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.academicYears.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteAcademicYear() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => academicService.deleteAcademicYear(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.academicYears.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateTerms() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (yearId: number) => academicService.generateTerms(yearId),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.academicTerms.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAcademicTerm() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AcademicTermCreateRequest) => academicService.createTerm(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.academicTerms.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateDepartment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: DepartmentCreateRequest) => academicService.createDepartment(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.departments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateDepartment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<DepartmentCreateRequest> }) =>
|
||||
academicService.updateDepartment(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.departments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteDepartment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => academicService.deleteDepartment(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.departments.all }),
|
||||
});
|
||||
}
|
||||
60
src/hooks/queries/useActivities.ts
Normal file
60
src/hooks/queries/useActivities.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { activityService } from "@/services/activity.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { ActivityCreateRequest, ActivityType } from "@/types/activity";
|
||||
|
||||
export function useActivities(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.activities.list(params),
|
||||
queryFn: () => activityService.listActivities(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useActivityTypes(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.activityTypes.list(params),
|
||||
queryFn: () => activityService.listActivityTypes(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateActivity() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: ActivityCreateRequest) => activityService.createActivity(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.activities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateActivity() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<ActivityCreateRequest> }) =>
|
||||
activityService.updateActivity(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.activities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteActivity() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => activityService.deleteActivity(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.activities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateActivityType() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Pick<ActivityType, "name">) => activityService.createActivityType(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.activityTypes.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteActivityType() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => activityService.deleteActivityType(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.activityTypes.all }),
|
||||
});
|
||||
}
|
||||
81
src/hooks/queries/useAdaptive.ts
Normal file
81
src/hooks/queries/useAdaptive.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { adaptiveService } from "@/services";
|
||||
import { taxonomyService } from "@/services";
|
||||
|
||||
export function useSubjects() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.taxonomy.subjects,
|
||||
queryFn: () => taxonomyService.listSubjects(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTaxonomyTree(subjectId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.taxonomy.tree(subjectId),
|
||||
queryFn: () => taxonomyService.getTaxonomyTree(subjectId),
|
||||
enabled: !!subjectId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useProficiency(params?: { subject_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptive.proficiency(params),
|
||||
queryFn: () => adaptiveService.getProficiency(params ?? {}),
|
||||
});
|
||||
}
|
||||
|
||||
export function useProficiencySummary() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptive.summary,
|
||||
queryFn: () => adaptiveService.getProficiencySummary(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useLearningPlan(params?: { subject_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptive.plan(params),
|
||||
queryFn: () => adaptiveService.getLearningPlan(params ?? {}),
|
||||
enabled: !!params?.subject_id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTopicContent(topicId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptive.topicContent(topicId),
|
||||
queryFn: () => adaptiveService.getTopicContent(topicId),
|
||||
enabled: !!topicId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useStartDiagnostic() {
|
||||
return useMutation({
|
||||
mutationFn: (subjectId: number) => adaptiveService.startDiagnostic(subjectId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAnswerDiagnostic() {
|
||||
return useMutation({
|
||||
mutationFn: adaptiveService.answerDiagnostic,
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateLearningPlan() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { subject_id: number; target_completion?: string }) =>
|
||||
adaptiveService.generateLearningPlan(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["adaptive", "plan"] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitMasteryQuiz() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ topicId, answers }: { topicId: number; answers: unknown[] }) =>
|
||||
adaptiveService.submitMasteryQuiz(topicId, answers),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["adaptive"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
51
src/hooks/queries/useAdaptiveEngine.ts
Normal file
51
src/hooks/queries/useAdaptiveEngine.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { adaptiveEngineService } from "@/services/adaptive-engine.service";
|
||||
import type { AdaptiveThresholdSettings } from "@/types";
|
||||
|
||||
export function useAdaptiveDashboard() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptiveEngine.dashboard,
|
||||
queryFn: () => adaptiveEngineService.getDashboard(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdaptiveStudents(params?: { page?: number; limit?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptiveEngine.students(params as Record<string, unknown> | undefined),
|
||||
queryFn: () => adaptiveEngineService.getStudents(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentSignals(studentId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptiveEngine.signals(studentId ?? 0),
|
||||
queryFn: () => adaptiveEngineService.getStudentSignals(studentId!),
|
||||
enabled: !!studentId && studentId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentAbility(studentId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptiveEngine.ability(studentId ?? 0),
|
||||
queryFn: () => adaptiveEngineService.getStudentAbility(studentId!),
|
||||
enabled: !!studentId && studentId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdaptiveSettings() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.adaptiveEngine.settings,
|
||||
queryFn: () => adaptiveEngineService.getSettings(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateAdaptiveSettings() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AdaptiveThresholdSettings) => adaptiveEngineService.updateSettings(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.adaptiveEngine.settings });
|
||||
},
|
||||
});
|
||||
}
|
||||
94
src/hooks/queries/useAdmissions.ts
Normal file
94
src/hooks/queries/useAdmissions.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { admissionService } from "@/services/admission.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type {
|
||||
AdmissionRegisterCreateRequest,
|
||||
AdmissionCreateRequest,
|
||||
} from "@/types/admission";
|
||||
|
||||
export function useAdmissionRegisters(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.admissionRegisters.list(params),
|
||||
queryFn: () => admissionService.listRegisters(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdmissions(params?: PaginationParams & { state?: string; register_id?: number; course_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.admissions.list(params),
|
||||
queryFn: () => admissionService.listAdmissions(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdmission(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.admissions.detail(id),
|
||||
queryFn: () => admissionService.getAdmission(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAdmissionRegister() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AdmissionRegisterCreateRequest) => admissionService.createRegister(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissionRegisters.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useConfirmRegister() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.confirmRegister(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissionRegisters.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCloseRegister() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.closeRegister(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissionRegisters.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAdmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AdmissionCreateRequest) => admissionService.createAdmission(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitAdmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.submitAdmission(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useConfirmAdmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.confirmAdmission(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAdmitAdmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.admitAdmission(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRejectAdmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => admissionService.rejectAdmission(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.admissions.all }),
|
||||
});
|
||||
}
|
||||
97
src/hooks/queries/useAiCourse.ts
Normal file
97
src/hooks/queries/useAiCourse.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { aiCourseService } from "@/services/ai-course.service";
|
||||
import type { ExaminerReview } from "@/types";
|
||||
|
||||
export function useAiCourse(courseId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.aiCourse.course(courseId ?? 0),
|
||||
queryFn: () => aiCourseService.getCourse(courseId!),
|
||||
enabled: !!courseId && courseId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAiCourseTracks(courseId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.aiCourse.tracks(courseId ?? 0),
|
||||
queryFn: () => aiCourseService.getTracks(courseId!),
|
||||
enabled: !!courseId && courseId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateEnglishCourse() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { current_level: string; target_level: string; learning_style: string[] }) =>
|
||||
aiCourseService.createEnglish(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["ai-course"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateIeltsCourse() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { exam_type: string; target_band: number; skills: string[] }) =>
|
||||
aiCourseService.createIelts(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["ai-course"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useQualityGate(courseId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.aiCourse.quality(courseId ?? 0),
|
||||
queryFn: () => aiCourseService.getQualityGate(courseId!),
|
||||
enabled: !!courseId && courseId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useApproveQuality() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (courseId: number) => aiCourseService.approveQuality(courseId),
|
||||
onSuccess: (_d, courseId) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.aiCourse.quality(courseId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.aiCourse.course(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRejectQuality() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ courseId, notes }: { courseId: number; notes: string }) =>
|
||||
aiCourseService.rejectQuality(courseId, notes),
|
||||
onSuccess: (_d, { courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.aiCourse.quality(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useEnglishTaxonomy() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.aiCourse.englishTaxonomy,
|
||||
queryFn: () => aiCourseService.getEnglishTaxonomy(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useIeltsValidation(courseId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.aiCourse.ieltsValidation(courseId ?? 0),
|
||||
queryFn: () => aiCourseService.getIeltsValidation(courseId!),
|
||||
enabled: !!courseId && courseId > 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitExaminerReview() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: ExaminerReview) => aiCourseService.submitExaminerReview(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["ai-course"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
27
src/hooks/queries/useAssignments.ts
Normal file
27
src/hooks/queries/useAssignments.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { assignmentsService, type AssignmentListParams } from "@/services/assignments.service";
|
||||
import type { AssignmentCreateRequest } from "@/types";
|
||||
|
||||
export function useAssignments(params?: AssignmentListParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.assignments.list(params),
|
||||
queryFn: () => assignmentsService.list(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAssignment(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.assignments.detail(id),
|
||||
queryFn: () => assignmentsService.getById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AssignmentCreateRequest) => assignmentsService.create(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["assignments"] }),
|
||||
});
|
||||
}
|
||||
140
src/hooks/queries/useCommunication.ts
Normal file
140
src/hooks/queries/useCommunication.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { communicationService } from "@/services/communication.service";
|
||||
import type {
|
||||
DiscussionBoard,
|
||||
PostCreateRequest,
|
||||
AnnouncementCreateRequest,
|
||||
MessageCreateRequest,
|
||||
} from "@/types/communication";
|
||||
import type { PaginationParams } from "@/types";
|
||||
|
||||
export function useDiscussionBoards(params?: { course_id?: number; batch_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.discussionBoards(params),
|
||||
queryFn: () => communicationService.listDiscussionBoards(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePosts(boardId: number, params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.posts(boardId, params),
|
||||
queryFn: () => communicationService.listPosts(boardId, params),
|
||||
enabled: !!boardId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAnnouncements(params?: { course_id?: number; priority?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.announcements(params),
|
||||
queryFn: () => communicationService.listAnnouncements(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMessages(params?: { is_read?: boolean } & PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.messages(params),
|
||||
queryFn: () => communicationService.listMessages(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSentMessages(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.sentMessages(params),
|
||||
queryFn: () => communicationService.listSentMessages(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUnreadMessageCount() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.unreadMessages,
|
||||
queryFn: () => communicationService.getUnreadMessageCount(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateBoard() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (
|
||||
data: Partial<DiscussionBoard> & Pick<DiscussionBoard, "name" | "course_id">,
|
||||
) => communicationService.createDiscussionBoard(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["discussion-boards"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreatePost() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ boardId, data }: { boardId: number; data: PostCreateRequest }) =>
|
||||
communicationService.createPost(boardId, data),
|
||||
onSuccess: (_, { boardId }) => {
|
||||
qc.invalidateQueries({ queryKey: ["posts", boardId] });
|
||||
qc.invalidateQueries({ queryKey: ["discussion-boards"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function usePinPost() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: number; body?: { pinned?: boolean } }) =>
|
||||
communicationService.pinPost(id, body),
|
||||
onSuccess: (post) => {
|
||||
qc.invalidateQueries({ queryKey: ["posts", post.board_id] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useResolvePost() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => communicationService.resolvePost(id),
|
||||
onSuccess: (post) => {
|
||||
qc.invalidateQueries({ queryKey: ["posts", post.board_id] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAnnouncement() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AnnouncementCreateRequest) => communicationService.createAnnouncement(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["announcements"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function usePublishAnnouncement() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => communicationService.publishAnnouncement(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["announcements"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSendMessage() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: MessageCreateRequest) => communicationService.sendMessage(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["messages"] });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.unreadMessages });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteMessage() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => communicationService.deleteMessage(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["messages"] });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.unreadMessages });
|
||||
},
|
||||
});
|
||||
}
|
||||
48
src/hooks/queries/useCourseGeneration.ts
Normal file
48
src/hooks/queries/useCourseGeneration.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { courseGenerationService } from "@/services/course-generation.service";
|
||||
import type { CourseConfig, CourseProgress } from "@/types";
|
||||
|
||||
export function useGapAnalysis(source: "exam" | "placement", sourceId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.courseGeneration.gapAnalysis(source, sourceId),
|
||||
queryFn: () => courseGenerationService.getGapAnalysis(source, sourceId),
|
||||
enabled: !!sourceId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAutoGenerateCourse() {
|
||||
return useMutation({
|
||||
mutationFn: (sourceId: number) => courseGenerationService.autoGenerate(sourceId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCourse() {
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<CourseConfig>) => courseGenerationService.create(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCourseDetail(courseId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.courseGeneration.course(courseId),
|
||||
queryFn: () => courseGenerationService.getCourse(courseId),
|
||||
enabled: !!courseId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateCourse() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { courseId: number; payload: Partial<CourseConfig> }) =>
|
||||
courseGenerationService.updateCourse(data.courseId, data.payload),
|
||||
onSuccess: (_, vars) => qc.invalidateQueries({ queryKey: queryKeys.courseGeneration.course(vars.courseId) }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTrackProgress() {
|
||||
return useMutation({
|
||||
mutationFn: (data: { courseId: number; progress: CourseProgress }) =>
|
||||
courseGenerationService.trackProgress(data.courseId, data.progress),
|
||||
});
|
||||
}
|
||||
204
src/hooks/queries/useCourseware.ts
Normal file
204
src/hooks/queries/useCourseware.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { coursewareService } from "@/services/courseware.service";
|
||||
import type {
|
||||
ChapterCreateRequest,
|
||||
WorkbenchGenerateRequest,
|
||||
} from "@/types/courseware";
|
||||
|
||||
export function useChapters(courseId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.chapters(courseId),
|
||||
queryFn: () => coursewareService.listChapters(courseId),
|
||||
enabled: !!courseId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useChapter(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.chapter(id),
|
||||
queryFn: () => coursewareService.getChapter(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useChapterMaterials(chapterId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.chapterMaterials(chapterId),
|
||||
queryFn: () => coursewareService.listMaterials(chapterId),
|
||||
enabled: !!chapterId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useChapterProgress(chapterId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.chapterProgress(chapterId),
|
||||
queryFn: () => coursewareService.getChapterProgress(chapterId),
|
||||
enabled: !!chapterId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ courseId, data }: { courseId: number; data: ChapterCreateRequest }) =>
|
||||
coursewareService.createChapter(courseId, data),
|
||||
onSuccess: (_, { courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<ChapterCreateRequest> }) =>
|
||||
coursewareService.updateChapter(id, data),
|
||||
onSuccess: (chapter) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(chapter.id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(chapter.course_id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, courseId }: { id: number; courseId: number }) =>
|
||||
coursewareService.deleteChapter(id),
|
||||
onSuccess: (_, { courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUnlockChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, courseId }: { id: number; courseId: number }) =>
|
||||
coursewareService.unlockChapter(id),
|
||||
onSuccess: (_, { id, courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useLockChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, courseId }: { id: number; courseId: number }) =>
|
||||
coursewareService.lockChapter(id),
|
||||
onSuccess: (_, { id, courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useReorderChapters() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ courseId, body }: { courseId: number; body: { ids: number[] } }) =>
|
||||
coursewareService.reorderChapters(courseId, body),
|
||||
onSuccess: (_, { courseId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUploadMaterial() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ chapterId, formData }: { chapterId: number; formData: FormData }) =>
|
||||
coursewareService.uploadMaterial(chapterId, formData),
|
||||
onSuccess: (_, { chapterId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterMaterials(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterProgress(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(chapterId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteMaterial() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, chapterId }: { id: number; chapterId: number }) =>
|
||||
coursewareService.deleteMaterial(id),
|
||||
onSuccess: (_, { chapterId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterMaterials(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterProgress(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(chapterId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkMaterialViewed() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, chapterId }: { id: number; chapterId: number }) =>
|
||||
coursewareService.markMaterialViewed(id),
|
||||
onSuccess: (_, { chapterId }) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterMaterials(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterProgress(chapterId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCompleteChapter() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (chapterId: number) => coursewareService.markChapterComplete(chapterId),
|
||||
onSuccess: (_, chapterId) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapterProgress(chapterId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapter(chapterId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateOutline() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: WorkbenchGenerateRequest) => coursewareService.generateOutline(data),
|
||||
onSuccess: (_, data) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(data.course_id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.lms.course(data.course_id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateChapterContent() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: WorkbenchGenerateRequest) => coursewareService.generateChapterContent(data),
|
||||
onSuccess: (_, data) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(data.course_id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.lms.course(data.course_id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateRubric() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: WorkbenchGenerateRequest) => coursewareService.generateRubric(data),
|
||||
onSuccess: (_, data) => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(data.course_id) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.lms.course(data.course_id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function usePublishWorkbench() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Record<string, unknown>) => coursewareService.publishWorkbench(data),
|
||||
onSuccess: (_, data) => {
|
||||
const courseId = data.course_id;
|
||||
if (typeof courseId === "number") {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.chapters(courseId) });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.lms.course(courseId) });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
27
src/hooks/queries/useEntities.ts
Normal file
27
src/hooks/queries/useEntities.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { entitiesService } from "@/services";
|
||||
import type { PaginationParams } from "@/types";
|
||||
|
||||
export function useEntities(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.entities.list(params),
|
||||
queryFn: () => entitiesService.list(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useEntity(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.entities.detail(id),
|
||||
queryFn: () => entitiesService.getById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useEntityRoles(entityId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.entities.roles(entityId),
|
||||
queryFn: () => entitiesService.getRoles(entityId),
|
||||
enabled: !!entityId,
|
||||
});
|
||||
}
|
||||
57
src/hooks/queries/useEntityOnboarding.ts
Normal file
57
src/hooks/queries/useEntityOnboarding.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { entityOnboardingService } from "@/services/entity-onboarding.service";
|
||||
import type { CredentialFilters } from "@/types";
|
||||
|
||||
export function useValidateCSV() {
|
||||
return useMutation({
|
||||
mutationFn: (file: File) => entityOnboardingService.validateCsv(file),
|
||||
});
|
||||
}
|
||||
|
||||
export function useBulkCreate() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (payload: { validate_session_id?: string }) => entityOnboardingService.bulkCreate(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["entity-onboarding"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSendCredentials() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (studentIds: number[]) => entityOnboardingService.sendCredentials(studentIds),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.entityOnboarding.credentials() });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCredentialStatuses(filters?: CredentialFilters & { page?: number; limit?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.entityOnboarding.credentials(filters as Record<string, unknown> | undefined),
|
||||
queryFn: () => entityOnboardingService.getCredentialStatuses(filters),
|
||||
});
|
||||
}
|
||||
|
||||
export function useResendCredential() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (studentId: number) => entityOnboardingService.resendCredential(studentId),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["entity-onboarding", "credentials"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useResendAllPendingCredentials() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => entityOnboardingService.resendAllPending(),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["entity-onboarding", "credentials"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
34
src/hooks/queries/useExamSession.ts
Normal file
34
src/hooks/queries/useExamSession.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { examSessionService } from "@/services/exam-session.service";
|
||||
import type { ExamAutoSave } from "@/types";
|
||||
|
||||
export function useExamSession(examId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.examSession.session(examId),
|
||||
queryFn: () => examSessionService.getSession(examId),
|
||||
enabled: !!examId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useExamStatus(examId: number, enabled: boolean) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.examSession.status(examId),
|
||||
queryFn: () => examSessionService.getStatus(examId),
|
||||
enabled: !!examId && enabled,
|
||||
refetchInterval: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useExamAutoSave() {
|
||||
return useMutation({
|
||||
mutationFn: (data: { examId: number; payload: ExamAutoSave }) =>
|
||||
examSessionService.autoSave(data.examId, data.payload),
|
||||
});
|
||||
}
|
||||
|
||||
export function useExamSubmit() {
|
||||
return useMutation({
|
||||
mutationFn: (examId: number) => examSessionService.submit(examId),
|
||||
});
|
||||
}
|
||||
85
src/hooks/queries/useExamTemplates.ts
Normal file
85
src/hooks/queries/useExamTemplates.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { examTemplateService } from "@/services/exam-template.service";
|
||||
import { ieltsExamService } from "@/services/ielts-exam.service";
|
||||
import { customExamService } from "@/services/custom-exam.service";
|
||||
import type { IELTSExamConfig, ContentPoolFilters, ExamAssignRequest, CustomExamCreateRequest, SaveAsTemplateRequest } from "@/types";
|
||||
|
||||
export function useInternationalTemplates() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.examTemplates.international,
|
||||
queryFn: examTemplateService.listInternational,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCustomTemplates() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.examTemplates.custom,
|
||||
queryFn: examTemplateService.listCustom,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateIeltsExam() {
|
||||
return useMutation({
|
||||
mutationFn: (data: IELTSExamConfig) => ieltsExamService.create(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useIeltsSkills(examId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.ieltsExam.skills(examId),
|
||||
queryFn: () => ieltsExamService.getSkills(examId),
|
||||
enabled: !!examId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useIeltsContentPool(examId: number, filters: ContentPoolFilters) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.ieltsExam.contentPool(examId, filters),
|
||||
queryFn: () => ieltsExamService.getContentPool(examId, filters),
|
||||
enabled: !!examId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useIeltsAutoAssemble() {
|
||||
return useMutation({
|
||||
mutationFn: (examId: number) => ieltsExamService.autoAssemble(examId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useIeltsValidation(examId: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.ieltsExam.validation(examId),
|
||||
queryFn: () => ieltsExamService.validate(examId),
|
||||
enabled: !!examId,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePublishIeltsExam() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (examId: number) => ieltsExamService.update(examId, { status: "published" }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["ielts-exam"] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAssignIeltsExam() {
|
||||
return useMutation({
|
||||
mutationFn: (data: { examId: number; assignment: ExamAssignRequest }) =>
|
||||
ieltsExamService.assign(data.examId, data.assignment),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCustomExam() {
|
||||
return useMutation({
|
||||
mutationFn: (data: CustomExamCreateRequest) => customExamService.create(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveAsTemplate() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: SaveAsTemplateRequest) => examTemplateService.saveCustomTemplate(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.examTemplates.custom }),
|
||||
});
|
||||
}
|
||||
48
src/hooks/queries/useExams.ts
Normal file
48
src/hooks/queries/useExams.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { examsService } from "@/services";
|
||||
import type { ExamModule, PaginationParams } from "@/types";
|
||||
|
||||
export function useExams(module: ExamModule, params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.exams.list(module, params),
|
||||
queryFn: () => examsService.list(module, params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useExam(module: ExamModule, id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.exams.detail(module, id),
|
||||
queryFn: () => examsService.getById(module, id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useRubrics(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.exams.rubrics(params),
|
||||
queryFn: () => examsService.listRubrics(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRubricGroups(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.exams.rubricGroups(params),
|
||||
queryFn: () => examsService.listRubricGroups(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useExamStructures(params?: PaginationParams & { entity_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.exams.structures(params),
|
||||
queryFn: () => examsService.listStructures(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteExam() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => examsService.delete(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["exams"] }),
|
||||
});
|
||||
}
|
||||
60
src/hooks/queries/useFacilities.ts
Normal file
60
src/hooks/queries/useFacilities.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { facilityService } from "@/services/facility.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { FacilityCreateRequest, AssetCreateRequest } from "@/types/facility";
|
||||
|
||||
export function useFacilities(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.facilities.list(params),
|
||||
queryFn: () => facilityService.listFacilities(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateFacility() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: FacilityCreateRequest) => facilityService.createFacility(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.facilities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFacility() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<FacilityCreateRequest> }) =>
|
||||
facilityService.updateFacility(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.facilities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFacility() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => facilityService.deleteFacility(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.facilities.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAssets(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.assets.list(params),
|
||||
queryFn: () => facilityService.listAssets(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAsset() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: AssetCreateRequest) => facilityService.createAsset(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.assets.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteAsset() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => facilityService.deleteAsset(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.assets.all }),
|
||||
});
|
||||
}
|
||||
85
src/hooks/queries/useFaq.ts
Normal file
85
src/hooks/queries/useFaq.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { faqService } from "@/services/faq.service";
|
||||
import type { FaqCategoryCreateRequest, FaqItemCreateRequest } from "@/types/faq";
|
||||
|
||||
export function useFaqCategories(audience?: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.faqCategories(audience),
|
||||
queryFn: () => faqService.listCategories(audience ? { audience } : undefined),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFaqItems(params?: { category_id?: number; audience?: string; search?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.faqItems(params),
|
||||
queryFn: () => faqService.listItems(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateFaqCategory() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: FaqCategoryCreateRequest) => faqService.createCategory(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFaqCategory() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<FaqCategoryCreateRequest> }) =>
|
||||
faqService.updateCategory(id, data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
qc.invalidateQueries({ queryKey: ["faq", "items"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFaqCategory() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => faqService.deleteCategory(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
qc.invalidateQueries({ queryKey: ["faq", "items"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateFaqItem() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: FaqItemCreateRequest) => faqService.createItem(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "items"] });
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFaqItem() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<FaqItemCreateRequest> }) =>
|
||||
faqService.updateItem(id, data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "items"] });
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFaqItem() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => faqService.deleteItem(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["faq", "items"] });
|
||||
qc.invalidateQueries({ queryKey: ["faq", "categories"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
33
src/hooks/queries/useFees.ts
Normal file
33
src/hooks/queries/useFees.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { feesService } from "@/services/fees.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
|
||||
export function useFeesPlans(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.feesPlans.list(params),
|
||||
queryFn: () => feesService.listFeesPlans(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFeesPlan(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.feesPlans.detail(id),
|
||||
queryFn: () => feesService.getFeesPlan(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentFees(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.studentFees.list(params),
|
||||
queryFn: () => feesService.listStudentFees(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFeesTerms(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.feesTerms.list(params),
|
||||
queryFn: () => feesService.listFeesTerms(params),
|
||||
});
|
||||
}
|
||||
52
src/hooks/queries/useGradebook.ts
Normal file
52
src/hooks/queries/useGradebook.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { gradebookService } from "@/services/gradebook.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { GradingAssignmentCreateRequest } from "@/types/gradebook";
|
||||
|
||||
export function useGradebooks(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.gradebooks.list(params),
|
||||
queryFn: () => gradebookService.listGradebooks(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGradebookLines(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.gradebookLines.list(params),
|
||||
queryFn: () => gradebookService.listGradebookLines(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGradingAssignments(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.gradingAssignments.list(params),
|
||||
queryFn: () => gradebookService.listGradingAssignments(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateGradingAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: GradingAssignmentCreateRequest) =>
|
||||
gradebookService.createGradingAssignment(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.gradingAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateGradingAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<GradingAssignmentCreateRequest> }) =>
|
||||
gradebookService.updateGradingAssignment(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.gradingAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteGradingAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => gradebookService.deleteGradingAssignment(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.gradingAssignments.all }),
|
||||
});
|
||||
}
|
||||
42
src/hooks/queries/useGrading.ts
Normal file
42
src/hooks/queries/useGrading.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { gradingService } from "@/services/grading.service";
|
||||
import type { GradeSubmission } from "@/types";
|
||||
|
||||
export function useGradingQueue(params?: { skill?: string; status?: string; exam_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.grading.queue(params),
|
||||
queryFn: () => gradingService.getQueue(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentResponse(attemptId: number, skill: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.grading.response(attemptId, skill),
|
||||
queryFn: () => gradingService.getStudentResponse(attemptId, skill),
|
||||
enabled: !!attemptId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useGradingRubric(attemptId: number, skill: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.grading.rubric(attemptId, skill),
|
||||
queryFn: () => gradingService.getRubric(attemptId, skill),
|
||||
enabled: !!attemptId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAIGradeSuggestion() {
|
||||
return useMutation({
|
||||
mutationFn: (data: { attemptId: number; skill: string }) =>
|
||||
gradingService.getAISuggestion(data.attemptId, data.skill),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitGrade() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: GradeSubmission) => gradingService.submitGrade(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["grading"] }),
|
||||
});
|
||||
}
|
||||
199
src/hooks/queries/useInstitutionalExams.ts
Normal file
199
src/hooks/queries/useInstitutionalExams.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { institutionalExamService } from "@/services/institutional-exam.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type {
|
||||
InstExamSessionCreateRequest,
|
||||
InstExamCreateRequest,
|
||||
ResultTemplateCreateRequest,
|
||||
CourseAssignmentCreateRequest,
|
||||
SubjectRegistrationCreateRequest,
|
||||
AssignmentSubmission,
|
||||
} from "@/types/institutional-exam";
|
||||
|
||||
export function useInstitutionalExamSessions(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.instExamSessions.list(params),
|
||||
queryFn: () => institutionalExamService.listSessions(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstitutionalExamSession(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.instExamSessions.detail(id),
|
||||
queryFn: () => institutionalExamService.getSession(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useExamTypes(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.examTypes.list(params),
|
||||
queryFn: () => institutionalExamService.listExamTypes(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGradeConfigs(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.gradeConfigs.list(params),
|
||||
queryFn: () => institutionalExamService.listGradeConfigs(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useResultTemplates(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.resultTemplates.list(params),
|
||||
queryFn: () => institutionalExamService.listResultTemplates(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarksheets(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.marksheets.list(params),
|
||||
queryFn: () => institutionalExamService.listMarksheets(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarksheet(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.marksheets.detail(id),
|
||||
queryFn: () => institutionalExamService.getMarksheet(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCourseAssignments(params?: PaginationParams & { course_id?: number; batch_id?: number; state?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.courseAssignments.list(params),
|
||||
queryFn: () => institutionalExamService.listCourseAssignments(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCourseAssignment(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.courseAssignments.detail(id),
|
||||
queryFn: () => institutionalExamService.getCourseAssignment(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubjectRegistrations(params?: PaginationParams & { student_id?: number; course_id?: number; state?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.subjectRegistrations.list(params),
|
||||
queryFn: () => institutionalExamService.listSubjectRegistrations(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAvailableSubjects(params?: { student_id?: number; course_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.subjectRegistrations.available(params),
|
||||
queryFn: () => institutionalExamService.getAvailableSubjects(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateExamSession() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: InstExamSessionCreateRequest) => institutionalExamService.createSession(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.instExamSessions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useScheduleExamSession() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => institutionalExamService.scheduleSession(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.instExamSessions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateInstitutionalExam() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: InstExamCreateRequest) => institutionalExamService.createExam(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.instExamSessions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useEnterMarks() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ examId, data }: { examId: number; data: { marks: { student_id: number; score: number }[] } }) =>
|
||||
institutionalExamService.enterMarks(examId, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.instExamSessions.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateResultTemplate() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: ResultTemplateCreateRequest) => institutionalExamService.createResultTemplate(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.resultTemplates.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGenerateMarksheets() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (templateId: number) => institutionalExamService.generateMarksheets(templateId),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.marksheets.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useValidateMarksheet() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => institutionalExamService.validateMarksheet(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.marksheets.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCourseAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: CourseAssignmentCreateRequest) => institutionalExamService.createCourseAssignment(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.courseAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePublishCourseAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => institutionalExamService.publishCourseAssignment(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.courseAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitAssignment() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ assignmentId, file }: { assignmentId: number; file: File }) =>
|
||||
institutionalExamService.submitAssignment(assignmentId, file),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.courseAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGradeSubmission() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ submissionId, data }: { submissionId: number; data: Partial<AssignmentSubmission> }) =>
|
||||
institutionalExamService.gradeSubmission(submissionId, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.courseAssignments.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSubjectRegistration() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: SubjectRegistrationCreateRequest) => institutionalExamService.createSubjectRegistration(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.subjectRegistrations.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useConfirmSubjectRegistration() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => institutionalExamService.confirmSubjectRegistration(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.subjectRegistrations.all }),
|
||||
});
|
||||
}
|
||||
37
src/hooks/queries/useLessons.ts
Normal file
37
src/hooks/queries/useLessons.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { lessonService } from "@/services/lesson.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { LessonCreateRequest } from "@/types/lesson";
|
||||
|
||||
export function useLessons(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lessons.list(params),
|
||||
queryFn: () => lessonService.listLessons(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateLesson() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: LessonCreateRequest) => lessonService.createLesson(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.lessons.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateLesson() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<LessonCreateRequest> }) =>
|
||||
lessonService.updateLesson(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.lessons.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteLesson() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => lessonService.deleteLesson(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.lessons.all }),
|
||||
});
|
||||
}
|
||||
79
src/hooks/queries/useLibrary.ts
Normal file
79
src/hooks/queries/useLibrary.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { libraryService } from "@/services/library.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { MediaCreateRequest, MediaMovement } from "@/types/library";
|
||||
|
||||
type MovementWrite = Partial<
|
||||
Omit<MediaMovement, "id" | "media_name" | "student_name" | "faculty_name">
|
||||
>;
|
||||
|
||||
export function useLibraryMedia(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.libraryMedia.list(params),
|
||||
queryFn: () => libraryService.listMedia(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateMedia() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: MediaCreateRequest) => libraryService.createMedia(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryMedia.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useLibraryMovements(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.libraryMovements.list(params),
|
||||
queryFn: () => libraryService.listMovements(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateMovement() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: MovementWrite) => libraryService.createMovement(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryMovements.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateMovement() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: MovementWrite }) =>
|
||||
libraryService.updateMovement(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryMovements.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteMedia() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => libraryService.deleteMedia(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryMedia.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useReturnMovement() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => libraryService.returnMovement(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryMovements.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useLibraryCards(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.libraryCards.list(params),
|
||||
queryFn: () => libraryService.listCards(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCard() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { student_id: number }) => libraryService.createCard(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.libraryCards.all }),
|
||||
});
|
||||
}
|
||||
97
src/hooks/queries/useLms.ts
Normal file
97
src/hooks/queries/useLms.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { lmsService } from "@/services";
|
||||
import type { PaginationParams, CourseCreateRequest, LmsStudentCreateRequest, LmsTeacherCreateRequest } from "@/types";
|
||||
|
||||
export function useCourses(params?: PaginationParams & { status?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.courses(params),
|
||||
queryFn: () => lmsService.listCourses(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCourse(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.course(id),
|
||||
queryFn: () => lmsService.getCourse(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCourse() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: CourseCreateRequest) => lmsService.createCourse(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["lms", "courses"] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudents(params?: PaginationParams & { search?: string; batch_id?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.students(params),
|
||||
queryFn: () => lmsService.listStudents(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateStudent() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: LmsStudentCreateRequest) => lmsService.createStudent(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["lms", "students"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeachers(params?: PaginationParams & { search?: string }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.teachers(params),
|
||||
queryFn: () => lmsService.listTeachers(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateTeacher() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: LmsTeacherCreateRequest) => lmsService.createTeacher(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["lms", "teachers"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useBatches(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.batches(params),
|
||||
queryFn: () => lmsService.listBatches(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useBatch(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.batch(id),
|
||||
queryFn: () => lmsService.getBatch(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTimetable(params?: Record<string, unknown>) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.timetable(params),
|
||||
queryFn: () => lmsService.getTimetable(params as Record<string, string | number | boolean | undefined>),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAttendance(params?: Record<string, unknown>) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.attendance(params),
|
||||
queryFn: () => lmsService.getAttendance(params as Record<string, string | number | boolean | undefined>),
|
||||
});
|
||||
}
|
||||
|
||||
export function useGrades(params?: Record<string, unknown>) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.lms.grades(params),
|
||||
queryFn: () => lmsService.getGrades(params as Record<string, string | number | boolean | undefined>),
|
||||
});
|
||||
}
|
||||
103
src/hooks/queries/useNotifications.ts
Normal file
103
src/hooks/queries/useNotifications.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { notificationService } from "@/services/notification.service";
|
||||
import type { NotificationPreferences, NotificationRuleCreateRequest } from "@/types/notification";
|
||||
import type { PaginationParams } from "@/types";
|
||||
|
||||
export function useNotifications(params?: PaginationParams & { type?: string; is_read?: boolean }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.notifications(params),
|
||||
queryFn: () => notificationService.listNotifications(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUnreadNotificationCount() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.unreadNotifications,
|
||||
queryFn: () => notificationService.getUnreadNotificationCount(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useNotificationRules() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.notificationRules,
|
||||
queryFn: () => notificationService.listNotificationRules(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useNotificationPreferences() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.notificationPreferences,
|
||||
queryFn: () => notificationService.getNotificationPreferences(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkNotificationRead() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => notificationService.markNotificationRead(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["notifications"] });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.unreadNotifications });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkAllNotificationsRead() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => notificationService.markAllNotificationsRead(),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["notifications"] });
|
||||
qc.invalidateQueries({ queryKey: queryKeys.unreadNotifications });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateNotificationRule() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: NotificationRuleCreateRequest) =>
|
||||
notificationService.createNotificationRule(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.notificationRules });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateNotificationRule() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
data,
|
||||
}: {
|
||||
id: number;
|
||||
data: Partial<NotificationRuleCreateRequest>;
|
||||
}) => notificationService.updateNotificationRule(id, data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.notificationRules });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteNotificationRule() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => notificationService.deleteNotificationRule(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.notificationRules });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateNotificationPreferences() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<NotificationPreferences>) =>
|
||||
notificationService.updateNotificationPreferences(data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.notificationPreferences });
|
||||
},
|
||||
});
|
||||
}
|
||||
54
src/hooks/queries/usePlacement.ts
Normal file
54
src/hooks/queries/usePlacement.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { placementService } from "@/services/placement.service";
|
||||
import type { PlacementSubject, CATAnswer, CATAutoSave } from "@/types";
|
||||
|
||||
export function usePlacementStart() {
|
||||
return useMutation({
|
||||
mutationFn: (subject: PlacementSubject) => placementService.start(subject),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePlacementAnswer() {
|
||||
return useMutation({
|
||||
mutationFn: (data: CATAnswer) => placementService.submitAnswer(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePlacementAutoSave() {
|
||||
return useMutation({
|
||||
mutationFn: (data: CATAutoSave) => placementService.autoSave(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePlacementResults(sessionId: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.placement.results(sessionId),
|
||||
queryFn: () => placementService.getResults(sessionId),
|
||||
enabled: !!sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useSpeakingStatus(sessionId: string, enabled: boolean) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.placement.speakingStatus(sessionId),
|
||||
queryFn: () => placementService.getSpeakingStatus(sessionId),
|
||||
enabled: !!sessionId && enabled,
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useLearningPath(sessionId: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.placement.learningPath(sessionId),
|
||||
queryFn: () => placementService.getLearningPath(sessionId),
|
||||
enabled: !!sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useSpeakingUpload() {
|
||||
return useMutation({
|
||||
mutationFn: (data: { sessionId: string; promptId: number; audioFile: File }) =>
|
||||
placementService.uploadSpeaking(data.sessionId, data.promptId, data.audioFile),
|
||||
});
|
||||
}
|
||||
41
src/hooks/queries/useScoreRelease.ts
Normal file
41
src/hooks/queries/useScoreRelease.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { scoreReleaseService } from "@/services/score-release.service";
|
||||
|
||||
export function usePendingScores(params?: { page?: number; limit?: number }) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.scoreRelease.pending(params as Record<string, unknown> | undefined),
|
||||
queryFn: () => scoreReleaseService.getPending(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useReleaseScore() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (attemptId: number) => scoreReleaseService.release(attemptId),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["score-release", "pending"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRejectScore() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ attemptId, reason }: { attemptId: number; reason: string }) =>
|
||||
scoreReleaseService.reject(attemptId, reason),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["score-release", "pending"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useBulkRelease() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (attemptIds: number[]) => scoreReleaseService.bulkRelease(attemptIds),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["score-release", "pending"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
41
src/hooks/queries/useSignup.ts
Normal file
41
src/hooks/queries/useSignup.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { signupService } from "@/services/signup.service";
|
||||
import type { RegisterRequest, VerifyEmailRequest, OnboardingData } from "@/types";
|
||||
|
||||
export function useGoals() {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.signup.goals,
|
||||
queryFn: signupService.getGoals,
|
||||
});
|
||||
}
|
||||
|
||||
export function useRegister() {
|
||||
return useMutation({
|
||||
mutationFn: (data: RegisterRequest) => signupService.register(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useVerifyEmail() {
|
||||
return useMutation({
|
||||
mutationFn: (data: VerifyEmailRequest) => signupService.verifyEmail(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useResendOtp() {
|
||||
return useMutation({
|
||||
mutationFn: (email: string) => signupService.resendOtp({ email }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCheckEmail() {
|
||||
return useMutation({
|
||||
mutationFn: (email: string) => signupService.checkEmail(email),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCompleteOnboarding() {
|
||||
return useMutation({
|
||||
mutationFn: (data: OnboardingData) => signupService.completeOnboarding(data),
|
||||
});
|
||||
}
|
||||
60
src/hooks/queries/useStudentLeave.ts
Normal file
60
src/hooks/queries/useStudentLeave.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { studentLeaveService } from "@/services/student-leave.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
import type { StudentLeaveCreateRequest, StudentLeaveType } from "@/types/student-leave";
|
||||
|
||||
export function useStudentLeaves(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.studentLeaves.list(params),
|
||||
queryFn: () => studentLeaveService.listLeaves(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentLeaveTypes(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.studentLeaveTypes.list(params),
|
||||
queryFn: () => studentLeaveService.listLeaveTypes(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateStudentLeave() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: StudentLeaveCreateRequest) => studentLeaveService.createLeave(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.studentLeaves.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateStudentLeave() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<StudentLeaveCreateRequest> }) =>
|
||||
studentLeaveService.updateLeave(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.studentLeaves.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useApproveStudentLeave() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => studentLeaveService.approveLeave(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.studentLeaves.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRejectStudentLeave() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => studentLeaveService.rejectLeave(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.studentLeaves.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateStudentLeaveType() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Pick<StudentLeaveType, "name">) => studentLeaveService.createLeaveType(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.studentLeaveTypes.all }),
|
||||
});
|
||||
}
|
||||
11
src/hooks/queries/useStudentProgress.ts
Normal file
11
src/hooks/queries/useStudentProgress.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { studentProgressService } from "@/services/student-progress.service";
|
||||
import type { PaginationParams } from "@/types";
|
||||
|
||||
export function useStudentProgressList(params?: PaginationParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.studentProgress.list(params),
|
||||
queryFn: () => studentProgressService.listProgress(params),
|
||||
});
|
||||
}
|
||||
18
src/hooks/queries/useUsers.ts
Normal file
18
src/hooks/queries/useUsers.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { queryKeys } from "./keys";
|
||||
import { usersService, type UserListParams } from "@/services/users.service";
|
||||
|
||||
export function useUsers(params: UserListParams) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.users.list(params),
|
||||
queryFn: () => usersService.list(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUser(id: number) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.users.detail(id),
|
||||
queryFn: () => usersService.getById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
19
src/hooks/use-mobile.tsx
Normal file
19
src/hooks/use-mobile.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from "react";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
mql.addEventListener("change", onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
return () => mql.removeEventListener("change", onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile;
|
||||
}
|
||||
186
src/hooks/use-toast.ts
Normal file
186
src/hooks/use-toast.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import * as React from "react";
|
||||
|
||||
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
|
||||
|
||||
const TOAST_LIMIT = 1;
|
||||
const TOAST_REMOVE_DELAY = 1000000;
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
action?: ToastActionElement;
|
||||
};
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const;
|
||||
|
||||
let count = 0;
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
||||
return count.toString();
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes;
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"];
|
||||
toast: ToasterToast;
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"];
|
||||
toast: Partial<ToasterToast>;
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"];
|
||||
toastId?: ToasterToast["id"];
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"];
|
||||
toastId?: ToasterToast["id"];
|
||||
};
|
||||
|
||||
interface State {
|
||||
toasts: ToasterToast[];
|
||||
}
|
||||
|
||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
const addToRemoveQueue = (toastId: string) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId);
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: toastId,
|
||||
});
|
||||
}, TOAST_REMOVE_DELAY);
|
||||
|
||||
toastTimeouts.set(toastId, timeout);
|
||||
};
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "ADD_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||
};
|
||||
|
||||
case "UPDATE_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
|
||||
};
|
||||
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action;
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId);
|
||||
} else {
|
||||
state.toasts.forEach((toast) => {
|
||||
addToRemoveQueue(toast.id);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
open: false,
|
||||
}
|
||||
: t,
|
||||
),
|
||||
};
|
||||
}
|
||||
case "REMOVE_TOAST":
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const listeners: Array<(state: State) => void> = [];
|
||||
|
||||
let memoryState: State = { toasts: [] };
|
||||
|
||||
function dispatch(action: Action) {
|
||||
memoryState = reducer(memoryState, action);
|
||||
listeners.forEach((listener) => {
|
||||
listener(memoryState);
|
||||
});
|
||||
}
|
||||
|
||||
type Toast = Omit<ToasterToast, "id">;
|
||||
|
||||
function toast({ ...props }: Toast) {
|
||||
const id = genId();
|
||||
|
||||
const update = (props: ToasterToast) =>
|
||||
dispatch({
|
||||
type: "UPDATE_TOAST",
|
||||
toast: { ...props, id },
|
||||
});
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
toast: {
|
||||
...props,
|
||||
id,
|
||||
open: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) dismiss();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: id,
|
||||
dismiss,
|
||||
update,
|
||||
};
|
||||
}
|
||||
|
||||
function useToast() {
|
||||
const [state, setState] = React.useState<State>(memoryState);
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setState);
|
||||
return () => {
|
||||
const index = listeners.indexOf(setState);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
return {
|
||||
...state,
|
||||
toast,
|
||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||
};
|
||||
}
|
||||
|
||||
export { useToast, toast };
|
||||
54
src/hooks/usePermissions.ts
Normal file
54
src/hooks/usePermissions.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { entitiesService } from "@/services/entities.service";
|
||||
|
||||
interface UsePermissionsReturn {
|
||||
permissions: string[];
|
||||
isLoading: boolean;
|
||||
hasPermission: (permission: string) => boolean;
|
||||
hasAnyPermission: (permissions: string[]) => boolean;
|
||||
hasAllPermissions: (permissions: string[]) => boolean;
|
||||
isAdmin: boolean;
|
||||
isDeveloper: boolean;
|
||||
}
|
||||
|
||||
export function usePermissions(entityId?: number): UsePermissionsReturn {
|
||||
const { user } = useAuth();
|
||||
|
||||
const activeEntityId = entityId ?? user?.entities?.[0]?.id;
|
||||
|
||||
const { data: permissions = [], isLoading } = useQuery({
|
||||
queryKey: ["permissions", activeEntityId],
|
||||
queryFn: () => entitiesService.getPermissions(activeEntityId!),
|
||||
enabled: !!activeEntityId && !!user,
|
||||
staleTime: 10 * 60 * 1000,
|
||||
});
|
||||
|
||||
const isAdmin = user?.user_type === "admin" || user?.user_type === "developer";
|
||||
const isDeveloper = user?.user_type === "developer";
|
||||
|
||||
const hasPermission = (permission: string): boolean => {
|
||||
if (isAdmin) return true;
|
||||
return permissions.includes(permission);
|
||||
};
|
||||
|
||||
const hasAnyPermission = (perms: string[]): boolean => {
|
||||
if (isAdmin) return true;
|
||||
return perms.some((p) => permissions.includes(p));
|
||||
};
|
||||
|
||||
const hasAllPermissions = (perms: string[]): boolean => {
|
||||
if (isAdmin) return true;
|
||||
return perms.every((p) => permissions.includes(p));
|
||||
};
|
||||
|
||||
return {
|
||||
permissions,
|
||||
isLoading,
|
||||
hasPermission,
|
||||
hasAnyPermission,
|
||||
hasAllPermissions,
|
||||
isAdmin,
|
||||
isDeveloper,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user