@appcorp/fusion-storybook 0.2.23 → 0.2.25

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,13 +10,17 @@ import { CSVS } from "../../constants";
10
10
  import { teacherFormValidation } from "./validate";
11
11
  import { TEACHER_API_ROUTES } from "./constants";
12
12
  import { invalidateTeachersCache } from "./cache";
13
- import { useTeacherContext, TEACHER_ACTION_TYPES } from "./context";
13
+ import { TEACHER_ACTION_TYPES, useTeacherModule } from "./context";
14
14
  import { useRef, useEffect, useCallback } from "react";
15
15
  const workspace = getCachedWorkspaceSync();
16
16
  const POLL_INTERVAL_MS = 2000;
17
17
  const POLL_TIMEOUT_MS = 300000;
18
18
  const handleGetAllRecords = async (schoolId) => {
19
- const response = await fetch(`${TEACHER_API_ROUTES.UNIT}?pageLimit=1000&currentPage=1&schoolId=${schoolId}`);
19
+ const response = await fetch(`${TEACHER_API_ROUTES.UNIT}?pageLimit=1000&currentPage=1&schoolId=${schoolId}`, {
20
+ headers: {
21
+ "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
22
+ },
23
+ });
20
24
  const result = await response.json();
21
25
  const csv = await converter.json2csv(result.items || [], {});
22
26
  const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
@@ -27,11 +31,14 @@ async function submitBulkJob(csvData, method, signal) {
27
31
  const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
28
32
  if (!schoolId)
29
33
  throw new Error("School ID not found");
34
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
35
+ if (!apiKey)
36
+ throw new Error("API key not configured");
30
37
  const res = await fetch(TEACHER_API_ROUTES.BULK, {
31
38
  method,
32
39
  headers: {
33
40
  "Content-Type": "application/json",
34
- "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
41
+ "x-api-token": apiKey,
35
42
  },
36
43
  body: JSON.stringify({ schoolId, csvData }),
37
44
  signal,
@@ -43,8 +50,9 @@ async function submitBulkJob(csvData, method, signal) {
43
50
  const data = (await res.json());
44
51
  return data.jobId;
45
52
  }
46
- async function pollBulkJob(jobId, signal) {
53
+ async function pollBulkJob(jobId, signal, onProgress) {
47
54
  const startTime = Date.now();
55
+ let lastProgressToast = 0;
48
56
  while (true) {
49
57
  if (signal.aborted)
50
58
  throw new Error("Polling cancelled");
@@ -54,9 +62,12 @@ async function pollBulkJob(jobId, signal) {
54
62
  await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
55
63
  if (signal.aborted)
56
64
  throw new Error("Polling cancelled");
65
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
66
+ if (!apiKey)
67
+ throw new Error("API key not configured");
57
68
  const res = await fetch(TEACHER_API_ROUTES.BULK_STATUS(jobId), {
58
69
  headers: {
59
- "x-api-token": process.env.NEXT_PUBLIC_API_KEY,
70
+ "x-api-token": apiKey,
60
71
  },
61
72
  signal,
62
73
  });
@@ -66,6 +77,11 @@ async function pollBulkJob(jobId, signal) {
66
77
  if (data.status === "completed" || data.status === "failed") {
67
78
  return data;
68
79
  }
80
+ // Show progress toast every 10 seconds while running
81
+ if (data.status === "running" && Date.now() - lastProgressToast > 10000) {
82
+ lastProgressToast = Date.now();
83
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(data.processed, data.total);
84
+ }
69
85
  }
70
86
  }
71
87
  function formatErrorSummary(errors) {
@@ -79,7 +95,7 @@ function formatErrorSummary(errors) {
79
95
  }
80
96
  export const TeacherMoreActions = () => {
81
97
  const t = useTranslations("teacher");
82
- const { dispatch } = useTeacherContext();
98
+ const { dispatch, listFetchNow } = useTeacherModule();
83
99
  const abortRef = useRef(null);
84
100
  // Abort any in-flight polling on unmount
85
101
  useEffect(() => {
@@ -88,14 +104,14 @@ export const TeacherMoreActions = () => {
88
104
  (_a = abortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
89
105
  };
90
106
  }, []);
91
- const closeDrawer = () => {
92
- dispatch({
93
- type: TEACHER_ACTION_TYPES.SET_DRAWER,
94
- payload: { drawer: null },
95
- });
96
- };
97
107
  const handleBulkFlow = useCallback(async (files, method) => {
98
108
  var _a, _b, _c;
109
+ const closeDrawer = () => {
110
+ dispatch({
111
+ type: TEACHER_ACTION_TYPES.SET_DRAWER,
112
+ payload: { drawer: null },
113
+ });
114
+ };
99
115
  // Cancel previous in-flight request
100
116
  (_a = abortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
101
117
  const controller = new AbortController();
@@ -132,8 +148,17 @@ export const TeacherMoreActions = () => {
132
148
  }
133
149
  try {
134
150
  showInfoToast(`Bulk ${label} job queued (${records.length} records). Processing...`);
135
- const jobId = await submitBulkJob(text, method, signal);
136
- const status = await pollBulkJob(jobId, signal);
151
+ let jobId;
152
+ try {
153
+ jobId = await submitBulkJob(text, method, signal);
154
+ }
155
+ catch (submitError) {
156
+ showErrorToast(`Failed to submit ${label} job: ${submitError instanceof Error ? submitError.message : "Unknown error"}`);
157
+ return;
158
+ }
159
+ const status = await pollBulkJob(jobId, signal, (processed, total) => {
160
+ showInfoToast(`Processing ${processed}/${total} teachers...`);
161
+ });
137
162
  if (signal.aborted)
138
163
  return;
139
164
  if (status.status === "completed") {
@@ -149,6 +174,7 @@ export const TeacherMoreActions = () => {
149
174
  showSuccessToast("Bulk operation completed successfully");
150
175
  }
151
176
  invalidateTeachersCache();
177
+ listFetchNow();
152
178
  closeDrawer();
153
179
  }
154
180
  else {
@@ -164,7 +190,7 @@ export const TeacherMoreActions = () => {
164
190
  return;
165
191
  showErrorToast(`Bulk ${label} failed: ${error instanceof Error ? error.message : "Unknown error"}`);
166
192
  }
167
- }, [closeDrawer]);
193
+ }, [dispatch, listFetchNow]);
168
194
  const handleBulkCreate = useCallback((files) => handleBulkFlow(files, "POST"), [handleBulkFlow]);
169
195
  const handleBulkUpdate = useCallback((files) => handleBulkFlow(files, "PUT"), [handleBulkFlow]);
170
196
  const create = [
@@ -9,7 +9,14 @@ export const Timeline = ({ heading, events, handleOnBulkCreate, }) => {
9
9
  // EnhancedDropzone calls onChange synchronously during render.
10
10
  queueMicrotask(() => setFiles(f));
11
11
  }, []);
12
+ const handleUploadClick = useCallback(() => {
13
+ if (files.length > 0) {
14
+ handleOnBulkCreate === null || handleOnBulkCreate === void 0 ? void 0 : handleOnBulkCreate(files);
15
+ // Clear files after upload to allow resubmission
16
+ queueMicrotask(() => setFiles([]));
17
+ }
18
+ }, [files, handleOnBulkCreate]);
12
19
  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: handleDropzoneChange, onRemoveRemote: () => {
13
20
  // Handle remote file removal if needed
14
- } }), _jsx(Button, { disabled: files.length === 0, onClick: () => handleOnBulkCreate === null || handleOnBulkCreate === void 0 ? void 0 : handleOnBulkCreate(files), children: "Upload and Process CSV" })] })] }));
21
+ } }), _jsx(Button, { disabled: files.length === 0, onClick: handleUploadClick, children: "Upload and Process CSV" })] })] }));
15
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",