@appcorp/fusion-storybook 0.2.10 → 0.2.12

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.
@@ -32,7 +32,6 @@ import { teacherFormValidation } from "./validate";
32
32
  import { getCachedTeachers, invalidateTeachersCache } from "./cache";
33
33
  import { getCachedWorkspaceSync } from "../workspace/cache";
34
34
  import { processAvatarFile } from "./avatar-upload";
35
- import { v4 as uuidv4 } from "uuid";
36
35
  // ============================================================================
37
36
  // 1.1 DRAWER TYPES
38
37
  // ============================================================================
@@ -107,6 +106,8 @@ export const useTeacherModule = () => {
107
106
  const schoolId = ((_a = workspace === null || workspace === void 0 ? void 0 : workspace.school) === null || _a === void 0 ? void 0 : _a.id) || "";
108
107
  const listFetchNowRef = useRef(null);
109
108
  const debouncedQuery = useDebounce(state.searchQuery, 800);
109
+ const uploadInProgressRef = useRef(false);
110
+ const lastUploadRef = useRef(null);
110
111
  // ============================================================================
111
112
  // 1.4.2 API PARAMETERS
112
113
  // ============================================================================
@@ -355,30 +356,30 @@ export const useTeacherModule = () => {
355
356
  }, [dispatch]);
356
357
  const handleAvatarUpload = useCallback(async (file) => {
357
358
  try {
358
- // Set uploading state
359
- // dispatch({
360
- // type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
361
- // payload: { key: "avatarUploading", value: true },
362
- // });
359
+ // Prevent duplicate concurrent uploads
360
+ const sig = `${file.name}-${file.size}-${file.lastModified}`;
361
+ const now = Date.now();
362
+ // If same file uploaded within last 5s, ignore
363
+ if (lastUploadRef.current &&
364
+ lastUploadRef.current.sig === sig &&
365
+ now - lastUploadRef.current.ts < 5000)
366
+ return;
367
+ if (uploadInProgressRef.current)
368
+ return;
369
+ uploadInProgressRef.current = true;
370
+ lastUploadRef.current = { sig, ts: now };
363
371
  // Process avatar file (validate, convert to WebP)
364
372
  const result = await processAvatarFile(file);
365
373
  if ("type" in result) {
366
374
  // Error result
367
375
  const error = result;
368
376
  showToast(error.message, TOAST_VARIANT.ERROR);
369
- // dispatch({
370
- // type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
371
- // payload: { key: "avatarUploading", value: false },
372
- // });
373
377
  return;
374
378
  }
375
- const teacherId = state.firstName + state.lastName + uuidv4().slice(0, 5);
379
+ const teacherId = state.id;
376
380
  if (!teacherId) {
377
381
  showToast("Please save the teacher record first before uploading an avatar", TOAST_VARIANT.ERROR);
378
- // dispatch({
379
- // type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
380
- // payload: { key: "avatarUploading", value: false },
381
- // });
382
+ uploadInProgressRef.current = false;
382
383
  return;
383
384
  }
384
385
  // Upload to S3 via teacher-avatar endpoint
@@ -392,21 +393,18 @@ export const useTeacherModule = () => {
392
393
  teacherId,
393
394
  fileBase64: result.fileBase64,
394
395
  contentType: result.contentType,
396
+ workspaceId: workspace === null || workspace === void 0 ? void 0 : workspace.id,
395
397
  }),
396
398
  });
397
399
  if (!response.ok) {
398
400
  const errorData = await response.json().catch(() => ({}));
399
401
  const errorMessage = errorData.error || `Upload failed with status ${response.status}`;
400
402
  showToast(errorMessage, TOAST_VARIANT.ERROR);
401
- // dispatch({
402
- // type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
403
- // payload: { key: "avatarUploading", value: false },
404
- // });
403
+ uploadInProgressRef.current = false;
405
404
  return;
406
405
  }
407
406
  const data = await response.json();
408
407
  const avatarUrl = data.url;
409
- // Update avatar field with presigned URL
410
408
  dispatch({
411
409
  type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
412
410
  payload: { key: "avatar", value: avatarUrl },
@@ -420,12 +418,9 @@ export const useTeacherModule = () => {
420
418
  showToast(errorMessage, TOAST_VARIANT.ERROR);
421
419
  }
422
420
  finally {
423
- // dispatch({
424
- // type: TEACHER_ACTION_TYPES.SET_INPUT_FIELD,
425
- // payload: { key: "avatarUploading", value: false },
426
- // });
421
+ uploadInProgressRef.current = false;
427
422
  }
428
- }, [dispatch, showToast, state.firstName, state.lastName]);
423
+ }, [dispatch, showToast, state.id, workspace === null || workspace === void 0 ? void 0 : workspace.id]);
429
424
  const applyFilters = useCallback(() => {
430
425
  dispatch({
431
426
  type: TEACHER_ACTION_TYPES.SET_CURRENT_PAGE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",