@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,35 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { IllustratedEmptyState as r } from "@burdenoff/fe-libs/ui";
|
|
4
|
+
//#region src/buildmyiq/components/EmptyState.tsx
|
|
5
|
+
var i = ({ icon: i, illustration: a, title: o, description: s, action: c, className: l }) => a ? /* @__PURE__ */ t(r, {
|
|
6
|
+
illustration: a,
|
|
7
|
+
title: o,
|
|
8
|
+
description: s,
|
|
9
|
+
action: c,
|
|
10
|
+
className: l
|
|
11
|
+
}) : /* @__PURE__ */ n("div", {
|
|
12
|
+
className: e("flex flex-col items-center justify-center py-12 px-4 text-center", l),
|
|
13
|
+
children: [
|
|
14
|
+
i && /* @__PURE__ */ t("div", {
|
|
15
|
+
className: "mb-4 text-text-secondary opacity-50",
|
|
16
|
+
children: i
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ t("h3", {
|
|
19
|
+
className: "text-lg font-medium text-text-primary",
|
|
20
|
+
children: o
|
|
21
|
+
}),
|
|
22
|
+
s && /* @__PURE__ */ t("p", {
|
|
23
|
+
className: "mt-2 text-sm text-text-secondary max-w-sm",
|
|
24
|
+
children: s
|
|
25
|
+
}),
|
|
26
|
+
c && /* @__PURE__ */ t("div", {
|
|
27
|
+
className: "mt-6",
|
|
28
|
+
children: c
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { i as EmptyState };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=EmptyState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/buildmyiq/components/EmptyState.tsx"],"sourcesContent":["/**\n * Empty State Component\n * @module buildmyiq/components\n *\n * Boff UI 2.0 zero-state. When an `illustration` name is provided it renders the\n * fe-libs {@link IllustratedEmptyState} (on-brand glass illustration); otherwise\n * it falls back to the icon-based layout.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n /**\n * On-brand illustration name → `/illustrations/<name>.webp`. Standard names:\n * empty-data, empty-search, empty-files, empty-people, empty-notifications,\n * onboarding, empty-generic.\n */\n illustration?: string;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({\n icon,\n illustration,\n title,\n description,\n action,\n className,\n}) => {\n if (illustration) {\n return (\n <IllustratedEmptyState\n illustration={illustration}\n title={title}\n description={description}\n action={action}\n className={className}\n />\n );\n }\n\n return (\n <div\n className={cn('flex flex-col items-center justify-center py-12 px-4 text-center', className)}\n >\n {icon && <div className=\"mb-4 text-text-secondary opacity-50\">{icon}</div>}\n <h3 className=\"text-lg font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"mt-2 text-sm text-text-secondary max-w-sm\">{description}</p>}\n {action && <div className=\"mt-6\">{action}</div>}\n </div>\n );\n};\n"],"mappings":";;;;AA2BA,IAAa,KAAmC,EAC9C,SACA,iBACA,UACA,gBACA,WACA,mBAEI,IAEA,kBAAC,GAAD;CACgB;CACP;CACM;CACL;CACG;CACX,CAAA,GAKJ,kBAAC,OAAD;CACE,WAAW,EAAG,oEAAoE,EAAU;WAD9F;EAGG,KAAQ,kBAAC,OAAD;GAAK,WAAU;aAAuC;GAAW,CAAA;EAC1E,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA;EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAgB,CAAA;EACzF,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAQ;GAAa,CAAA;EAC3C"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useBuiFileUpload as e } from "../hooks/useBuiFiles.js";
|
|
2
|
+
import "../hooks/index.js";
|
|
3
|
+
import { useRef as t } from "react";
|
|
4
|
+
import { Paperclip as n, Upload as r, X as i } from "lucide-react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
import { Button as s, Label as c, Spinner as l } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/buildmyiq/components/EvidenceUpload.tsx
|
|
8
|
+
function u({ id: u, label: d, value: f, onChange: p, accept: m = "image/*,application/pdf,audio/*", disabled: h = !1, hint: g }) {
|
|
9
|
+
let _ = t(null), { upload: v, isUploading: y, progress: b, error: x, reset: S } = e();
|
|
10
|
+
return /* @__PURE__ */ o("div", {
|
|
11
|
+
className: "grid gap-1.5",
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ a(c, {
|
|
14
|
+
htmlFor: u,
|
|
15
|
+
children: d
|
|
16
|
+
}),
|
|
17
|
+
/* @__PURE__ */ a("input", {
|
|
18
|
+
ref: _,
|
|
19
|
+
id: u,
|
|
20
|
+
type: "file",
|
|
21
|
+
accept: m,
|
|
22
|
+
className: "hidden",
|
|
23
|
+
disabled: h || y,
|
|
24
|
+
onChange: async (e) => {
|
|
25
|
+
let t = e.target.files?.[0];
|
|
26
|
+
if (e.target.value = "", t) try {
|
|
27
|
+
p(await v(t));
|
|
28
|
+
} catch {}
|
|
29
|
+
}
|
|
30
|
+
}),
|
|
31
|
+
f ? /* @__PURE__ */ o("div", {
|
|
32
|
+
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2",
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ a(n, { className: "h-4 w-4 shrink-0 text-text-tertiary" }),
|
|
35
|
+
/* @__PURE__ */ a("span", {
|
|
36
|
+
className: "min-w-0 flex-1 truncate text-sm text-text-primary",
|
|
37
|
+
title: f.fileName,
|
|
38
|
+
children: f.fileName
|
|
39
|
+
}),
|
|
40
|
+
/* @__PURE__ */ a("button", {
|
|
41
|
+
type: "button",
|
|
42
|
+
onClick: () => {
|
|
43
|
+
S(), p(null);
|
|
44
|
+
},
|
|
45
|
+
disabled: h,
|
|
46
|
+
"aria-label": "Remove attachment",
|
|
47
|
+
className: "rounded-md p-1 text-text-tertiary hover:bg-bg-sunken hover:text-text-primary",
|
|
48
|
+
children: /* @__PURE__ */ a(i, { className: "h-4 w-4" })
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}) : y ? /* @__PURE__ */ o("div", {
|
|
52
|
+
className: "flex items-center gap-3 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ a(l, { size: "sm" }),
|
|
55
|
+
/* @__PURE__ */ o("span", {
|
|
56
|
+
className: "text-sm text-text-secondary",
|
|
57
|
+
children: [
|
|
58
|
+
"Uploading… ",
|
|
59
|
+
b,
|
|
60
|
+
"%"
|
|
61
|
+
]
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ a("div", {
|
|
64
|
+
className: "ml-auto h-1.5 w-24 overflow-hidden rounded-full bg-bg-sunken",
|
|
65
|
+
children: /* @__PURE__ */ a("div", {
|
|
66
|
+
className: "h-full rounded-full bg-action-primary-bg transition-[width] duration-150",
|
|
67
|
+
style: { width: `${b}%` }
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
}) : /* @__PURE__ */ o(s, {
|
|
72
|
+
type: "button",
|
|
73
|
+
variant: "outline",
|
|
74
|
+
disabled: h,
|
|
75
|
+
onClick: () => _.current?.click(),
|
|
76
|
+
className: "justify-start",
|
|
77
|
+
children: [/* @__PURE__ */ a(r, { className: "mr-1.5 h-4 w-4" }), " Choose file"]
|
|
78
|
+
}),
|
|
79
|
+
g && !x && /* @__PURE__ */ a("p", {
|
|
80
|
+
className: "text-xs text-text-tertiary",
|
|
81
|
+
children: g
|
|
82
|
+
}),
|
|
83
|
+
x && /* @__PURE__ */ a("p", {
|
|
84
|
+
className: "text-xs text-status-error-text",
|
|
85
|
+
children: x
|
|
86
|
+
})
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { u as EvidenceUpload };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=EvidenceUpload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvidenceUpload.js","names":[],"sources":["../../../src/buildmyiq/components/EvidenceUpload.tsx"],"sourcesContent":["/**\n * EvidenceUpload — a controlled file-attach control (Integration A).\n * @module buildmyiq/components/EvidenceUpload\n *\n * Wraps `useBuiFileUpload` (the wspace-files 3-step flow). On file select it\n * uploads, shows byte-progress + the filename, and emits `{ fileId, fileName }`\n * via `onChange`. The parent stores `fileId` and passes it to the buildmyiq mutations\n * (`buiCreateCourse.coverFileId`, `buiIssueCertificate.fileId`).\n */\n\nimport { useRef, type ChangeEvent } from 'react';\nimport { Paperclip, Upload, X } from 'lucide-react';\nimport { Button, Label, Spinner } from '@burdenoff/fe-libs/ui';\nimport { useBuiFileUpload, type UploadedBuiFile } from '../hooks';\n\ninterface EvidenceUploadProps {\n id: string;\n label: string;\n value: UploadedBuiFile | null;\n onChange: (value: UploadedBuiFile | null) => void;\n accept?: string;\n disabled?: boolean;\n hint?: string;\n}\n\nexport function EvidenceUpload({\n id,\n label,\n value,\n onChange,\n accept = 'image/*,application/pdf,audio/*',\n disabled = false,\n hint,\n}: EvidenceUploadProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const { upload, isUploading, progress, error, reset } = useBuiFileUpload();\n\n const handleSelect = async (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Clear the native input so the same file can be re-selected after a remove.\n event.target.value = '';\n if (!file) return;\n try {\n const uploaded = await upload(file);\n onChange(uploaded);\n } catch {\n // Error surfaced inline via `error`; keep the form usable.\n }\n };\n\n const handleRemove = () => {\n reset();\n onChange(null);\n };\n\n return (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={id}>{label}</Label>\n\n <input\n ref={inputRef}\n id={id}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n disabled={disabled || isUploading}\n onChange={handleSelect}\n />\n\n {value ? (\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2\">\n <Paperclip className=\"h-4 w-4 shrink-0 text-text-tertiary\" />\n <span\n className=\"min-w-0 flex-1 truncate text-sm text-text-primary\"\n title={value.fileName}\n >\n {value.fileName}\n </span>\n <button\n type=\"button\"\n onClick={handleRemove}\n disabled={disabled}\n aria-label=\"Remove attachment\"\n className=\"rounded-md p-1 text-text-tertiary hover:bg-bg-sunken hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : isUploading ? (\n <div className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2\">\n <Spinner size=\"sm\" />\n <span className=\"text-sm text-text-secondary\">Uploading… {progress}%</span>\n <div className=\"ml-auto h-1.5 w-24 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg transition-[width] duration-150\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n ) : (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled}\n onClick={() => inputRef.current?.click()}\n className=\"justify-start\"\n >\n <Upload className=\"mr-1.5 h-4 w-4\" /> Choose file\n </Button>\n )}\n\n {hint && !error && <p className=\"text-xs text-text-tertiary\">{hint}</p>}\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAyBA,SAAgB,EAAe,EAC7B,OACA,UACA,UACA,aACA,YAAS,mCACT,cAAW,IACX,WACsB;CACtB,IAAM,IAAW,EAAyB,KAAK,EACzC,EAAE,WAAQ,gBAAa,aAAU,UAAO,aAAU,GAAkB;AAoB1E,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IAAO,SAAS;cAAK;IAAc,CAAA;GAEnC,kBAAC,SAAD;IACE,KAAK;IACD;IACJ,MAAK;IACG;IACR,WAAU;IACV,UAAU,KAAY;IACtB,UA7Be,OAAO,MAAyC;KACnE,IAAM,IAAO,EAAM,OAAO,QAAQ;AAElC,WAAM,OAAO,QAAQ,IAChB,EACL,KAAI;AAEF,QADiB,MAAM,EAAO,EAAK,CACjB;aACZ;;IAsBJ,CAAA;GAED,IACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAW,WAAU,uCAAwC,CAAA;KAC7D,kBAAC,QAAD;MACE,WAAU;MACV,OAAO,EAAM;gBAEZ,EAAM;MACF,CAAA;KACP,kBAAC,UAAD;MACE,MAAK;MACL,eA9BiB;AAEzB,OADA,GAAO,EACP,EAAS,KAAK;;MA6BI;MACV,cAAW;MACX,WAAU;gBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;MAClB,CAAA;KACL;QACJ,IACF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAS,MAAK,MAAO,CAAA;KACrB,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAA8C;OAAY;OAAS;OAAQ;;KAC3E,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,EAAS,IAAI;OAChC,CAAA;MACE,CAAA;KACF;QAEN,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACE;IACV,eAAe,EAAS,SAAS,OAAO;IACxC,WAAU;cALZ,CAOE,kBAAC,GAAD,EAAQ,WAAU,kBAAmB,CAAA,EAAA,eAC9B;;GAGV,KAAQ,CAAC,KAAS,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAS,CAAA;GACtE,KAAS,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAAU,CAAA;GAC/D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEcoFile as e } from "../hooks/useBuiFiles.js";
|
|
2
|
+
import "../hooks/index.js";
|
|
3
|
+
import { FileText as t, Loader2 as n } from "lucide-react";
|
|
4
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
5
|
+
//#region src/buildmyiq/components/FileLink.tsx
|
|
6
|
+
function a({ fileId: a, className: o }) {
|
|
7
|
+
let { data: s, isLoading: c, isError: l } = e(a);
|
|
8
|
+
return a ? c ? /* @__PURE__ */ i("span", {
|
|
9
|
+
className: "inline-flex items-center gap-1.5 text-sm text-text-tertiary",
|
|
10
|
+
children: [/* @__PURE__ */ r(n, { className: "h-3.5 w-3.5 animate-spin" }), " Loading attachment…"]
|
|
11
|
+
}) : l || !s ? /* @__PURE__ */ i("span", {
|
|
12
|
+
className: "inline-flex items-center gap-1.5 text-sm text-text-tertiary",
|
|
13
|
+
children: [/* @__PURE__ */ r(t, { className: "h-3.5 w-3.5" }), " Attachment unavailable"]
|
|
14
|
+
}) : s.downloadUrl ? /* @__PURE__ */ i("a", {
|
|
15
|
+
href: s.downloadUrl,
|
|
16
|
+
target: "_blank",
|
|
17
|
+
rel: "noopener noreferrer",
|
|
18
|
+
className: o ?? "inline-flex items-center gap-1.5 text-sm font-medium text-action-primary-bg hover:underline",
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ r(t, { className: "h-3.5 w-3.5" }),
|
|
21
|
+
" ",
|
|
22
|
+
s.name
|
|
23
|
+
]
|
|
24
|
+
}) : /* @__PURE__ */ i("span", {
|
|
25
|
+
className: o ?? "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ r(t, { className: "h-3.5 w-3.5" }),
|
|
28
|
+
" ",
|
|
29
|
+
s.name
|
|
30
|
+
]
|
|
31
|
+
}) : null;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { a as FileLink };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=FileLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileLink.js","names":[],"sources":["../../../src/buildmyiq/components/FileLink.tsx"],"sourcesContent":["/**\n * FileLink — resolves a federated File id to a download link (Integration A).\n * @module buildmyiq/components/FileLink\n *\n * The buildmyiq queries select `cover { id }` / `media { id }` (just the id). This\n * resolves the platform File via `getFile(id)` for its name + signed\n * `downloadUrl` so evidence/report artifacts render as a real link.\n */\n\nimport { FileText, Loader2 } from 'lucide-react';\nimport { useEcoFile } from '../hooks';\n\ninterface FileLinkProps {\n fileId: string | null | undefined;\n className?: string;\n}\n\nexport function FileLink({ fileId, className }: FileLinkProps) {\n const { data, isLoading, isError } = useEcoFile(fileId);\n\n if (!fileId) return null;\n\n if (isLoading) {\n return (\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-tertiary\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" /> Loading attachment…\n </span>\n );\n }\n\n if (isError || !data) {\n return (\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-tertiary\">\n <FileText className=\"h-3.5 w-3.5\" /> Attachment unavailable\n </span>\n );\n }\n\n const base =\n 'inline-flex items-center gap-1.5 text-sm font-medium text-action-primary-bg hover:underline';\n\n if (!data.downloadUrl) {\n return (\n <span className={className ?? 'inline-flex items-center gap-1.5 text-sm text-text-secondary'}>\n <FileText className=\"h-3.5 w-3.5\" /> {data.name}\n </span>\n );\n }\n\n return (\n <a\n href={data.downloadUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={className ?? base}\n >\n <FileText className=\"h-3.5 w-3.5\" /> {data.name}\n </a>\n );\n}\n"],"mappings":";;;;;AAiBA,SAAgB,EAAS,EAAE,WAAQ,gBAA4B;CAC7D,IAAM,EAAE,SAAM,cAAW,eAAY,EAAW,EAAO;AA+BvD,QA7BK,IAED,IAEA,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,EAAA,uBAC3C;MAIP,KAAW,CAAC,IAEZ,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EAAA,0BAC/B;MAON,EAAK,cASR,kBAAC,KAAD;EACE,MAAM,EAAK;EACX,QAAO;EACP,KAAI;EACJ,WAAW,KAAa;YAJ1B;GAME,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;;GAAE,EAAK;GACzC;MAdF,kBAAC,QAAD;EAAM,WAAW,KAAa;YAA9B;GACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;;GAAE,EAAK;GACtC;MAzBS"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
//#region src/buildmyiq/components/FilterTabs.tsx
|
|
4
|
+
function r({ options: r, value: i, onChange: a, className: o }) {
|
|
5
|
+
return /* @__PURE__ */ t("div", {
|
|
6
|
+
role: "tablist",
|
|
7
|
+
className: e("inline-flex flex-wrap items-center gap-1 rounded-xl border border-border-seam bg-bg-surface p-1", o),
|
|
8
|
+
children: r.map((r) => {
|
|
9
|
+
let o = r.value === i;
|
|
10
|
+
return /* @__PURE__ */ n("button", {
|
|
11
|
+
type: "button",
|
|
12
|
+
role: "tab",
|
|
13
|
+
"aria-selected": o,
|
|
14
|
+
onClick: () => a(r.value),
|
|
15
|
+
className: e("rounded-lg px-3 py-1.5 text-sm font-medium transition-colors", o ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"),
|
|
16
|
+
children: [r.label, typeof r.count == "number" && /* @__PURE__ */ t("span", {
|
|
17
|
+
className: e("ml-1.5 rounded-full px-1.5 py-0.5 text-xs tabular-nums", o ? "bg-black/10 dark:bg-white/15" : "bg-bg-sunken"),
|
|
18
|
+
children: r.count
|
|
19
|
+
})]
|
|
20
|
+
}, r.value);
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { r as FilterTabs };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=FilterTabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterTabs.js","names":[],"sources":["../../../src/buildmyiq/components/FilterTabs.tsx"],"sourcesContent":["/**\n * FilterTabs — a segmented status/enum filter control.\n * @module buildmyiq/components\n */\n\nimport { cn } from '../utils/cn';\n\nexport interface FilterOption<V extends string> {\n value: V;\n label: string;\n count?: number;\n}\n\ninterface FilterTabsProps<V extends string> {\n options: FilterOption<V>[];\n value: V;\n onChange: (value: V) => void;\n className?: string;\n}\n\nexport function FilterTabs<V extends string>({\n options,\n value,\n onChange,\n className,\n}: FilterTabsProps<V>) {\n return (\n <div\n role=\"tablist\"\n className={cn(\n 'inline-flex flex-wrap items-center gap-1 rounded-xl border border-border-seam bg-bg-surface p-1',\n className\n )}\n >\n {options.map((opt) => {\n const active = opt.value === value;\n return (\n <button\n key={opt.value}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={() => onChange(opt.value)}\n className={cn(\n 'rounded-lg px-3 py-1.5 text-sm font-medium transition-colors',\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n {opt.label}\n {typeof opt.count === 'number' && (\n <span\n className={cn(\n 'ml-1.5 rounded-full px-1.5 py-0.5 text-xs tabular-nums',\n active ? 'bg-black/10 dark:bg-white/15' : 'bg-bg-sunken'\n )}\n >\n {opt.count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;AAoBA,SAAgB,EAA6B,EAC3C,YACA,UACA,aACA,gBACqB;AACrB,QACE,kBAAC,OAAD;EACE,MAAK;EACL,WAAW,EACT,mGACA,EACD;YAEA,EAAQ,KAAK,MAAQ;GACpB,IAAM,IAAS,EAAI,UAAU;AAC7B,UACE,kBAAC,UAAD;IAEE,MAAK;IACL,MAAK;IACL,iBAAe;IACf,eAAe,EAAS,EAAI,MAAM;IAClC,WAAW,EACT,gEACA,IACI,kDACA,iEACL;cAXH,CAaG,EAAI,OACJ,OAAO,EAAI,SAAU,YACpB,kBAAC,QAAD;KACE,WAAW,EACT,0DACA,IAAS,iCAAiC,eAC3C;eAEA,EAAI;KACA,CAAA,CAEF;MAvBF,EAAI,MAuBF;IAEX;EACE,CAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { Minus as t, TrendingDown as n, TrendingUp as r } from "lucide-react";
|
|
3
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
+
import { GlassCard as o } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
//#region src/buildmyiq/components/MetricsCard.tsx
|
|
6
|
+
var s = {
|
|
7
|
+
up: {
|
|
8
|
+
icon: r,
|
|
9
|
+
color: "text-status-success-text"
|
|
10
|
+
},
|
|
11
|
+
down: {
|
|
12
|
+
icon: n,
|
|
13
|
+
color: "text-status-error-text"
|
|
14
|
+
},
|
|
15
|
+
neutral: {
|
|
16
|
+
icon: t,
|
|
17
|
+
color: "text-text-secondary"
|
|
18
|
+
}
|
|
19
|
+
}, c = {
|
|
20
|
+
success: "text-status-success-text",
|
|
21
|
+
warning: "text-status-warning-text",
|
|
22
|
+
error: "text-status-error-text"
|
|
23
|
+
}, l = {
|
|
24
|
+
success: "shadow-[inset_0_1px_0_0_var(--color-status-success-text)]",
|
|
25
|
+
warning: "shadow-[inset_0_1px_0_0_var(--color-status-warning-text)]",
|
|
26
|
+
error: "shadow-[inset_0_1px_0_0_var(--color-status-error-text)]"
|
|
27
|
+
}, u = ({ icon: t, value: n, label: r, trend: u, trendValue: d, className: f, featured: p = !1, accent: m, pill: h, footer: g }) => {
|
|
28
|
+
let _ = u ? s[u] : null, v = _?.icon;
|
|
29
|
+
return /* @__PURE__ */ a(o, {
|
|
30
|
+
treatment: p ? "glass" : "metric",
|
|
31
|
+
glow: p,
|
|
32
|
+
className: e("p-6", m && l[m], f),
|
|
33
|
+
children: [/* @__PURE__ */ a("div", {
|
|
34
|
+
className: "flex items-center justify-between",
|
|
35
|
+
children: [/* @__PURE__ */ i("div", {
|
|
36
|
+
className: e("text-text-secondary", m && c[m]),
|
|
37
|
+
children: t
|
|
38
|
+
}), h ?? (u && _ && v ? /* @__PURE__ */ a("div", {
|
|
39
|
+
className: e("flex items-center gap-1 text-xs font-medium", _.color),
|
|
40
|
+
children: [/* @__PURE__ */ i(v, { className: "h-3.5 w-3.5" }), d && /* @__PURE__ */ i("span", { children: d })]
|
|
41
|
+
}) : null)]
|
|
42
|
+
}), /* @__PURE__ */ a("div", {
|
|
43
|
+
className: "mt-3",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ i("p", {
|
|
46
|
+
className: "text-2xl font-bold text-text-primary tabular-nums",
|
|
47
|
+
children: n
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ i("p", {
|
|
50
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
51
|
+
children: r
|
|
52
|
+
}),
|
|
53
|
+
g && /* @__PURE__ */ i("div", {
|
|
54
|
+
className: "mt-3",
|
|
55
|
+
children: g
|
|
56
|
+
})
|
|
57
|
+
]
|
|
58
|
+
})]
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { u as MetricsCard };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=MetricsCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricsCard.js","names":[],"sources":["../../../src/buildmyiq/components/MetricsCard.tsx"],"sourcesContent":["/**\n * Metrics Card Component\n * @module buildmyiq/components\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport type { TrendDirection } from '../types';\n\ntype Accent = 'success' | 'warning' | 'error';\n\ninterface MetricsCardProps {\n icon: ReactNode;\n value: string | number;\n label: string;\n trend?: TrendDirection;\n trendValue?: string;\n className?: string;\n /** Render as the page's hero metric (glass treatment + accent glow). */\n featured?: boolean;\n /** Severity accent — tints the icon and adds a soft top-edge glow. */\n accent?: Accent;\n /** Optional pill shown in the top-right (e.g. \"Critical\"). */\n pill?: ReactNode;\n /** Optional slot under the value (e.g. a MiniBar or sub-label). */\n footer?: ReactNode;\n}\n\nconst trendConfig: Record<TrendDirection, { icon: typeof TrendingUp; color: string }> = {\n up: { icon: TrendingUp, color: 'text-status-success-text' },\n down: { icon: TrendingDown, color: 'text-status-error-text' },\n neutral: { icon: Minus, color: 'text-text-secondary' },\n};\n\nconst accentIcon: Record<Accent, string> = {\n success: 'text-status-success-text',\n warning: 'text-status-warning-text',\n error: 'text-status-error-text',\n};\n\nconst accentGlow: Record<Accent, string> = {\n success: 'shadow-[inset_0_1px_0_0_var(--color-status-success-text)]',\n warning: 'shadow-[inset_0_1px_0_0_var(--color-status-warning-text)]',\n error: 'shadow-[inset_0_1px_0_0_var(--color-status-error-text)]',\n};\n\nexport const MetricsCard: FC<MetricsCardProps> = ({\n icon,\n value,\n label,\n trend,\n trendValue,\n className,\n featured = false,\n accent,\n pill,\n footer,\n}) => {\n const trendInfo = trend ? trendConfig[trend] : null;\n const TrendIcon = trendInfo?.icon;\n\n return (\n <GlassCard\n treatment={featured ? 'glass' : 'metric'}\n glow={featured}\n className={cn('p-6', accent && accentGlow[accent], className)}\n >\n <div className=\"flex items-center justify-between\">\n <div className={cn('text-text-secondary', accent && accentIcon[accent])}>{icon}</div>\n {pill ??\n (trend && trendInfo && TrendIcon ? (\n <div className={cn('flex items-center gap-1 text-xs font-medium', trendInfo.color)}>\n <TrendIcon className=\"h-3.5 w-3.5\" />\n {trendValue && <span>{trendValue}</span>}\n </div>\n ) : null)}\n </div>\n <div className=\"mt-3\">\n <p className=\"text-2xl font-bold text-text-primary tabular-nums\">{value}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">{label}</p>\n {footer && <div className=\"mt-3\">{footer}</div>}\n </div>\n </GlassCard>\n );\n};\n"],"mappings":";;;;;AA8BA,IAAM,IAAkF;CACtF,IAAI;EAAE,MAAM;EAAY,OAAO;EAA4B;CAC3D,MAAM;EAAE,MAAM;EAAc,OAAO;EAA0B;CAC7D,SAAS;EAAE,MAAM;EAAO,OAAO;EAAuB;CACvD,EAEK,IAAqC;CACzC,SAAS;CACT,SAAS;CACT,OAAO;CACR,EAEK,IAAqC;CACzC,SAAS;CACT,SAAS;CACT,OAAO;CACR,EAEY,KAAqC,EAChD,SACA,UACA,UACA,UACA,eACA,cACA,cAAW,IACX,WACA,SACA,gBACI;CACJ,IAAM,IAAY,IAAQ,EAAY,KAAS,MACzC,IAAY,GAAW;AAE7B,QACE,kBAAC,GAAD;EACE,WAAW,IAAW,UAAU;EAChC,MAAM;EACN,WAAW,EAAG,OAAO,KAAU,EAAW,IAAS,EAAU;YAH/D,CAKE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAW,EAAG,uBAAuB,KAAU,EAAW,GAAQ;cAAG;IAAW,CAAA,EACpF,MACE,KAAS,KAAa,IACrB,kBAAC,OAAD;IAAK,WAAW,EAAG,+CAA+C,EAAU,MAAM;cAAlF,CACE,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA,EACpC,KAAc,kBAAC,QAAD,EAAA,UAAO,GAAkB,CAAA,CACpC;QACJ,MACF;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,KAAD;KAAG,WAAU;eAAqD;KAAU,CAAA;IAC5E,kBAAC,KAAD;KAAG,WAAU;eAAoC;KAAU,CAAA;IAC1D,KAAU,kBAAC,OAAD;KAAK,WAAU;eAAQ;KAAa,CAAA;IAC3C;KACI"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
3
|
+
//#region src/buildmyiq/components/MiniBar.tsx
|
|
4
|
+
function n({ value: n, className: r, barClassName: i = "bg-accent", height: a = 6 }) {
|
|
5
|
+
let o = Math.max(0, Math.min(100, n));
|
|
6
|
+
return /* @__PURE__ */ t("div", {
|
|
7
|
+
className: e("w-full overflow-hidden rounded-full bg-bg-sunken", r),
|
|
8
|
+
style: { height: a },
|
|
9
|
+
role: "progressbar",
|
|
10
|
+
"aria-valuenow": Math.round(o),
|
|
11
|
+
"aria-valuemin": 0,
|
|
12
|
+
"aria-valuemax": 100,
|
|
13
|
+
children: /* @__PURE__ */ t("div", {
|
|
14
|
+
className: e("h-full rounded-full transition-[width] duration-700 ease-out", i),
|
|
15
|
+
style: { width: `${o}%` }
|
|
16
|
+
})
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { n as MiniBar };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=MiniBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MiniBar.js","names":[],"sources":["../../../src/buildmyiq/components/MiniBar.tsx"],"sourcesContent":["/**\n * MiniBar — a thin inline progress bar (target progress, completion, breach %).\n * @module buildmyiq/components\n */\n\nimport { cn } from '../utils/cn';\n\ninterface MiniBarProps {\n /** 0–100. */\n value: number;\n className?: string;\n /** Bar fill token class (defaults to accent). */\n barClassName?: string;\n /** Height in px. */\n height?: number;\n}\n\nexport function MiniBar({\n value,\n className,\n barClassName = 'bg-accent',\n height = 6,\n}: MiniBarProps) {\n const clamped = Math.max(0, Math.min(100, value));\n return (\n <div\n className={cn('w-full overflow-hidden rounded-full bg-bg-sunken', className)}\n style={{ height }}\n role=\"progressbar\"\n aria-valuenow={Math.round(clamped)}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div\n className={cn('h-full rounded-full transition-[width] duration-700 ease-out', barClassName)}\n style={{ width: `${clamped}%` }}\n />\n </div>\n );\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAQ,EACtB,UACA,cACA,kBAAe,aACf,YAAS,KACM;CACf,IAAM,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,EAAM,CAAC;AACjD,QACE,kBAAC,OAAD;EACE,WAAW,EAAG,oDAAoD,EAAU;EAC5E,OAAO,EAAE,WAAQ;EACjB,MAAK;EACL,iBAAe,KAAK,MAAM,EAAQ;EAClC,iBAAe;EACf,iBAAe;YAEf,kBAAC,OAAD;GACE,WAAW,EAAG,gEAAgE,EAAa;GAC3F,OAAO,EAAE,OAAO,GAAG,EAAQ,IAAI;GAC/B,CAAA;EACE,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { AuroraBackground as r, GradientText as i } from "@burdenoff/fe-libs/ui";
|
|
4
|
+
//#region src/buildmyiq/components/PageLayout.tsx
|
|
5
|
+
var a = ({ title: a, description: o, actions: s, children: c, className: l, titleGradient: u = !1, aurora: d = !1 }) => /* @__PURE__ */ n("div", {
|
|
6
|
+
className: e("flex flex-col h-full", l),
|
|
7
|
+
children: [/* @__PURE__ */ n("div", {
|
|
8
|
+
className: e("relative flex flex-col gap-3 px-6 py-4 border-b border-border-seam sm:flex-row sm:items-center sm:justify-between sm:gap-4", d ? "overflow-hidden bg-gloss-card" : "bg-bg-surface"),
|
|
9
|
+
children: [
|
|
10
|
+
d && /* @__PURE__ */ t(r, { intensity: "subtle" }),
|
|
11
|
+
/* @__PURE__ */ n("div", {
|
|
12
|
+
className: "min-w-0",
|
|
13
|
+
children: [u && typeof a == "string" ? /* @__PURE__ */ t(i, {
|
|
14
|
+
as: "h1",
|
|
15
|
+
className: "text-2xl font-semibold tracking-tight",
|
|
16
|
+
children: a
|
|
17
|
+
}) : /* @__PURE__ */ t("h1", {
|
|
18
|
+
className: "text-2xl font-semibold text-text-primary",
|
|
19
|
+
children: a
|
|
20
|
+
}), o && /* @__PURE__ */ t("p", {
|
|
21
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
22
|
+
children: o
|
|
23
|
+
})]
|
|
24
|
+
}),
|
|
25
|
+
s && /* @__PURE__ */ t("div", {
|
|
26
|
+
className: "flex flex-wrap items-center gap-3 shrink-0 max-sm:w-full",
|
|
27
|
+
children: s
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
}), /* @__PURE__ */ t("div", {
|
|
31
|
+
className: "flex-1 overflow-auto p-6",
|
|
32
|
+
children: c
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
//#endregion
|
|
36
|
+
export { a as PageLayout };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=PageLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/buildmyiq/components/PageLayout.tsx"],"sourcesContent":["/**\n * Page Layout Component\n * @module buildmyiq/components\n *\n * Boff UI 2.0 page wrapper. Supports a living-glass {@link AuroraBackground}\n * hero backdrop and a gradient hero title for the MFE's primary page.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { AuroraBackground, GradientText } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /** Render the title as a gradient hero heading (reserve for the ONE primary page). */\n titleGradient?: boolean;\n /** Drop a subtle living-glass aurora behind the header (primary page only). */\n aurora?: boolean;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({\n title,\n description,\n actions,\n children,\n className,\n titleGradient = false,\n aurora = false,\n}) => {\n return (\n <div className={cn('flex flex-col h-full', className)}>\n <div\n className={cn(\n 'relative flex flex-col gap-3 px-6 py-4 border-b border-border-seam sm:flex-row sm:items-center sm:justify-between sm:gap-4',\n aurora ? 'overflow-hidden bg-gloss-card' : 'bg-bg-surface'\n )}\n >\n {aurora && <AuroraBackground intensity=\"subtle\" />}\n <div className=\"min-w-0\">\n {titleGradient && typeof title === 'string' ? (\n <GradientText as=\"h1\" className=\"text-2xl font-semibold tracking-tight\">\n {title}\n </GradientText>\n ) : (\n <h1 className=\"text-2xl font-semibold text-text-primary\">{title}</h1>\n )}\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && (\n <div className=\"flex flex-wrap items-center gap-3 shrink-0 max-sm:w-full\">{actions}</div>\n )}\n </div>\n\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n};\n"],"mappings":";;;;AAwBA,IAAa,KAAmC,EAC9C,UACA,gBACA,YACA,aACA,cACA,mBAAgB,IAChB,YAAS,SAGP,kBAAC,OAAD;CAAK,WAAW,EAAG,wBAAwB,EAAU;WAArD,CACE,kBAAC,OAAD;EACE,WAAW,EACT,8HACA,IAAS,kCAAkC,gBAC5C;YAJH;GAMG,KAAU,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA;GAClD,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KAAiB,OAAO,KAAU,WACjC,kBAAC,GAAD;KAAc,IAAG;KAAK,WAAU;eAC7B;KACY,CAAA,GAEf,kBAAC,MAAD;KAAI,WAAU;eAA4C;KAAW,CAAA,EAEtE,KAAe,kBAAC,KAAD;KAAG,WAAU;eAAoC;KAAgB,CAAA,CAC7E;;GACL,KACC,kBAAC,OAAD;IAAK,WAAU;cAA4D;IAAc,CAAA;GAEvF;KAEN,kBAAC,OAAD;EAAK,WAAU;EAA4B;EAAe,CAAA,CACtD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { ChevronLeft as t, ChevronRight as n } from "lucide-react";
|
|
3
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
4
|
+
import { Button as a } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
//#region src/buildmyiq/components/Pagination.tsx
|
|
6
|
+
function o({ page: o, pageSize: s, itemCount: c, hasMore: l, onPageChange: u, className: d }) {
|
|
7
|
+
if (o === 0 && !l) return null;
|
|
8
|
+
let f = c === 0 ? 0 : o * s + 1, p = o * s + c;
|
|
9
|
+
return /* @__PURE__ */ i("div", {
|
|
10
|
+
className: e("mt-4 flex flex-col items-center justify-between gap-3 sm:flex-row", d),
|
|
11
|
+
children: [/* @__PURE__ */ i("p", {
|
|
12
|
+
className: "text-sm text-text-secondary tabular-nums",
|
|
13
|
+
"aria-live": "polite",
|
|
14
|
+
children: [
|
|
15
|
+
"Showing ",
|
|
16
|
+
f,
|
|
17
|
+
"–",
|
|
18
|
+
p
|
|
19
|
+
]
|
|
20
|
+
}), /* @__PURE__ */ i("div", {
|
|
21
|
+
className: "flex items-center gap-2",
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ i(a, {
|
|
24
|
+
variant: "outline",
|
|
25
|
+
size: "sm",
|
|
26
|
+
onClick: () => u(o - 1),
|
|
27
|
+
disabled: o === 0,
|
|
28
|
+
"aria-label": "Previous page",
|
|
29
|
+
children: [/* @__PURE__ */ r(t, { className: "mr-1 h-4 w-4" }), " Previous"]
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ i("span", {
|
|
32
|
+
className: "min-w-[4.5rem] text-center text-sm text-text-secondary tabular-nums",
|
|
33
|
+
children: ["Page ", o + 1]
|
|
34
|
+
}),
|
|
35
|
+
/* @__PURE__ */ i(a, {
|
|
36
|
+
variant: "outline",
|
|
37
|
+
size: "sm",
|
|
38
|
+
onClick: () => u(o + 1),
|
|
39
|
+
disabled: !l,
|
|
40
|
+
"aria-label": "Next page",
|
|
41
|
+
children: ["Next ", /* @__PURE__ */ r(n, { className: "ml-1 h-4 w-4" })]
|
|
42
|
+
})
|
|
43
|
+
]
|
|
44
|
+
})]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { o as Pagination };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=Pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/buildmyiq/components/Pagination.tsx"],"sourcesContent":["/**\n * Pagination — cursor-aware Prev/Next control for Bui* connection lists.\n * @module buildmyiq/components\n *\n * The svc list connections return `pageInfo { cursor, hasMore }` and paginate by\n * cursor (offset is a no-op). Pages track a 0-indexed `page` for display and a\n * cursor stack for navigation; this control exposes Prev/Next, disabling Prev on\n * page 0 and Next when the server reports no more rows.\n */\n\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface PaginationProps {\n page: number;\n pageSize: number;\n /** Number of rows on the current page. */\n itemCount: number;\n /** Server flag: are there more rows after this page? */\n hasMore: boolean;\n onPageChange: (page: number) => void;\n className?: string;\n}\n\nexport function Pagination({\n page,\n pageSize,\n itemCount,\n hasMore,\n onPageChange,\n className,\n}: PaginationProps) {\n // Nothing to paginate: single page that also has no more rows.\n if (page === 0 && !hasMore) return null;\n\n const from = itemCount === 0 ? 0 : page * pageSize + 1;\n const to = page * pageSize + itemCount;\n\n return (\n <div\n className={cn('mt-4 flex flex-col items-center justify-between gap-3 sm:flex-row', className)}\n >\n <p className=\"text-sm text-text-secondary tabular-nums\" aria-live=\"polite\">\n Showing {from}–{to}\n </p>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(page - 1)}\n disabled={page === 0}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"mr-1 h-4 w-4\" /> Previous\n </Button>\n <span className=\"min-w-[4.5rem] text-center text-sm text-text-secondary tabular-nums\">\n Page {page + 1}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(page + 1)}\n disabled={!hasMore}\n aria-label=\"Next page\"\n >\n Next <ChevronRight className=\"ml-1 h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAyBA,SAAgB,EAAW,EACzB,SACA,aACA,cACA,YACA,iBACA,gBACkB;AAElB,KAAI,MAAS,KAAK,CAAC,EAAS,QAAO;CAEnC,IAAM,IAAO,MAAc,IAAI,IAAI,IAAO,IAAW,GAC/C,IAAK,IAAO,IAAW;AAE7B,QACE,kBAAC,OAAD;EACE,WAAW,EAAG,qEAAqE,EAAU;YAD/F,CAGE,kBAAC,KAAD;GAAG,WAAU;GAA2C,aAAU;aAAlE;IAA2E;IAChE;IAAK;IAAE;IACd;MACJ,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,IAAO,EAAE;KACrC,UAAU,MAAS;KACnB,cAAW;eALb,CAOE,kBAAC,GAAD,EAAa,WAAU,gBAAiB,CAAA,EAAA,YACjC;;IACT,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAsF,SAC9E,IAAO,EACR;;IACP,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAa,IAAO,EAAE;KACrC,UAAU,CAAC;KACX,cAAW;eALb,CAMC,SACM,kBAAC,GAAD,EAAc,WAAU,gBAAiB,CAAA,CACvC;;IACL;KACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { AlertCircle as t } from "lucide-react";
|
|
3
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
|
+
import { Spinner as i } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
//#region src/buildmyiq/components/StateViews.tsx
|
|
6
|
+
var a = ({ label: t = "Loading…", className: a }) => /* @__PURE__ */ r("div", {
|
|
7
|
+
className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3", a),
|
|
8
|
+
children: [/* @__PURE__ */ n(i, { size: "lg" }), /* @__PURE__ */ n("p", {
|
|
9
|
+
className: "text-sm text-text-secondary",
|
|
10
|
+
children: t
|
|
11
|
+
})]
|
|
12
|
+
}), o = ({ title: i = "Something went wrong", message: a, onRetry: o, className: s }) => /* @__PURE__ */ r("div", {
|
|
13
|
+
className: e("flex min-h-[40vh] flex-col items-center justify-center gap-4 text-center", s),
|
|
14
|
+
children: [
|
|
15
|
+
/* @__PURE__ */ n(t, { className: "h-10 w-10 text-status-error-text" }),
|
|
16
|
+
/* @__PURE__ */ r("div", { children: [/* @__PURE__ */ n("h2", {
|
|
17
|
+
className: "text-lg font-semibold text-text-primary",
|
|
18
|
+
children: i
|
|
19
|
+
}), a && /* @__PURE__ */ n("p", {
|
|
20
|
+
className: "mt-1 max-w-md text-sm text-text-secondary",
|
|
21
|
+
children: a
|
|
22
|
+
})] }),
|
|
23
|
+
o && /* @__PURE__ */ n("button", {
|
|
24
|
+
type: "button",
|
|
25
|
+
onClick: o,
|
|
26
|
+
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
27
|
+
children: "Retry"
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
}), s = ({ title: t, description: i, action: a, children: o, className: s }) => /* @__PURE__ */ r("section", {
|
|
31
|
+
className: e("mt-8 first:mt-0", s),
|
|
32
|
+
children: [/* @__PURE__ */ r("div", {
|
|
33
|
+
className: "mb-3 flex items-end justify-between gap-3",
|
|
34
|
+
children: [/* @__PURE__ */ r("div", { children: [/* @__PURE__ */ n("h2", {
|
|
35
|
+
className: "text-base font-semibold text-text-primary",
|
|
36
|
+
children: t
|
|
37
|
+
}), i && /* @__PURE__ */ n("p", {
|
|
38
|
+
className: "mt-0.5 text-sm text-text-secondary",
|
|
39
|
+
children: i
|
|
40
|
+
})] }), a && /* @__PURE__ */ n("div", {
|
|
41
|
+
className: "shrink-0",
|
|
42
|
+
children: a
|
|
43
|
+
})]
|
|
44
|
+
}), o]
|
|
45
|
+
});
|
|
46
|
+
//#endregion
|
|
47
|
+
export { o as ErrorView, a as LoadingView, s as Section };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=StateViews.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateViews.js","names":[],"sources":["../../../src/buildmyiq/components/StateViews.tsx"],"sourcesContent":["/**\n * Shared loading / error / section state views\n * @module buildmyiq/components\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface LoadingViewProps {\n label?: string;\n className?: string;\n}\n\nexport const LoadingView: FC<LoadingViewProps> = ({ label = 'Loading…', className }) => (\n <div className={cn('flex min-h-[40vh] flex-col items-center justify-center gap-3', className)}>\n <Spinner size=\"lg\" />\n <p className=\"text-sm text-text-secondary\">{label}</p>\n </div>\n);\n\ninterface ErrorViewProps {\n title?: string;\n message?: string;\n onRetry?: () => void;\n className?: string;\n}\n\nexport const ErrorView: FC<ErrorViewProps> = ({\n title = 'Something went wrong',\n message,\n onRetry,\n className,\n}) => (\n <div\n className={cn(\n 'flex min-h-[40vh] flex-col items-center justify-center gap-4 text-center',\n className\n )}\n >\n <AlertCircle className=\"h-10 w-10 text-status-error-text\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n {message && <p className=\"mt-1 max-w-md text-sm text-text-secondary\">{message}</p>}\n </div>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Retry\n </button>\n )}\n </div>\n);\n\ninterface SectionProps {\n title: string;\n description?: string;\n action?: ReactNode;\n children: ReactNode;\n className?: string;\n}\n\nexport const Section: FC<SectionProps> = ({ title, description, action, children, className }) => (\n <section className={cn('mt-8 first:mt-0', className)}>\n <div className=\"mb-3 flex items-end justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n {description && <p className=\"mt-0.5 text-sm text-text-secondary\">{description}</p>}\n </div>\n {action && <div className=\"shrink-0\">{action}</div>}\n </div>\n {children}\n </section>\n);\n"],"mappings":";;;;;AAeA,IAAa,KAAqC,EAAE,WAAQ,YAAY,mBACtE,kBAAC,OAAD;CAAK,WAAW,EAAG,gEAAgE,EAAU;WAA7F,CACE,kBAAC,GAAD,EAAS,MAAK,MAAO,CAAA,EACrB,kBAAC,KAAD;EAAG,WAAU;YAA+B;EAAU,CAAA,CAClD;IAUK,KAAiC,EAC5C,WAAQ,wBACR,YACA,YACA,mBAEA,kBAAC,OAAD;CACE,WAAW,EACT,4EACA,EACD;WAJH;EAME,kBAAC,GAAD,EAAa,WAAU,oCAAqC,CAAA;EAC5D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA2C;GAAW,CAAA,EACnE,KAAW,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAY,CAAA,CAC9E,EAAA,CAAA;EACL,KACC,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;aACX;GAEQ,CAAA;EAEP;IAWK,KAA6B,EAAE,UAAO,gBAAa,WAAQ,aAAU,mBAChF,kBAAC,WAAD;CAAS,WAAW,EAAG,mBAAmB,EAAU;WAApD,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA6C;GAAW,CAAA,EACrE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAAsC;GAAgB,CAAA,CAC/E,EAAA,CAAA,EACL,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAY;GAAa,CAAA,CAC/C;KACL,EACO"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { formatEnumLabel as t } from "../utils/formatters.js";
|
|
3
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
|
+
//#region src/buildmyiq/components/StatusBadge.tsx
|
|
5
|
+
var i = {
|
|
6
|
+
success: "bg-status-success-bg-subtle text-status-success-text",
|
|
7
|
+
warning: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
8
|
+
destructive: "bg-status-error-bg-subtle text-status-error-text",
|
|
9
|
+
secondary: "bg-bg-sunken text-text-secondary",
|
|
10
|
+
info: "bg-[var(--color-accent-soft)] text-text-primary",
|
|
11
|
+
outline: "border border-border-seam text-text-primary"
|
|
12
|
+
}, a = new Set([
|
|
13
|
+
"IN_PROGRESS",
|
|
14
|
+
"ACTIVE",
|
|
15
|
+
"GRADING"
|
|
16
|
+
]), o = {
|
|
17
|
+
DRAFT: "secondary",
|
|
18
|
+
IN_REVIEW: "warning",
|
|
19
|
+
PUBLISHED: "success",
|
|
20
|
+
ARCHIVED: "outline",
|
|
21
|
+
ENROLLED: "info",
|
|
22
|
+
IN_PROGRESS: "info",
|
|
23
|
+
COMPLETED: "success",
|
|
24
|
+
DROPPED: "destructive",
|
|
25
|
+
EXPIRED: "outline",
|
|
26
|
+
NOT_STARTED: "secondary",
|
|
27
|
+
SUBMITTED: "info",
|
|
28
|
+
GRADING: "warning",
|
|
29
|
+
GRADED: "success",
|
|
30
|
+
RETURNED: "warning",
|
|
31
|
+
SCHEDULED: "secondary",
|
|
32
|
+
ENROLLING: "info",
|
|
33
|
+
ACTIVE: "success",
|
|
34
|
+
CANCELLED: "destructive",
|
|
35
|
+
ISSUED: "success",
|
|
36
|
+
REVOKED: "destructive",
|
|
37
|
+
BEGINNER: "success",
|
|
38
|
+
INTERMEDIATE: "info",
|
|
39
|
+
ADVANCED: "warning",
|
|
40
|
+
EXPERT: "destructive",
|
|
41
|
+
TECHNICAL: "outline",
|
|
42
|
+
BUSINESS: "outline",
|
|
43
|
+
CREATIVE: "outline",
|
|
44
|
+
COGNITIVE: "outline",
|
|
45
|
+
SOCIAL: "outline",
|
|
46
|
+
LANGUAGE: "outline"
|
|
47
|
+
}, s = ({ status: s, className: c }) => {
|
|
48
|
+
let l = s.toUpperCase(), u = o[l] ?? "secondary", d = a.has(l);
|
|
49
|
+
return /* @__PURE__ */ r("span", {
|
|
50
|
+
className: e("inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium", i[u], c),
|
|
51
|
+
children: [d && /* @__PURE__ */ n("span", {
|
|
52
|
+
"aria-hidden": "true",
|
|
53
|
+
className: "size-1.5 rounded-full bg-current motion-safe:animate-vc-glow-pulse"
|
|
54
|
+
}), t(s)]
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { s as StatusBadge };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=StatusBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBadge.js","names":[],"sources":["../../../src/buildmyiq/components/StatusBadge.tsx"],"sourcesContent":["/**\n * Status Badge Component\n * @module buildmyiq/components\n *\n * A colored badge for Buildmyiq domain statuses (course status, enrollment\n * status, assessment status, submission status, cohort status, certificate\n * status, lesson-progress status) plus course levels and skill categories, with\n * semantic color mapping. Accepts the raw server enum value.\n */\n\nimport type { FC } from 'react';\nimport { cn } from '../utils/cn';\nimport { formatEnumLabel } from '../utils/formatters';\n\ninterface StatusBadgeProps {\n status: string;\n className?: string;\n}\n\ntype BadgeVariant = 'success' | 'warning' | 'destructive' | 'secondary' | 'info' | 'outline';\n\nconst variantStyles: Record<BadgeVariant, string> = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n destructive: 'bg-status-error-bg-subtle text-status-error-text',\n secondary: 'bg-bg-sunken text-text-secondary',\n info: 'bg-[var(--color-accent-soft)] text-text-primary',\n outline: 'border border-border-seam text-text-primary',\n};\n\n// Statuses that represent a \"live\" / in-progress condition get a subtle,\n// opacity-only glow pulse (reduced-motion safe via motion-safe:).\nconst liveStatuses = new Set(['IN_PROGRESS', 'ACTIVE', 'GRADING']);\n\nconst statusVariantMap: Record<string, BadgeVariant> = {\n // Authoring lifecycle (course + assessment)\n DRAFT: 'secondary',\n IN_REVIEW: 'warning',\n PUBLISHED: 'success',\n ARCHIVED: 'outline',\n\n // Enrollment status\n ENROLLED: 'info',\n IN_PROGRESS: 'info',\n COMPLETED: 'success',\n DROPPED: 'destructive',\n EXPIRED: 'outline',\n\n // Lesson-progress status\n NOT_STARTED: 'secondary',\n\n // Submission status\n SUBMITTED: 'info',\n GRADING: 'warning',\n GRADED: 'success',\n RETURNED: 'warning',\n\n // Cohort status\n SCHEDULED: 'secondary',\n ENROLLING: 'info',\n ACTIVE: 'success',\n CANCELLED: 'destructive',\n\n // Certificate status\n ISSUED: 'success',\n REVOKED: 'destructive',\n\n // Course level\n BEGINNER: 'success',\n INTERMEDIATE: 'info',\n ADVANCED: 'warning',\n EXPERT: 'destructive',\n\n // Skill category (neutral badges)\n TECHNICAL: 'outline',\n BUSINESS: 'outline',\n CREATIVE: 'outline',\n COGNITIVE: 'outline',\n SOCIAL: 'outline',\n LANGUAGE: 'outline',\n};\n\nexport const StatusBadge: FC<StatusBadgeProps> = ({ status, className }) => {\n const key = status.toUpperCase();\n const variant = statusVariantMap[key] ?? 'secondary';\n const isLive = liveStatuses.has(key);\n\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium',\n variantStyles[variant],\n className\n )}\n >\n {isLive && (\n <span\n aria-hidden=\"true\"\n className=\"size-1.5 rounded-full bg-current motion-safe:animate-vc-glow-pulse\"\n />\n )}\n {formatEnumLabel(status)}\n </span>\n );\n};\n"],"mappings":";;;;AAqBA,IAAM,IAA8C;CAClD,SAAS;CACT,SAAS;CACT,aAAa;CACb,WAAW;CACX,MAAM;CACN,SAAS;CACV,EAIK,IAAe,IAAI,IAAI;CAAC;CAAe;CAAU;CAAU,CAAC,EAE5D,IAAiD;CAErD,OAAO;CACP,WAAW;CACX,WAAW;CACX,UAAU;CAGV,UAAU;CACV,aAAa;CACb,WAAW;CACX,SAAS;CACT,SAAS;CAGT,aAAa;CAGb,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CAGV,WAAW;CACX,WAAW;CACX,QAAQ;CACR,WAAW;CAGX,QAAQ;CACR,SAAS;CAGT,UAAU;CACV,cAAc;CACd,UAAU;CACV,QAAQ;CAGR,WAAW;CACX,UAAU;CACV,UAAU;CACV,WAAW;CACX,QAAQ;CACR,UAAU;CACX,EAEY,KAAqC,EAAE,WAAQ,mBAAgB;CAC1E,IAAM,IAAM,EAAO,aAAa,EAC1B,IAAU,EAAiB,MAAQ,aACnC,IAAS,EAAa,IAAI,EAAI;AAEpC,QACE,kBAAC,QAAD;EACE,WAAW,EACT,mFACA,EAAc,IACd,EACD;YALH,CAOG,KACC,kBAAC,QAAD;GACE,eAAY;GACZ,WAAU;GACV,CAAA,EAEH,EAAgB,EAAO,CACnB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
//#region src/buildmyiq/components/Tabs.tsx
|
|
4
|
+
function r({ items: r, value: i, onChange: a, className: o }) {
|
|
5
|
+
return /* @__PURE__ */ t("div", {
|
|
6
|
+
role: "tablist",
|
|
7
|
+
className: e("flex flex-wrap items-center gap-1 border-b border-border-seam", o),
|
|
8
|
+
children: r.map((r) => {
|
|
9
|
+
let o = r.value === i;
|
|
10
|
+
return /* @__PURE__ */ n("button", {
|
|
11
|
+
type: "button",
|
|
12
|
+
role: "tab",
|
|
13
|
+
"aria-selected": o,
|
|
14
|
+
onClick: () => a(r.value),
|
|
15
|
+
className: e("relative -mb-px inline-flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors", o ? "border-accent text-text-primary" : "border-transparent text-text-secondary hover:text-text-primary"),
|
|
16
|
+
children: [
|
|
17
|
+
r.icon,
|
|
18
|
+
r.label,
|
|
19
|
+
typeof r.count == "number" && /* @__PURE__ */ t("span", {
|
|
20
|
+
className: e("rounded-full px-1.5 py-0.5 text-xs tabular-nums", o ? "bg-[var(--color-accent-soft)] text-text-primary" : "bg-bg-sunken text-text-secondary"),
|
|
21
|
+
children: r.count
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
}, r.value);
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { r as Tabs };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=Tabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tabs.js","names":[],"sources":["../../../src/buildmyiq/components/Tabs.tsx"],"sourcesContent":["/**\n * Tabs — a top-level segmented navigation control for multi-section pages\n * (Monitoring: Sensors/Measurements/Thresholds; Carbon: Ledger/Factors/Targets).\n * @module buildmyiq/components\n *\n * Visually heavier than {@link FilterTabs} (underline-style page tabs vs a pill\n * filter). Purely presentational — the parent owns the active-tab state.\n */\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\n\nexport interface TabItem<V extends string> {\n value: V;\n label: string;\n icon?: ReactNode;\n count?: number;\n}\n\ninterface TabsProps<V extends string> {\n items: TabItem<V>[];\n value: V;\n onChange: (value: V) => void;\n className?: string;\n}\n\nexport function Tabs<V extends string>({ items, value, onChange, className }: TabsProps<V>) {\n return (\n <div\n role=\"tablist\"\n className={cn('flex flex-wrap items-center gap-1 border-b border-border-seam', className)}\n >\n {items.map((item) => {\n const active = item.value === value;\n return (\n <button\n key={item.value}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={() => onChange(item.value)}\n className={cn(\n 'relative -mb-px inline-flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors',\n active\n ? 'border-accent text-text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n )}\n >\n {item.icon}\n {item.label}\n {typeof item.count === 'number' && (\n <span\n className={cn(\n 'rounded-full px-1.5 py-0.5 text-xs tabular-nums',\n active\n ? 'bg-[var(--color-accent-soft)] text-text-primary'\n : 'bg-bg-sunken text-text-secondary'\n )}\n >\n {item.count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;AA0BA,SAAgB,EAAuB,EAAE,UAAO,UAAO,aAAU,gBAA2B;AAC1F,QACE,kBAAC,OAAD;EACE,MAAK;EACL,WAAW,EAAG,iEAAiE,EAAU;YAExF,EAAM,KAAK,MAAS;GACnB,IAAM,IAAS,EAAK,UAAU;AAC9B,UACE,kBAAC,UAAD;IAEE,MAAK;IACL,MAAK;IACL,iBAAe;IACf,eAAe,EAAS,EAAK,MAAM;IACnC,WAAW,EACT,+GACA,IACI,oCACA,iEACL;cAXH;KAaG,EAAK;KACL,EAAK;KACL,OAAO,EAAK,SAAU,YACrB,kBAAC,QAAD;MACE,WAAW,EACT,mDACA,IACI,oDACA,mCACL;gBAEA,EAAK;MACD,CAAA;KAEF;MA1BF,EAAK,MA0BH;IAEX;EACE,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "./PageLayout.js";
|
|
2
|
+
import "./MetricsCard.js";
|
|
3
|
+
import "./StatusBadge.js";
|
|
4
|
+
import "./EmptyState.js";
|
|
5
|
+
import "./StateViews.js";
|
|
6
|
+
import "./DataTable.js";
|
|
7
|
+
import "./FilterTabs.js";
|
|
8
|
+
import "./Tabs.js";
|
|
9
|
+
import "./Pagination.js";
|
|
10
|
+
import "./MiniBar.js";
|
|
11
|
+
import "./EvidenceUpload.js";
|
|
12
|
+
import "./FileLink.js";
|
|
13
|
+
import "./BuiDiscussion.js";
|
|
14
|
+
import "./BuiTags.js";
|