@crimson-education/sdk 0.3.0 → 0.3.1
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.
- package/dist/core/tasks.d.ts +5 -1
- package/dist/core/tasks.js +9 -0
- package/dist/core/types.d.ts +6 -1
- package/dist/react/hooks/index.d.ts +2 -1
- package/dist/react/hooks/index.js +4 -1
- package/dist/react/hooks/useTasks.d.ts +7 -1
- package/dist/react/hooks/useTasks.js +111 -1
- package/package.json +1 -1
package/dist/core/tasks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CrimsonClient } from "./client";
|
|
2
|
-
import type { Task, PaginationParams, PaginatedResult, ActionItemResource, AddResourceInput, UploadUrlResponse, DownloadUrlResponse, UpdateTaskResourceInput, User } from "./types";
|
|
2
|
+
import type { Task, PaginationParams, PaginatedResult, ActionItemResource, AddResourceInput, UploadUrlResponse, DownloadUrlResponse, UpdateTaskResourceInput, User, ActionItemActivityBrief } from "./types";
|
|
3
3
|
export type TaskOrderBy = "priority" | "dueDate" | "missionTitle" | "createdAt" | "description";
|
|
4
4
|
export interface TaskFilters extends PaginationParams {
|
|
5
5
|
status?: string[];
|
|
@@ -143,4 +143,8 @@ export declare class TasksApi {
|
|
|
143
143
|
url: string;
|
|
144
144
|
key: string;
|
|
145
145
|
}>;
|
|
146
|
+
/**
|
|
147
|
+
* Get activities (e.g. strict due date history) for tasks
|
|
148
|
+
*/
|
|
149
|
+
getActivities(actionItemIds: string[], type?: string): Promise<Record<string, ActionItemActivityBrief[]>>;
|
|
146
150
|
}
|
package/dist/core/tasks.js
CHANGED
|
@@ -322,5 +322,14 @@ class TasksApi {
|
|
|
322
322
|
return { url, key };
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Get activities (e.g. strict due date history) for tasks
|
|
327
|
+
*/
|
|
328
|
+
getActivities(actionItemIds, type = "duedate") {
|
|
329
|
+
return this.client.fetch("/roadmap/action-items/activities", {
|
|
330
|
+
method: "POST",
|
|
331
|
+
body: JSON.stringify({ actionItemIds, type }),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
325
334
|
}
|
|
326
335
|
exports.TasksApi = TasksApi;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -79,7 +79,8 @@ export interface Task {
|
|
|
79
79
|
roadmapMissionId: string;
|
|
80
80
|
userId: string;
|
|
81
81
|
creatorId?: string;
|
|
82
|
-
roadmapId
|
|
82
|
+
/** null roadmapId is used to clear roadmapId when update */
|
|
83
|
+
roadmapId?: string | null;
|
|
83
84
|
isComplete?: boolean;
|
|
84
85
|
dueDate?: string | null;
|
|
85
86
|
missionId?: string | null;
|
|
@@ -521,3 +522,7 @@ export interface StudentProfile {
|
|
|
521
522
|
profileImageId?: string;
|
|
522
523
|
[key: string]: unknown;
|
|
523
524
|
}
|
|
525
|
+
export interface ActionItemActivityBrief {
|
|
526
|
+
data: any;
|
|
527
|
+
createdAt: string;
|
|
528
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { useAuthState } from "./useAuthState";
|
|
2
|
+
export { useQueryClient } from "@tanstack/react-query";
|
|
2
3
|
export { useOAuth, useOAuthCallback } from "./useOAuth";
|
|
3
4
|
export type { UseOAuthResult } from "./useOAuth";
|
|
4
5
|
export { useMissions, useMissionsInfinite, useCreateMission, useUpdateMission, useDeleteMission, missionKeys, } from "./useMissions";
|
|
5
|
-
export { useTasks, useAllUserTasks, useTasksByRoadmapId, useTasksInfinite, useCreateTask, useUpdateTask, useDeleteTask, useBulkDeleteTasks, useBulkUpdateTasks, taskKeys, useGetDownloadUrl, useTaskCreators, } from "./useTasks";
|
|
6
|
+
export { useTasks, useAllUserTasks, useTasksByRoadmapId, useTasksInfinite, useCreateTask, useUpdateTask, useDeleteTask, useBulkDeleteTasks, useBulkUpdateTasks, taskKeys, useGetDownloadUrl, useTaskCreators, useTaskActivities, } from "./useTasks";
|
|
6
7
|
export { useUsers, useUser, userKeys } from "./useUsers";
|
|
7
8
|
export { useCurrentUserRoles, useMyStudents, accountKeys } from "./useAccount";
|
|
8
9
|
export { useRoadmapContext } from "./useRoadmapContext";
|
|
@@ -14,9 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.indigoKeys = exports.useTutorStudents = exports.useStudentTutors = exports.useIndigoMe = exports.studentProfileKeys = exports.useStudentPrefilledInfo = exports.useStudentProfile = exports.useDeleteTemplateMission = exports.useCreateTemplateMission = exports.missionLibraryKeys = exports.useCreateFromPredefinedTasks = exports.useAssignBulkTask = exports.useAssignBulkMission = exports.useCopyTemplateMission = exports.useUpdateTemplateMission = exports.useTemplateMissionDetail = exports.useTemplateTasksInfinite = exports.useTemplateTasks = exports.useTemplateMissionsInfinite = exports.useTemplateMissions = exports.useRoadmapContext = exports.accountKeys = exports.useMyStudents = exports.useCurrentUserRoles = exports.userKeys = exports.useUser = exports.useUsers = exports.useTaskCreators = exports.useGetDownloadUrl = exports.taskKeys = exports.useBulkUpdateTasks = exports.useBulkDeleteTasks = 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;
|
|
17
|
+
exports.indigoKeys = exports.useTutorStudents = exports.useStudentTutors = exports.useIndigoMe = exports.studentProfileKeys = exports.useStudentPrefilledInfo = exports.useStudentProfile = exports.useDeleteTemplateMission = exports.useCreateTemplateMission = exports.missionLibraryKeys = exports.useCreateFromPredefinedTasks = exports.useAssignBulkTask = exports.useAssignBulkMission = exports.useCopyTemplateMission = exports.useUpdateTemplateMission = exports.useTemplateMissionDetail = exports.useTemplateTasksInfinite = exports.useTemplateTasks = exports.useTemplateMissionsInfinite = exports.useTemplateMissions = exports.useRoadmapContext = exports.accountKeys = exports.useMyStudents = exports.useCurrentUserRoles = exports.userKeys = exports.useUser = exports.useUsers = exports.useTaskActivities = exports.useTaskCreators = exports.useGetDownloadUrl = exports.taskKeys = exports.useBulkUpdateTasks = exports.useBulkDeleteTasks = 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.useQueryClient = exports.useAuthState = void 0;
|
|
18
18
|
var useAuthState_1 = require("./useAuthState");
|
|
19
19
|
Object.defineProperty(exports, "useAuthState", { enumerable: true, get: function () { return useAuthState_1.useAuthState; } });
|
|
20
|
+
var react_query_1 = require("@tanstack/react-query");
|
|
21
|
+
Object.defineProperty(exports, "useQueryClient", { enumerable: true, get: function () { return react_query_1.useQueryClient; } });
|
|
20
22
|
var useOAuth_1 = require("./useOAuth");
|
|
21
23
|
Object.defineProperty(exports, "useOAuth", { enumerable: true, get: function () { return useOAuth_1.useOAuth; } });
|
|
22
24
|
Object.defineProperty(exports, "useOAuthCallback", { enumerable: true, get: function () { return useOAuth_1.useOAuthCallback; } });
|
|
@@ -40,6 +42,7 @@ Object.defineProperty(exports, "useBulkUpdateTasks", { enumerable: true, get: fu
|
|
|
40
42
|
Object.defineProperty(exports, "taskKeys", { enumerable: true, get: function () { return useTasks_1.taskKeys; } });
|
|
41
43
|
Object.defineProperty(exports, "useGetDownloadUrl", { enumerable: true, get: function () { return useTasks_1.useGetDownloadUrl; } });
|
|
42
44
|
Object.defineProperty(exports, "useTaskCreators", { enumerable: true, get: function () { return useTasks_1.useTaskCreators; } });
|
|
45
|
+
Object.defineProperty(exports, "useTaskActivities", { enumerable: true, get: function () { return useTasks_1.useTaskActivities; } });
|
|
43
46
|
var useUsers_1 = require("./useUsers");
|
|
44
47
|
Object.defineProperty(exports, "useUsers", { enumerable: true, get: function () { return useUsers_1.useUsers; } });
|
|
45
48
|
Object.defineProperty(exports, "useUser", { enumerable: true, get: function () { return useUsers_1.useUser; } });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InfiniteData } from "@tanstack/react-query";
|
|
2
|
-
import type { Task, PaginatedResult, User } from "../../core/types";
|
|
2
|
+
import type { Task, PaginatedResult, User, ActionItemActivityBrief } from "../../core/types";
|
|
3
3
|
import type { TaskFilters } from "../../core/tasks";
|
|
4
4
|
export declare const taskKeys: {
|
|
5
5
|
all: readonly ["tasks"];
|
|
@@ -9,9 +9,15 @@ export declare const taskKeys: {
|
|
|
9
9
|
details: () => readonly ["tasks", "detail"];
|
|
10
10
|
detail: (id: string) => readonly ["tasks", "detail", string];
|
|
11
11
|
creators: (roadmapId: string) => readonly ["tasks", "creators", string];
|
|
12
|
+
activitiesOfTask: (id: string) => readonly ["task-activities", string];
|
|
13
|
+
activity: (id: string, type?: string) => readonly ["task-activities", string, string];
|
|
12
14
|
};
|
|
13
15
|
export declare function useTasks(missionId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
|
|
14
16
|
export declare function useTaskCreators(roadmapId: string | null, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<User[], Error>;
|
|
17
|
+
export declare function useTaskActivities(actionItemIds: string[], type?: string, enabled?: boolean): {
|
|
18
|
+
data: Record<string, ActionItemActivityBrief[]>;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
};
|
|
15
21
|
export declare function useAllUserTasks(missionLinkIds: string[], enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
|
|
16
22
|
export declare function useTasksByRoadmapId(roadmapId: string | null, userId: string | null, enabled: boolean): import("@tanstack/react-query").UseQueryResult<Task[], Error>;
|
|
17
23
|
/**
|
|
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.taskKeys = void 0;
|
|
14
14
|
exports.useTasks = useTasks;
|
|
15
15
|
exports.useTaskCreators = useTaskCreators;
|
|
16
|
+
exports.useTaskActivities = useTaskActivities;
|
|
16
17
|
exports.useAllUserTasks = useAllUserTasks;
|
|
17
18
|
exports.useTasksByRoadmapId = useTasksByRoadmapId;
|
|
18
19
|
exports.useTasksInfinite = useTasksInfinite;
|
|
@@ -23,6 +24,7 @@ exports.useBulkDeleteTasks = useBulkDeleteTasks;
|
|
|
23
24
|
exports.useBulkUpdateTasks = useBulkUpdateTasks;
|
|
24
25
|
exports.useGetDownloadUrl = useGetDownloadUrl;
|
|
25
26
|
const react_query_1 = require("@tanstack/react-query");
|
|
27
|
+
const react_1 = require("react");
|
|
26
28
|
const provider_1 = require("../provider");
|
|
27
29
|
// Query keys
|
|
28
30
|
exports.taskKeys = {
|
|
@@ -33,6 +35,8 @@ exports.taskKeys = {
|
|
|
33
35
|
details: () => [...exports.taskKeys.all, "detail"],
|
|
34
36
|
detail: (id) => [...exports.taskKeys.details(), id],
|
|
35
37
|
creators: (roadmapId) => [...exports.taskKeys.all, "creators", roadmapId],
|
|
38
|
+
activitiesOfTask: (id) => ["task-activities", id],
|
|
39
|
+
activity: (id, type = "duedate") => [...exports.taskKeys.activitiesOfTask(id), type],
|
|
36
40
|
};
|
|
37
41
|
function useTasks(missionId, enabled) {
|
|
38
42
|
const client = (0, provider_1.useCrimsonClient)();
|
|
@@ -61,6 +65,107 @@ function useTaskCreators(roadmapId, enabled = true) {
|
|
|
61
65
|
enabled: !!roadmapId && enabled,
|
|
62
66
|
});
|
|
63
67
|
}
|
|
68
|
+
function useTaskActivities(actionItemIds, type = "duedate", enabled = true) {
|
|
69
|
+
const client = (0, provider_1.useCrimsonClient)();
|
|
70
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
71
|
+
// Track which IDs we know are in the cache (or have been fetched)
|
|
72
|
+
const [cachedIds, setCachedIds] = (0, react_1.useState)(() => {
|
|
73
|
+
const s = new Set();
|
|
74
|
+
// We only check for existence, not staleness. Stale data will be refetched by useQueries if needed,
|
|
75
|
+
// but we want to prevent the *initial* fetch of empty data which causes the waterfall/storm.
|
|
76
|
+
// However, checking queryCache synchronously here is good for hydration.
|
|
77
|
+
return s;
|
|
78
|
+
});
|
|
79
|
+
// Sync effect to update cachedIds from props?
|
|
80
|
+
// actually, we should just check the cache on render or effect.
|
|
81
|
+
// But doing it in effect allows the batch fetch to be the primary mechanism.
|
|
82
|
+
(0, react_1.useEffect)(() => {
|
|
83
|
+
if (!enabled)
|
|
84
|
+
return;
|
|
85
|
+
// Identify which IDs are missing from our "known cached" set
|
|
86
|
+
// AND missing from the actual query cache (double check to be safe)
|
|
87
|
+
const missing = actionItemIds.filter((id) => {
|
|
88
|
+
if (cachedIds.has(id))
|
|
89
|
+
return false;
|
|
90
|
+
const data = queryClient.getQueryData(exports.taskKeys.activity(id, type));
|
|
91
|
+
if (data) {
|
|
92
|
+
// If it's already there (maybe from another component), we can mark it as cached
|
|
93
|
+
// We'll update state later to avoid set-during-render warning if we did it synchronously above constantly
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
if (missing.length === 0) {
|
|
99
|
+
// If we found some inconsistencies (e.g. they were in queryClient but not in our local state), align them
|
|
100
|
+
if (actionItemIds.length !== cachedIds.size) {
|
|
101
|
+
// This logic is tricky. Let's simplify:
|
|
102
|
+
// Just fetch what we really need.
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const fetchActivities = () => __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
try {
|
|
108
|
+
const result = yield client.tasks.getActivities(missing, type);
|
|
109
|
+
// Populate Cache
|
|
110
|
+
Object.entries(result).forEach(([id, activities]) => {
|
|
111
|
+
queryClient.setQueryData(exports.taskKeys.activity(id, type), activities);
|
|
112
|
+
});
|
|
113
|
+
// Also handling IDs that returned no result (empty array) to prevent infinite re-fetching?
|
|
114
|
+
// The API returns Record<string, ...>. If an ID is missing, we should probably set it to empty.
|
|
115
|
+
missing.forEach(id => {
|
|
116
|
+
if (!result[id]) {
|
|
117
|
+
queryClient.setQueryData(exports.taskKeys.activity(id, type), []);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Update local state to enable the queries
|
|
121
|
+
setCachedIds((prev) => {
|
|
122
|
+
const next = new Set(prev);
|
|
123
|
+
missing.forEach((id) => next.add(id));
|
|
124
|
+
return next;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
console.error("Failed to batch fetch activities", e);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
fetchActivities();
|
|
132
|
+
}, [actionItemIds, type, enabled, client, queryClient, cachedIds]); // cachedIds dep is important
|
|
133
|
+
// Also, we need to make sure we "discover" items that are already cached
|
|
134
|
+
// without fetching them. Use an effect to sync "initially present" items?
|
|
135
|
+
(0, react_1.useEffect)(() => {
|
|
136
|
+
if (!enabled)
|
|
137
|
+
return;
|
|
138
|
+
const present = actionItemIds.filter(id => !cachedIds.has(id) && queryClient.getQueryData(exports.taskKeys.activity(id, type)));
|
|
139
|
+
if (present.length > 0) {
|
|
140
|
+
setCachedIds(prev => {
|
|
141
|
+
const next = new Set(prev);
|
|
142
|
+
present.forEach(id => next.add(id));
|
|
143
|
+
return next;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}, [actionItemIds, type, enabled, queryClient, cachedIds]);
|
|
147
|
+
const queries = (0, react_query_1.useQueries)({
|
|
148
|
+
queries: actionItemIds.map((id) => ({
|
|
149
|
+
queryKey: exports.taskKeys.activity(id, type),
|
|
150
|
+
queryFn: () => __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
const result = yield client.tasks.getActivities([id], type);
|
|
152
|
+
return result[id] || [];
|
|
153
|
+
}),
|
|
154
|
+
// Only enable if we have "acknowledged" that the data is ready/cached.
|
|
155
|
+
// OR if it was already in the cache (optimization).
|
|
156
|
+
enabled: enabled && !!id && cachedIds.has(id),
|
|
157
|
+
staleTime: 5 * 60 * 1000,
|
|
158
|
+
})),
|
|
159
|
+
});
|
|
160
|
+
const isLoading = queries.some((q) => q.isLoading);
|
|
161
|
+
const data = {};
|
|
162
|
+
queries.forEach((q, index) => {
|
|
163
|
+
if (q.data) {
|
|
164
|
+
data[actionItemIds[index]] = q.data;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return { data, isLoading };
|
|
168
|
+
}
|
|
64
169
|
function useAllUserTasks(missionLinkIds, enabled) {
|
|
65
170
|
const client = (0, provider_1.useCrimsonClient)();
|
|
66
171
|
return (0, react_query_1.useQuery)({
|
|
@@ -197,8 +302,13 @@ function useUpdateTask(options) {
|
|
|
197
302
|
queryClient.setQueryData(JSON.parse(keyStr), old);
|
|
198
303
|
});
|
|
199
304
|
},
|
|
200
|
-
onSettled: () => {
|
|
305
|
+
onSettled: (data, error, variables) => {
|
|
201
306
|
queryClient.invalidateQueries({ queryKey: exports.taskKeys.all });
|
|
307
|
+
if (variables.id) {
|
|
308
|
+
queryClient.invalidateQueries({
|
|
309
|
+
queryKey: exports.taskKeys.activitiesOfTask(variables.id),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
202
312
|
},
|
|
203
313
|
});
|
|
204
314
|
}
|