@crimson-education/sdk 0.3.11 → 0.3.13

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.
@@ -7,6 +7,8 @@ export interface MissionFilters extends PaginationParams {
7
7
  groupBy?: string;
8
8
  dueDateStart?: string;
9
9
  dueDateEnd?: string;
10
+ visibleSc?: boolean;
11
+ visibleRoadmap?: boolean;
10
12
  }
11
13
  export declare class MissionsApi {
12
14
  private client;
@@ -22,7 +24,10 @@ export declare class MissionsApi {
22
24
  * Handles both new paginated response and legacy category-grouped response
23
25
  */
24
26
  listPaginated(userId: string, filters?: Omit<MissionFilters, "start" | "limit">, pagination?: PaginationParams): Promise<PaginatedResult<Mission>>;
25
- create(data: Partial<Mission>): Promise<Mission>;
27
+ create(data: Partial<Mission> & {
28
+ visibleSc?: boolean;
29
+ visibleRoadmap?: boolean;
30
+ }): Promise<Mission>;
26
31
  update(linkId: string, data: Partial<Mission>): Promise<Mission>;
27
32
  delete(linkId: string): Promise<void>;
28
33
  /**
@@ -31,7 +36,7 @@ export declare class MissionsApi {
31
36
  * @param roadmapId - The roadmap ID
32
37
  * @param missions - Array of mission operations
33
38
  */
34
- batchEdit(userId: string, roadmapId: string, missions: BatchMissionOperation[]): Promise<BatchEditMissionsResult[]>;
39
+ batchEdit(userId: string, roadmapId: string, missions: BatchMissionOperation[], visibleSc?: boolean, visibleRoadmap?: boolean): Promise<BatchEditMissionsResult[]>;
35
40
  /**
36
41
  * Batch delete missions and their action items by link IDs
37
42
  * @param linkIds - Array of mission link IDs to delete
@@ -40,6 +40,12 @@ class MissionsApi {
40
40
  if (filters === null || filters === void 0 ? void 0 : filters.dueDateEnd) {
41
41
  params.append("dueDateEnd", filters.dueDateEnd);
42
42
  }
43
+ if ((filters === null || filters === void 0 ? void 0 : filters.visibleSc) !== undefined) {
44
+ params.append("visibleSc", String(filters.visibleSc));
45
+ }
46
+ if ((filters === null || filters === void 0 ? void 0 : filters.visibleRoadmap) !== undefined) {
47
+ params.append("visibleRoadmap", String(filters.visibleRoadmap));
48
+ }
43
49
  if ((filters === null || filters === void 0 ? void 0 : filters.start) !== undefined) {
44
50
  params.append("start", String(filters.start));
45
51
  }
@@ -110,10 +116,16 @@ class MissionsApi {
110
116
  * @param roadmapId - The roadmap ID
111
117
  * @param missions - Array of mission operations
112
118
  */
113
- batchEdit(userId, roadmapId, missions) {
119
+ batchEdit(userId, roadmapId, missions, visibleSc, visibleRoadmap) {
114
120
  return this.client.fetch("/roadmap/missions/batch", {
115
121
  method: "POST",
116
- body: JSON.stringify({ userId, roadmapId, missions }),
122
+ body: JSON.stringify({
123
+ userId,
124
+ roadmapId,
125
+ missions,
126
+ visibleSc,
127
+ visibleRoadmap,
128
+ }),
117
129
  });
118
130
  }
119
131
  /**
@@ -0,0 +1,111 @@
1
+ import type { CrimsonClient } from "./client";
2
+ import type { Task } from "./types";
3
+ export interface ReflectionComment {
4
+ id: string;
5
+ category?: string;
6
+ content?: string;
7
+ creatorUid?: string;
8
+ creatorName?: string;
9
+ updatedAt?: string;
10
+ createdAt?: string;
11
+ }
12
+ export interface AddReflectionCommentInput {
13
+ content: string;
14
+ category?: string;
15
+ }
16
+ export interface CreateReflectionInput {
17
+ missionId: string;
18
+ description?: string;
19
+ startAt?: string;
20
+ dueDate?: string;
21
+ status?: string;
22
+ }
23
+ export interface UpdateReflectionInput {
24
+ missionId: string;
25
+ description?: string;
26
+ startAt?: string;
27
+ dueDate?: string;
28
+ status?: string;
29
+ }
30
+ export interface BatchUpdateReflectionsInput {
31
+ missionId: string;
32
+ status?: string;
33
+ startAt?: string;
34
+ dueDate?: string;
35
+ }
36
+ export interface GroupedMission {
37
+ mission: Task["mission"];
38
+ reflections: Task[];
39
+ latestReflectionAt: string;
40
+ }
41
+ export interface ListReflectionsResponse {
42
+ data: GroupedMission[];
43
+ pagination: {
44
+ total: number;
45
+ start: number;
46
+ limit: number;
47
+ hasMore: boolean;
48
+ };
49
+ }
50
+ export declare class ReflectionsApi {
51
+ private client;
52
+ constructor(client: CrimsonClient);
53
+ /**
54
+ * Get all reflections of a student, grouped by mission
55
+ */
56
+ list(params: {
57
+ studentId: string;
58
+ roadmapId?: string;
59
+ status?: string[];
60
+ description?: string;
61
+ /** how many missions to return */
62
+ limit?: number;
63
+ start?: number;
64
+ /** limit number of reflections returned per mission; default 1 */
65
+ perMissionLimit?: number;
66
+ }): Promise<ListReflectionsResponse>;
67
+ /**
68
+ * Get all reflections by mission ID
69
+ */
70
+ getByMission(missionId: string): Promise<Task[]>;
71
+ /**
72
+ * Create a new reflection for a mission
73
+ */
74
+ create(missionId: string, data: Omit<CreateReflectionInput, "missionId">): Promise<Task>;
75
+ /**
76
+ * Update an existing reflection by ID
77
+ */
78
+ update(id: string, data: UpdateReflectionInput): Promise<Task>;
79
+ /**
80
+ * Batch update all reflections belonging to a mission
81
+ */
82
+ batchUpdateByMission(missionId: string, data: Omit<BatchUpdateReflectionsInput, "missionId">): Promise<{
83
+ success: boolean;
84
+ }>;
85
+ /**
86
+ * Delete a reflection by ID
87
+ */
88
+ delete(id: string): Promise<void>;
89
+ /**
90
+ * Get all comments for a reflection
91
+ */
92
+ getComments(reflectionId: string): Promise<ReflectionComment[]>;
93
+ /**
94
+ * Add a comment to a reflection
95
+ */
96
+ addComment(reflectionId: string, data: AddReflectionCommentInput): Promise<ReflectionComment>;
97
+ /**
98
+ * Update a reflection comment by comment ID
99
+ */
100
+ updateComment(commentId: string, content: string): Promise<ReflectionComment>;
101
+ /**
102
+ * Delete a single reflection comment by comment ID
103
+ */
104
+ deleteComment(commentId: string): Promise<void>;
105
+ /**
106
+ * Batch delete all comments for a reflection
107
+ */
108
+ deleteAllComments(reflectionId: string): Promise<{
109
+ success: boolean;
110
+ }>;
111
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReflectionsApi = void 0;
4
+ class ReflectionsApi {
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ /**
9
+ * Get all reflections of a student, grouped by mission
10
+ */
11
+ list(params) {
12
+ const query = new URLSearchParams();
13
+ if (params.studentId)
14
+ query.append("studentId", params.studentId);
15
+ if (params.roadmapId)
16
+ query.append("roadmapId", params.roadmapId);
17
+ if (params.status)
18
+ params.status.forEach((s) => query.append("status", s));
19
+ if (params.description)
20
+ query.append("description", params.description);
21
+ if (params.limit !== undefined)
22
+ query.append("limit", params.limit.toString());
23
+ if (params.start !== undefined)
24
+ query.append("start", params.start.toString());
25
+ if (params.perMissionLimit !== undefined)
26
+ query.append("perMissionLimit", params.perMissionLimit.toString());
27
+ return this.client.fetch(`/roadmap/reflections?${query.toString()}`);
28
+ }
29
+ /**
30
+ * Get all reflections by mission ID
31
+ */
32
+ getByMission(missionId) {
33
+ return this.client.fetch(`/roadmap/missions/${missionId}/reflections`);
34
+ }
35
+ /**
36
+ * Create a new reflection for a mission
37
+ */
38
+ create(missionId, data) {
39
+ return this.client.fetch(`/roadmap/missions/${missionId}/reflections`, {
40
+ method: "POST",
41
+ body: JSON.stringify(data),
42
+ });
43
+ }
44
+ /**
45
+ * Update an existing reflection by ID
46
+ */
47
+ update(id, data) {
48
+ return this.client.fetch(`/roadmap/reflections/${id}`, {
49
+ method: "PUT",
50
+ body: JSON.stringify(data),
51
+ });
52
+ }
53
+ /**
54
+ * Batch update all reflections belonging to a mission
55
+ */
56
+ batchUpdateByMission(missionId, data) {
57
+ return this.client.fetch(`/roadmap/missions/${missionId}/reflections/batch`, {
58
+ method: "PUT",
59
+ body: JSON.stringify(data),
60
+ });
61
+ }
62
+ /**
63
+ * Delete a reflection by ID
64
+ */
65
+ delete(id) {
66
+ return this.client.fetch(`/roadmap/reflections/${id}`, {
67
+ method: "DELETE",
68
+ });
69
+ }
70
+ /**
71
+ * Get all comments for a reflection
72
+ */
73
+ getComments(reflectionId) {
74
+ return this.client.fetch(`/roadmap/reflections/${reflectionId}/comments`);
75
+ }
76
+ /**
77
+ * Add a comment to a reflection
78
+ */
79
+ addComment(reflectionId, data) {
80
+ return this.client.fetch(`/roadmap/reflections/${reflectionId}/comments`, {
81
+ method: "POST",
82
+ body: JSON.stringify(data),
83
+ });
84
+ }
85
+ /**
86
+ * Update a reflection comment by comment ID
87
+ */
88
+ updateComment(commentId, content) {
89
+ return this.client.fetch(`/roadmap/reflections/comments/${commentId}`, {
90
+ method: "PUT",
91
+ body: JSON.stringify({ content }),
92
+ });
93
+ }
94
+ /**
95
+ * Delete a single reflection comment by comment ID
96
+ */
97
+ deleteComment(commentId) {
98
+ return this.client.fetch(`/roadmap/reflections/comments/${commentId}`, {
99
+ method: "DELETE",
100
+ });
101
+ }
102
+ /**
103
+ * Batch delete all comments for a reflection
104
+ */
105
+ deleteAllComments(reflectionId) {
106
+ return this.client.fetch(`/roadmap/reflections/${reflectionId}/comments`, {
107
+ method: "DELETE",
108
+ });
109
+ }
110
+ }
111
+ exports.ReflectionsApi = ReflectionsApi;
@@ -23,6 +23,8 @@ export interface TaskFilters extends PaginationParams {
23
23
  creatorId?: string;
24
24
  dueDateStart?: string;
25
25
  dueDateEnd?: string;
26
+ visibleSc?: boolean;
27
+ visibleRoadmap?: boolean;
26
28
  orderBy?: TaskOrderBy;
27
29
  order?: "asc" | "desc";
28
30
  }
@@ -43,7 +45,10 @@ export declare class TasksApi {
43
45
  /**
44
46
  * List tasks with pagination (requires roadmapId, always returns paginated result)
45
47
  */
46
- listPaginated(roadmapId: string, userId: string, filters?: Omit<TaskFilters, "start" | "limit">, pagination?: PaginationParams): Promise<PaginatedResult<Task>>;
48
+ listPaginated(roadmapId: string, userId: string, filters: Omit<TaskFilters, "start" | "limit"> & {
49
+ visibleSc?: boolean;
50
+ visibleRoadmap?: boolean;
51
+ }, pagination?: PaginationParams): Promise<PaginatedResult<Task>>;
47
52
  /**
48
53
  * Get all creators of tasks in a roadmap
49
54
  */
@@ -85,6 +85,12 @@ class TasksApi {
85
85
  if (filters === null || filters === void 0 ? void 0 : filters.dueDateEnd) {
86
86
  searchParams.append("dueDateEnd", filters.dueDateEnd);
87
87
  }
88
+ if ((filters === null || filters === void 0 ? void 0 : filters.visibleSc) !== undefined) {
89
+ searchParams.append("visibleSc", String(filters.visibleSc));
90
+ }
91
+ if ((filters === null || filters === void 0 ? void 0 : filters.visibleRoadmap) !== undefined) {
92
+ searchParams.append("visibleRoadmap", String(filters.visibleRoadmap));
93
+ }
88
94
  if (filters === null || filters === void 0 ? void 0 : filters.orderBy) {
89
95
  searchParams.append("orderBy", filters.orderBy);
90
96
  }
@@ -9,7 +9,7 @@ export declare const missionKeys: {
9
9
  details: () => readonly ["missions", "detail"];
10
10
  detail: (id: string) => readonly ["missions", "detail", string];
11
11
  };
12
- export declare function useMissions(userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Mission[], Error>;
12
+ export declare function useMissions(userId: string | null, enabled: boolean, filters?: MissionFilters): import("@tanstack/react-query").UseQueryResult<Mission[], Error>;
13
13
  /**
14
14
  * Infinite query hook for paginated mission loading
15
15
  */
@@ -20,7 +20,10 @@ export declare function useMissionsInfinite(userId: string | null, filters?: Omi
20
20
  export interface OptimisticOptions {
21
21
  optimistic?: boolean;
22
22
  }
23
- export declare function useCreateMission(options?: OptimisticOptions): import("@tanstack/react-query").UseMutationResult<Mission, Error, Partial<Mission>, {
23
+ export declare function useCreateMission(options?: OptimisticOptions): import("@tanstack/react-query").UseMutationResult<Mission, Error, Partial<Mission> & {
24
+ visibleSc?: boolean;
25
+ visibleRoadmap?: boolean;
26
+ }, {
24
27
  previousMissions: Mission[] | undefined;
25
28
  } | undefined>;
26
29
  export declare function useUpdateMission(options?: OptimisticOptions): import("@tanstack/react-query").UseMutationResult<Mission, Error, {
@@ -27,7 +27,7 @@ exports.missionKeys = {
27
27
  details: () => [...exports.missionKeys.all, "detail"],
28
28
  detail: (id) => [...exports.missionKeys.details(), id],
29
29
  };
30
- function useMissions(userId, enabled) {
30
+ function useMissions(userId, enabled, filters) {
31
31
  const client = (0, provider_1.useCrimsonClient)();
32
32
  return (0, react_query_1.useQuery)({
33
33
  queryKey: exports.missionKeys.list(userId || ""),
@@ -36,7 +36,7 @@ function useMissions(userId, enabled) {
36
36
  throw new Error("Missing userId");
37
37
  }
38
38
  // Without pagination params, list() returns MissionsCategory[]
39
- const categories = (yield client.missions.list(userId));
39
+ const categories = (yield client.missions.list(userId, filters));
40
40
  return categories.flatMap((category) => category.missions || []);
41
41
  }),
42
42
  enabled: !!userId && enabled,
@@ -0,0 +1,101 @@
1
+ import { type UseQueryOptions, type UseMutationOptions } from "@tanstack/react-query";
2
+ import type { Task } from "../../core/types";
3
+ import type { CreateReflectionInput, UpdateReflectionInput, BatchUpdateReflectionsInput, ListReflectionsResponse, ReflectionComment, AddReflectionCommentInput } from "../../core/reflections";
4
+ export declare const REFLECTION_KEYS: {
5
+ all: readonly ["reflections"];
6
+ list: (params: {
7
+ studentId: string;
8
+ roadmapId?: string;
9
+ status?: string[];
10
+ description?: string;
11
+ limit?: number;
12
+ start?: number;
13
+ perMissionLimit?: number;
14
+ }) => readonly ["reflections", "list", {
15
+ studentId: string;
16
+ roadmapId?: string;
17
+ status?: string[];
18
+ description?: string;
19
+ limit?: number;
20
+ start?: number;
21
+ perMissionLimit?: number;
22
+ }];
23
+ byMission: (missionId: string) => readonly ["reflections", "mission", string];
24
+ comments: (reflectionId: string) => readonly ["reflections", "comments", string];
25
+ };
26
+ export declare function useAllReflections(params: {
27
+ studentId: string;
28
+ roadmapId?: string;
29
+ status?: string[];
30
+ description?: string;
31
+ limit?: number;
32
+ start?: number;
33
+ perMissionLimit?: number;
34
+ }, options?: Omit<UseQueryOptions<ListReflectionsResponse, Error, ListReflectionsResponse, readonly any[]>, "queryKey" | "queryFn">): import("@tanstack/react-query").UseQueryResult<ListReflectionsResponse, Error>;
35
+ export declare function useReflections(missionId: string, options?: Omit<UseQueryOptions<Task[], Error, Task[], readonly string[]>, "queryKey" | "queryFn">): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
36
+ export declare function useCreateReflection(options?: Omit<UseMutationOptions<Task, Error, CreateReflectionInput>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<Task, Error, CreateReflectionInput, unknown>;
37
+ export declare function useUpdateReflection(options?: Omit<UseMutationOptions<Task, Error, {
38
+ id: string;
39
+ missionId: string;
40
+ } & UpdateReflectionInput>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<Task, Error, {
41
+ id: string;
42
+ missionId: string;
43
+ } & UpdateReflectionInput, any>;
44
+ export declare function useBatchUpdateReflections(options?: Omit<UseMutationOptions<{
45
+ success: boolean;
46
+ }, Error, BatchUpdateReflectionsInput>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<{
47
+ success: boolean;
48
+ }, Error, BatchUpdateReflectionsInput, unknown>;
49
+ export declare function useDeleteReflection(options?: Omit<UseMutationOptions<void, Error, {
50
+ id: string;
51
+ missionId: string;
52
+ }>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<void, Error, {
53
+ id: string;
54
+ missionId: string;
55
+ }, unknown>;
56
+ /**
57
+ * Fetch all comments for a reflection (action item)
58
+ */
59
+ export declare function useReflectionComments(reflectionId: string, options?: Omit<UseQueryOptions<ReflectionComment[], Error, ReflectionComment[], readonly string[]>, "queryKey" | "queryFn">): import("@tanstack/react-query").UseQueryResult<ReflectionComment[], Error>;
60
+ /**
61
+ * Add a comment to a reflection
62
+ */
63
+ export declare function useAddReflectionComment(options?: Omit<UseMutationOptions<ReflectionComment, Error, {
64
+ reflectionId: string;
65
+ } & AddReflectionCommentInput>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<ReflectionComment, Error, {
66
+ reflectionId: string;
67
+ } & AddReflectionCommentInput, any>;
68
+ /**
69
+ * Update a reflection comment
70
+ */
71
+ export declare function useUpdateReflectionComment(options?: Omit<UseMutationOptions<ReflectionComment, Error, {
72
+ commentId: string;
73
+ reflectionId: string;
74
+ content: string;
75
+ }>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<ReflectionComment, Error, {
76
+ commentId: string;
77
+ reflectionId: string;
78
+ content: string;
79
+ }, unknown>;
80
+ /**
81
+ * Delete a single reflection comment
82
+ */
83
+ export declare function useDeleteReflectionComment(options?: Omit<UseMutationOptions<void, Error, {
84
+ commentId: string;
85
+ reflectionId: string;
86
+ }>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<void, Error, {
87
+ commentId: string;
88
+ reflectionId: string;
89
+ }, unknown>;
90
+ /**
91
+ * Batch delete all comments for a reflection
92
+ */
93
+ export declare function useDeleteAllReflectionComments(options?: Omit<UseMutationOptions<{
94
+ success: boolean;
95
+ }, Error, {
96
+ reflectionId: string;
97
+ }>, "mutationFn">): import("@tanstack/react-query").UseMutationResult<{
98
+ success: boolean;
99
+ }, Error, {
100
+ reflectionId: string;
101
+ }, unknown>;
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.REFLECTION_KEYS = void 0;
24
+ exports.useAllReflections = useAllReflections;
25
+ exports.useReflections = useReflections;
26
+ exports.useCreateReflection = useCreateReflection;
27
+ exports.useUpdateReflection = useUpdateReflection;
28
+ exports.useBatchUpdateReflections = useBatchUpdateReflections;
29
+ exports.useDeleteReflection = useDeleteReflection;
30
+ exports.useReflectionComments = useReflectionComments;
31
+ exports.useAddReflectionComment = useAddReflectionComment;
32
+ exports.useUpdateReflectionComment = useUpdateReflectionComment;
33
+ exports.useDeleteReflectionComment = useDeleteReflectionComment;
34
+ exports.useDeleteAllReflectionComments = useDeleteAllReflectionComments;
35
+ const react_query_1 = require("@tanstack/react-query");
36
+ const provider_1 = require("../provider");
37
+ exports.REFLECTION_KEYS = {
38
+ all: ["reflections"],
39
+ list: (params) => [...exports.REFLECTION_KEYS.all, "list", params],
40
+ byMission: (missionId) => [...exports.REFLECTION_KEYS.all, "mission", missionId],
41
+ comments: (reflectionId) => [...exports.REFLECTION_KEYS.all, "comments", reflectionId],
42
+ };
43
+ function useAllReflections(params, options) {
44
+ const client = (0, provider_1.useCrimsonClient)();
45
+ return (0, react_query_1.useQuery)(Object.assign({ queryKey: exports.REFLECTION_KEYS.list(params), queryFn: () => client.reflections.list(params), enabled: !!client && !!params.studentId && (options === null || options === void 0 ? void 0 : options.enabled) !== false }, options));
46
+ }
47
+ function useReflections(missionId, options) {
48
+ const client = (0, provider_1.useCrimsonClient)();
49
+ return (0, react_query_1.useQuery)(Object.assign({ queryKey: exports.REFLECTION_KEYS.byMission(missionId), queryFn: () => client.reflections.getByMission(missionId), enabled: !!client && !!missionId && (options === null || options === void 0 ? void 0 : options.enabled) !== false }, options));
50
+ }
51
+ function useCreateReflection(options) {
52
+ const client = (0, provider_1.useCrimsonClient)();
53
+ const queryClient = (0, react_query_1.useQueryClient)();
54
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: (data) => {
55
+ const { missionId } = data, rest = __rest(data, ["missionId"]);
56
+ return client.reflections.create(missionId, rest);
57
+ }, onSuccess: (data, variables, context) => {
58
+ queryClient.invalidateQueries({
59
+ queryKey: [...exports.REFLECTION_KEYS.all, "list"],
60
+ });
61
+ queryClient.invalidateQueries({
62
+ queryKey: exports.REFLECTION_KEYS.byMission(variables.missionId),
63
+ });
64
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
65
+ options.onSuccess(data, variables, context, context);
66
+ }
67
+ } }, options));
68
+ }
69
+ function useUpdateReflection(options) {
70
+ const client = (0, provider_1.useCrimsonClient)();
71
+ const queryClient = (0, react_query_1.useQueryClient)();
72
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: (_a) => {
73
+ var { id } = _a, data = __rest(_a, ["id"]);
74
+ return client.reflections.update(id, data);
75
+ }, onMutate: (variables) => __awaiter(this, void 0, void 0, function* () {
76
+ // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
77
+ yield queryClient.cancelQueries({ queryKey: exports.REFLECTION_KEYS.all });
78
+ const previousReflectionsMap = new Map();
79
+ // Snapshot the previous value
80
+ queryClient
81
+ .getQueriesData({ queryKey: exports.REFLECTION_KEYS.all })
82
+ .forEach(([key, old]) => {
83
+ previousReflectionsMap.set(JSON.stringify(key), old);
84
+ if (!old)
85
+ return;
86
+ // Handle ListReflectionsResponse (list)
87
+ if (typeof old === "object" && "data" in old && Array.isArray(old.data)) {
88
+ queryClient.setQueryData(key, (oldData) => {
89
+ if (!oldData)
90
+ return oldData;
91
+ return Object.assign(Object.assign({}, oldData), { data: oldData.data.map((group) => (Object.assign(Object.assign({}, group), { reflections: group.reflections.map((reflection) => reflection.id === variables.id
92
+ ? Object.assign(Object.assign({}, reflection), variables) : reflection) }))) });
93
+ });
94
+ }
95
+ // Handle Task[] (byMission)
96
+ else if (Array.isArray(old)) {
97
+ queryClient.setQueryData(key, (oldData) => {
98
+ return oldData === null || oldData === void 0 ? void 0 : oldData.map((reflection) => reflection.id === variables.id
99
+ ? Object.assign(Object.assign({}, reflection), variables) : reflection);
100
+ });
101
+ }
102
+ });
103
+ return { previousReflectionsMap };
104
+ }), onError: (err, variables, context) => {
105
+ if (context === null || context === void 0 ? void 0 : context.previousReflectionsMap) {
106
+ context.previousReflectionsMap.forEach((old, keyStr) => {
107
+ queryClient.setQueryData(JSON.parse(keyStr), old);
108
+ });
109
+ }
110
+ }, onSettled: (_data, _error, variables) => {
111
+ queryClient.invalidateQueries({
112
+ queryKey: [...exports.REFLECTION_KEYS.all, "list"],
113
+ });
114
+ if (variables.missionId) {
115
+ queryClient.invalidateQueries({
116
+ queryKey: exports.REFLECTION_KEYS.byMission(variables.missionId),
117
+ });
118
+ }
119
+ } }, options));
120
+ }
121
+ function useBatchUpdateReflections(options) {
122
+ const client = (0, provider_1.useCrimsonClient)();
123
+ const queryClient = (0, react_query_1.useQueryClient)();
124
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: (data) => {
125
+ const { missionId } = data, rest = __rest(data, ["missionId"]);
126
+ return client.reflections.batchUpdateByMission(missionId, rest);
127
+ }, onSuccess: (data, variables, context) => {
128
+ queryClient.invalidateQueries({
129
+ queryKey: [...exports.REFLECTION_KEYS.all, "list"],
130
+ });
131
+ queryClient.invalidateQueries({
132
+ queryKey: exports.REFLECTION_KEYS.byMission(variables.missionId),
133
+ });
134
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
135
+ options.onSuccess(data, variables, context, context);
136
+ }
137
+ } }, options));
138
+ }
139
+ function useDeleteReflection(options) {
140
+ const client = (0, provider_1.useCrimsonClient)();
141
+ const queryClient = (0, react_query_1.useQueryClient)();
142
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: ({ id }) => client.reflections.delete(id), onSuccess: (data, variables, context) => {
143
+ queryClient.invalidateQueries({
144
+ queryKey: [...exports.REFLECTION_KEYS.all, "list"],
145
+ });
146
+ queryClient.invalidateQueries({
147
+ queryKey: exports.REFLECTION_KEYS.byMission(variables.missionId),
148
+ });
149
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
150
+ options.onSuccess(data, variables, context, context);
151
+ }
152
+ } }, options));
153
+ }
154
+ /**
155
+ * Fetch all comments for a reflection (action item)
156
+ */
157
+ function useReflectionComments(reflectionId, options) {
158
+ const client = (0, provider_1.useCrimsonClient)();
159
+ return (0, react_query_1.useQuery)(Object.assign({ queryKey: exports.REFLECTION_KEYS.comments(reflectionId), queryFn: () => client.reflections.getComments(reflectionId), enabled: !!client && !!reflectionId && (options === null || options === void 0 ? void 0 : options.enabled) !== false }, options));
160
+ }
161
+ /**
162
+ * Add a comment to a reflection
163
+ */
164
+ function useAddReflectionComment(options) {
165
+ const client = (0, provider_1.useCrimsonClient)();
166
+ const queryClient = (0, react_query_1.useQueryClient)();
167
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: (_a) => {
168
+ var { reflectionId } = _a, data = __rest(_a, ["reflectionId"]);
169
+ return client.reflections.addComment(reflectionId, data);
170
+ }, onMutate: (variables) => __awaiter(this, void 0, void 0, function* () {
171
+ // Cancel outgoing refetches
172
+ yield queryClient.cancelQueries({ queryKey: exports.REFLECTION_KEYS.all });
173
+ const previousReflectionsMap = new Map();
174
+ // Snapshot the previous values
175
+ queryClient
176
+ .getQueriesData({ queryKey: exports.REFLECTION_KEYS.all })
177
+ .forEach(([key, old]) => {
178
+ previousReflectionsMap.set(JSON.stringify(key), old);
179
+ if (!old)
180
+ return;
181
+ // Handle ReflectionComment[] (comments for specific reflection)
182
+ if (Array.isArray(old) && key.includes("comments") && key.includes(variables.reflectionId)) {
183
+ const optimisticComment = {
184
+ id: `temp-${Date.now()}`,
185
+ content: variables.content,
186
+ createdAt: new Date().toISOString(),
187
+ creatorName: "You", // placeholder, will be updated by server
188
+ };
189
+ queryClient.setQueryData(key, (oldData) => {
190
+ return oldData ? [...oldData, optimisticComment] : [optimisticComment];
191
+ });
192
+ }
193
+ // Handle ListReflectionsResponse (updating latest comment summary in the list)
194
+ else if (typeof old === "object" && "data" in old && Array.isArray(old.data)) {
195
+ queryClient.setQueryData(key, (oldData) => {
196
+ if (!oldData)
197
+ return oldData;
198
+ const optimisticComment = {
199
+ id: `temp-${Date.now()}`,
200
+ content: variables.content,
201
+ createdAt: new Date().toISOString(),
202
+ creatorName: "You",
203
+ };
204
+ return Object.assign(Object.assign({}, oldData), { data: oldData.data.map((group) => (Object.assign(Object.assign({}, group), { reflections: group.reflections.map((reflection) => {
205
+ var _a, _b;
206
+ if (reflection.id === variables.reflectionId) {
207
+ const currentTotal = (_b = (_a = reflection.comments) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 0;
208
+ return Object.assign(Object.assign({}, reflection), { comments: {
209
+ total: currentTotal + 1,
210
+ latest: optimisticComment,
211
+ } });
212
+ }
213
+ return reflection;
214
+ }) }))) });
215
+ });
216
+ }
217
+ });
218
+ return { previousReflectionsMap };
219
+ }), onError: (err, variables, context) => {
220
+ if (context === null || context === void 0 ? void 0 : context.previousReflectionsMap) {
221
+ context.previousReflectionsMap.forEach((old, keyStr) => {
222
+ queryClient.setQueryData(JSON.parse(keyStr), old);
223
+ });
224
+ }
225
+ }, onSettled: (_data, _error, variables) => {
226
+ queryClient.invalidateQueries({
227
+ queryKey: [...exports.REFLECTION_KEYS.all, "list"],
228
+ });
229
+ queryClient.invalidateQueries({
230
+ queryKey: exports.REFLECTION_KEYS.comments(variables.reflectionId),
231
+ });
232
+ } }, options));
233
+ }
234
+ /**
235
+ * Update a reflection comment
236
+ */
237
+ function useUpdateReflectionComment(options) {
238
+ const client = (0, provider_1.useCrimsonClient)();
239
+ const queryClient = (0, react_query_1.useQueryClient)();
240
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: ({ commentId, content }) => client.reflections.updateComment(commentId, content), onSuccess: (data, variables, context) => {
241
+ queryClient.invalidateQueries({
242
+ queryKey: exports.REFLECTION_KEYS.comments(variables.reflectionId),
243
+ });
244
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
245
+ options.onSuccess(data, variables, context, context);
246
+ }
247
+ } }, options));
248
+ }
249
+ /**
250
+ * Delete a single reflection comment
251
+ */
252
+ function useDeleteReflectionComment(options) {
253
+ const client = (0, provider_1.useCrimsonClient)();
254
+ const queryClient = (0, react_query_1.useQueryClient)();
255
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: ({ commentId }) => client.reflections.deleteComment(commentId), onSuccess: (data, variables, context) => {
256
+ queryClient.invalidateQueries({
257
+ queryKey: exports.REFLECTION_KEYS.comments(variables.reflectionId),
258
+ });
259
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
260
+ options.onSuccess(data, variables, context, context);
261
+ }
262
+ } }, options));
263
+ }
264
+ /**
265
+ * Batch delete all comments for a reflection
266
+ */
267
+ function useDeleteAllReflectionComments(options) {
268
+ const client = (0, provider_1.useCrimsonClient)();
269
+ const queryClient = (0, react_query_1.useQueryClient)();
270
+ return (0, react_query_1.useMutation)(Object.assign({ mutationFn: ({ reflectionId }) => client.reflections.deleteAllComments(reflectionId), onSuccess: (data, variables, context) => {
271
+ queryClient.invalidateQueries({
272
+ queryKey: exports.REFLECTION_KEYS.comments(variables.reflectionId),
273
+ });
274
+ if (options === null || options === void 0 ? void 0 : options.onSuccess) {
275
+ options.onSuccess(data, variables, context, context);
276
+ }
277
+ } }, options));
278
+ }
@@ -27,11 +27,17 @@ export declare function useTaskActivities(actionItemIds: string[], type?: string
27
27
  isLoading: boolean;
28
28
  };
