@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.
@@ -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.courseId === courseId)
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 { CourseBE, EnrollmentBE, SectionBE, StudentProfileBE } from "../../type";
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: { courseId, enabled, enrollmentDate, errors, sectionId, studentProfileId, }, handleChange, } = useEnrollmentModule();
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("formNoCourseEmpty"), error: errors.courseId, id: "courseId", info: t("formCourseInfo"), label: t("formCourseLabel"), onValueChange: (value) => handleChange("courseId", value), options: (getCachedCoursesSync().items || []).map((c) => ({
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,
@@ -24,7 +24,6 @@ interface Props {
24
24
  drawerViewDescription: string;
25
25
  drawerViewTitle: string;
26
26
  labelActions: string;
27
- labelCourse: string;
28
27
  labelEnabled: string;
29
28
  labelEnrollmentDate: string;
30
29
  labelId: string;
@@ -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, labelCourse, labelEnabled, labelEnrollmentDate, labelId, labelSection, labelStudent, saveLabel, searchPlaceholder, tableDescription, tableTitle, }) => {
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: "20%" },
79
- { label: labelCourse, width: "20%" },
80
- { label: labelSection, width: "20%" },
81
- { label: labelEnrollmentDate, width: "15%" },
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,
@@ -6,7 +6,6 @@
6
6
  import { z } from "zod";
7
7
  export declare const enrollmentFormValidation: z.ZodObject<{
8
8
  studentProfileId: z.ZodString;
9
- courseId: z.ZodString;
10
9
  sectionId: z.ZodString;
11
10
  enrollmentDate: z.ZodString;
12
11
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -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, _d, _e;
28
- const { state: { courseId, enrollmentDate, enabled, sectionId, studentProfileId }, } = useEnrollmentModule();
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
- ? ((_e = section.class) === null || _e === void 0 ? void 0 : _e.code)
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("viewFieldCourse") }), _jsx("p", { className: "text-base", children: courseName })] }), _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
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
- return ((_a = enrollments[0].course) === null || _a === void 0 ? void 0 : _a.code) || "Unknown course";
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} courses`;
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: false,
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, teacherCode, userId, } = context.state;
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.teacherCode, id: "teacherCode", info: t("formUniqueTeacherIdentifierLabel"), label: t("formTeacherCodeLabel"), onChange: (e) => handleChange("teacherCode", e.target.value), placeholder: t("formTeacherCodePlaceholder"), required: true, value: teacherCode }), _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
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, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { API_METHODS, downloadFromUrl } from "@react-pakistan/util-functions";
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&currentPage=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("https://nwolvgylwmjuqxsngjxt.supabase.co/storage/v1/object/public/public-blob/common-assets/teacher.csv", "teacher-template.csv");
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
- const update = [
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().min(1, "Teacher code is required"),
50
+ teacherCode: z.string().optional(),
51
51
  userId: z.string().optional().nullable(),
52
52
  });
@@ -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;
@@ -1,4 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const Timeline = ({ heading, events }) => {
3
- return (_jsxs("div", { className: "space-y-6", children: [_jsx("h2", { className: "text-foreground text-xl font-semibold", children: heading }), _jsxs("div", { className: "relative", children: [_jsx("div", { className: "bg-border absolute top-0 bottom-0 left-3 w-px" }), _jsx("div", { className: "space-y-4", children: events.map((event) => (_jsxs("div", { className: "relative flex items-start gap-4", children: [_jsx("div", { className: "relative 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 ? "underline cursor-pointer" : ""}`, children: event.title }), event.description && (_jsx("div", { className: "text-foreground mt-1 text-sm", children: event.description }))] })] }, event.id))) })] })] }));
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.14",
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",