@burdenoff/microfe-buildmyiq 2026.525.1 → 2026.705.2
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/buildmyiq/BuildmyiqRoot.js +123 -0
- package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -0
- package/dist/buildmyiq/BuildmyiqRoutes.js +84 -0
- package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -0
- package/dist/buildmyiq/components/BuiDiscussion.js +109 -0
- package/dist/buildmyiq/components/BuiDiscussion.js.map +1 -0
- package/dist/buildmyiq/components/BuiTags.js +149 -0
- package/dist/buildmyiq/components/BuiTags.js.map +1 -0
- package/dist/buildmyiq/components/DataTable.js +71 -0
- package/dist/buildmyiq/components/DataTable.js.map +1 -0
- package/dist/buildmyiq/components/EmptyState.js +35 -0
- package/dist/buildmyiq/components/EmptyState.js.map +1 -0
- package/dist/buildmyiq/components/EvidenceUpload.js +93 -0
- package/dist/buildmyiq/components/EvidenceUpload.js.map +1 -0
- package/dist/buildmyiq/components/FileLink.js +36 -0
- package/dist/buildmyiq/components/FileLink.js.map +1 -0
- package/dist/buildmyiq/components/FilterTabs.js +27 -0
- package/dist/buildmyiq/components/FilterTabs.js.map +1 -0
- package/dist/buildmyiq/components/MetricsCard.js +64 -0
- package/dist/buildmyiq/components/MetricsCard.js.map +1 -0
- package/dist/buildmyiq/components/MiniBar.js +22 -0
- package/dist/buildmyiq/components/MiniBar.js.map +1 -0
- package/dist/buildmyiq/components/PageLayout.js +38 -0
- package/dist/buildmyiq/components/PageLayout.js.map +1 -0
- package/dist/buildmyiq/components/Pagination.js +50 -0
- package/dist/buildmyiq/components/Pagination.js.map +1 -0
- package/dist/buildmyiq/components/StateViews.js +49 -0
- package/dist/buildmyiq/components/StateViews.js.map +1 -0
- package/dist/buildmyiq/components/StatusBadge.js +60 -0
- package/dist/buildmyiq/components/StatusBadge.js.map +1 -0
- package/dist/buildmyiq/components/Tabs.js +31 -0
- package/dist/buildmyiq/components/Tabs.js.map +1 -0
- package/dist/buildmyiq/components/index.js +14 -0
- package/dist/buildmyiq/context/BuildmyiqContext.js +85 -0
- package/dist/buildmyiq/context/BuildmyiqContext.js.map +1 -0
- package/dist/buildmyiq/context/index.js +1 -0
- package/dist/buildmyiq/hooks/index.js +5 -0
- package/dist/buildmyiq/hooks/useBuiComments.js +45 -0
- package/dist/buildmyiq/hooks/useBuiComments.js.map +1 -0
- package/dist/buildmyiq/hooks/useBuiFiles.js +81 -0
- package/dist/buildmyiq/hooks/useBuiFiles.js.map +1 -0
- package/dist/buildmyiq/hooks/useBuiTags.js +89 -0
- package/dist/buildmyiq/hooks/useBuiTags.js.map +1 -0
- package/dist/buildmyiq/hooks/useBuildmyiqApi.js +412 -0
- package/dist/buildmyiq/hooks/useBuildmyiqApi.js.map +1 -0
- package/dist/buildmyiq/hooks/useCursorPager.js +25 -0
- package/dist/buildmyiq/hooks/useCursorPager.js.map +1 -0
- package/dist/buildmyiq/index.js +6 -3
- package/dist/buildmyiq/pages/DashboardPage.js +134 -247
- package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
- package/dist/buildmyiq/pages/_shared/CourseCard.js +74 -0
- package/dist/buildmyiq/pages/_shared/CourseCard.js.map +1 -0
- package/dist/buildmyiq/pages/_shared/index.js +1 -0
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +499 -0
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -0
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +390 -0
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -0
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js +252 -0
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -0
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +680 -0
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -0
- package/dist/buildmyiq/pages/authoring/_forms.js +54 -0
- package/dist/buildmyiq/pages/authoring/_forms.js.map +1 -0
- package/dist/buildmyiq/pages/catalog/CatalogPage.js +97 -0
- package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -0
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +354 -0
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -0
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js +363 -0
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -0
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js +339 -0
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -0
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +303 -0
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -0
- package/dist/buildmyiq/pages/learning/MyLearningPage.js +187 -0
- package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -0
- package/dist/buildmyiq/pages/skills/SkillsPage.js +230 -0
- package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -0
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +433 -0
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -0
- package/dist/buildmyiq/types/index.js +44 -0
- package/dist/buildmyiq/types/index.js.map +1 -0
- package/dist/buildmyiq/utils/cn.js +10 -0
- package/dist/buildmyiq/utils/cn.js.map +1 -0
- package/dist/buildmyiq/utils/formatters.js +62 -0
- package/dist/buildmyiq/utils/formatters.js.map +1 -0
- package/dist/buildmyiq/utils/navigation.js +2 -1
- package/dist/buildmyiq/utils/navigation.js.map +1 -1
- package/dist/index.js +6 -4
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +3 -3
- package/dist/buildmyiq/BuildMyIQRoot.js +0 -329
- package/dist/buildmyiq/BuildMyIQRoot.js.map +0 -1
- package/dist/buildmyiq/BuildMyIQRoutes.js +0 -171
- package/dist/buildmyiq/BuildMyIQRoutes.js.map +0 -1
- package/dist/buildmyiq/pages/AdminAdminAnalyticsPagePage.js +0 -271
- package/dist/buildmyiq/pages/AdminAdminAnalyticsPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AdminAdminCoursesPagePage.js +0 -212
- package/dist/buildmyiq/pages/AdminAdminCoursesPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AdminAdminUsersPagePage.js +0 -235
- package/dist/buildmyiq/pages/AdminAdminUsersPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AdminIndexPage.js +0 -274
- package/dist/buildmyiq/pages/AdminIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/AiAichatPagePage.js +0 -209
- package/dist/buildmyiq/pages/AiAichatPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AiAicontentGenerationPagePage.js +0 -318
- package/dist/buildmyiq/pages/AiAicontentGenerationPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AiAitutoringPagePage.js +0 -290
- package/dist/buildmyiq/pages/AiAitutoringPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AiIndexPage.js +0 -191
- package/dist/buildmyiq/pages/AiIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/AssessmentsAssessmentBuilderPagePage.js +0 -290
- package/dist/buildmyiq/pages/AssessmentsAssessmentBuilderPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AssessmentsAssessmentGradingPagePage.js +0 -215
- package/dist/buildmyiq/pages/AssessmentsAssessmentGradingPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AssessmentsAssessmentResultsPagePage.js +0 -224
- package/dist/buildmyiq/pages/AssessmentsAssessmentResultsPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AssessmentsAssessmentTakePagePage.js +0 -174
- package/dist/buildmyiq/pages/AssessmentsAssessmentTakePagePage.js.map +0 -1
- package/dist/buildmyiq/pages/AssessmentsIndexPage.js +0 -174
- package/dist/buildmyiq/pages/AssessmentsIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/CalendarPage.js +0 -242
- package/dist/buildmyiq/pages/CalendarPage.js.map +0 -1
- package/dist/buildmyiq/pages/CommunicationAnnouncementsPagePage.js +0 -180
- package/dist/buildmyiq/pages/CommunicationAnnouncementsPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CommunicationForumsPagePage.js +0 -154
- package/dist/buildmyiq/pages/CommunicationForumsPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CommunicationIndexPage.js +0 -191
- package/dist/buildmyiq/pages/CommunicationIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/CommunicationMessagesPagePage.js +0 -261
- package/dist/buildmyiq/pages/CommunicationMessagesPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesCourseContentPagePage.js +0 -164
- package/dist/buildmyiq/pages/CoursesCourseContentPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesCourseCreatePagePage.js +0 -239
- package/dist/buildmyiq/pages/CoursesCourseCreatePagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesCourseDashboardPagePage.js +0 -368
- package/dist/buildmyiq/pages/CoursesCourseDashboardPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesCourseDetailPagePage.js +0 -219
- package/dist/buildmyiq/pages/CoursesCourseDetailPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesCourseListPagePage.js +0 -153
- package/dist/buildmyiq/pages/CoursesCourseListPagePage.js.map +0 -1
- package/dist/buildmyiq/pages/CoursesIndexPage.js +0 -138
- package/dist/buildmyiq/pages/CoursesIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/MessagesPage.js +0 -261
- package/dist/buildmyiq/pages/MessagesPage.js.map +0 -1
- package/dist/buildmyiq/pages/ProfileProgressPage.js +0 -194
- package/dist/buildmyiq/pages/ProfileProgressPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingAnalyticsPage.js +0 -243
- package/dist/buildmyiq/pages/TeachingAnalyticsPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingContentPage.js +0 -165
- package/dist/buildmyiq/pages/TeachingContentPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingCoursesPage.js +0 -151
- package/dist/buildmyiq/pages/TeachingCoursesPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingGradingPage.js +0 -204
- package/dist/buildmyiq/pages/TeachingGradingPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingIndexPage.js +0 -220
- package/dist/buildmyiq/pages/TeachingIndexPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingLessonPlansPage.js +0 -157
- package/dist/buildmyiq/pages/TeachingLessonPlansPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingQuestionsPage.js +0 -184
- package/dist/buildmyiq/pages/TeachingQuestionsPage.js.map +0 -1
- package/dist/buildmyiq/pages/TeachingStudentsPage.js +0 -204
- package/dist/buildmyiq/pages/TeachingStudentsPage.js.map +0 -1
- package/dist/buildmyiq/providers/BuildMyIQProvider.js +0 -27
- package/dist/buildmyiq/providers/BuildMyIQProvider.js.map +0 -1
- package/dist/buildmyiq/store/buildmyiqStore.js +0 -1160
- package/dist/buildmyiq/store/buildmyiqStore.js.map +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { joinPath as e } from "../utils/navigation.js";
|
|
2
|
+
import { createContext as t, useCallback as n, useContext as r, useMemo as i } from "react";
|
|
3
|
+
import { MemoryRouter as a, useNavigate as o } from "react-router-dom";
|
|
4
|
+
import { jsx as s } from "react/jsx-runtime";
|
|
5
|
+
//#region src/buildmyiq/context/BuildmyiqContext.tsx
|
|
6
|
+
var c = t(null);
|
|
7
|
+
function l() {
|
|
8
|
+
let e = r(c);
|
|
9
|
+
if (!e) throw Error("useBuildmyiq must be used within an BuildmyiqProvider");
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
var u = ({ children: t, basePath: r, currentUser: a, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f }) => {
|
|
13
|
+
let p = o(), m = r || "/", h = n((t) => e(m, t), [m]), g = n((t) => {
|
|
14
|
+
p(e(m, t));
|
|
15
|
+
}, [m, p]), _ = i(() => ({
|
|
16
|
+
basePath: m,
|
|
17
|
+
navigate: void 0,
|
|
18
|
+
currentUser: a,
|
|
19
|
+
tenantId: l,
|
|
20
|
+
workspaceId: u,
|
|
21
|
+
apiGatewayUrl: d,
|
|
22
|
+
authToken: f,
|
|
23
|
+
isStandalone: !0,
|
|
24
|
+
navigateTo: g,
|
|
25
|
+
getPath: h
|
|
26
|
+
}), [
|
|
27
|
+
m,
|
|
28
|
+
a,
|
|
29
|
+
l,
|
|
30
|
+
u,
|
|
31
|
+
d,
|
|
32
|
+
f,
|
|
33
|
+
g,
|
|
34
|
+
h
|
|
35
|
+
]);
|
|
36
|
+
return /* @__PURE__ */ s(c.Provider, {
|
|
37
|
+
value: _,
|
|
38
|
+
children: t
|
|
39
|
+
});
|
|
40
|
+
}, d = ({ children: t, basePath: r, navigate: a, currentUser: o, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f }) => {
|
|
41
|
+
let p = r || "/", m = n((t) => e(p, t), [p]), h = n((t) => {
|
|
42
|
+
a(e(p, t));
|
|
43
|
+
}, [p, a]), g = i(() => ({
|
|
44
|
+
basePath: p,
|
|
45
|
+
navigate: a,
|
|
46
|
+
currentUser: o,
|
|
47
|
+
tenantId: l,
|
|
48
|
+
workspaceId: u,
|
|
49
|
+
apiGatewayUrl: d,
|
|
50
|
+
authToken: f,
|
|
51
|
+
isStandalone: !1,
|
|
52
|
+
navigateTo: h,
|
|
53
|
+
getPath: m
|
|
54
|
+
}), [
|
|
55
|
+
p,
|
|
56
|
+
a,
|
|
57
|
+
o,
|
|
58
|
+
l,
|
|
59
|
+
u,
|
|
60
|
+
d,
|
|
61
|
+
f,
|
|
62
|
+
h,
|
|
63
|
+
m
|
|
64
|
+
]);
|
|
65
|
+
return /* @__PURE__ */ s(c.Provider, {
|
|
66
|
+
value: g,
|
|
67
|
+
children: t
|
|
68
|
+
});
|
|
69
|
+
}, f = (e) => {
|
|
70
|
+
let { children: t, navigate: n } = e;
|
|
71
|
+
return n ? /* @__PURE__ */ s(d, {
|
|
72
|
+
...e,
|
|
73
|
+
navigate: n,
|
|
74
|
+
isStandalone: !1,
|
|
75
|
+
children: t
|
|
76
|
+
}) : /* @__PURE__ */ s(a, { children: /* @__PURE__ */ s(u, {
|
|
77
|
+
...e,
|
|
78
|
+
isStandalone: !0,
|
|
79
|
+
children: t
|
|
80
|
+
}) });
|
|
81
|
+
};
|
|
82
|
+
//#endregion
|
|
83
|
+
export { f as BuildmyiqProvider, l as useBuildmyiq };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=BuildmyiqContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BuildmyiqContext.js","names":[],"sources":["../../../src/buildmyiq/context/BuildmyiqContext.tsx"],"sourcesContent":["/**\n * Buildmyiq Context Provider\n * @module buildmyiq/context\n *\n * Splits standalone mode (wraps its own MemoryRouter + useNavigate) from\n * integrated mode (the host shell supplies `navigate`). Exposes gateway/auth\n * context + relative navigation helpers to every page.\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router-dom';\nimport type { BuildmyiqRootProps } from '../types';\nimport { joinPath } from '../utils/navigation';\n\ninterface BuildmyiqContextValue extends Omit<\n BuildmyiqRootProps,\n 'registerNavItems' | 'registerFooterItems' | 'navigate'\n> {\n isStandalone: boolean;\n /** Shell's navigate function (only available in integrated mode). */\n navigate?: (path: string) => void;\n /** Navigate to a path relative to basePath. */\n navigateTo: (relativePath: string) => void;\n /** Get the full path for a relative path. */\n getPath: (relativePath: string) => string;\n}\n\nconst BuildmyiqContext = createContext<BuildmyiqContextValue | null>(null);\n\nexport function useBuildmyiq(): BuildmyiqContextValue {\n const context = useContext(BuildmyiqContext);\n if (!context) {\n throw new Error('useBuildmyiq must be used within an BuildmyiqProvider');\n }\n return context;\n}\n\ntype ProviderProps = Omit<BuildmyiqRootProps, 'registerNavItems' | 'registerFooterItems'> & {\n isStandalone: boolean;\n};\n\nconst StandaloneProviderInner: FC<PropsWithChildren<ProviderProps>> = ({\n children,\n basePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const routerNavigate = useRouterNavigate();\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n routerNavigate(joinPath(effectiveBasePath, relativePath));\n },\n [effectiveBasePath, routerNavigate]\n );\n\n const contextValue = useMemo<BuildmyiqContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate: undefined,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: true,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n ]\n );\n\n return <BuildmyiqContext.Provider value={contextValue}>{children}</BuildmyiqContext.Provider>;\n};\n\nconst IntegratedProviderInner: FC<\n PropsWithChildren<ProviderProps & { navigate: (path: string) => void }>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n navigate(joinPath(effectiveBasePath, relativePath));\n },\n [effectiveBasePath, navigate]\n );\n\n const contextValue = useMemo<BuildmyiqContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: false,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n ]\n );\n\n return <BuildmyiqContext.Provider value={contextValue}>{children}</BuildmyiqContext.Provider>;\n};\n\nexport const BuildmyiqProvider: FC<PropsWithChildren<BuildmyiqRootProps>> = (props) => {\n const { children, navigate } = props;\n const isStandalone = !navigate;\n\n if (isStandalone) {\n return (\n <MemoryRouter>\n <StandaloneProviderInner {...props} isStandalone={true}>\n {children}\n </StandaloneProviderInner>\n </MemoryRouter>\n );\n }\n\n return (\n <IntegratedProviderInner {...props} navigate={navigate} isStandalone={false}>\n {children}\n </IntegratedProviderInner>\n );\n};\n"],"mappings":";;;;;AA4BA,IAAM,IAAmB,EAA4C,KAAK;AAE1E,SAAgB,IAAsC;CACpD,IAAM,IAAU,EAAW,EAAiB;AAC5C,KAAI,CAAC,EACH,OAAU,MAAM,wDAAwD;AAE1E,QAAO;;AAOT,IAAM,KAAiE,EACrE,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAiB,GAAmB,EACpC,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AACxB,IAAe,EAAS,GAAmB,EAAa,CAAC;IAE3D,CAAC,GAAmB,EAAe,CACpC,EAEK,IAAe,SACZ;EACL,UAAU;EACV,UAAU,KAAA;EACV;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAiB,UAAlB;EAA2B,OAAO;EAAe;EAAqC,CAAA;GAGzF,KAED,EACH,aACA,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AACxB,IAAS,EAAS,GAAmB,EAAa,CAAC;IAErD,CAAC,GAAmB,EAAS,CAC9B,EAEK,IAAe,SACZ;EACL,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAiB,UAAlB;EAA2B,OAAO;EAAe;EAAqC,CAAA;GAGlF,KAAgE,MAAU;CACrF,IAAM,EAAE,aAAU,gBAAa;AAa/B,QAZsB,IAapB,kBAAC,GAAD;EAAyB,GAAI;EAAiB;EAAU,cAAc;EACnE;EACuB,CAAA,GAXxB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAyB,GAAI;EAAO,cAAc;EAC/C;EACuB,CAAA,EACb,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./BuildmyiqContext.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
|
|
2
|
+
import "../context/index.js";
|
|
3
|
+
import { wspaceFetch as t } from "./useBuiFiles.js";
|
|
4
|
+
import { useMutation as n, useQuery as r, useQueryClient as i } from "@tanstack/react-query";
|
|
5
|
+
//#region src/buildmyiq/hooks/useBuiComments.ts
|
|
6
|
+
var a = (e, t) => [
|
|
7
|
+
"buildmyiq",
|
|
8
|
+
"comments",
|
|
9
|
+
e,
|
|
10
|
+
t
|
|
11
|
+
], o = (n, i) => {
|
|
12
|
+
let { apiGatewayUrl: o } = e();
|
|
13
|
+
return r({
|
|
14
|
+
queryKey: a(n, i ?? ""),
|
|
15
|
+
enabled: !!i,
|
|
16
|
+
queryFn: async () => {
|
|
17
|
+
let e = await t("query BuiGetEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n total\n items {\n id\n authorId\n authorType\n content { text markdown }\n createdAt\n edited\n parentCommentId\n replyCount\n }\n }\n }", { input: {
|
|
18
|
+
entityType: n,
|
|
19
|
+
entityId: i,
|
|
20
|
+
take: 100,
|
|
21
|
+
parentCommentId: null
|
|
22
|
+
} }, o), r = e.getEntityComments.items.filter((e) => !e.parentCommentId);
|
|
23
|
+
return r.sort((e, t) => new Date(e.createdAt).getTime() - new Date(t.createdAt).getTime()), {
|
|
24
|
+
items: r,
|
|
25
|
+
total: e.getEntityComments.total
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}, s = (r, o) => {
|
|
30
|
+
let { apiGatewayUrl: s } = e(), c = i();
|
|
31
|
+
return n({
|
|
32
|
+
mutationFn: async (e) => (await t("mutation BuiCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { id }\n }", { input: {
|
|
33
|
+
entityType: r,
|
|
34
|
+
entityId: o,
|
|
35
|
+
content: { text: e }
|
|
36
|
+
} }, s)).createEntityComment,
|
|
37
|
+
onSuccess: () => {
|
|
38
|
+
c.invalidateQueries({ queryKey: a(r, o) });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { o as useBuiComments, s as useEcoCreateComment };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=useBuiComments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuiComments.js","names":[],"sources":["../../../src/buildmyiq/hooks/useBuiComments.ts"],"sourcesContent":["/**\n * Buildmyiq — wspace-conversations-svc EntityComment REUSE hooks (Integration B).\n * @module buildmyiq/hooks/useBuiComments\n *\n * Adds a discussion surface to Buildmyiq entities WITHOUT any buildmyiq-svc change by\n * reusing the generic EntityComment API composed into the WORKSPACE supergraph.\n * Comments attach to any entity by `(entityType, entityId)`:\n * - `getEntityComments(input): EntityCommentConnection!`\n * - `createEntityComment(input): EntityComment!`\n *\n * Transport reuses `wspaceFetch` from `useBuiFiles` (workspace gateway +\n * workspace token). Lives in a separate file; `useBuildmyiqApi.ts` is\n * untouched.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { useBuildmyiq } from '../context';\nimport { wspaceFetch } from './useBuiFiles';\n\nexport interface BuiComment {\n id: string;\n authorId: string;\n authorType: string;\n content: {\n text: string | null;\n markdown: string | null;\n };\n createdAt: string;\n edited: boolean;\n parentCommentId: string | null;\n replyCount: number;\n}\n\nconst commentsKey = (entityType: string, entityId: string) =>\n ['buildmyiq', 'comments', entityType, entityId] as const;\n\n/** Top-level (root) comments for an Buildmyiq entity, oldest first. */\nexport const useBuiComments = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBuildmyiq();\n return useQuery({\n queryKey: commentsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{\n getEntityComments: { items: BuiComment[]; total: number };\n }>(\n `query BuiGetEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n total\n items {\n id\n authorId\n authorType\n content { text markdown }\n createdAt\n edited\n parentCommentId\n replyCount\n }\n }\n }`,\n {\n input: {\n entityType,\n entityId,\n take: 100,\n parentCommentId: null,\n },\n },\n apiGatewayUrl\n );\n // Render root comments oldest-first (server order is not guaranteed).\n const roots = data.getEntityComments.items.filter((c) => !c.parentCommentId);\n roots.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());\n return { items: roots, total: data.getEntityComments.total };\n },\n });\n};\n\nexport const useEcoCreateComment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBuildmyiq();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (text: string) => {\n const data = await wspaceFetch<{ createEntityComment: { id: string } }>(\n `mutation BuiCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { id }\n }`,\n {\n input: {\n entityType,\n entityId,\n content: { text },\n },\n },\n apiGatewayUrl\n );\n return data.createEntityComment;\n },\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: commentsKey(entityType, entityId) });\n },\n });\n};\n"],"mappings":";;;;;AAkCA,IAAM,KAAe,GAAoB,MACvC;CAAC;CAAa;CAAY;CAAY;CAAS,EAGpC,KAAkB,GAAoB,MAAiC;CAClF,IAAM,EAAE,qBAAkB,GAAc;AACxC,QAAO,EAAS;EACd,UAAU,EAAY,GAAY,KAAY,GAAG;EACjD,SAAS,EAAQ;EACjB,SAAS,YAAY;GACnB,IAAM,IAAO,MAAM,EAGjB,6YAeA,EACE,OAAO;IACL;IACA;IACA,MAAM;IACN,iBAAiB;IAClB,EACF,EACD,EACD,EAEK,IAAQ,EAAK,kBAAkB,MAAM,QAAQ,MAAM,CAAC,EAAE,gBAAgB;AAE5E,UADA,EAAM,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,EAChF;IAAE,OAAO;IAAO,OAAO,EAAK,kBAAkB;IAAO;;EAE/D,CAAC;GAGS,KAAuB,GAAoB,MAAqB;CAC3E,IAAM,EAAE,qBAAkB,GAAc,EAClC,IAAK,GAAgB;AAC3B,QAAO,EAAY;EACjB,YAAY,OAAO,OACJ,MAAM,EACjB,wIAGA,EACE,OAAO;GACL;GACA;GACA,SAAS,EAAE,SAAM;GAClB,EACF,EACD,EACD,EACW;EAEd,iBAAiB;AACf,KAAG,kBAAkB,EAAE,UAAU,EAAY,GAAY,EAAS,EAAE,CAAC;;EAExE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
|
|
2
|
+
import "../context/index.js";
|
|
3
|
+
import { useCallback as t, useState as n } from "react";
|
|
4
|
+
import { useQuery as r } from "@tanstack/react-query";
|
|
5
|
+
import { graphqlFetch as i } from "@burdenoff/fe-libs/shared/graphql";
|
|
6
|
+
//#region src/buildmyiq/hooks/useBuiFiles.ts
|
|
7
|
+
var a = () => {
|
|
8
|
+
let e = globalThis;
|
|
9
|
+
e.__graphqlFetchResponseCache?.clear(), e.__graphqlFetchPendingResponses?.clear(), e.__boffGraphQLResponseCache?.clear();
|
|
10
|
+
}, o = async (e, t, n) => {
|
|
11
|
+
let r = await i({
|
|
12
|
+
gateway: "workspace",
|
|
13
|
+
baseUrl: n,
|
|
14
|
+
query: e,
|
|
15
|
+
variables: t,
|
|
16
|
+
workspaceToken: !0
|
|
17
|
+
});
|
|
18
|
+
if (r.errors?.length) throw Error(r.errors[0]?.message ?? "GraphQL request failed");
|
|
19
|
+
if (!r.data) throw Error("GraphQL response did not include data");
|
|
20
|
+
return /^\s*mutation\b/.test(e) && a(), r.data;
|
|
21
|
+
};
|
|
22
|
+
function s(e, t, n, r) {
|
|
23
|
+
return new Promise((i, a) => {
|
|
24
|
+
let o = new XMLHttpRequest();
|
|
25
|
+
o.open("PUT", e), o.setRequestHeader("Content-Type", n), o.upload.onprogress = (e) => {
|
|
26
|
+
e.lengthComputable && r(Math.round(e.loaded / e.total * 100));
|
|
27
|
+
}, o.onload = () => {
|
|
28
|
+
o.status >= 200 && o.status < 300 ? i() : a(/* @__PURE__ */ Error(`File storage rejected the upload (HTTP ${o.status}).`));
|
|
29
|
+
}, o.onerror = () => a(/* @__PURE__ */ Error("Network error while uploading the file.")), o.onabort = () => a(/* @__PURE__ */ Error("The upload was cancelled.")), o.send(t);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
var c = () => {
|
|
33
|
+
let { apiGatewayUrl: r } = e(), [i, a] = n(!1), [c, l] = n(0), [u, d] = n(null), f = t(() => {
|
|
34
|
+
a(!1), l(0), d(null);
|
|
35
|
+
}, []);
|
|
36
|
+
return {
|
|
37
|
+
upload: t(async (e) => {
|
|
38
|
+
a(!0), l(0), d(null);
|
|
39
|
+
try {
|
|
40
|
+
let t = e.name.lastIndexOf("."), n = t > -1 ? e.name.slice(t + 1) : "", i = e.type || "application/octet-stream", a = await o("mutation BuiInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }", { input: {
|
|
41
|
+
fileName: e.name,
|
|
42
|
+
extension: n,
|
|
43
|
+
mimeType: i,
|
|
44
|
+
fileSize: String(e.size),
|
|
45
|
+
visibility: "WORKSPACE"
|
|
46
|
+
} }, r), { uploadUrl: c } = a.initiateUpload, u = a.initiateUpload.session.id;
|
|
47
|
+
if (!c) throw Error("The file service did not return an upload URL.");
|
|
48
|
+
await s(c, e, i, l);
|
|
49
|
+
let d = await o("mutation BuiCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file { id name }\n }\n }", { input: { sessionId: u } }, r);
|
|
50
|
+
return l(100), {
|
|
51
|
+
fileId: d.completeUpload.fileId,
|
|
52
|
+
fileName: d.completeUpload.file?.name ?? e.name
|
|
53
|
+
};
|
|
54
|
+
} catch (e) {
|
|
55
|
+
let t = e instanceof Error ? e.message : "The file could not be uploaded.";
|
|
56
|
+
throw d(t), e instanceof Error ? e : Error(t);
|
|
57
|
+
} finally {
|
|
58
|
+
a(!1);
|
|
59
|
+
}
|
|
60
|
+
}, [r]),
|
|
61
|
+
isUploading: i,
|
|
62
|
+
progress: c,
|
|
63
|
+
error: u,
|
|
64
|
+
reset: f
|
|
65
|
+
};
|
|
66
|
+
}, l = (t) => {
|
|
67
|
+
let { apiGatewayUrl: n } = e();
|
|
68
|
+
return r({
|
|
69
|
+
queryKey: [
|
|
70
|
+
"buildmyiq",
|
|
71
|
+
"file",
|
|
72
|
+
t ?? ""
|
|
73
|
+
],
|
|
74
|
+
enabled: !!t,
|
|
75
|
+
queryFn: async () => (await o("query BuiGetFile($id: ID!) {\n getFile(id: $id) {\n id\n name\n downloadUrl\n mimeType\n size\n }\n }", { id: t }, n)).getFile
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
export { c as useBuiFileUpload, l as useEcoFile, o as wspaceFetch };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=useBuiFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuiFiles.js","names":[],"sources":["../../../src/buildmyiq/hooks/useBuiFiles.ts"],"sourcesContent":["/**\n * Buildmyiq — wspace-files-svc REUSE hooks (Integration A).\n * @module buildmyiq/hooks/useBuiFiles\n *\n * Reuses the platform file store (`wspace-files-svc`) WITHOUT any buildmyiq-svc\n * change: the buildmyiq schema already exposes `BuiLesson.mediaFileId` /\n * `BuiCertificate.fileId`; this module lets the UI actually produce a `fileId` via\n * the canonical 3-step upload flow composed into the WORKSPACE supergraph:\n * 1. `initiateUpload(input)` → `{ uploadUrl, session { id } }`\n * 2. HTTP `PUT` the bytes to the returned signed `uploadUrl`\n * 3. `completeUpload(input: { sessionId })` → `{ fileId }`\n * plus `getFile(id)` to resolve an existing evidence/report file's name + URL.\n *\n * Transport mirrors `useBuildmyiqApi` exactly — `graphqlFetch({ gateway:\n * 'workspace', workspaceToken: true })` — so no ApolloProvider is required.\n * These hooks live in a SEPARATE file by design; `useBuildmyiqApi.ts` is\n * left untouched.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useBuildmyiq } from '../context';\n\n// ============================================================================\n// Shared workspace-gateway transport (reused by the comments + tags hooks too)\n// ============================================================================\n\ntype GraphqlReadCacheGlobals = {\n __graphqlFetchResponseCache?: { clear: () => void };\n __graphqlFetchPendingResponses?: { clear: () => void };\n __boffGraphQLResponseCache?: { clear: () => void };\n};\n\n/**\n * fe-libs `graphqlFetch` coalesces `query` responses in a short-lived in-memory\n * cache. After a write we drop those caches so the invalidation refetch that\n * follows reads fresh data (same remedy `useBuildmyiqApi` applies).\n */\nconst clearGraphqlReadCaches = (): void => {\n const g = globalThis as GraphqlReadCacheGlobals;\n g.__graphqlFetchResponseCache?.clear();\n g.__graphqlFetchPendingResponses?.clear();\n g.__boffGraphQLResponseCache?.clear();\n};\n\n/** Execute a workspace-gateway GraphQL operation with the workspace token. */\nexport const wspaceFetch = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlReadCaches();\n }\n return response.data;\n};\n\n// ============================================================================\n// File upload (initiate → PUT → complete)\n// ============================================================================\n\nexport interface UploadedBuiFile {\n fileId: string;\n fileName: string;\n}\n\n/** `PUT` the blob to the signed URL, reporting byte-progress via `onProgress`. */\nfunction putWithProgress(\n url: string,\n file: File,\n contentType: string,\n onProgress: (percent: number) => void\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', contentType);\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`File storage rejected the upload (HTTP ${xhr.status}).`));\n }\n };\n xhr.onerror = () => reject(new Error('Network error while uploading the file.'));\n xhr.onabort = () => reject(new Error('The upload was cancelled.'));\n xhr.send(file);\n });\n}\n\nexport interface UseBuiFileUploadResult {\n upload: (file: File) => Promise<UploadedBuiFile>;\n isUploading: boolean;\n /** 0–100 byte-progress of the signed-URL PUT. */\n progress: number;\n error: string | null;\n reset: () => void;\n}\n\n/**\n * Imperative upload hook: `await upload(file)` runs the platform 3-step flow and\n * resolves to `{ fileId, fileName }`. `fileId` is what the buildmyiq mutations accept\n * (`buiCreateCourse.coverFileId`, `buiIssueCertificate.fileId`).\n */\nexport const useBuiFileUpload = (): UseBuiFileUploadResult => {\n const { apiGatewayUrl } = useBuildmyiq();\n const [isUploading, setIsUploading] = useState(false);\n const [progress, setProgress] = useState(0);\n const [error, setError] = useState<string | null>(null);\n\n const reset = useCallback(() => {\n setIsUploading(false);\n setProgress(0);\n setError(null);\n }, []);\n\n const upload = useCallback(\n async (file: File): Promise<UploadedBuiFile> => {\n setIsUploading(true);\n setProgress(0);\n setError(null);\n try {\n const dotIndex = file.name.lastIndexOf('.');\n const extension = dotIndex > -1 ? file.name.slice(dotIndex + 1) : '';\n const mimeType = file.type || 'application/octet-stream';\n\n // 1. initiate\n const initiated = await wspaceFetch<{\n initiateUpload: { uploadUrl: string | null; session: { id: string } };\n }>(\n `mutation BuiInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n extension,\n mimeType,\n fileSize: String(file.size),\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n\n const { uploadUrl } = initiated.initiateUpload;\n const sessionId = initiated.initiateUpload.session.id;\n if (!uploadUrl) {\n throw new Error('The file service did not return an upload URL.');\n }\n\n // 2. PUT the bytes\n await putWithProgress(uploadUrl, file, mimeType, setProgress);\n\n // 3. complete\n const completed = await wspaceFetch<{\n completeUpload: { fileId: string; file: { id: string; name: string } | null };\n }>(\n `mutation BuiCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file { id name }\n }\n }`,\n { input: { sessionId } },\n apiGatewayUrl\n );\n\n setProgress(100);\n return {\n fileId: completed.completeUpload.fileId,\n fileName: completed.completeUpload.file?.name ?? file.name,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : 'The file could not be uploaded.';\n setError(message);\n throw err instanceof Error ? err : new Error(message);\n } finally {\n setIsUploading(false);\n }\n },\n [apiGatewayUrl]\n );\n\n return { upload, isUploading, progress, error, reset };\n};\n\n// ============================================================================\n// Resolve an existing file (name + download URL) for rendering evidence links\n// ============================================================================\n\nexport interface BuiFileMeta {\n id: string;\n name: string;\n downloadUrl: string | null;\n mimeType: string;\n size: string;\n}\n\nexport const useEcoFile = (id: string | null | undefined) => {\n const { apiGatewayUrl } = useBuildmyiq();\n return useQuery({\n queryKey: ['buildmyiq', 'file', id ?? ''],\n enabled: Boolean(id),\n queryFn: async () => {\n const data = await wspaceFetch<{ getFile: BuiFileMeta | null }>(\n `query BuiGetFile($id: ID!) {\n getFile(id: $id) {\n id\n name\n downloadUrl\n mimeType\n size\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.getFile;\n },\n });\n};\n"],"mappings":";;;;;;AAwCA,IAAM,UAAqC;CACzC,IAAM,IAAI;AAGV,CAFA,EAAE,6BAA6B,OAAO,EACtC,EAAE,gCAAgC,OAAO,EACzC,EAAE,4BAA4B,OAAO;GAI1B,IAAc,OACzB,GACA,GACA,MACmB;CACnB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA;EACA,gBAAgB;EACjB,CAAC;AAEF,KAAI,EAAS,QAAQ,OACnB,OAAU,MAAM,EAAS,OAAO,IAAI,WAAW,yBAAyB;AAE1E,KAAI,CAAC,EAAS,KACZ,OAAU,MAAM,wCAAwC;AAK1D,QAHI,iBAAiB,KAAK,EAAM,IAC9B,GAAwB,EAEnB,EAAS;;AAalB,SAAS,EACP,GACA,GACA,GACA,GACe;AACf,QAAO,IAAI,SAAe,GAAS,MAAW;EAC5C,IAAM,IAAM,IAAI,gBAAgB;AAiBhC,EAhBA,EAAI,KAAK,OAAO,EAAI,EACpB,EAAI,iBAAiB,gBAAgB,EAAY,EACjD,EAAI,OAAO,cAAc,MAAU;AACjC,GAAI,EAAM,oBACR,EAAW,KAAK,MAAO,EAAM,SAAS,EAAM,QAAS,IAAI,CAAC;KAG9D,EAAI,eAAe;AACjB,GAAI,EAAI,UAAU,OAAO,EAAI,SAAS,MACpC,GAAS,GAET,EAAO,gBAAI,MAAM,0CAA0C,EAAI,OAAO,IAAI,CAAC;KAG/E,EAAI,gBAAgB,EAAO,gBAAI,MAAM,0CAA0C,CAAC,EAChF,EAAI,gBAAgB,EAAO,gBAAI,MAAM,4BAA4B,CAAC,EAClE,EAAI,KAAK,EAAK;GACd;;AAiBJ,IAAa,UAAiD;CAC5D,IAAM,EAAE,qBAAkB,GAAc,EAClC,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAU,KAAe,EAAS,EAAE,EACrC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAQ,QAAkB;AAG9B,EAFA,EAAe,GAAM,EACrB,EAAY,EAAE,EACd,EAAS,KAAK;IACb,EAAE,CAAC;AAyEN,QAAO;EAAE,QAvEM,EACb,OAAO,MAAyC;AAG9C,GAFA,EAAe,GAAK,EACpB,EAAY,EAAE,EACd,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAW,EAAK,KAAK,YAAY,IAAI,EACrC,IAAY,IAAW,KAAK,EAAK,KAAK,MAAM,IAAW,EAAE,GAAG,IAC5D,IAAW,EAAK,QAAQ,4BAGxB,IAAY,MAAM,EAGtB,8LAMA,EACE,OAAO;KACL,UAAU,EAAK;KACf;KACA;KACA,UAAU,OAAO,EAAK,KAAK;KAC3B,YAAY;KACb,EACF,EACD,EACD,EAEK,EAAE,iBAAc,EAAU,gBAC1B,IAAY,EAAU,eAAe,QAAQ;AACnD,QAAI,CAAC,EACH,OAAU,MAAM,iDAAiD;AAInE,UAAM,EAAgB,GAAW,GAAM,GAAU,EAAY;IAG7D,IAAM,IAAY,MAAM,EAGtB,6LAMA,EAAE,OAAO,EAAE,cAAW,EAAE,EACxB,EACD;AAGD,WADA,EAAY,IAAI,EACT;KACL,QAAQ,EAAU,eAAe;KACjC,UAAU,EAAU,eAAe,MAAM,QAAQ,EAAK;KACvD;YACM,GAAK;IACZ,IAAM,IAAU,aAAe,QAAQ,EAAI,UAAU;AAErD,UADA,EAAS,EAAQ,EACX,aAAe,QAAQ,IAAU,MAAM,EAAQ;aAC7C;AACR,MAAe,GAAM;;KAGzB,CAAC,EAAc,CAChB;EAEgB;EAAa;EAAU;EAAO;EAAO;GAe3C,KAAc,MAAkC;CAC3D,IAAM,EAAE,qBAAkB,GAAc;AACxC,QAAO,EAAS;EACd,UAAU;GAAC;GAAa;GAAQ,KAAM;GAAG;EACzC,SAAS,EAAQ;EACjB,SAAS,aACM,MAAM,EACjB,yLASA,EAAE,OAAI,EACN,EACD,EACW;EAEf,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
|
|
2
|
+
import "../context/index.js";
|
|
3
|
+
import { wspaceFetch as t } from "./useBuiFiles.js";
|
|
4
|
+
import { useMutation as n, useQuery as r, useQueryClient as i } from "@tanstack/react-query";
|
|
5
|
+
//#region src/buildmyiq/hooks/useBuiTags.ts
|
|
6
|
+
var a = "buildmyiq", o = (e, t) => [
|
|
7
|
+
"buildmyiq",
|
|
8
|
+
"entity-tags",
|
|
9
|
+
e,
|
|
10
|
+
t
|
|
11
|
+
], s = "id label color key", c = (e) => e.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "tag", l = (n, i) => {
|
|
12
|
+
let { apiGatewayUrl: a } = e();
|
|
13
|
+
return r({
|
|
14
|
+
queryKey: o(n, i ?? ""),
|
|
15
|
+
enabled: !!i,
|
|
16
|
+
queryFn: async () => (await t(`query BuiGetEntityTags($entityType: String!, $entityId: ID!) {
|
|
17
|
+
getEntityTags(entityType: $entityType, entityId: $entityId) {
|
|
18
|
+
id
|
|
19
|
+
tag { ${s} }
|
|
20
|
+
}
|
|
21
|
+
}`, {
|
|
22
|
+
entityType: n,
|
|
23
|
+
entityId: i
|
|
24
|
+
}, a)).getEntityTags
|
|
25
|
+
});
|
|
26
|
+
}, u = (n) => {
|
|
27
|
+
let { apiGatewayUrl: i } = e(), a = n.trim();
|
|
28
|
+
return r({
|
|
29
|
+
queryKey: [
|
|
30
|
+
"buildmyiq",
|
|
31
|
+
"tag-search",
|
|
32
|
+
a
|
|
33
|
+
],
|
|
34
|
+
enabled: a.length > 0,
|
|
35
|
+
queryFn: async () => (await t(`query BuiSearchTags($query: String!, $limit: Int) {
|
|
36
|
+
searchTags(query: $query, limit: $limit) { ${s} }
|
|
37
|
+
}`, {
|
|
38
|
+
query: a,
|
|
39
|
+
limit: 8
|
|
40
|
+
}, i)).searchTags
|
|
41
|
+
});
|
|
42
|
+
}, d = (r, a) => {
|
|
43
|
+
let { apiGatewayUrl: s } = e(), c = i();
|
|
44
|
+
return n({
|
|
45
|
+
mutationFn: async (e) => (await t("mutation BuiAssignTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }", { input: {
|
|
46
|
+
entityType: r,
|
|
47
|
+
entityId: a,
|
|
48
|
+
tagId: e
|
|
49
|
+
} }, s)).assignTag,
|
|
50
|
+
onSuccess: () => c.invalidateQueries({ queryKey: o(r, a) })
|
|
51
|
+
});
|
|
52
|
+
}, f = (r, a) => {
|
|
53
|
+
let { apiGatewayUrl: s } = e(), c = i();
|
|
54
|
+
return n({
|
|
55
|
+
mutationFn: async (e) => (await t("mutation BuiRemoveTagAssignment($id: ID!) {\n removeTagAssignment(id: $id)\n }", { id: e }, s)).removeTagAssignment,
|
|
56
|
+
onSuccess: () => c.invalidateQueries({ queryKey: o(r, a) })
|
|
57
|
+
});
|
|
58
|
+
}, p = async (e) => {
|
|
59
|
+
let n = await t("query BuiGetVocabulary($key: String!) {\n getVocabularyByKey(key: $key) { id }\n }", { key: a }, e);
|
|
60
|
+
return n.getVocabularyByKey?.id ? n.getVocabularyByKey.id : (await t("mutation BuiCreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) { id }\n }", { input: {
|
|
61
|
+
key: a,
|
|
62
|
+
name: "Buildmyiq",
|
|
63
|
+
description: "Tags for Buildmyiq sites, species and other records.",
|
|
64
|
+
allowCustomTags: !0
|
|
65
|
+
} }, e)).createVocabulary.id;
|
|
66
|
+
}, m = (r, a) => {
|
|
67
|
+
let { apiGatewayUrl: s } = e(), l = i();
|
|
68
|
+
return n({
|
|
69
|
+
mutationFn: async (e) => {
|
|
70
|
+
let n = e.trim();
|
|
71
|
+
if (!n) throw Error("A tag label is required.");
|
|
72
|
+
let i = await t("mutation BuiCreateTag($input: CreateTagInput!) {\n createTag(input: $input) { id }\n }", { input: {
|
|
73
|
+
vocabularyId: await p(s),
|
|
74
|
+
key: `${c(n)}-${Math.random().toString(36).slice(2, 6)}`,
|
|
75
|
+
label: n
|
|
76
|
+
} }, s);
|
|
77
|
+
return await t("mutation BuiAssignCreatedTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }", { input: {
|
|
78
|
+
entityType: r,
|
|
79
|
+
entityId: a,
|
|
80
|
+
tagId: i.createTag.id
|
|
81
|
+
} }, s), i.createTag;
|
|
82
|
+
},
|
|
83
|
+
onSuccess: () => l.invalidateQueries({ queryKey: o(r, a) })
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
export { d as useEcoAssignTag, m as useEcoCreateAndAssignTag, l as useEcoEntityTags, f as useEcoRemoveTagAssignment, u as useEcoTagSearch };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=useBuiTags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuiTags.js","names":[],"sources":["../../../src/buildmyiq/hooks/useBuiTags.ts"],"sourcesContent":["/**\n * Buildmyiq — wspace-tags-svc UniversalTag REUSE hooks (Integration C).\n * @module buildmyiq/hooks/useBuiTags\n *\n * The buildmyiq svc does NOT expose a tags field, so we drive the platform tag\n * assignment store directly (approach (b) in the reuse audit) — no buildmyiq-svc\n * change. All operations are composed into the WORKSPACE supergraph:\n * - `getEntityTags(entityType, entityId)` → current assignments (display)\n * - `searchTags(query)` → pick an existing tag\n * - `createTag(input)` → create a new tag (needs a vocabulary)\n * - `assignTag(input)` → link tag ↔ entity\n * - `removeTagAssignment(id)` → unlink\n *\n * `createTag` requires a `vocabularyId`; we resolve a stable, workspace-scoped\n * \"buildmyiq\" vocabulary (`getVocabularyByKey`, creating it on first use).\n *\n * Transport reuses `wspaceFetch` from `useBuiFiles`. Lives in a separate file;\n * `useBuildmyiqApi.ts` is untouched.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { useBuildmyiq } from '../context';\nimport { wspaceFetch } from './useBuiFiles';\n\nexport interface BuiTag {\n id: string;\n label: string;\n color: string | null;\n key: string;\n}\n\nexport interface BuiTagAssignment {\n id: string;\n tag: BuiTag;\n}\n\nconst BUI_VOCABULARY_KEY = 'buildmyiq';\n\nconst entityTagsKey = (entityType: string, entityId: string) =>\n ['buildmyiq', 'entity-tags', entityType, entityId] as const;\n\nconst TAG_FIELDS = 'id label color key';\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48) || 'tag';\n\n/** Current tag assignments for an Buildmyiq entity. */\nexport const useEcoEntityTags = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBuildmyiq();\n return useQuery({\n queryKey: entityTagsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getEntityTags: BuiTagAssignment[] }>(\n `query BuiGetEntityTags($entityType: String!, $entityId: ID!) {\n getEntityTags(entityType: $entityType, entityId: $entityId) {\n id\n tag { ${TAG_FIELDS} }\n }\n }`,\n { entityType, entityId },\n apiGatewayUrl\n );\n return data.getEntityTags;\n },\n });\n};\n\n/** Search existing tags (enabled only for a non-empty query). */\nexport const useEcoTagSearch = (query: string) => {\n const { apiGatewayUrl } = useBuildmyiq();\n const trimmed = query.trim();\n return useQuery({\n queryKey: ['buildmyiq', 'tag-search', trimmed],\n enabled: trimmed.length > 0,\n queryFn: async () => {\n const data = await wspaceFetch<{ searchTags: BuiTag[] }>(\n `query BuiSearchTags($query: String!, $limit: Int) {\n searchTags(query: $query, limit: $limit) { ${TAG_FIELDS} }\n }`,\n { query: trimmed, limit: 8 },\n apiGatewayUrl\n );\n return data.searchTags;\n },\n });\n};\n\n/** Assign an existing tag to the entity. */\nexport const useEcoAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBuildmyiq();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (tagId: string) => {\n const data = await wspaceFetch<{ assignTag: { id: string } }>(\n `mutation BuiAssignTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId } },\n apiGatewayUrl\n );\n return data.assignTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\n/** Remove a tag assignment by its assignment id. */\nexport const useEcoRemoveTagAssignment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBuildmyiq();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (assignmentId: string) => {\n const data = await wspaceFetch<{ removeTagAssignment: boolean }>(\n `mutation BuiRemoveTagAssignment($id: ID!) {\n removeTagAssignment(id: $id)\n }`,\n { id: assignmentId },\n apiGatewayUrl\n );\n return data.removeTagAssignment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\n/** Resolve (creating on first use) the workspace-scoped \"buildmyiq\" vocabulary. */\nconst resolveEcoVocabularyId = async (baseUrl?: string): Promise<string> => {\n const existing = await wspaceFetch<{ getVocabularyByKey: { id: string } | null }>(\n `query BuiGetVocabulary($key: String!) {\n getVocabularyByKey(key: $key) { id }\n }`,\n { key: BUI_VOCABULARY_KEY },\n baseUrl\n );\n if (existing.getVocabularyByKey?.id) {\n return existing.getVocabularyByKey.id;\n }\n const created = await wspaceFetch<{ createVocabulary: { id: string } }>(\n `mutation BuiCreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) { id }\n }`,\n {\n input: {\n key: BUI_VOCABULARY_KEY,\n name: 'Buildmyiq',\n description: 'Tags for Buildmyiq sites, species and other records.',\n allowCustomTags: true,\n },\n },\n baseUrl\n );\n return created.createVocabulary.id;\n};\n\n/** Create a new tag from a free-text label and assign it to the entity. */\nexport const useEcoCreateAndAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBuildmyiq();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (label: string) => {\n const trimmed = label.trim();\n if (!trimmed) {\n throw new Error('A tag label is required.');\n }\n const vocabularyId = await resolveEcoVocabularyId(apiGatewayUrl);\n // A unique key avoids collisions with existing tags in the vocabulary;\n // the (non-unique) label is what users see.\n const key = `${slugify(trimmed)}-${Math.random().toString(36).slice(2, 6)}`;\n const created = await wspaceFetch<{ createTag: { id: string } }>(\n `mutation BuiCreateTag($input: CreateTagInput!) {\n createTag(input: $input) { id }\n }`,\n { input: { vocabularyId, key, label: trimmed } },\n apiGatewayUrl\n );\n await wspaceFetch<{ assignTag: { id: string } }>(\n `mutation BuiAssignCreatedTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId: created.createTag.id } },\n apiGatewayUrl\n );\n return created.createTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n"],"mappings":";;;;;AAqCA,IAAM,IAAqB,aAErB,KAAiB,GAAoB,MACzC;CAAC;CAAa;CAAe;CAAY;CAAS,EAE9C,IAAa,sBAEb,KAAW,MACf,EACG,aAAa,CACb,MAAM,CACN,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,MAAM,GAAG,GAAG,IAAI,OAGR,KAAoB,GAAoB,MAAiC;CACpF,IAAM,EAAE,qBAAkB,GAAc;AACxC,QAAO,EAAS;EACd,UAAU,EAAc,GAAY,KAAY,GAAG;EACnD,SAAS,EAAQ;EACjB,SAAS,aACM,MAAM,EACjB;;;oBAGY,EAAW;;YAGvB;GAAE;GAAY;GAAU,EACxB,EACD,EACW;EAEf,CAAC;GAIS,KAAmB,MAAkB;CAChD,IAAM,EAAE,qBAAkB,GAAc,EAClC,IAAU,EAAM,MAAM;AAC5B,QAAO,EAAS;EACd,UAAU;GAAC;GAAa;GAAc;GAAQ;EAC9C,SAAS,EAAQ,SAAS;EAC1B,SAAS,aACM,MAAM,EACjB;uDAC+C,EAAW;YAE1D;GAAE,OAAO;GAAS,OAAO;GAAG,EAC5B,EACD,EACW;EAEf,CAAC;GAIS,KAAmB,GAAoB,MAAqB;CACvE,IAAM,EAAE,qBAAkB,GAAc,EAClC,IAAK,GAAgB;AAC3B,QAAO,EAAY;EACjB,YAAY,OAAO,OACJ,MAAM,EACjB,0GAGA,EAAE,OAAO;GAAE;GAAY;GAAU;GAAO,EAAE,EAC1C,EACD,EACW;EAEd,iBAAiB,EAAG,kBAAkB,EAAE,UAAU,EAAc,GAAY,EAAS,EAAE,CAAC;EACzF,CAAC;GAIS,KAA6B,GAAoB,MAAqB;CACjF,IAAM,EAAE,qBAAkB,GAAc,EAClC,IAAK,GAAgB;AAC3B,QAAO,EAAY;EACjB,YAAY,OAAO,OACJ,MAAM,EACjB,kGAGA,EAAE,IAAI,GAAc,EACpB,EACD,EACW;EAEd,iBAAiB,EAAG,kBAAkB,EAAE,UAAU,EAAc,GAAY,EAAS,EAAE,CAAC;EACzF,CAAC;GAIE,IAAyB,OAAO,MAAsC;CAC1E,IAAM,IAAW,MAAM,EACrB,8FAGA,EAAE,KAAK,GAAoB,EAC3B,EACD;AAkBD,QAjBI,EAAS,oBAAoB,KACxB,EAAS,mBAAmB,MAErB,MAAM,EACpB,uHAGA,EACE,OAAO;EACL,KAAK;EACL,MAAM;EACN,aAAa;EACb,iBAAiB;EAClB,EACF,EACD,EACD,EACc,iBAAiB;GAIrB,KAA4B,GAAoB,MAAqB;CAChF,IAAM,EAAE,qBAAkB,GAAc,EAClC,IAAK,GAAgB;AAC3B,QAAO,EAAY;EACjB,YAAY,OAAO,MAAkB;GACnC,IAAM,IAAU,EAAM,MAAM;AAC5B,OAAI,CAAC,EACH,OAAU,MAAM,2BAA2B;GAM7C,IAAM,IAAU,MAAM,EACpB,0GAGA,EAAE,OAAO;IAAE,cARQ,MAAM,EAAuB,EAAc;IAQrC,KALf,GAAG,EAAQ,EAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;IAKzC,OAAO;IAAS,EAAE,EAChD,EACD;AAQD,UAPA,MAAM,EACJ,iHAGA,EAAE,OAAO;IAAE;IAAY;IAAU,OAAO,EAAQ,UAAU;IAAI,EAAE,EAChE,EACD,EACM,EAAQ;;EAEjB,iBAAiB,EAAG,kBAAkB,EAAE,UAAU,EAAc,GAAY,EAAS,EAAE,CAAC;EACzF,CAAC"}
|