29
29
  export declare function useAllUserTasks(missionLinkIds: string[], enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
30
- export declare function useTasksByRoadmapId(roadmapId: string | null, userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
30
+ export declare function useTasksByRoadmapId(roadmapId: string | null, userId: string | null, enabled: boolean, filters: {
31
+ visibleSc?: boolean;
32
+ visibleRoadmap?: boolean;
33
+ }): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
31
34
  /**
32
35
  * Infinite query hook for paginated task loading (requires roadmapId)
33
36
  */
34
- export declare function useTasksInfinite(roadmapId: string | null, userId: string | null, filters?: Omit<TaskFilters, "start" | "limit">, options?: {
37
+ export declare function useTasksInfinite(roadmapId: string | null, userId: string | null, filters: Omit<TaskFilters, "start" | "limit"> & {
38
+ visibleSc?: boolean;
39
+ visibleRoadmap?: boolean;
40
+ }, options?: {
35
41
  enabled?: boolean;
36
42
  pageSize?: number;
37
43
  }): import("@tanstack/react-query").UseInfiniteQueryResult<InfiniteData<PaginatedResult<Task>, unknown>, Error>;
@@ -39,6 +39,94 @@ exports.taskKeys = {
39
39
  activitiesOfTask: (id) => ["task-activities", id],
40
40
  activity: (id, type = "duedate") => [...exports.taskKeys.activitiesOfTask(id), type],
41
41
  };
42
+ function isInfiniteTasksData(data) {
43
+ return (typeof data === "object" &&
44
+ data !== null &&
45
+ "pages" in data &&
46
+ Array.isArray(data.pages));
47
+ }
48
+ function dedupeTasksById(tasks) {
49
+ const seen = new Set();
50
+ return tasks.filter((task) => {
51
+ if (seen.has(task.id))
52
+ return false;
53
+ seen.add(task.id);
54
+ return true;
55
+ });
56
+ }
57
+ function taskMatchesInfiniteFilters(task, filters) {
58
+ var _a;
59
+ if (!filters)
60
+ return true;
61
+ if (((_a = filters.status) === null || _a === void 0 ? void 0 : _a.length) && !filters.status.includes(task.status)) {
62
+ return false;
63
+ }
64
+ if (filters.creatorId && filters.creatorId !== (task.creatorId || task.userId)) {
65
+ return false;
66
+ }
67
+ if (filters.description) {
68
+ const keyword = filters.description.toLowerCase();
69
+ const name = (task.name || "").toLowerCase();
70
+ const description = (task.description || "").toLowerCase();
71
+ if (!name.includes(keyword) && !description.includes(keyword)) {
72
+ return false;
73
+ }
74
+ }
75
+ return true;
76
+ }
77
+ function updateInfiniteQueriesWithCreatedTask(queryClient, createdTask) {
78
+ const infiniteQueries = queryClient.getQueriesData({
79
+ queryKey: [...exports.taskKeys.all, "infinite"],
80
+ });
81
+ infiniteQueries.forEach(([queryKey, queryData]) => {
82
+ if (!isInfiniteTasksData(queryData) || queryData.pages.length === 0) {
83
+ return;
84
+ }
85
+ if (!Array.isArray(queryKey)) {
86
+ return;
87
+ }
88
+ const scope = queryKey[1];
89
+ const queryUserId = queryKey[3];
90
+ const rawFilters = queryKey[4];
91
+ if (scope !== "infinite") {
92
+ return;
93
+ }
94
+ if (typeof queryUserId === "string" && createdTask.userId !== queryUserId) {
95
+ return;
96
+ }
97
+ if (!taskMatchesInfiniteFilters(createdTask, rawFilters)) {
98
+ return;
99
+ }
100
+ queryClient.setQueryData(queryKey, (oldData) => {
101
+ var _a, _b;
102
+ if (!oldData || oldData.pages.length === 0) {
103
+ return oldData;
104
+ }
105
+ const taskAlreadyExists = oldData.pages.some((page) => page.data.some((task) => task.id === createdTask.id));
106
+ if (taskAlreadyExists) {
107
+ return oldData;
108
+ }
109
+ const currentTotal = (_b = (_a = oldData.pages[0]) === null || _a === void 0 ? void 0 : _a.pagination) === null || _b === void 0 ? void 0 : _b.total;
110
+ const nextTotal = typeof currentTotal === "number"
111
+ ? currentTotal + 1
112
+ : oldData.pages.reduce((sum, page) => sum + page.data.length, 0) + 1;
113
+ return Object.assign(Object.assign({}, oldData), { pages: oldData.pages.map((page, pageIndex) => {
114
+ const nextData = pageIndex === 0
115
+ ? dedupeTasksById([createdTask, ...page.data])
116
+ : page.data;
117
+ return Object.assign(Object.assign({}, page), { data: nextData, pagination: page.pagination
118
+ ? Object.assign(Object.assign({}, page.pagination), { total: nextTotal, hasMore: nextTotal > page.pagination.start + page.pagination.limit }) : page.pagination });
119
+ }) });
120
+ });
121
+ });
122
+ }
123
+ function invalidateTaskQueriesExcludingInfinite(queryClient) {
124
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.lists() });
125
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.details() });
126
+ queryClient.invalidateQueries({ queryKey: [...exports.taskKeys.all, "roadmap"] });
127
+ queryClient.invalidateQueries({ queryKey: [...exports.taskKeys.all, "missionLinks"] });
128
+ queryClient.invalidateQueries({ queryKey: [...exports.taskKeys.all, "creators"] });
129
+ }
42
130
  function useTasks(missionId, enabled) {
43
131
  const client = (0, provider_1.useCrimsonClient)();
44
132
  return (0, react_query_1.useQuery)({
@@ -149,7 +237,7 @@ function useAllUserTasks(missionLinkIds, enabled) {
149
237
  enabled: enabled && missionLinkIds.length > 0,
150
238
  });
151
239
  }
152
- function useTasksByRoadmapId(roadmapId, userId, enabled) {
240
+ function useTasksByRoadmapId(roadmapId, userId, enabled, filters) {
153
241
  const client = (0, provider_1.useCrimsonClient)();
154
242
  return (0, react_query_1.useQuery)({
155
243
  queryKey: [...exports.taskKeys.all, "roadmap", roadmapId, userId],
@@ -158,7 +246,10 @@ function useTasksByRoadmapId(roadmapId, userId, enabled) {
158
246
  return [];
159
247
  }
160
248
  // Without pagination params, list() returns Task[]
161
- return (yield client.tasks.list({ roadmapId, userId }));
249
+ return (yield client.tasks.list({ roadmapId, userId }, {
250
+ visibleSc: filters.visibleSc,
251
+ visibleRoadmap: filters.visibleRoadmap,
252
+ }));
162
253
  }),
163
254
  enabled: !!roadmapId && !!userId && enabled,
164
255
  });
@@ -224,13 +315,25 @@ function useCreateTask(options) {
224
315
  }
225
316
  return { previousTasks };
226
317
  }),
