@appcorp/fusion-storybook 0.2.15 → 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.
@@ -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.15",
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",