@crimson-education/sdk 0.2.0

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.
Files changed (70) hide show
  1. package/README.md +377 -0
  2. package/dist/core/account.d.ts +14 -0
  3. package/dist/core/account.js +30 -0
  4. package/dist/core/auth/index.d.ts +11 -0
  5. package/dist/core/auth/index.js +25 -0
  6. package/dist/core/auth/oauth-adapter.d.ts +78 -0
  7. package/dist/core/auth/oauth-adapter.js +341 -0
  8. package/dist/core/auth/pkce.d.ts +20 -0
  9. package/dist/core/auth/pkce.js +112 -0
  10. package/dist/core/auth/token-manager.d.ts +68 -0
  11. package/dist/core/auth/token-manager.js +294 -0
  12. package/dist/core/auth/token-storage.d.ts +46 -0
  13. package/dist/core/auth/token-storage.js +155 -0
  14. package/dist/core/auth/types.d.ts +148 -0
  15. package/dist/core/auth/types.js +15 -0
  16. package/dist/core/client.d.ts +84 -0
  17. package/dist/core/client.js +229 -0
  18. package/dist/core/index.d.ts +11 -0
  19. package/dist/core/index.js +47 -0
  20. package/dist/core/missionLibrary.d.ts +68 -0
  21. package/dist/core/missionLibrary.js +143 -0
  22. package/dist/core/missions.d.ts +45 -0
  23. package/dist/core/missions.js +140 -0
  24. package/dist/core/roadmap.d.ts +8 -0
  25. package/dist/core/roadmap.js +18 -0
  26. package/dist/core/studentProfile.d.ts +21 -0
  27. package/dist/core/studentProfile.js +41 -0
  28. package/dist/core/tasks.d.ts +117 -0
  29. package/dist/core/tasks.js +288 -0
  30. package/dist/core/types.d.ts +402 -0
  31. package/dist/core/types.js +2 -0
  32. package/dist/core/users.d.ts +21 -0
  33. package/dist/core/users.js +46 -0
  34. package/dist/iframe/auth-state.d.ts +7 -0
  35. package/dist/iframe/auth-state.js +125 -0
  36. package/dist/iframe/constants.d.ts +8 -0
  37. package/dist/iframe/constants.js +29 -0
  38. package/dist/iframe/index.d.ts +5 -0
  39. package/dist/iframe/index.js +17 -0
  40. package/dist/iframe/listener.d.ts +2 -0
  41. package/dist/iframe/listener.js +57 -0
  42. package/dist/iframe/types.d.ts +18 -0
  43. package/dist/iframe/types.js +2 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +22 -0
  46. package/dist/react/hooks/index.d.ts +10 -0
  47. package/dist/react/hooks/index.js +48 -0
  48. package/dist/react/hooks/useAccount.d.ts +13 -0
  49. package/dist/react/hooks/useAccount.js +39 -0
  50. package/dist/react/hooks/useAuthState.d.ts +2 -0
  51. package/dist/react/hooks/useAuthState.js +18 -0
  52. package/dist/react/hooks/useMissionLibrary.d.ts +31 -0
  53. package/dist/react/hooks/useMissionLibrary.js +183 -0
  54. package/dist/react/hooks/useMissions.d.ts +24 -0
  55. package/dist/react/hooks/useMissions.js +104 -0
  56. package/dist/react/hooks/useOAuth.d.ts +94 -0
  57. package/dist/react/hooks/useOAuth.js +211 -0
  58. package/dist/react/hooks/useRoadmapContext.d.ts +2 -0
  59. package/dist/react/hooks/useRoadmapContext.js +29 -0
  60. package/dist/react/hooks/useStudentProfile.d.ts +24 -0
  61. package/dist/react/hooks/useStudentProfile.js +65 -0
  62. package/dist/react/hooks/useTasks.d.ts +26 -0
  63. package/dist/react/hooks/useTasks.js +137 -0
  64. package/dist/react/hooks/useUsers.d.ts +9 -0
  65. package/dist/react/hooks/useUsers.js +50 -0
  66. package/dist/react/index.d.ts +3 -0
  67. package/dist/react/index.js +21 -0
  68. package/dist/react/provider.d.ts +16 -0
  69. package/dist/react/provider.js +41 -0
  70. package/package.json +61 -0
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupIframeListener = setupIframeListener;
4
+ const constants_1 = require("./constants");
5
+ function setupIframeListener(allowedOrigins) {
6
+ if (typeof window === "undefined") {
7
+ return () => { };
8
+ }
9
+ const origins = allowedOrigins || (0, constants_1.getDefaultAllowedOrigins)();
10
+ // Notify parent that iframe is ready
11
+ if (window.self !== window.top && window.parent) {
12
+ try {
13
+ window.parent.postMessage({
14
+ type: "IFRAME_READY",
15
+ source: "new-roadmap-ui",
16
+ timestamp: Date.now(),
17
+ }, "*");
18
+ }
19
+ catch (_a) {
20
+ // Silently fail - parent notification is not critical
21
+ }
22
+ }
23
+ const messageHandler = (event) => {
24
+ if (!origins.includes(event.origin)) {
25
+ console.warn(`[iframe-listener] Rejected unauthorized origin: ${event.origin}`, `Allowed origins:`, origins);
26
+ return;
27
+ }
28
+ const isLocalhost = event.origin.includes("localhost") || event.origin.includes("127.0.0.1");
29
+ const isHttps = event.origin.startsWith("https://");
30
+ if (!isLocalhost && !isHttps) {
31
+ console.error(`[iframe-listener] Insecure HTTP connection from ${event.origin}`);
32
+ if (process.env.NODE_ENV === "production") {
33
+ return;
34
+ }
35
+ }
36
+ if (event.data && event.data.type === "INIT") {
37
+ const { token, userId, studentId, user } = event.data.payload || {};
38
+ if (token && studentId) {
39
+ window.xprops = {
40
+ token,
41
+ userId,
42
+ studentId,
43
+ user,
44
+ };
45
+ window.dispatchEvent(new CustomEvent(constants_1.XPROPS_READY_EVENT));
46
+ }
47
+ else {
48
+ console.warn("[iframe-listener] Missing token or studentId in INIT message");
49
+ }
50
+ }
51
+ };
52
+ window.addEventListener("message", messageHandler);
53
+ // Return cleanup function
54
+ return () => {
55
+ window.removeEventListener("message", messageHandler);
56
+ };
57
+ }
@@ -0,0 +1,18 @@
1
+ export interface ZoidProps {
2
+ token: string;
3
+ userId: string;
4
+ studentId: string;
5
+ user?: Record<string, unknown>;
6
+ }
7
+ export interface AuthState {
8
+ token: string | null;
9
+ userId: string | null;
10
+ studentId: string | null;
11
+ user?: Record<string, unknown>;
12
+ ready: boolean;
13
+ }
14
+ declare global {
15
+ interface Window {
16
+ xprops?: ZoidProps;
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export * from "./core";
2
+ export * from "./iframe";
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Core layer (framework-agnostic) - backward compatible exports
18
+ __exportStar(require("./core"), exports);
19
+ // iframe layer (postMessage communication)
20
+ __exportStar(require("./iframe"), exports);
21
+ // React layer is available via "@crimson/sdk/react" subpath
22
+ // Do NOT export here to avoid forcing React dependency on non-React consumers
@@ -0,0 +1,10 @@
1
+ export { useAuthState } from "./useAuthState";
2
+ export { useOAuth, useOAuthCallback } from "./useOAuth";
3
+ export type { UseOAuthResult } from "./useOAuth";
4
+ export { useMissions, useMissionsInfinite, useCreateMission, useUpdateMission, useDeleteMission, missionKeys, } from "./useMissions";
5
+ export { useTasks, useAllUserTasks, useTasksByRoadmapId, useTasksInfinite, useCreateTask, useUpdateTask, useDeleteTask, taskKeys, } from "./useTasks";
6
+ export { useUsers, useUser, userKeys } from "./useUsers";
7
+ export { useCurrentUserRoles, accountKeys } from "./useAccount";
8
+ export { useRoadmapContext } from "./useRoadmapContext";
9
+ export { useTemplateMissions, useTemplateMissionsInfinite, useTemplateTasks, useTemplateTasksInfinite, useTemplateMissionDetail, useCopyTemplateMission, useAssignBulkMission, useAssignBulkTask, useCreateFromPredefinedTasks, missionLibraryKeys, } from "./useMissionLibrary";
10
+ export { useStudentProfile, useStudentPrefilledInfo, studentProfileKeys, } from "./useStudentProfile";
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.studentProfileKeys = exports.useStudentPrefilledInfo = exports.useStudentProfile = exports.missionLibraryKeys = exports.useCreateFromPredefinedTasks = exports.useAssignBulkTask = exports.useAssignBulkMission = exports.useCopyTemplateMission = exports.useTemplateMissionDetail = exports.useTemplateTasksInfinite = exports.useTemplateTasks = exports.useTemplateMissionsInfinite = exports.useTemplateMissions = exports.useRoadmapContext = exports.accountKeys = exports.useCurrentUserRoles = exports.userKeys = exports.useUser = exports.useUsers = exports.taskKeys = exports.useDeleteTask = exports.useUpdateTask = exports.useCreateTask = exports.useTasksInfinite = exports.useTasksByRoadmapId = exports.useAllUserTasks = exports.useTasks = exports.missionKeys = exports.useDeleteMission = exports.useUpdateMission = exports.useCreateMission = exports.useMissionsInfinite = exports.useMissions = exports.useOAuthCallback = exports.useOAuth = exports.useAuthState = void 0;
4
+ var useAuthState_1 = require("./useAuthState");
5
+ Object.defineProperty(exports, "useAuthState", { enumerable: true, get: function () { return useAuthState_1.useAuthState; } });
6
+ var useOAuth_1 = require("./useOAuth");
7
+ Object.defineProperty(exports, "useOAuth", { enumerable: true, get: function () { return useOAuth_1.useOAuth; } });
8
+ Object.defineProperty(exports, "useOAuthCallback", { enumerable: true, get: function () { return useOAuth_1.useOAuthCallback; } });
9
+ var useMissions_1 = require("./useMissions");
10
+ Object.defineProperty(exports, "useMissions", { enumerable: true, get: function () { return useMissions_1.useMissions; } });
11
+ Object.defineProperty(exports, "useMissionsInfinite", { enumerable: true, get: function () { return useMissions_1.useMissionsInfinite; } });
12
+ Object.defineProperty(exports, "useCreateMission", { enumerable: true, get: function () { return useMissions_1.useCreateMission; } });
13
+ Object.defineProperty(exports, "useUpdateMission", { enumerable: true, get: function () { return useMissions_1.useUpdateMission; } });
14
+ Object.defineProperty(exports, "useDeleteMission", { enumerable: true, get: function () { return useMissions_1.useDeleteMission; } });
15
+ Object.defineProperty(exports, "missionKeys", { enumerable: true, get: function () { return useMissions_1.missionKeys; } });
16
+ var useTasks_1 = require("./useTasks");
17
+ Object.defineProperty(exports, "useTasks", { enumerable: true, get: function () { return useTasks_1.useTasks; } });
18
+ Object.defineProperty(exports, "useAllUserTasks", { enumerable: true, get: function () { return useTasks_1.useAllUserTasks; } });
19
+ Object.defineProperty(exports, "useTasksByRoadmapId", { enumerable: true, get: function () { return useTasks_1.useTasksByRoadmapId; } });
20
+ Object.defineProperty(exports, "useTasksInfinite", { enumerable: true, get: function () { return useTasks_1.useTasksInfinite; } });
21
+ Object.defineProperty(exports, "useCreateTask", { enumerable: true, get: function () { return useTasks_1.useCreateTask; } });
22
+ Object.defineProperty(exports, "useUpdateTask", { enumerable: true, get: function () { return useTasks_1.useUpdateTask; } });
23
+ Object.defineProperty(exports, "useDeleteTask", { enumerable: true, get: function () { return useTasks_1.useDeleteTask; } });
24
+ Object.defineProperty(exports, "taskKeys", { enumerable: true, get: function () { return useTasks_1.taskKeys; } });
25
+ var useUsers_1 = require("./useUsers");
26
+ Object.defineProperty(exports, "useUsers", { enumerable: true, get: function () { return useUsers_1.useUsers; } });
27
+ Object.defineProperty(exports, "useUser", { enumerable: true, get: function () { return useUsers_1.useUser; } });
28
+ Object.defineProperty(exports, "userKeys", { enumerable: true, get: function () { return useUsers_1.userKeys; } });
29
+ var useAccount_1 = require("./useAccount");
30
+ Object.defineProperty(exports, "useCurrentUserRoles", { enumerable: true, get: function () { return useAccount_1.useCurrentUserRoles; } });
31
+ Object.defineProperty(exports, "accountKeys", { enumerable: true, get: function () { return useAccount_1.accountKeys; } });
32
+ var useRoadmapContext_1 = require("./useRoadmapContext");
33
+ Object.defineProperty(exports, "useRoadmapContext", { enumerable: true, get: function () { return useRoadmapContext_1.useRoadmapContext; } });
34
+ var useMissionLibrary_1 = require("./useMissionLibrary");
35
+ Object.defineProperty(exports, "useTemplateMissions", { enumerable: true, get: function () { return useMissionLibrary_1.useTemplateMissions; } });
36
+ Object.defineProperty(exports, "useTemplateMissionsInfinite", { enumerable: true, get: function () { return useMissionLibrary_1.useTemplateMissionsInfinite; } });
37
+ Object.defineProperty(exports, "useTemplateTasks", { enumerable: true, get: function () { return useMissionLibrary_1.useTemplateTasks; } });
38
+ Object.defineProperty(exports, "useTemplateTasksInfinite", { enumerable: true, get: function () { return useMissionLibrary_1.useTemplateTasksInfinite; } });
39
+ Object.defineProperty(exports, "useTemplateMissionDetail", { enumerable: true, get: function () { return useMissionLibrary_1.useTemplateMissionDetail; } });
40
+ Object.defineProperty(exports, "useCopyTemplateMission", { enumerable: true, get: function () { return useMissionLibrary_1.useCopyTemplateMission; } });
41
+ Object.defineProperty(exports, "useAssignBulkMission", { enumerable: true, get: function () { return useMissionLibrary_1.useAssignBulkMission; } });
42
+ Object.defineProperty(exports, "useAssignBulkTask", { enumerable: true, get: function () { return useMissionLibrary_1.useAssignBulkTask; } });
43
+ Object.defineProperty(exports, "useCreateFromPredefinedTasks", { enumerable: true, get: function () { return useMissionLibrary_1.useCreateFromPredefinedTasks; } });
44
+ Object.defineProperty(exports, "missionLibraryKeys", { enumerable: true, get: function () { return useMissionLibrary_1.missionLibraryKeys; } });
45
+ var useStudentProfile_1 = require("./useStudentProfile");
46
+ Object.defineProperty(exports, "useStudentProfile", { enumerable: true, get: function () { return useStudentProfile_1.useStudentProfile; } });
47
+ Object.defineProperty(exports, "useStudentPrefilledInfo", { enumerable: true, get: function () { return useStudentProfile_1.useStudentPrefilledInfo; } });
48
+ Object.defineProperty(exports, "studentProfileKeys", { enumerable: true, get: function () { return useStudentProfile_1.studentProfileKeys; } });
@@ -0,0 +1,13 @@
1
+ import type { CurrentUserRoles } from "../../core/types";
2
+ export declare const accountKeys: {
3
+ all: readonly ["account"];
4
+ currentUserRoles: () => readonly ["account", "currentUserRoles"];
5
+ };
6
+ /**
7
+ * Hook to get current user's roles
8
+ *
9
+ * @returns Query result containing:
10
+ * - userId: The user's ID
11
+ * - roles: Array of role objects with roleId and isPrimary flag
12
+ */
13
+ export declare function useCurrentUserRoles(enabled?: boolean): import("@tanstack/react-query").UseQueryResult<CurrentUserRoles, Error>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.accountKeys = void 0;
14
+ exports.useCurrentUserRoles = useCurrentUserRoles;
15
+ const react_query_1 = require("@tanstack/react-query");
16
+ const provider_1 = require("../provider");
17
+ // Query keys
18
+ exports.accountKeys = {
19
+ all: ["account"],
20
+ currentUserRoles: () => [...exports.accountKeys.all, "currentUserRoles"],
21
+ };
22
+ /**
23
+ * Hook to get current user's roles
24
+ *
25
+ * @returns Query result containing:
26
+ * - userId: The user's ID
27
+ * - roles: Array of role objects with roleId and isPrimary flag
28
+ */
29
+ function useCurrentUserRoles(enabled = true) {
30
+ const client = (0, provider_1.useCrimsonClient)();
31
+ return (0, react_query_1.useQuery)({
32
+ queryKey: exports.accountKeys.currentUserRoles(),
33
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
34
+ return client.account.getCurrentUserRoles();
35
+ }),
36
+ enabled,
37
+ staleTime: 1000 * 60 * 5, // 5 minutes - roles don't change often
38
+ });
39
+ }
@@ -0,0 +1,2 @@
1
+ import { type AuthState } from "../../iframe";
2
+ export declare function useAuthState(): AuthState;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useAuthState = useAuthState;
5
+ const react_1 = require("react");
6
+ const iframe_1 = require("../../iframe");
7
+ const emptyState = {
8
+ token: null,
9
+ userId: null,
10
+ studentId: null,
11
+ user: undefined,
12
+ ready: false,
13
+ };
14
+ // Server snapshot always returns empty state to ensure consistent SSR
15
+ const getServerSnapshot = () => emptyState;
16
+ function useAuthState() {
17
+ return (0, react_1.useSyncExternalStore)(iframe_1.subscribeToAuthState, iframe_1.getAuthState, getServerSnapshot);
18
+ }
@@ -0,0 +1,31 @@
1
+ import type { TemplateMission, TemplateTask, MissionDetail, AssignBulkMissionInput, AssignBulkTaskInput } from "../../core/types";
2
+ import type { TemplateMissionFilters, TemplateTaskFilters, CopyTemplateMissionInput, CreateFromPredefinedInput } from "../../core/missionLibrary";
3
+ export declare const missionLibraryKeys: {
4
+ all: readonly ["missionLibrary"];
5
+ missions: (filters?: TemplateMissionFilters) => readonly ["missionLibrary", "missions", TemplateMissionFilters | undefined];
6
+ missionsInfinite: (filters?: Omit<TemplateMissionFilters, "start" | "limit">) => readonly ["missionLibrary", "missions", "infinite", Omit<TemplateMissionFilters, "start" | "limit"> | undefined];
7
+ tasks: (filters?: TemplateTaskFilters) => readonly ["missionLibrary", "tasks", TemplateTaskFilters | undefined];
8
+ tasksInfinite: (filters?: Omit<TemplateTaskFilters, "start" | "limit">) => readonly ["missionLibrary", "tasks", "infinite", Omit<TemplateTaskFilters, "start" | "limit"> | undefined];
9
+ missionDetail: (id: string) => readonly ["missionLibrary", "mission", string];
10
+ };
11
+ export declare function useTemplateMissions(filters?: TemplateMissionFilters, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<import("../../core/types").PaginatedResult<TemplateMission>, Error>;
12
+ export declare function useTemplateMissionsInfinite(filters?: Omit<TemplateMissionFilters, "start" | "limit">, options?: {
13
+ enabled?: boolean;
14
+ pageSize?: number;
15
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<import("../../core/types").PaginatedResult<TemplateMission>, unknown>, Error>;
16
+ export declare function useTemplateTasks(filters?: TemplateTaskFilters, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<import("../../core/types").PaginatedResult<TemplateTask>, Error>;
17
+ export declare function useTemplateTasksInfinite(filters?: Omit<TemplateTaskFilters, "start" | "limit">, options?: {
18
+ enabled?: boolean;
19
+ pageSize?: number;
20
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<import("../../core/types").PaginatedResult<TemplateTask>, unknown>, Error>;
21
+ export declare function useTemplateMissionDetail(missionId: string, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<MissionDetail | null, Error>;
22
+ export declare function useCopyTemplateMission(): import("@tanstack/react-query").UseMutationResult<TemplateMission[], Error, CopyTemplateMissionInput, unknown>;
23
+ export declare function useAssignBulkMission(): import("@tanstack/react-query").UseMutationResult<{
24
+ code: number;
25
+ msg?: string;
26
+ }, Error, AssignBulkMissionInput[], unknown>;
27
+ export declare function useAssignBulkTask(): import("@tanstack/react-query").UseMutationResult<{
28
+ code: number;
29
+ msg?: string;
30
+ }, Error, AssignBulkTaskInput[], unknown>;
31
+ export declare function useCreateFromPredefinedTasks(): import("@tanstack/react-query").UseMutationResult<TemplateTask[], Error, CreateFromPredefinedInput, unknown>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.missionLibraryKeys = void 0;
14
+ exports.useTemplateMissions = useTemplateMissions;
15
+ exports.useTemplateMissionsInfinite = useTemplateMissionsInfinite;
16
+ exports.useTemplateTasks = useTemplateTasks;
17
+ exports.useTemplateTasksInfinite = useTemplateTasksInfinite;
18
+ exports.useTemplateMissionDetail = useTemplateMissionDetail;
19
+ exports.useCopyTemplateMission = useCopyTemplateMission;
20
+ exports.useAssignBulkMission = useAssignBulkMission;
21
+ exports.useAssignBulkTask = useAssignBulkTask;
22
+ exports.useCreateFromPredefinedTasks = useCreateFromPredefinedTasks;
23
+ const react_query_1 = require("@tanstack/react-query");
24
+ const provider_1 = require("../provider");
25
+ const useMissions_1 = require("./useMissions");
26
+ const useTasks_1 = require("./useTasks");
27
+ // Query keys
28
+ exports.missionLibraryKeys = {
29
+ all: ["missionLibrary"],
30
+ missions: (filters) => [...exports.missionLibraryKeys.all, "missions", filters],
31
+ missionsInfinite: (filters) => [...exports.missionLibraryKeys.all, "missions", "infinite", filters],
32
+ tasks: (filters) => [...exports.missionLibraryKeys.all, "tasks", filters],
33
+ tasksInfinite: (filters) => [...exports.missionLibraryKeys.all, "tasks", "infinite", filters],
34
+ missionDetail: (id) => [...exports.missionLibraryKeys.all, "mission", id],
35
+ };
36
+ // --- Template Missions ---
37
+ function useTemplateMissions(filters, enabled = true) {
38
+ const client = (0, provider_1.useCrimsonClient)();
39
+ return (0, react_query_1.useQuery)({
40
+ queryKey: exports.missionLibraryKeys.missions(filters),
41
+ queryFn: () => client.library.listTemplateMissions(filters),
42
+ enabled,
43
+ });
44
+ }
45
+ function useTemplateMissionsInfinite(filters, options) {
46
+ var _a, _b;
47
+ const client = (0, provider_1.useCrimsonClient)();
48
+ const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
49
+ const pageSize = (_b = options === null || options === void 0 ? void 0 : options.pageSize) !== null && _b !== void 0 ? _b : 20;
50
+ return (0, react_query_1.useInfiniteQuery)({
51
+ queryKey: exports.missionLibraryKeys.missionsInfinite(filters),
52
+ queryFn: (_a) => __awaiter(this, [_a], void 0, function* ({ pageParam = 0 }) {
53
+ return client.library.listTemplateMissions(Object.assign(Object.assign({}, filters), { start: pageParam, limit: pageSize }));
54
+ }),
55
+ initialPageParam: 0,
56
+ getNextPageParam: (lastPage) => {
57
+ if (!(lastPage === null || lastPage === void 0 ? void 0 : lastPage.pagination))
58
+ return undefined;
59
+ return lastPage.pagination.hasMore
60
+ ? lastPage.pagination.start + lastPage.pagination.limit
61
+ : undefined;
62
+ },
63
+ enabled,
64
+ });
65
+ }
66
+ // --- Template Tasks ---
67
+ function useTemplateTasks(filters, enabled = true) {
68
+ const client = (0, provider_1.useCrimsonClient)();
69
+ return (0, react_query_1.useQuery)({
70
+ queryKey: exports.missionLibraryKeys.tasks(filters),
71
+ queryFn: () => client.library.listTemplateTasks(filters),
72
+ enabled,
73
+ });
74
+ }
75
+ function useTemplateTasksInfinite(filters, options) {
76
+ var _a, _b;
77
+ const client = (0, provider_1.useCrimsonClient)();
78
+ const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
79
+ const pageSize = (_b = options === null || options === void 0 ? void 0 : options.pageSize) !== null && _b !== void 0 ? _b : 20;
80
+ return (0, react_query_1.useInfiniteQuery)({
81
+ queryKey: exports.missionLibraryKeys.tasksInfinite(filters),
82
+ queryFn: (_a) => __awaiter(this, [_a], void 0, function* ({ pageParam = 0 }) {
83
+ return client.library.listTemplateTasks(Object.assign(Object.assign({}, filters), { start: pageParam, limit: pageSize }));
84
+ }),
85
+ initialPageParam: 0,
86
+ getNextPageParam: (lastPage) => {
87
+ if (!(lastPage === null || lastPage === void 0 ? void 0 : lastPage.pagination))
88
+ return undefined;
89
+ return lastPage.pagination.hasMore
90
+ ? lastPage.pagination.start + lastPage.pagination.limit
91
+ : undefined;
92
+ },
93
+ enabled,
94
+ });
95
+ }
96
+ // --- Mission Detail ---
97
+ function useTemplateMissionDetail(missionId, enabled = true) {
98
+ const client = (0, provider_1.useCrimsonClient)();
99
+ return (0, react_query_1.useQuery)({
100
+ queryKey: exports.missionLibraryKeys.missionDetail(missionId),
101
+ queryFn: () => client.library.getMissionById(missionId),
102
+ enabled: !!missionId && enabled,
103
+ });
104
+ }
105
+ // --- Mutations ---
106
+ function useCopyTemplateMission() {
107
+ const client = (0, provider_1.useCrimsonClient)();
108
+ const queryClient = (0, react_query_1.useQueryClient)();
109
+ return (0, react_query_1.useMutation)({
110
+ mutationFn: (input) => client.library.copyTemplateMission(input),
111
+ onSuccess: (_, variables) => {
112
+ queryClient.invalidateQueries({
113
+ queryKey: useMissions_1.missionKeys.list(variables.userId),
114
+ });
115
+ queryClient.invalidateQueries({
116
+ queryKey: [...useMissions_1.missionKeys.all, "infinite", variables.userId],
117
+ });
118
+ },
119
+ });
120
+ }
121
+ function useAssignBulkMission() {
122
+ const client = (0, provider_1.useCrimsonClient)();
123
+ const queryClient = (0, react_query_1.useQueryClient)();
124
+ return (0, react_query_1.useMutation)({
125
+ mutationFn: (input) => client.library.assignBulkMission(input),
126
+ onSuccess: (_, variables) => {
127
+ const uniqueUsers = Array.from(new Set(variables.map((v) => v.studentUid)));
128
+ uniqueUsers.forEach((userId) => {
129
+ queryClient.invalidateQueries({
130
+ queryKey: useMissions_1.missionKeys.list(userId),
131
+ });
132
+ queryClient.invalidateQueries({
133
+ queryKey: [...useMissions_1.missionKeys.all, "infinite", userId],
134
+ });
135
+ });
136
+ },
137
+ });
138
+ }
139
+ function useAssignBulkTask() {
140
+ const client = (0, provider_1.useCrimsonClient)();
141
+ const queryClient = (0, react_query_1.useQueryClient)();
142
+ return (0, react_query_1.useMutation)({
143
+ mutationFn: (input) => client.library.assignBulkTask(input),
144
+ onSuccess: (_, variables) => {
145
+ const uniqueInfiniteKeys = new Set();
146
+ const uniqueMissionIds = new Set();
147
+ variables.forEach((input) => {
148
+ const userId = input.studentUid;
149
+ input.tasks.forEach((task) => {
150
+ const roadmapId = task.mission.roadmapId;
151
+ const missionId = task.mission.id;
152
+ uniqueInfiniteKeys.add(`${roadmapId}|${userId}`);
153
+ if (missionId) {
154
+ uniqueMissionIds.add(missionId);
155
+ }
156
+ });
157
+ });
158
+ uniqueInfiniteKeys.forEach((key) => {
159
+ const [roadmapId, userId] = key.split("|");
160
+ queryClient.invalidateQueries({
161
+ queryKey: [...useTasks_1.taskKeys.all, "infinite", roadmapId, userId],
162
+ });
163
+ });
164
+ uniqueMissionIds.forEach((missionId) => {
165
+ queryClient.invalidateQueries({
166
+ queryKey: useTasks_1.taskKeys.list(missionId),
167
+ });
168
+ });
169
+ },
170
+ });
171
+ }
172
+ function useCreateFromPredefinedTasks() {
173
+ const client = (0, provider_1.useCrimsonClient)();
174
+ const queryClient = (0, react_query_1.useQueryClient)();
175
+ return (0, react_query_1.useMutation)({
176
+ mutationFn: (input) => client.library.createFromPredefinedTasks(input),
177
+ onSuccess: (_, variables) => {
178
+ queryClient.invalidateQueries({
179
+ queryKey: useTasks_1.taskKeys.list(variables.missionId),
180
+ });
181
+ },
182
+ });
183
+ }
@@ -0,0 +1,24 @@
1
+ import type { Mission, PaginatedResult } from "../../core/types";
2
+ import type { MissionFilters } from "../../core/missions";
3
+ export declare const missionKeys: {
4
+ all: readonly ["missions"];
5
+ lists: () => readonly ["missions", "list"];
6
+ list: (userId: string) => readonly ["missions", "list", string];
7
+ infinite: (userId: string, filters?: Omit<MissionFilters, "start" | "limit">) => readonly ["missions", "infinite", string, Omit<MissionFilters, "start" | "limit"> | undefined];
8
+ details: () => readonly ["missions", "detail"];
9
+ detail: (id: string) => readonly ["missions", "detail", string];
10
+ };
11
+ export declare function useMissions(userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Mission[], Error>;
12
+ /**
13
+ * Infinite query hook for paginated mission loading
14
+ */
15
+ export declare function useMissionsInfinite(userId: string | null, filters?: Omit<MissionFilters, "start" | "limit">, options?: {
16
+ enabled?: boolean;
17
+ pageSize?: number;
18
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<PaginatedResult<Mission>, unknown>, Error>;
19
+ export declare function useCreateMission(): import("@tanstack/react-query").UseMutationResult<Mission, Error, Partial<Mission>, unknown>;
20
+ export declare function useUpdateMission(): import("@tanstack/react-query").UseMutationResult<Mission, Error, {
21
+ id: string;
22
+ data: Partial<Mission>;
23
+ }, unknown>;
24
+ export declare function useDeleteMission(): import("@tanstack/react-query").UseMutationResult<void, Error, string, unknown>;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ "use client";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.missionKeys = void 0;
14
+ exports.useMissions = useMissions;
15
+ exports.useMissionsInfinite = useMissionsInfinite;
16
+ exports.useCreateMission = useCreateMission;
17
+ exports.useUpdateMission = useUpdateMission;
18
+ exports.useDeleteMission = useDeleteMission;
19
+ const react_query_1 = require("@tanstack/react-query");
20
+ const provider_1 = require("../provider");
21
+ // Query keys
22
+ exports.missionKeys = {
23
+ all: ["missions"],
24
+ lists: () => [...exports.missionKeys.all, "list"],
25
+ list: (userId) => [...exports.missionKeys.lists(), userId],
26
+ infinite: (userId, filters) => [...exports.missionKeys.all, "infinite", userId, filters],
27
+ details: () => [...exports.missionKeys.all, "detail"],
28
+ detail: (id) => [...exports.missionKeys.details(), id],
29
+ };
30
+ function useMissions(userId, enabled) {
31
+ const client = (0, provider_1.useCrimsonClient)();
32
+ return (0, react_query_1.useQuery)({
33
+ queryKey: exports.missionKeys.list(userId || ""),
34
+ queryFn: () => __awaiter(this, void 0, void 0, function* () {
35
+ if (!userId) {
36
+ throw new Error("Missing userId");
37
+ }
38
+ // Without pagination params, list() returns MissionsCategory[]
39
+ const categories = (yield client.missions.list(userId));
40
+ return categories.flatMap((category) => category.missions || []);
41
+ }),
42
+ enabled: !!userId && enabled,
43
+ });
44
+ }
45
+ /**
46
+ * Infinite query hook for paginated mission loading
47
+ */
48
+ function useMissionsInfinite(userId, filters, options) {
49
+ var _a, _b;
50
+ const client = (0, provider_1.useCrimsonClient)();
51
+ const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
52
+ const pageSize = (_b = options === null || options === void 0 ? void 0 : options.pageSize) !== null && _b !== void 0 ? _b : 20;
53
+ return (0, react_query_1.useInfiniteQuery)({
54
+ queryKey: exports.missionKeys.infinite(userId || "", filters),
55
+ queryFn: (_a) => __awaiter(this, [_a], void 0, function* ({ pageParam = 0 }) {
56
+ if (!userId) {
57
+ throw new Error("Missing userId");
58
+ }
59
+ return client.missions.listPaginated(userId, filters, {
60
+ start: pageParam,
61
+ limit: pageSize,
62
+ });
63
+ }),
64
+ initialPageParam: 0,
65
+ getNextPageParam: (lastPage) => {
66
+ if (!(lastPage === null || lastPage === void 0 ? void 0 : lastPage.pagination))
67
+ return undefined;
68
+ return lastPage.pagination.hasMore
69
+ ? lastPage.pagination.start + lastPage.pagination.limit
70
+ : undefined;
71
+ },
72
+ enabled: !!userId && enabled,
73
+ });
74
+ }
75
+ function useCreateMission() {
76
+ const client = (0, provider_1.useCrimsonClient)();
77
+ const queryClient = (0, react_query_1.useQueryClient)();
78
+ return (0, react_query_1.useMutation)({
79
+ mutationFn: (data) => client.missions.create(data),
80
+ onSuccess: () => {
81
+ queryClient.invalidateQueries({ queryKey: exports.missionKeys.all });
82
+ },
83
+ });
84
+ }
85
+ function useUpdateMission() {
86
+ const client = (0, provider_1.useCrimsonClient)();
87
+ const queryClient = (0, react_query_1.useQueryClient)();
88
+ return (0, react_query_1.useMutation)({
89
+ mutationFn: ({ id, data }) => client.missions.update(id, data),
90
+ onSuccess: () => {
91
+ queryClient.invalidateQueries({ queryKey: exports.missionKeys.all });
92
+ },
93
+ });
94
+ }
95
+ function useDeleteMission() {
96
+ const client = (0, provider_1.useCrimsonClient)();
97
+ const queryClient = (0, react_query_1.useQueryClient)();
98
+ return (0, react_query_1.useMutation)({
99
+ mutationFn: (id) => client.missions.delete(id),
100
+ onSuccess: () => {
101
+ queryClient.invalidateQueries({ queryKey: exports.missionKeys.all });
102
+ },
103
+ });
104
+ }