@appcorp/fusion-storybook 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base-modules/attendance/more-actions.js +7 -2
- package/base-modules/enrollment/context.d.ts +1 -13
- package/base-modules/enrollment/context.js +0 -3
- package/base-modules/enrollment/form.js +2 -6
- package/base-modules/enrollment/page.d.ts +0 -1
- package/base-modules/enrollment/page.js +5 -13
- package/base-modules/enrollment/validate.d.ts +0 -1
- package/base-modules/enrollment/validate.js +0 -1
- package/base-modules/enrollment/view.js +4 -7
- package/base-modules/student-profile/constants.js +7 -3
- package/base-modules/teacher/context.js +1 -1
- package/base-modules/teacher/form.js +2 -2
- package/base-modules/teacher/more-actions.js +71 -23
- package/base-modules/teacher/validate.d.ts +1 -1
- package/base-modules/teacher/validate.js +1 -1
- package/components/timeline.d.ts +2 -1
- package/components/timeline.js +8 -2
- package/constants.d.ts +40 -0
- package/constants.js +40 -0
- package/package.json +2 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/academics.d.ts +2 -4
|
@@ -10,15 +10,20 @@ import { useMemo, useState } from "react";
|
|
|
10
10
|
export const AttendanceMoreActions = ({ labelAttendanceStatus, labelCourse, labelCourseInfo, labelCoursePlaceholder, labelNoCourseFound, labelRemarks, labelRemarksInfo, labelRemarksPlaceholder, labelSearchCourses, labelStatusAbsent, labelStatusExcused, labelStatusHalfDay, labelStatusLate, labelStatusPresent, }) => {
|
|
11
11
|
const enrollment = getCachedEnrollmentsSync().items;
|
|
12
12
|
const studentProfiles = getCachedStudentProfilesSync().items;
|
|
13
|
+
const courses = getCachedCoursesSync().items;
|
|
13
14
|
const [courseId, setCourseId] = useState("");
|
|
14
15
|
const [remarks, setRemarks] = useState({});
|
|
15
16
|
const [attendance, setAttendance] = useState({});
|
|
16
17
|
const [errors] = useState({});
|
|
18
|
+
// Find the section for the selected course, then get students enrolled in that section
|
|
17
19
|
const studentsInCourse = useMemo(() => {
|
|
20
|
+
const courseObj = courses.find((c) => c.id === courseId);
|
|
21
|
+
if (!courseObj)
|
|
22
|
+
return [];
|
|
18
23
|
return enrollment
|
|
19
|
-
.filter((e) => e.
|
|
24
|
+
.filter((e) => e.sectionId === courseObj.sectionId)
|
|
20
25
|
.map((e) => e.studentProfileId);
|
|
21
|
-
}, [enrollment, courseId]);
|
|
26
|
+
}, [enrollment, courseId, courses]);
|
|
22
27
|
return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedCombobox, { emptyText: labelNoCourseFound, error: errors.courseId, id: "courseId", info: labelCourseInfo, label: labelCourse, onValueChange: (value) => setCourseId(value), options: getCachedCoursesSync().items.map((c) => ({
|
|
23
28
|
label: c.code.trim(),
|
|
24
29
|
value: c.id,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RowAction, type TableRow } from "@appcorp/shadcn/components/enhanced-table";
|
|
2
|
-
import {
|
|
2
|
+
import { EnrollmentBE, SectionBE, StudentProfileBE } from "../../type";
|
|
3
3
|
export declare const ENROLLMENT_DRAWER: {
|
|
4
4
|
readonly FILTER_DRAWER: string;
|
|
5
5
|
readonly FORM_DRAWER: string;
|
|
@@ -26,7 +26,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
26
26
|
searchQuery: string;
|
|
27
27
|
disableSaveButton: boolean;
|
|
28
28
|
drawer: string | null;
|
|
29
|
-
courseId: string;
|
|
30
29
|
enabled: boolean;
|
|
31
30
|
enrollmentDate: string;
|
|
32
31
|
errors: Record<string, string>;
|
|
@@ -34,7 +33,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
34
33
|
id: string;
|
|
35
34
|
sectionId: string;
|
|
36
35
|
studentProfileId: string;
|
|
37
|
-
course: CourseBE | undefined;
|
|
38
36
|
section: SectionBE | undefined;
|
|
39
37
|
studentProfile: StudentProfileBE | undefined;
|
|
40
38
|
}>, initialEnrollmentState: {
|
|
@@ -45,7 +43,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
45
43
|
searchQuery: string;
|
|
46
44
|
disableSaveButton: boolean;
|
|
47
45
|
drawer: string | null;
|
|
48
|
-
courseId: string;
|
|
49
46
|
enabled: boolean;
|
|
50
47
|
enrollmentDate: string;
|
|
51
48
|
errors: Record<string, string>;
|
|
@@ -53,7 +50,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
53
50
|
id: string;
|
|
54
51
|
sectionId: string;
|
|
55
52
|
studentProfileId: string;
|
|
56
|
-
course: CourseBE | undefined;
|
|
57
53
|
section: SectionBE | undefined;
|
|
58
54
|
studentProfile: StudentProfileBE | undefined;
|
|
59
55
|
}, EnrollmentProvider: import("react").FC<{
|
|
@@ -66,7 +62,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
66
62
|
searchQuery: string;
|
|
67
63
|
disableSaveButton: boolean;
|
|
68
64
|
drawer: string | null;
|
|
69
|
-
courseId: string;
|
|
70
65
|
enabled: boolean;
|
|
71
66
|
enrollmentDate: string;
|
|
72
67
|
errors: Record<string, string>;
|
|
@@ -74,7 +69,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
74
69
|
id: string;
|
|
75
70
|
sectionId: string;
|
|
76
71
|
studentProfileId: string;
|
|
77
|
-
course: CourseBE | undefined;
|
|
78
72
|
section: SectionBE | undefined;
|
|
79
73
|
studentProfile: StudentProfileBE | undefined;
|
|
80
74
|
}, action: any) => {
|
|
@@ -85,7 +79,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
85
79
|
searchQuery: string;
|
|
86
80
|
disableSaveButton: boolean;
|
|
87
81
|
drawer: string | null;
|
|
88
|
-
courseId: string;
|
|
89
82
|
enabled: boolean;
|
|
90
83
|
enrollmentDate: string;
|
|
91
84
|
errors: Record<string, string>;
|
|
@@ -93,7 +86,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
93
86
|
id: string;
|
|
94
87
|
sectionId: string;
|
|
95
88
|
studentProfileId: string;
|
|
96
|
-
course: CourseBE | undefined;
|
|
97
89
|
section: SectionBE | undefined;
|
|
98
90
|
studentProfile: StudentProfileBE | undefined;
|
|
99
91
|
}, useEnrollmentContext: () => import("@react-pakistan/util-functions/factory/generic-module-factory").GenericModuleContext<{
|
|
@@ -104,7 +96,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
104
96
|
searchQuery: string;
|
|
105
97
|
disableSaveButton: boolean;
|
|
106
98
|
drawer: string | null;
|
|
107
|
-
courseId: string;
|
|
108
99
|
enabled: boolean;
|
|
109
100
|
enrollmentDate: string;
|
|
110
101
|
errors: Record<string, string>;
|
|
@@ -112,7 +103,6 @@ export declare const ENROLLMENT_ACTION_TYPES: {
|
|
|
112
103
|
id: string;
|
|
113
104
|
sectionId: string;
|
|
114
105
|
studentProfileId: string;
|
|
115
|
-
course: CourseBE | undefined;
|
|
116
106
|
section: SectionBE | undefined;
|
|
117
107
|
studentProfile: StudentProfileBE | undefined;
|
|
118
108
|
}>;
|
|
@@ -152,7 +142,6 @@ export declare const useEnrollmentModule: () => {
|
|
|
152
142
|
searchQuery: string;
|
|
153
143
|
disableSaveButton: boolean;
|
|
154
144
|
drawer: string | null;
|
|
155
|
-
courseId: string;
|
|
156
145
|
enabled: boolean;
|
|
157
146
|
enrollmentDate: string;
|
|
158
147
|
errors: Record<string, string>;
|
|
@@ -160,7 +149,6 @@ export declare const useEnrollmentModule: () => {
|
|
|
160
149
|
id: string;
|
|
161
150
|
sectionId: string;
|
|
162
151
|
studentProfileId: string;
|
|
163
|
-
course: CourseBE | undefined;
|
|
164
152
|
section: SectionBE | undefined;
|
|
165
153
|
studentProfile: StudentProfileBE | undefined;
|
|
166
154
|
};
|
|
@@ -54,7 +54,6 @@ const enrollmentConfig = {
|
|
|
54
54
|
disableSaveButton: false,
|
|
55
55
|
drawer: null,
|
|
56
56
|
// Form State
|
|
57
|
-
courseId: "",
|
|
58
57
|
enabled: true,
|
|
59
58
|
enrollmentDate: new Date().toISOString().split("T")[0],
|
|
60
59
|
errors: {},
|
|
@@ -63,7 +62,6 @@ const enrollmentConfig = {
|
|
|
63
62
|
sectionId: "",
|
|
64
63
|
studentProfileId: "",
|
|
65
64
|
// Relations (populated from byId fetch)
|
|
66
|
-
course: undefined,
|
|
67
65
|
section: undefined,
|
|
68
66
|
studentProfile: undefined,
|
|
69
67
|
},
|
|
@@ -102,7 +100,6 @@ export const useEnrollmentModule = () => {
|
|
|
102
100
|
debouncedQuery,
|
|
103
101
|
]);
|
|
104
102
|
const updateParams = useMemo(() => ({
|
|
105
|
-
courseId: state.courseId,
|
|
106
103
|
enabled: state.enabled,
|
|
107
104
|
enrollmentDate: state.enrollmentDate,
|
|
108
105
|
id: state.id,
|
|
@@ -16,24 +16,20 @@ import { Separator } from "@appcorp/shadcn/components/ui/separator";
|
|
|
16
16
|
import { DATE_FORMATS, formatDate } from "@react-pakistan/util-functions";
|
|
17
17
|
import { useEnrollmentModule } from "./context";
|
|
18
18
|
import { getCachedStudentProfilesSync } from "../student-profile/cache";
|
|
19
|
-
import { getCachedCoursesSync } from "../course/cache";
|
|
20
19
|
import { getCachedSectionsSync } from "../section/cache";
|
|
21
20
|
import { useTranslations } from "next-intl";
|
|
22
21
|
// ============================================================================
|
|
23
22
|
// COMPONENT
|
|
24
23
|
// ============================================================================
|
|
25
24
|
export const EnrollmentForm = () => {
|
|
26
|
-
const { state: {
|
|
25
|
+
const { state: { enabled, enrollmentDate, errors, sectionId, studentProfileId, }, handleChange, } = useEnrollmentModule();
|
|
27
26
|
const t = useTranslations("enrollment");
|
|
28
27
|
return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.enrollmentDate, id: "enrollmentDate", info: t("formEnrollmentDateInfo"), label: t("formEnrollmentDateLabel"), onChange: (e) => handleChange("enrollmentDate", e.target.value), placeholder: t("formEnrollmentDatePlaceholder"), required: true, type: "date", value: enrollmentDate
|
|
29
28
|
? formatDate(enrollmentDate, DATE_FORMATS.YYYY_MM_DD)
|
|
30
29
|
: "" }), _jsx(Separator, {}), _jsx(EnhancedCombobox, { emptyText: t("formNoStudentEmpty"), error: errors.studentProfileId, id: "studentProfileId", info: t("formStudentInfo"), label: t("formStudentLabel"), onValueChange: (value) => handleChange("studentProfileId", value), options: (getCachedStudentProfilesSync().items || []).map((s) => ({
|
|
31
30
|
label: s.studentCode || s.id,
|
|
32
31
|
value: s.id,
|
|
33
|
-
})), placeholder: t("formStudentPlaceholder"), required: true, searchPlaceholder: t("formSearchStudentsPlaceholder"), value: studentProfileId || "" }), _jsx(EnhancedCombobox, { emptyText: t("
|
|
34
|
-
label: c.code || c.id,
|
|
35
|
-
value: c.id,
|
|
36
|
-
})), placeholder: t("formCoursePlaceholder"), required: true, searchPlaceholder: t("formSearchCoursesPlaceholder"), value: courseId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoSectionEmpty"), error: errors.sectionId, id: "sectionId", info: t("sectionInfo"), label: t("section"), onValueChange: (value) => handleChange("sectionId", value), options: (getCachedSectionsSync().items || []).map((s) => {
|
|
32
|
+
})), placeholder: t("formStudentPlaceholder"), required: true, searchPlaceholder: t("formSearchStudentsPlaceholder"), value: studentProfileId || "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoSectionEmpty"), error: errors.sectionId, id: "sectionId", info: t("sectionInfo"), label: t("section"), onValueChange: (value) => handleChange("sectionId", value), options: (getCachedSectionsSync().items || []).map((s) => {
|
|
37
33
|
var _a;
|
|
38
34
|
return ({
|
|
39
35
|
label: ((_a = s.class) === null || _a === void 0 ? void 0 : _a.code) ? `${s.class.code}-${s.name}` : s.name,
|
|
@@ -34,11 +34,6 @@ const tableBodyCols = [
|
|
|
34
34
|
key: "studentProfile",
|
|
35
35
|
textFormatter: (_, row) => { var _a, _b; return (_b = (_a = row.studentProfile) === null || _a === void 0 ? void 0 : _a.studentCode) !== null && _b !== void 0 ? _b : "N/A"; },
|
|
36
36
|
},
|
|
37
|
-
{
|
|
38
|
-
componentType: COMPONENT_TYPE.TEXT,
|
|
39
|
-
key: "course",
|
|
40
|
-
textFormatter: (_, row) => { var _a, _b; return (_b = (_a = row.course) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : "N/A"; },
|
|
41
|
-
},
|
|
42
37
|
{
|
|
43
38
|
componentType: COMPONENT_TYPE.TEXT,
|
|
44
39
|
key: "section",
|
|
@@ -69,17 +64,16 @@ const createComponentInstances = () => ({
|
|
|
69
64
|
moreActions: _jsx(EnrollmentMoreActions, {}),
|
|
70
65
|
view: _jsx(EnrollmentView, {}),
|
|
71
66
|
});
|
|
72
|
-
const createEnrollmentConfig = ({ cancelLabel, dispatch, drawerFilterDescription, drawerFilterTitle, drawerFormDescription, drawerFormTitle, drawerMoreActionsDescription, drawerMoreActionsTitle, drawerViewDescription, drawerViewTitle, labelActions,
|
|
67
|
+
const createEnrollmentConfig = ({ cancelLabel, dispatch, drawerFilterDescription, drawerFilterTitle, drawerFormDescription, drawerFormTitle, drawerMoreActionsDescription, drawerMoreActionsTitle, drawerViewDescription, drawerViewTitle, labelActions, labelEnabled, labelEnrollmentDate, labelId, labelSection, labelStudent, saveLabel, searchPlaceholder, tableDescription, tableTitle, }) => {
|
|
73
68
|
const components = createComponentInstances();
|
|
74
69
|
return {
|
|
75
70
|
moduleName: "enrollment",
|
|
76
71
|
tableColumns: [
|
|
77
72
|
{ label: labelId, width: "5%" },
|
|
78
|
-
{ label: labelStudent, width: "
|
|
79
|
-
{ label:
|
|
80
|
-
{ label:
|
|
81
|
-
{ label:
|
|
82
|
-
{ label: labelEnabled, width: "10%" },
|
|
73
|
+
{ label: labelStudent, width: "25%" },
|
|
74
|
+
{ label: labelSection, width: "25%" },
|
|
75
|
+
{ label: labelEnrollmentDate, width: "20%" },
|
|
76
|
+
{ label: labelEnabled, width: "15%" },
|
|
83
77
|
{ label: labelActions, width: "5%" },
|
|
84
78
|
],
|
|
85
79
|
cancelLabel,
|
|
@@ -122,7 +116,6 @@ const EnrollmentPageInner = (props) => {
|
|
|
122
116
|
drawerMoreActionsTitle: props.drawerMoreActionsTitle,
|
|
123
117
|
drawerMoreActionsDescription: props.drawerMoreActionsDescription,
|
|
124
118
|
labelActions: props.labelActions,
|
|
125
|
-
labelCourse: props.labelCourse,
|
|
126
119
|
labelEnabled: props.labelEnabled,
|
|
127
120
|
labelEnrollmentDate: props.labelEnrollmentDate,
|
|
128
121
|
labelId: props.labelId,
|
|
@@ -144,7 +137,6 @@ const EnrollmentPageInner = (props) => {
|
|
|
144
137
|
props.drawerMoreActionsTitle,
|
|
145
138
|
props.drawerMoreActionsDescription,
|
|
146
139
|
props.labelActions,
|
|
147
|
-
props.labelCourse,
|
|
148
140
|
props.labelEnabled,
|
|
149
141
|
props.labelEnrollmentDate,
|
|
150
142
|
props.labelId,
|
|
@@ -9,7 +9,6 @@ import { z } from "zod";
|
|
|
9
9
|
// ============================================================================
|
|
10
10
|
export const enrollmentFormValidation = z.object({
|
|
11
11
|
studentProfileId: z.string().min(1, "Student is required"),
|
|
12
|
-
courseId: z.string().min(1, "Course is required"),
|
|
13
12
|
sectionId: z.string().min(1, "Section is required"),
|
|
14
13
|
enrollmentDate: z
|
|
15
14
|
.string()
|
|
@@ -17,27 +17,24 @@ import { BookOpen, CheckCircle2, XCircle } from "lucide-react";
|
|
|
17
17
|
import { DATE_FORMATS, formatDate } from "@react-pakistan/util-functions";
|
|
18
18
|
import { useEnrollmentModule } from "./context";
|
|
19
19
|
import { getCachedStudentProfilesSync } from "../student-profile/cache";
|
|
20
|
-
import { getCachedCoursesSync } from "../course/cache";
|
|
21
20
|
import { getCachedSectionsSync } from "../section/cache";
|
|
22
21
|
import { useTranslations } from "next-intl";
|
|
23
22
|
// ============================================================================
|
|
24
23
|
// COMPONENT
|
|
25
24
|
// ============================================================================
|
|
26
25
|
export const EnrollmentView = memo(() => {
|
|
27
|
-
var _a, _b, _c
|
|
28
|
-
const { state: {
|
|
26
|
+
var _a, _b, _c;
|
|
27
|
+
const { state: { enrollmentDate, enabled, sectionId, studentProfileId }, } = useEnrollmentModule();
|
|
29
28
|
const t = useTranslations("enrollment");
|
|
30
29
|
const studentProfile = getCachedStudentProfilesSync().items.find((s) => s.id === studentProfileId);
|
|
31
30
|
const studentName = (_b = (_a = studentProfile === null || studentProfile === void 0 ? void 0 : studentProfile.studentCode) !== null && _a !== void 0 ? _a : studentProfileId) !== null && _b !== void 0 ? _b : "—";
|
|
32
|
-
const course = getCachedCoursesSync().items.find((c) => c.id === courseId);
|
|
33
|
-
const courseName = (_d = (_c = course === null || course === void 0 ? void 0 : course.code) !== null && _c !== void 0 ? _c : courseId) !== null && _d !== void 0 ? _d : "—";
|
|
34
31
|
const section = getCachedSectionsSync().items.find((s) => s.id === sectionId);
|
|
35
32
|
const sectionName = section
|
|
36
|
-
? ((
|
|
33
|
+
? ((_c = section.class) === null || _c === void 0 ? void 0 : _c.code)
|
|
37
34
|
? `${section.class.code}-${section.name}`
|
|
38
35
|
: section.name
|
|
39
36
|
: (sectionId !== null && sectionId !== void 0 ? sectionId : "—");
|
|
40
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionEnrollmentDetails") })] }), _jsx(CardDescription, { children: t("viewSectionCompleteEnrollmentInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("viewFieldStudent") }), _jsx("p", { className: "text-base", children: studentName })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("
|
|
37
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { className: "text-lg", children: t("viewSectionEnrollmentDetails") })] }), _jsx(CardDescription, { children: t("viewSectionCompleteEnrollmentInformation") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("viewFieldStudent") }), _jsx("p", { className: "text-base", children: studentName })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("section") }), _jsx("p", { className: "text-base", children: sectionName })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("viewFieldEnrollmentDate") }), _jsx("p", { className: "text-base", children: enrollmentDate
|
|
41
38
|
? formatDate(enrollmentDate, DATE_FORMATS.LOCALE_DATE)
|
|
42
39
|
: "—" })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { className: "text-lg", children: t("viewFieldStatus") })] }), _jsx(CardDescription, { children: t("viewFieldEnrollmentAvailability") })] }), _jsx(Separator, {}), _jsx(CardContent, { className: "pt-6", children: _jsx("div", { className: "grid grid-cols-1 gap-6", children: _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("viewFieldStatus") }), _jsx(Badge, { variant: enabled ? "default" : "destructive", className: "gap-1", children: enabled ? (_jsxs(_Fragment, { children: [_jsx(CheckCircle2, { className: "h-3 w-3" }), " ", t("viewFieldStatusActive")] })) : (_jsxs(_Fragment, { children: [_jsx(XCircle, { className: "h-3 w-3" }), " ", t("viewFieldStatusInactive")] })) })] }) }) })] })] }));
|
|
43
40
|
});
|
|
@@ -59,14 +59,18 @@ export const tableBodyCols = [
|
|
|
59
59
|
componentType: COMPONENT_TYPE.TEXT,
|
|
60
60
|
key: "enrollments",
|
|
61
61
|
textFormatter: (value) => {
|
|
62
|
-
var _a;
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
63
|
const enrollments = value;
|
|
64
64
|
if (!enrollments || enrollments.length === 0)
|
|
65
65
|
return "No enrollments";
|
|
66
66
|
if (enrollments.length === 1) {
|
|
67
|
-
|
|
67
|
+
const section = enrollments[0].section;
|
|
68
|
+
const courseCount = (_b = (_a = section === null || section === void 0 ? void 0 : section.courses) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
69
|
+
return courseCount > 0
|
|
70
|
+
? `${(_c = section === null || section === void 0 ? void 0 : section.name) !== null && _c !== void 0 ? _c : "Section"} (${courseCount} subjects)`
|
|
71
|
+
: (_d = section === null || section === void 0 ? void 0 : section.name) !== null && _d !== void 0 ? _d : "Enrolled";
|
|
68
72
|
}
|
|
69
|
-
return `${enrollments.length}
|
|
73
|
+
return `${enrollments.length} sections`;
|
|
70
74
|
},
|
|
71
75
|
},
|
|
72
76
|
{
|
|
@@ -477,7 +477,7 @@ export const useTeacherModule = () => {
|
|
|
477
477
|
// ============================================================================
|
|
478
478
|
const headerActions = useMemo(() => [
|
|
479
479
|
{
|
|
480
|
-
enabled:
|
|
480
|
+
enabled: true,
|
|
481
481
|
handleOnClick: handleMoreActions,
|
|
482
482
|
label: t("actionHeaderMoreActions"),
|
|
483
483
|
order: 0,
|
|
@@ -14,11 +14,11 @@ export const TeacherForm = () => {
|
|
|
14
14
|
var _a;
|
|
15
15
|
const t = useTranslations("teacher");
|
|
16
16
|
const context = useTeacherModule();
|
|
17
|
-
const { address, avatar, bio, city, country, dateOfBirth, emergencyPhone, enabled, errors, experience, firstName, gender, joiningDate, lastName, phone, postalCode, qualification, specialization,
|
|
17
|
+
const { address, avatar, bio, city, country, dateOfBirth, emergencyPhone, enabled, errors, experience, firstName, gender, joiningDate, lastName, phone, postalCode, qualification, specialization, userId, } = context.state;
|
|
18
18
|
const stateValue = context.state.state;
|
|
19
19
|
const { handleChange, handleAvatarUpload } = context;
|
|
20
20
|
const users = getCachedUsersSync();
|
|
21
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.
|
|
21
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(EnhancedInput, { error: errors.firstName, id: "firstName", info: t("formTeachersFirstNameLabel"), label: t("formFirstNameLabel"), onChange: (e) => handleChange("firstName", e.target.value), placeholder: t("formFirstNamePlaceholder"), required: true, value: firstName }), _jsx(EnhancedInput, { error: errors.lastName, id: "lastName", info: t("formTeachersLastNameLabel"), label: t("formLastNameLabel"), onChange: (e) => handleChange("lastName", e.target.value), placeholder: t("formLastNamePlaceholder"), required: true, value: lastName }), _jsx(EnhancedInput, { error: errors.joiningDate, id: "joiningDate", info: t("formDateTeacherJoinedTheSchoolLabel"), label: t("formJoiningDateLabel"), onChange: (e) => handleChange("joiningDate", e.target.value), onClick: (e) => { var _a, _b; return (_b = (_a = e.currentTarget).showPicker) === null || _b === void 0 ? void 0 : _b.call(_a); }, required: true, type: "date", value: joiningDate
|
|
22
22
|
? new Date(joiningDate).toISOString().split("formTLabel")[0]
|
|
23
23
|
: "" }), _jsx(EnhancedCombobox, { emptyText: t("formNoTeacherUsersEmpty"), error: errors.userId, id: "userId", info: t("formLinkedSystemUserLabel"), label: t("formTeacherUserPlaceholder"), onValueChange: (value) => handleChange("userId", value || null), options: (_a = users.items) === null || _a === void 0 ? void 0 : _a.filter(({ workspaces }) => workspaces === null || workspaces === void 0 ? void 0 : workspaces.filter(({ role }) => (role === null || role === void 0 ? void 0 : role.userRole) === USER_ROLE.TEACHER).length).map((u) => ({
|
|
24
24
|
label: (u === null || u === void 0 ? void 0 : u.name) || (u === null || u === void 0 ? void 0 : u.email) || u.id,
|
|
@@ -1,26 +1,86 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import {
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFromUrl } from "@react-pakistan/util-functions";
|
|
4
|
+
import { showErrorToast, showSuccessToast, } from "@appcorp/shadcn/lib/toast-utils";
|
|
4
5
|
import { getCachedWorkspaceSync } from "../workspace/cache";
|
|
5
6
|
import converter from "json-2-csv";
|
|
6
7
|
import { Timeline } from "../../components/timeline";
|
|
7
8
|
import { useTranslations } from "next-intl";
|
|
9
|
+
import { CSVS } from "@/constants";
|
|
10
|
+
import { teacherFormValidation } from "./validate";
|
|
8
11
|
const workspace = getCachedWorkspaceSync();
|
|
9
|
-
const handleGetAllRecords = async (schoolId) => {
|
|
10
|
-
const response = await fetch(`/api/v1/teacher?pageLimit=1000¤tPage=1&schoolId=${schoolId}`, { method: API_METHODS.GET });
|
|
11
|
-
const result = await response.json();
|
|
12
|
-
const csv = await converter.json2csv(result.items || [], {});
|
|
13
|
-
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
14
|
-
await downloadFromUrl(blob, "teacher.csv");
|
|
15
|
-
};
|
|
16
12
|
export const TeacherMoreActions = () => {
|
|
17
13
|
const t = useTranslations("teacher");
|
|
14
|
+
const handleBulkCreate = async (files) => {
|
|
15
|
+
var _a;
|
|
16
|
+
if (!files || files.length === 0)
|
|
17
|
+
return;
|
|
18
|
+
const file = files[0];
|
|
19
|
+
try {
|
|
20
|
+
const text = await file.text();
|
|
21
|
+
const records = converter.csv2json(text);
|
|
22
|
+
if (!Array.isArray(records) || records.length === 0) {
|
|
23
|
+
showErrorToast("CSV file is empty or invalid");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const errors = [];
|
|
27
|
+
const validRecords = [];
|
|
28
|
+
for (let i = 0; i < records.length; i++) {
|
|
29
|
+
const result = teacherFormValidation.safeParse(records[i]);
|
|
30
|
+
if (!result.success) {
|
|
31
|
+
errors.push({
|
|
32
|
+
row: i + 1,
|
|
33
|
+
messages: result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
validRecords.push(result.data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (errors.length > 0) {
|
|
41
|
+
const summary = errors
|
|
42
|
+
.slice(0, 5)
|
|
43
|
+
.map((e) => `Row ${e.row}: ${e.messages.join("; ")}`)
|
|
44
|
+
.join("\n");
|
|
45
|
+
const remaining = errors.length - 5;
|
|
46
|
+
showErrorToast(`Validation failed for ${errors.length} row(s).\n${summary}${remaining > 0 ? `\n...and ${remaining} more` : ""}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch("/api/v1/teacher/bulk", {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
"x-api-token": process.env.NEXT_PUBLIC_API_KEY,
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
schoolId,
|
|
59
|
+
csvData: text,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
if (response.ok) {
|
|
63
|
+
showSuccessToast(`Successfully created ${validRecords.length} teacher(s)`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const errorData = await response.json().catch(() => ({}));
|
|
67
|
+
showErrorToast(errorData.error || `Bulk create failed with status ${response.status}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (_b) {
|
|
71
|
+
showErrorToast("Network error during bulk create");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
showErrorToast(`Failed to process CSV: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
18
78
|
const create = [
|
|
19
79
|
{
|
|
20
80
|
id: "1",
|
|
21
81
|
title: t("moreActionsDownloadEmptyCsvTemplate"),
|
|
22
82
|
handleOnClick: async () => {
|
|
23
|
-
await downloadFromUrl(
|
|
83
|
+
await downloadFromUrl(CSVS.Teacher, "teacher-template.csv");
|
|
24
84
|
},
|
|
25
85
|
},
|
|
26
86
|
{ id: "2", title: t("moreActionsAddYourDataToTheCsv") },
|
|
@@ -29,17 +89,5 @@ export const TeacherMoreActions = () => {
|
|
|
29
89
|
title: t("moreActionsUploadTheCompletedCsvToTheSystem"),
|
|
30
90
|
},
|
|
31
91
|
];
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
id: "1",
|
|
35
|
-
title: t("moreActionsDownloadPopulatedCsvTemplate"),
|
|
36
|
-
handleOnClick: async () => {
|
|
37
|
-
var _a;
|
|
38
|
-
await handleGetAllRecords(((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "");
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
{ id: "2", title: t("moreActionsUpdateYourDataToTheCsv") },
|
|
42
|
-
{ id: "3", title: t("moreActionsUploadTheCompletedCsvToTheSystem") },
|
|
43
|
-
];
|
|
44
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(Timeline, { events: create, heading: t("moreActionsBulkCreate") }), _jsx(Timeline, { events: update, heading: t("moreActionsBulkUpdate") })] }));
|
|
92
|
+
return (_jsx("div", { className: "space-y-4", children: _jsx(Timeline, { events: create, heading: t("moreActionsBulkCreate"), handleOnBulkCreate: handleBulkCreate }) }));
|
|
45
93
|
};
|
|
@@ -22,6 +22,6 @@ export declare const teacherFormValidation: z.ZodObject<{
|
|
|
22
22
|
qualification: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
23
23
|
specialization: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
24
24
|
state: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
25
|
-
teacherCode: z.ZodString
|
|
25
|
+
teacherCode: z.ZodOptional<z.ZodString>;
|
|
26
26
|
userId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
27
27
|
}, z.core.$strip>;
|
|
@@ -47,6 +47,6 @@ export const teacherFormValidation = z.object({
|
|
|
47
47
|
qualification: z.string().optional().nullable(),
|
|
48
48
|
specialization: z.string().optional().nullable(),
|
|
49
49
|
state: z.string().optional().nullable(),
|
|
50
|
-
teacherCode: z.string().
|
|
50
|
+
teacherCode: z.string().optional(),
|
|
51
51
|
userId: z.string().optional().nullable(),
|
|
52
52
|
});
|
package/components/timeline.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export interface TimelineEvent {
|
|
|
7
7
|
export interface TimelineProps {
|
|
8
8
|
events: TimelineEvent[];
|
|
9
9
|
heading: string;
|
|
10
|
+
handleOnBulkCreate?: (files: File[]) => void;
|
|
10
11
|
}
|
|
11
|
-
export declare const Timeline: ({ heading, events }: TimelineProps) => import("react").JSX.Element;
|
|
12
|
+
export declare const Timeline: ({ heading, events, handleOnBulkCreate, }: TimelineProps) => import("react").JSX.Element;
|
package/components/timeline.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { EnhancedDropzone } from "@appcorp/shadcn/components/enhanced-dropzone";
|
|
4
|
+
import { Button } from "@appcorp/shadcn/components/ui/button";
|
|
5
|
+
export const Timeline = ({ heading, events, handleOnBulkCreate, }) => {
|
|
6
|
+
const [files, setFiles] = useState([]);
|
|
7
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsx("h2", { className: "text-foreground text-xl font-semibold", children: heading }), _jsxs("div", { className: "space-y-4", children: [events.map((event) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "z-10 flex h-6 w-6 items-center justify-center", children: _jsx("div", { className: "bg-muted-foreground h-3 w-3 rounded-full" }) }), _jsxs("div", { className: "min-w-0 flex-1 pb-1", children: [_jsx("div", { onClick: event.handleOnClick, className: `text-muted-foreground text-sm ${event.handleOnClick ? "cursor-pointer underline" : ""}`, children: event.title }), event.description && (_jsx("div", { className: "text-foreground mt-1 text-sm", children: event.description }))] })] }, event.id))), _jsx(EnhancedDropzone, { className: "", label: "Upload CSV", id: "timeline-dropzone", info: "Drag and drop your CSV file here, or click to select.", error: "", accept: ["text/csv", ".csv"], maxFiles: 1, onChange: setFiles, onRemoveRemote: () => {
|
|
8
|
+
// Handle remote file removal if needed
|
|
9
|
+
} }), _jsx(Button, { disabled: files.length === 0, onClick: () => handleOnBulkCreate === null || handleOnBulkCreate === void 0 ? void 0 : handleOnBulkCreate(files), children: "Upload and Process CSV" })] })] }));
|
|
4
10
|
};
|
package/constants.d.ts
CHANGED
|
@@ -80,3 +80,43 @@ export declare const LS_KEYS: {
|
|
|
80
80
|
WORKSPACE_USERS: string;
|
|
81
81
|
WORKSPACE: string;
|
|
82
82
|
};
|
|
83
|
+
export declare const CSVS: {
|
|
84
|
+
Admission: string;
|
|
85
|
+
AdmissionAI: string;
|
|
86
|
+
Attendance: string;
|
|
87
|
+
BlogComment: string;
|
|
88
|
+
BlogCommentClap: string;
|
|
89
|
+
BlogPost: string;
|
|
90
|
+
Campaign: string;
|
|
91
|
+
Campus: string;
|
|
92
|
+
Class: string;
|
|
93
|
+
Course: string;
|
|
94
|
+
DashboardAnalytics: string;
|
|
95
|
+
DiscountCode: string;
|
|
96
|
+
Enrollment: string;
|
|
97
|
+
Expense: string;
|
|
98
|
+
Family: string;
|
|
99
|
+
FamilyMember: string;
|
|
100
|
+
FeeAgentActivity: string;
|
|
101
|
+
FeeConversation: string;
|
|
102
|
+
FeeStructure: string;
|
|
103
|
+
Grade: string;
|
|
104
|
+
MobileNotification: string;
|
|
105
|
+
NotificationPreference: string;
|
|
106
|
+
Permission: string;
|
|
107
|
+
Plan: string;
|
|
108
|
+
Role: string;
|
|
109
|
+
RolePermission: string;
|
|
110
|
+
Schedule: string;
|
|
111
|
+
School: string;
|
|
112
|
+
Section: string;
|
|
113
|
+
StudentFee: string;
|
|
114
|
+
StudentProfile: string;
|
|
115
|
+
Subject: string;
|
|
116
|
+
Teacher: string;
|
|
117
|
+
User: string;
|
|
118
|
+
UserPushToken: string;
|
|
119
|
+
Workspace: string;
|
|
120
|
+
WorkspaceUser: string;
|
|
121
|
+
WorkspaceUserPermission: string;
|
|
122
|
+
};
|
package/constants.js
CHANGED
|
@@ -80,3 +80,43 @@ export const LS_KEYS = {
|
|
|
80
80
|
WORKSPACE_USERS: "fusion_workspace_users",
|
|
81
81
|
WORKSPACE: "fusion_workspace",
|
|
82
82
|
};
|
|
83
|
+
export const CSVS = {
|
|
84
|
+
Admission: "https://storage.edupilotpro.com/fusion-public/data-tables/Admission.csv",
|
|
85
|
+
AdmissionAI: "https://storage.edupilotpro.com/fusion-public/data-tables/AdmissionAI.csv",
|
|
86
|
+
Attendance: "https://storage.edupilotpro.com/fusion-public/data-tables/Attendance.csv",
|
|
87
|
+
BlogComment: "https://storage.edupilotpro.com/fusion-public/data-tables/BlogComment.csv",
|
|
88
|
+
BlogCommentClap: "https://storage.edupilotpro.com/fusion-public/data-tables/BlogCommentClap.csv",
|
|
89
|
+
BlogPost: "https://storage.edupilotpro.com/fusion-public/data-tables/BlogPost.csv",
|
|
90
|
+
Campaign: "https://storage.edupilotpro.com/fusion-public/data-tables/Campaign.csv",
|
|
91
|
+
Campus: "https://storage.edupilotpro.com/fusion-public/data-tables/Campus.csv",
|
|
92
|
+
Class: "https://storage.edupilotpro.com/fusion-public/data-tables/Class.csv",
|
|
93
|
+
Course: "https://storage.edupilotpro.com/fusion-public/data-tables/Course.csv",
|
|
94
|
+
DashboardAnalytics: "https://storage.edupilotpro.com/fusion-public/data-tables/DashboardAnalytics.csv",
|
|
95
|
+
DiscountCode: "https://storage.edupilotpro.com/fusion-public/data-tables/DiscountCode.csv",
|
|
96
|
+
Enrollment: "https://storage.edupilotpro.com/fusion-public/data-tables/Enrollment.csv",
|
|
97
|
+
Expense: "https://storage.edupilotpro.com/fusion-public/data-tables/Expense.csv",
|
|
98
|
+
Family: "https://storage.edupilotpro.com/fusion-public/data-tables/Family.csv",
|
|
99
|
+
FamilyMember: "https://storage.edupilotpro.com/fusion-public/data-tables/FamilyMember.csv",
|
|
100
|
+
FeeAgentActivity: "https://storage.edupilotpro.com/fusion-public/data-tables/FeeAgentActivity.csv",
|
|
101
|
+
FeeConversation: "https://storage.edupilotpro.com/fusion-public/data-tables/FeeConversation.csv",
|
|
102
|
+
FeeStructure: "https://storage.edupilotpro.com/fusion-public/data-tables/FeeStructure.csv",
|
|
103
|
+
Grade: "https://storage.edupilotpro.com/fusion-public/data-tables/Grade.csv",
|
|
104
|
+
MobileNotification: "https://storage.edupilotpro.com/fusion-public/data-tables/MobileNotification.csv",
|
|
105
|
+
NotificationPreference: "https://storage.edupilotpro.com/fusion-public/data-tables/NotificationPreference.csv",
|
|
106
|
+
Permission: "https://storage.edupilotpro.com/fusion-public/data-tables/Permission.csv",
|
|
107
|
+
Plan: "https://storage.edupilotpro.com/fusion-public/data-tables/Plan.csv",
|
|
108
|
+
Role: "https://storage.edupilotpro.com/fusion-public/data-tables/Role.csv",
|
|
109
|
+
RolePermission: "https://storage.edupilotpro.com/fusion-public/data-tables/RolePermission.csv",
|
|
110
|
+
Schedule: "https://storage.edupilotpro.com/fusion-public/data-tables/Schedule.csv",
|
|
111
|
+
School: "https://storage.edupilotpro.com/fusion-public/data-tables/School.csv",
|
|
112
|
+
Section: "https://storage.edupilotpro.com/fusion-public/data-tables/Section.csv",
|
|
113
|
+
StudentFee: "https://storage.edupilotpro.com/fusion-public/data-tables/StudentFee.csv",
|
|
114
|
+
StudentProfile: "https://storage.edupilotpro.com/fusion-public/data-tables/StudentProfile.csv",
|
|
115
|
+
Subject: "https://storage.edupilotpro.com/fusion-public/data-tables/Subject.csv",
|
|
116
|
+
Teacher: "https://storage.edupilotpro.com/fusion-public/data-tables/Teacher.csv",
|
|
117
|
+
User: "https://storage.edupilotpro.com/fusion-public/data-tables/User.csv",
|
|
118
|
+
UserPushToken: "https://storage.edupilotpro.com/fusion-public/data-tables/UserPushToken.csv",
|
|
119
|
+
Workspace: "https://storage.edupilotpro.com/fusion-public/data-tables/Workspace.csv",
|
|
120
|
+
WorkspaceUser: "https://storage.edupilotpro.com/fusion-public/data-tables/WorkspaceUser.csv",
|
|
121
|
+
WorkspaceUserPermission: "https://storage.edupilotpro.com/fusion-public/data-tables/WorkspaceUserPermission.csv",
|
|
122
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appcorp/fusion-storybook",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build-storybook": "storybook build",
|
|
6
6
|
"build:next": "next build",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"react": "^19.2.5",
|
|
102
102
|
"react-dom": "^19.2.5",
|
|
103
103
|
"react-dropzone": "^15.0.0",
|
|
104
|
+
"react-easy-crop": "^5.5.7",
|
|
104
105
|
"rimraf": "^6.1.3",
|
|
105
106
|
"sonner": "^2.0.7",
|
|
106
107
|
"storybook": "^10.3.4",
|