318
+ onSuccess: (createdTask) => {
319
+ if (createdTask.missionId) {
320
+ queryClient.setQueryData(exports.taskKeys.list(createdTask.missionId), (old) => {
321
+ if (!old)
322
+ return [createdTask];
323
+ if (old.some((task) => task.id === createdTask.id))
324
+ return old;
325
+ return [...old.filter((task) => !task.id.startsWith("temp-")), createdTask];
326
+ });
327
+ }
328
+ updateInfiniteQueriesWithCreatedTask(queryClient, createdTask);
329
+ },
227
330
  onError: (err, newTodo, context) => {
228
331
  if (!optimistic || !(context === null || context === void 0 ? void 0 : context.previousTasks) || !newTodo.missionId)
229
332
  return;
230
333
  queryClient.setQueryData(exports.taskKeys.list(newTodo.missionId), context.previousTasks);
231
334
  },
232
335
  onSettled: () => {
233
- queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
336
+ invalidateTaskQueriesExcludingInfinite(queryClient);
234
337
  },
235
338
  });
236
339
  }
@@ -278,7 +381,12 @@ function useUpdateTask(options) {
278
381
  });
279
382
  },
280
383
  onSettled: (data, error, variables) => {
281
- queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
384
+ if (optimistic) {
385
+ invalidateTaskQueriesExcludingInfinite(queryClient);
386
+ }
387
+ else {
388
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
389
+ }
282
390
  if (variables.id) {
283
391
  queryClient.invalidateQueries({
284
392
  queryKey: exports.taskKeys.activitiesOfTask(variables.id),
@@ -330,7 +438,12 @@ function useDeleteTask(options) {
330
438
  });
331
439
  },
332
440
  onSettled: () => {
333
- queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
441
+ if (optimistic) {
442
+ invalidateTaskQueriesExcludingInfinite(queryClient);
443
+ }
444
+ else {
445
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
446
+ }
334
447
  },
335
448
  });
336
449
  }
@@ -377,7 +490,12 @@ function useBulkDeleteTasks(options) {
377
490
  });
378
491
  },
379
492
  onSettled: () => {
380
- queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
493
+ if (optimistic) {
494
+ invalidateTaskQueriesExcludingInfinite(queryClient);
495
+ }
496
+ else {
497
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
498
+ }
381
499
  },
382
500
  });
383
501
  }
@@ -424,7 +542,12 @@ function useBulkUpdateTasks(options) {
424
542
  });
425
543
  },
426
544
  onSettled: (data, error, variables) => {
427
- queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
545
+ if (optimistic) {
546
+ invalidateTaskQueriesExcludingInfinite(queryClient);
547
+ }
548
+ else {
549
+ queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
550
+ }
428
551
  // Invalidate activities for all updated tasks
429
552
  variables.ids.forEach(id => {
430
553
  queryClient.invalidateQueries({ queryKey: exports.taskKeys.activitiesOfTask(id) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimson-education/sdk",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "description": "Crimson SDK for accessing Crimson App APIs",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",