@carlonicora/nextjs-jsonapi 3.24.2 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AssistantMessageInterface-1Kbz8RVs.d.ts → AssistantMessageInterface-CubxuBIJ.d.ts} +1 -1
- package/dist/{AssistantMessageInterface-CoxJyuFM.d.mts → AssistantMessageInterface-sHjI2zPN.d.mts} +1 -1
- package/dist/{BlockNoteEditor-35D4R7HJ.js → BlockNoteEditor-4EIKMNY3.js} +31 -21
- package/dist/BlockNoteEditor-4EIKMNY3.js.map +1 -0
- package/dist/{BlockNoteEditor-65HR6ULV.mjs → BlockNoteEditor-AXYVDDXM.mjs} +16 -6
- package/dist/{BlockNoteEditor-65HR6ULV.mjs.map → BlockNoteEditor-AXYVDDXM.mjs.map} +1 -1
- package/dist/{BlockNoteViewer-QDXDVYEM.js → BlockNoteViewer-7BFNTN4E.js} +4 -4
- package/dist/{BlockNoteViewer-QDXDVYEM.js.map → BlockNoteViewer-7BFNTN4E.js.map} +1 -1
- package/dist/{BlockNoteViewer-KDKGUIG5.mjs → BlockNoteViewer-KRTAFTUX.mjs} +3 -3
- package/dist/{HandbookSectionInterface-OePKPbqN.d.ts → HandbookSectionInterface-Ck1wZJ81.d.ts} +1 -1
- package/dist/{HandbookSectionInterface-BYsmnPEz.d.mts → HandbookSectionInterface-DgYH--Ik.d.mts} +1 -1
- package/dist/JsonApiRequest-TE5WD3WS.js +26 -0
- package/dist/{JsonApiRequest-G625QAH5.js.map → JsonApiRequest-TE5WD3WS.js.map} +1 -1
- package/dist/{JsonApiRequest-RRMNV7YE.mjs → JsonApiRequest-UHOWNI3O.mjs} +2 -2
- package/dist/analytics.modules-CfczU12P.d.ts +25 -0
- package/dist/analytics.modules-pzGwMASI.d.mts +25 -0
- package/dist/{auth.interface-BC1jp_ni.d.ts → auth.interface-BzI4onI_.d.ts} +1 -1
- package/dist/{auth.interface-dR2-6gPM.d.mts → auth.interface-Df4vXbNz.d.mts} +1 -1
- package/dist/billing/index.js +402 -402
- package/dist/billing/index.mjs +4 -4
- package/dist/{chunk-YXRVPEEJ.js → chunk-26MQIJNB.js} +1 -1
- package/dist/{chunk-YXRVPEEJ.js.map → chunk-26MQIJNB.js.map} +1 -1
- package/dist/{chunk-T76WLDE5.mjs → chunk-37DMFVLN.mjs} +1 -1
- package/dist/{chunk-3AR4KA5D.js → chunk-4Z45NYPR.js} +13 -13
- package/dist/{chunk-3AR4KA5D.js.map → chunk-4Z45NYPR.js.map} +1 -1
- package/dist/chunk-7B4E42R5.mjs +280 -0
- package/dist/chunk-7B4E42R5.mjs.map +1 -0
- package/dist/{chunk-2XCUAHQT.js → chunk-ABVTYQIJ.js} +1563 -1336
- package/dist/chunk-ABVTYQIJ.js.map +1 -0
- package/dist/{chunk-U7QVISQS.js → chunk-CAKOFJZD.js} +450 -40
- package/dist/chunk-CAKOFJZD.js.map +1 -0
- package/dist/{chunk-IBVKPLHT.mjs → chunk-E3I4MT53.mjs} +422 -12
- package/dist/chunk-E3I4MT53.mjs.map +1 -0
- package/dist/chunk-GHFMKH3M.js +280 -0
- package/dist/chunk-GHFMKH3M.js.map +1 -0
- package/dist/chunk-GKDHYB2R.js +110 -0
- package/dist/chunk-GKDHYB2R.js.map +1 -0
- package/dist/{chunk-ZJI5ZZW5.mjs → chunk-Q6J2ICKO.mjs} +3 -3
- package/dist/{chunk-4O7R2Z2O.mjs → chunk-QWZAGQWJ.mjs} +2175 -1948
- package/dist/chunk-QWZAGQWJ.mjs.map +1 -0
- package/dist/chunk-U65JPKO3.mjs +110 -0
- package/dist/chunk-U65JPKO3.mjs.map +1 -0
- package/dist/client/index.d.mts +6 -5
- package/dist/client/index.d.ts +6 -5
- package/dist/client/index.js +5 -5
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +4 -4
- package/dist/components/index.d.mts +33 -10
- package/dist/components/index.d.ts +33 -10
- package/dist/components/index.js +7 -5
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +6 -4
- package/dist/{config-B6UB7j69.d.ts → config-3N1LSrcC.d.ts} +1 -1
- package/dist/{config-Cd5xTEfZ.d.ts → config-AVtuvfM1.d.ts} +1 -1
- package/dist/{config-n-ZpPmOL.d.mts → config-BSKTYDoh.d.mts} +1 -1
- package/dist/{config-YyhFHxkU.d.mts → config-BmufyEj6.d.mts} +1 -1
- package/dist/contexts/index.d.mts +5 -4
- package/dist/contexts/index.d.ts +5 -4
- package/dist/contexts/index.js +5 -5
- package/dist/contexts/index.mjs +4 -4
- package/dist/core/index.d.mts +31 -14
- package/dist/core/index.d.ts +31 -14
- package/dist/core/index.js +9 -5
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +8 -4
- package/dist/features/analytics/index.css +30 -0
- package/dist/features/analytics/index.css.map +1 -0
- package/dist/features/analytics/index.d.mts +508 -0
- package/dist/features/analytics/index.d.ts +508 -0
- package/dist/features/analytics/index.js +1166 -0
- package/dist/features/analytics/index.js.map +1 -0
- package/dist/features/analytics/index.mjs +1166 -0
- package/dist/features/analytics/index.mjs.map +1 -0
- package/dist/features/analytics/server.d.mts +28 -0
- package/dist/features/analytics/server.d.ts +28 -0
- package/dist/features/analytics/server.js +41 -0
- package/dist/features/analytics/server.js.map +1 -0
- package/dist/features/analytics/server.mjs +41 -0
- package/dist/features/analytics/server.mjs.map +1 -0
- package/dist/features/help/index.d.mts +2 -2
- package/dist/features/help/index.d.ts +2 -2
- package/dist/features/help/index.js +43 -43
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +9 -9
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.d.mts +2 -2
- package/dist/features/tokenusage/index.d.ts +2 -2
- package/dist/features/tokenusage/index.js +141 -223
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +28 -110
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/dist/index.d.mts +11 -9
- package/dist/index.d.ts +11 -9
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -5
- package/dist/notification.interface-BNCvnVCW.d.ts +34 -0
- package/dist/notification.interface-P_pNfzZQ.d.mts +34 -0
- package/dist/{s3.service-Bqr-fQmi.d.ts → s3.service-CbUa6a-8.d.ts} +3 -2
- package/dist/{s3.service-Buiy1ncj.d.mts → s3.service-DAp8FbuG.d.mts} +3 -2
- package/dist/server/index.d.mts +5 -4
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.js +39 -279
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +20 -260
- package/dist/server/index.mjs.map +1 -1
- package/dist/{types-bsm8Fp55.d.ts → types-Cn153-mH.d.ts} +1 -1
- package/dist/{types-Bq_UA8Lg.d.mts → types-D0uLYV2P.d.mts} +1 -1
- package/dist/{usePageUrlGenerator-J_ECv-oQ.d.ts → usePageUrlGenerator-B-3FRKBD.d.ts} +1 -1
- package/dist/{usePageUrlGenerator-uKBjAKqC.d.mts → usePageUrlGenerator-CCJ_cdQy.d.mts} +1 -1
- package/dist/{useSocket-BcH7l7B8.d.mts → useSocket-B9gTbuCj.d.mts} +1 -1
- package/dist/{useSocket-DCZd_eJk.d.ts → useSocket-BHVIyigI.d.ts} +1 -1
- package/dist/{notification.interface-MnRt1Tjm.d.mts → user.interface-CSaGWFbg.d.mts} +1 -31
- package/dist/{notification.interface-CjzV_KII.d.ts → user.interface-CrmwJLYW.d.ts} +1 -31
- package/package.json +11 -1
- package/src/client/index.ts +2 -0
- package/src/components/containers/PageContainer.tsx +1 -1
- package/src/components/containers/RoundPageContainer.tsx +60 -16
- package/src/components/containers/RoundPageContainerTitle.tsx +75 -35
- package/src/components/editors/BlockNoteEditor.tsx +13 -1
- package/src/components/forms/EntityMultiSelector.tsx +4 -4
- package/src/components/forms/EntitySelector.tsx +4 -4
- package/src/components/forms/FormBlockNote.tsx +21 -1
- package/src/components/forms/FormCheckbox.tsx +6 -2
- package/src/components/forms/__tests__/useEditorDialog.test.ts +47 -0
- package/src/components/forms/useEditorDialog.ts +22 -0
- package/src/components/navigations/Breadcrumb.tsx +45 -16
- package/src/components/navigations/Header.tsx +16 -3
- package/src/components/navigations/MobileNavigationBar.tsx +12 -0
- package/src/components/navigations/__tests__/MobileNavigationBar.spec.tsx +17 -0
- package/src/components/tables/ContentListTable.tsx +52 -32
- package/src/core/index.ts +4 -0
- package/src/core/registry/ModuleRegistry.ts +7 -0
- package/src/features/__tests__/i18n-keys.contract.spec.ts +1 -0
- package/src/features/analytics/__tests__/server-entry.spec.tsx +64 -0
- package/src/features/analytics/analytics.modules.ts +30 -0
- package/src/features/analytics/components/AnalyticsAdminContainer.tsx +97 -0
- package/src/features/analytics/components/AnalyticsAdminFilterBar.tsx +120 -0
- package/src/features/analytics/components/AnalyticsAdminPageContainer.tsx +25 -0
- package/src/features/analytics/components/AnalyticsAdminTiles.tsx +191 -0
- package/src/features/analytics/components/AnalyticsBreakdownTable.tsx +105 -0
- package/src/features/analytics/components/AnalyticsJourneyContainer.tsx +246 -0
- package/src/features/analytics/components/AnalyticsJourneyPageContainer.tsx +14 -0
- package/src/features/analytics/components/AnalyticsSessionsTable.tsx +107 -0
- package/src/features/analytics/components/AnalyticsTimelineChart.tsx +175 -0
- package/src/features/analytics/components/AnalyticsTracker.tsx +112 -0
- package/src/features/analytics/components/__tests__/AnalyticsAdminTiles.spec.tsx +39 -0
- package/src/features/analytics/components/__tests__/AnalyticsBreakdownTable.spec.tsx +39 -0
- package/src/features/analytics/components/__tests__/AnalyticsJourneyContainer.spec.tsx +124 -0
- package/src/features/analytics/components/__tests__/AnalyticsPageContainers.spec.tsx +117 -0
- package/src/features/analytics/components/__tests__/AnalyticsSessionsTable.spec.tsx +121 -0
- package/src/features/analytics/components/__tests__/AnalyticsTimelineChart.spec.tsx +35 -0
- package/src/features/analytics/components/__tests__/AnalyticsTracker.spec.tsx +248 -0
- package/src/features/analytics/components/server/AnalyticsAdminPage.tsx +28 -0
- package/src/features/analytics/components/server/AnalyticsJourneyPage.tsx +25 -0
- package/src/features/analytics/contexts/AnalyticsAdminContext.tsx +273 -0
- package/src/features/analytics/contexts/__tests__/AnalyticsAdminContext.spec.tsx +140 -0
- package/src/features/analytics/data/AnalyticsAdminService.ts +91 -0
- package/src/features/analytics/data/AnalyticsService.ts +23 -0
- package/src/features/analytics/data/__tests__/AnalyticsAdminService.spec.ts +117 -0
- package/src/features/analytics/data/__tests__/AnalyticsService.spec.ts +56 -0
- package/src/features/analytics/data/__tests__/models.spec.ts +300 -0
- package/src/features/analytics/data/analytics-breakdown.interface.ts +9 -0
- package/src/features/analytics/data/analytics-breakdown.ts +65 -0
- package/src/features/analytics/data/analytics-event.interface.ts +7 -0
- package/src/features/analytics/data/analytics-event.ts +63 -0
- package/src/features/analytics/data/analytics-page-view.interface.ts +9 -0
- package/src/features/analytics/data/analytics-page-view.ts +57 -0
- package/src/features/analytics/data/analytics-session.interface.ts +23 -0
- package/src/features/analytics/data/analytics-session.ts +144 -0
- package/src/features/analytics/data/analytics-summary.interface.ts +11 -0
- package/src/features/analytics/data/analytics-summary.ts +79 -0
- package/src/features/analytics/data/analytics-timeline.interface.ts +10 -0
- package/src/features/analytics/data/analytics-timeline.ts +67 -0
- package/src/features/analytics/data/analytics.types.ts +97 -0
- package/src/features/analytics/data/index.ts +15 -0
- package/src/features/analytics/i18n-keys.ts +76 -0
- package/src/features/analytics/index.ts +31 -0
- package/src/features/analytics/lib/__tests__/utm.spec.ts +20 -0
- package/src/features/analytics/lib/__tests__/visitorCookie.spec.ts +63 -0
- package/src/features/analytics/lib/utm.ts +29 -0
- package/src/features/analytics/lib/visitorCookie.ts +32 -0
- package/src/features/analytics/server-entry.ts +8 -0
- package/src/features/assistant/components/parts/AssistantBlockNoteComposer.tsx +1 -1
- package/src/features/assistant/components/parts/AssistantComposer.tsx +1 -1
- package/src/features/assistant-message/components/MessageItem.tsx +2 -2
- package/src/features/assistant-message/components/parts/tabs/CitationsTab.tsx +23 -5
- package/src/features/auth/components/containers/AuthContainer.tsx +8 -1
- package/src/features/company/components/details/CompanyContent.tsx +3 -1
- package/src/features/company/components/details/CompanyDetails.tsx +9 -1
- package/src/features/company/components/details/TokenStatusIndicator.tsx +42 -13
- package/src/features/help/components/HelpHeader.tsx +7 -5
- package/src/features/tokenusage/components/TokenUsageAdminFilterBar.tsx +9 -6
- package/src/features/tokenusage/lib/__tests__/formatters.spec.ts +21 -0
- package/src/features/tokenusage/lib/formatters.ts +5 -1
- package/src/features/waitlist/components/lists/WaitlistList.tsx +68 -121
- package/src/features/waitlist/data/WaitlistService.ts +2 -2
- package/src/features/waitlist/data/index.ts +1 -0
- package/src/features/waitlist/data/waitlist.fields.ts +8 -0
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +69 -30
- package/src/shadcnui/ui/button.tsx +1 -1
- package/src/shadcnui/ui/checkbox.tsx +1 -1
- package/src/shadcnui/ui/command.tsx +4 -4
- package/src/shadcnui/ui/dialog.tsx +2 -0
- package/src/shadcnui/ui/input.tsx +1 -1
- package/src/shadcnui/ui/popover.tsx +9 -0
- package/src/shadcnui/ui/select.tsx +1 -1
- package/src/shadcnui/ui/sheet.tsx +8 -1
- package/src/shadcnui/ui/table.tsx +2 -2
- package/src/shadcnui/ui/textarea.tsx +1 -1
- package/dist/BlockNoteEditor-35D4R7HJ.js.map +0 -1
- package/dist/JsonApiRequest-G625QAH5.js +0 -26
- package/dist/chunk-2XCUAHQT.js.map +0 -1
- package/dist/chunk-4O7R2Z2O.mjs.map +0 -1
- package/dist/chunk-IBVKPLHT.mjs.map +0 -1
- package/dist/chunk-U7QVISQS.js.map +0 -1
- /package/dist/{BlockNoteViewer-KDKGUIG5.mjs.map → BlockNoteViewer-KRTAFTUX.mjs.map} +0 -0
- /package/dist/{JsonApiRequest-RRMNV7YE.mjs.map → JsonApiRequest-UHOWNI3O.mjs.map} +0 -0
- /package/dist/{chunk-T76WLDE5.mjs.map → chunk-37DMFVLN.mjs.map} +0 -0
- /package/dist/{chunk-ZJI5ZZW5.mjs.map → chunk-Q6J2ICKO.mjs.map} +0 -0
|
@@ -7,6 +7,7 @@ import { ReactNode } from "react";
|
|
|
7
7
|
import { AttributeElement } from "../../../../components";
|
|
8
8
|
import { SectionHeader, MicroLabel } from "../../../../components/typography";
|
|
9
9
|
import { FiscalDataDisplay } from "../../../../components/fiscal/FiscalDataDisplay";
|
|
10
|
+
import { cn } from "../../../../utils";
|
|
10
11
|
import { CompanyInterface } from "../../data";
|
|
11
12
|
|
|
12
13
|
type CompanyContentProps = {
|
|
@@ -34,7 +35,8 @@ export function CompanyContent({ company, actions }: CompanyContentProps) {
|
|
|
34
35
|
return (
|
|
35
36
|
<div className="flex flex-col gap-y-8">
|
|
36
37
|
{/* Title Row */}
|
|
37
|
-
|
|
38
|
+
{/* Without actions the row only repeats the name the page header shows; hide it on a phone. */}
|
|
39
|
+
<div className={cn("flex w-full items-center justify-between", !actions && "max-md:hidden")}>
|
|
38
40
|
<SectionHeader level={2}>{company.name}</SectionHeader>
|
|
39
41
|
{actions && <div className="flex items-center gap-x-2">{actions}</div>}
|
|
40
42
|
</div>
|
|
@@ -17,7 +17,15 @@ export function CompanyDetails() {
|
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
19
|
<div className="flex w-full flex-col gap-y-2">
|
|
20
|
-
|
|
20
|
+
{/* On a phone the page header already shows the type, the name and the
|
|
21
|
+
commands, so this inner copy is hidden there. */}
|
|
22
|
+
<ContentTitle
|
|
23
|
+
module={Modules.Company}
|
|
24
|
+
type={title.type}
|
|
25
|
+
element={title.element}
|
|
26
|
+
functions={title.functions}
|
|
27
|
+
className="max-md:hidden"
|
|
28
|
+
/>
|
|
21
29
|
<TokenStatusIndicator size="md" />
|
|
22
30
|
<CompanyContent company={company} />
|
|
23
31
|
</div>
|
|
@@ -12,6 +12,12 @@ interface TokenStatusIndicatorProps {
|
|
|
12
12
|
className?: string;
|
|
13
13
|
size?: "sm" | "md" | "lg";
|
|
14
14
|
showExtraPages?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Render the indicator as a link to this URL instead of a hover tooltip.
|
|
17
|
+
* For touch surfaces (the mobile header), where a hover tooltip never opens:
|
|
18
|
+
* the link carries a 40px tap area and leads to the usage page.
|
|
19
|
+
*/
|
|
20
|
+
href?: string;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
/**
|
|
@@ -36,7 +42,12 @@ const asWholeCredits = (value: number): string => Math.trunc(value).toLocaleStri
|
|
|
36
42
|
* - BatteryLow: 5-25% available
|
|
37
43
|
* - Battery (empty): <5% available
|
|
38
44
|
*/
|
|
39
|
-
export function TokenStatusIndicator({
|
|
45
|
+
export function TokenStatusIndicator({
|
|
46
|
+
className,
|
|
47
|
+
size = "md",
|
|
48
|
+
showExtraPages = true,
|
|
49
|
+
href,
|
|
50
|
+
}: TokenStatusIndicatorProps) {
|
|
40
51
|
const { company } = useCurrentUserContext();
|
|
41
52
|
const t = useTranslations();
|
|
42
53
|
|
|
@@ -131,6 +142,35 @@ export function TokenStatusIndicator({ className, size = "md", showExtraPages =
|
|
|
131
142
|
</div>
|
|
132
143
|
);
|
|
133
144
|
|
|
145
|
+
const badge = (
|
|
146
|
+
<>
|
|
147
|
+
{getBatteryIcon()}
|
|
148
|
+
<span className={cn(textSize, "text-muted-foreground font-medium leading-none")}>
|
|
149
|
+
{asWholeCredits(availableMonthlyCredits)}
|
|
150
|
+
</span>
|
|
151
|
+
{showExtraPages && availableExtraCredits > 0 && (
|
|
152
|
+
<span className="inline-flex items-center gap-0.5">
|
|
153
|
+
<PlusCircle className={cn(smallIconSize, "text-blue-500")} />
|
|
154
|
+
<span className={cn(textSize, "text-blue-500 font-medium leading-none")}>
|
|
155
|
+
{asWholeCredits(availableExtraCredits)}
|
|
156
|
+
</span>
|
|
157
|
+
</span>
|
|
158
|
+
)}
|
|
159
|
+
</>
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (href) {
|
|
163
|
+
return (
|
|
164
|
+
<Link
|
|
165
|
+
href={href}
|
|
166
|
+
className={cn("inline-flex min-h-10 min-w-10 items-center justify-center gap-1.5 px-1", className)}
|
|
167
|
+
aria-label={t("billing.tokens.status", { defaultValue: "Page Status" })}
|
|
168
|
+
>
|
|
169
|
+
{badge}
|
|
170
|
+
</Link>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
134
174
|
return (
|
|
135
175
|
<Tooltip>
|
|
136
176
|
{/* The layout classes live on the trigger itself, which renders the
|
|
@@ -142,18 +182,7 @@ export function TokenStatusIndicator({ className, size = "md", showExtraPages =
|
|
|
142
182
|
className={cn("inline-flex items-center gap-1.5 cursor-default", className)}
|
|
143
183
|
aria-label={t("billing.tokens.status", { defaultValue: "Page Status" })}
|
|
144
184
|
>
|
|
145
|
-
{
|
|
146
|
-
<span className={cn(textSize, "text-muted-foreground font-medium leading-none")}>
|
|
147
|
-
{asWholeCredits(availableMonthlyCredits)}
|
|
148
|
-
</span>
|
|
149
|
-
{showExtraPages && availableExtraCredits > 0 && (
|
|
150
|
-
<span className="inline-flex items-center gap-0.5">
|
|
151
|
-
<PlusCircle className={cn(smallIconSize, "text-blue-500")} />
|
|
152
|
-
<span className={cn(textSize, "text-blue-500 font-medium leading-none")}>
|
|
153
|
-
{asWholeCredits(availableExtraCredits)}
|
|
154
|
-
</span>
|
|
155
|
-
</span>
|
|
156
|
-
)}
|
|
185
|
+
{badge}
|
|
157
186
|
</TooltipTrigger>
|
|
158
187
|
<TooltipContent side="top" className="max-w-xs">
|
|
159
188
|
{tooltipContent}
|
|
@@ -16,14 +16,16 @@ export function HelpHeader() {
|
|
|
16
16
|
const appHref = brand?.appHref ?? "/";
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
|
-
<header className="border-border bg-background flex items-center justify-between border-b px-4 py-3">
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
<header className="border-border bg-background flex items-center justify-between border-b px-4 py-3 max-md:gap-2">
|
|
20
|
+
{/* Phone: the title shrinks and the "· Help & Documentation" suffix
|
|
21
|
+
truncates with an ellipsis instead of wrapping onto a second line. */}
|
|
22
|
+
<Link href="/help" className="flex items-center gap-2 max-md:min-w-0">
|
|
23
|
+
{logo ? <Image src={logo} alt={label} width={28} height={28} className="max-md:shrink-0" /> : null}
|
|
24
|
+
<span className="text-base font-semibold max-md:truncate">
|
|
23
25
|
{label} · {t("help.footerLink")}
|
|
24
26
|
</span>
|
|
25
27
|
</Link>
|
|
26
|
-
<div className="flex items-center gap-2">
|
|
28
|
+
<div className="flex items-center gap-2 max-md:shrink-0">
|
|
27
29
|
<HelpAskAi />
|
|
28
30
|
{/* Styled Links, not <Button render={<Link/>} nativeButton={false}>. Base UI's
|
|
29
31
|
Button primitive warns when rendered as a non-button, and nativeButton={false}
|
|
@@ -41,12 +41,15 @@ export function TokenUsageAdminFilterBar({ granularity, companyId, metric, compa
|
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<div className="flex flex-wrap items-center gap-2">
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
{/* The date button is a fixed 300px; on a phone it takes the full row instead. */}
|
|
45
|
+
<div className="max-md:w-full max-md:[&_#date]:w-full">
|
|
46
|
+
<DateRangeSelector
|
|
47
|
+
onDateChange={(range) => {
|
|
48
|
+
if (!range?.from || !range?.to) return;
|
|
49
|
+
onChange({ from: range.from.toISOString(), to: range.to.toISOString() });
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
50
53
|
|
|
51
54
|
<Segmented
|
|
52
55
|
ariaLabel={t("token_usage.admin.granularity.label")}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createUsageFormatters } from "../formatters";
|
|
3
|
+
|
|
4
|
+
describe("createUsageFormatters.compact", () => {
|
|
5
|
+
const { compact } = createUsageFormatters("en-US", "EUR");
|
|
6
|
+
|
|
7
|
+
it("keeps sub-unit axis ticks distinct", () => {
|
|
8
|
+
const ticks = [0, 0.05, 0.1, 0.15, 0.2, 0.25];
|
|
9
|
+
const labels = ticks.map(compact);
|
|
10
|
+
expect(labels).toEqual(["0", "0.05", "0.1", "0.15", "0.2", "0.25"]);
|
|
11
|
+
expect(new Set(labels).size).toBe(ticks.length);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("keeps thousands ticks distinct and exact", () => {
|
|
15
|
+
expect([0, 250, 500, 750, 1000, 1250].map(compact)).toEqual(["0", "250", "500", "750", "1K", "1.25K"]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("still abbreviates large values", () => {
|
|
19
|
+
expect(compact(1500000)).toBe("1.5M");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -50,7 +50,11 @@ export function createUsageFormatters(locale: string, currency: string): UsageFo
|
|
|
50
50
|
|
|
51
51
|
const dayFormat = new Intl.DateTimeFormat(locale, { day: "numeric", month: "short", timeZone: "UTC" });
|
|
52
52
|
const monthFormat = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric", timeZone: "UTC" });
|
|
53
|
-
|
|
53
|
+
// Axis ticks step by 1, 2, 2.5 or 5 times a power of ten, so a tick never needs
|
|
54
|
+
// more than three significant digits. A fixed one-decimal cap rounded a 0.05
|
|
55
|
+
// step into repeated labels (0.1, 0.1, 0.2, 0.2); significant digits keep every
|
|
56
|
+
// tick distinct at any scale (0.05, 0.15, 1.25K).
|
|
57
|
+
const compactFormat = new Intl.NumberFormat(locale, { notation: "compact", maximumSignificantDigits: 3 });
|
|
54
58
|
const collator = new Intl.Collator(locale);
|
|
55
59
|
|
|
56
60
|
return {
|
|
@@ -1,68 +1,52 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { RefreshCw, Users } from "lucide-react";
|
|
3
|
+
import { RefreshCw } from "lucide-react";
|
|
5
4
|
import { useTranslations } from "next-intl";
|
|
6
|
-
import {
|
|
7
|
-
import { errorToast } from "../../../../components";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
Select,
|
|
12
|
-
SelectContent,
|
|
13
|
-
SelectItem,
|
|
14
|
-
SelectTrigger,
|
|
15
|
-
SelectValue,
|
|
16
|
-
Table,
|
|
17
|
-
TableBody,
|
|
18
|
-
TableCell,
|
|
19
|
-
TableHead,
|
|
20
|
-
TableHeader,
|
|
21
|
-
TableRow,
|
|
22
|
-
} from "../../../../shadcnui";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { ContentListTable, errorToast } from "../../../../components";
|
|
7
|
+
import { Modules } from "../../../../core";
|
|
8
|
+
import { DataListRetriever, useDataListRetriever } from "../../../../hooks";
|
|
9
|
+
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../../shadcnui";
|
|
23
10
|
import { showToast } from "../../../../utils/toast";
|
|
11
|
+
import { WaitlistFields } from "../../data/waitlist.fields";
|
|
24
12
|
import { WaitlistInterface } from "../../data/WaitlistInterface";
|
|
25
13
|
import { WaitlistService } from "../../data/WaitlistService";
|
|
26
|
-
import { useWaitlistTableStructure } from "../../hooks/useWaitlistTableStructure";
|
|
27
14
|
|
|
28
|
-
|
|
15
|
+
type WaitlistListProps = {
|
|
16
|
+
/**
|
|
17
|
+
* Pass this whenever the list is the page's own content inside a
|
|
18
|
+
* `RoundPageContainer fullWidth`. Without it ContentListTable wraps itself in
|
|
19
|
+
* `rounded-md border`, drawing a second bordered card inside the page's
|
|
20
|
+
* rounded shell.
|
|
21
|
+
*/
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function WaitlistList({ fullWidth }: WaitlistListProps = {}) {
|
|
29
26
|
const t = useTranslations();
|
|
30
|
-
const [entries, setEntries] = useState<WaitlistInterface[]>([]);
|
|
31
|
-
const [total, setTotal] = useState(0);
|
|
32
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
33
27
|
const [statusFilter, setStatusFilter] = useState<string>("all");
|
|
34
28
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
fetchAll: true,
|
|
41
|
-
});
|
|
42
|
-
setEntries(result);
|
|
43
|
-
setTotal(result.length);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
errorToast({ error });
|
|
46
|
-
} finally {
|
|
47
|
-
setIsLoading(false);
|
|
48
|
-
}
|
|
49
|
-
}, [statusFilter]);
|
|
50
|
-
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
loadEntries();
|
|
53
|
-
}, [loadEntries]);
|
|
29
|
+
const data: DataListRetriever<WaitlistInterface> = useDataListRetriever({
|
|
30
|
+
retriever: (params) => WaitlistService.findMany(params),
|
|
31
|
+
retrieverParams: {},
|
|
32
|
+
module: Modules.Waitlist,
|
|
33
|
+
});
|
|
54
34
|
|
|
55
35
|
const handleInvite = async (entry: WaitlistInterface) => {
|
|
56
36
|
try {
|
|
57
37
|
await WaitlistService.invite(entry.id);
|
|
58
38
|
showToast(t("waitlist.admin.invite_sent", { email: entry.email }));
|
|
59
|
-
|
|
39
|
+
void data.refresh();
|
|
60
40
|
} catch (error) {
|
|
61
41
|
errorToast({ error });
|
|
62
42
|
}
|
|
63
43
|
};
|
|
64
44
|
|
|
65
|
-
const
|
|
45
|
+
const handleStatusChange = (value: string | null) => {
|
|
46
|
+
const status = value ?? "all";
|
|
47
|
+
setStatusFilter(status);
|
|
48
|
+
data.addAdditionalParameter("status", status === "all" ? null : status);
|
|
49
|
+
};
|
|
66
50
|
|
|
67
51
|
const statusItems = useMemo(
|
|
68
52
|
() => ({
|
|
@@ -75,83 +59,46 @@ export function WaitlistList() {
|
|
|
75
59
|
[t],
|
|
76
60
|
);
|
|
77
61
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
<div className="flex items-center gap-4">
|
|
95
|
-
{/* Status Filter */}
|
|
96
|
-
<Select items={statusItems} value={statusFilter} onValueChange={(value) => setStatusFilter(value ?? "all")}>
|
|
97
|
-
<SelectTrigger className="w-40">
|
|
98
|
-
<SelectValue placeholder={t("waitlist.admin.filter_placeholder")} />
|
|
99
|
-
</SelectTrigger>
|
|
100
|
-
<SelectContent>
|
|
101
|
-
<SelectItem value="all">{t("waitlist.admin.all_statuses")}</SelectItem>
|
|
102
|
-
<SelectItem value="pending">{t("waitlist.admin.status.pending")}</SelectItem>
|
|
103
|
-
<SelectItem value="confirmed">{t("waitlist.admin.status.confirmed")}</SelectItem>
|
|
104
|
-
<SelectItem value="invited">{t("waitlist.admin.status.invited")}</SelectItem>
|
|
105
|
-
<SelectItem value="registered">{t("waitlist.admin.status.registered")}</SelectItem>
|
|
106
|
-
</SelectContent>
|
|
107
|
-
</Select>
|
|
62
|
+
const filters = (
|
|
63
|
+
<div className="flex items-center gap-4">
|
|
64
|
+
<Select items={statusItems} value={statusFilter} onValueChange={handleStatusChange}>
|
|
65
|
+
<SelectTrigger className="w-40">
|
|
66
|
+
<SelectValue placeholder={t("waitlist.admin.filter_placeholder")} />
|
|
67
|
+
</SelectTrigger>
|
|
68
|
+
<SelectContent>
|
|
69
|
+
<SelectItem value="all">{t("waitlist.admin.all_statuses")}</SelectItem>
|
|
70
|
+
<SelectItem value="pending">{t("waitlist.admin.status.pending")}</SelectItem>
|
|
71
|
+
<SelectItem value="confirmed">{t("waitlist.admin.status.confirmed")}</SelectItem>
|
|
72
|
+
<SelectItem value="invited">{t("waitlist.admin.status.invited")}</SelectItem>
|
|
73
|
+
<SelectItem value="registered">{t("waitlist.admin.status.registered")}</SelectItem>
|
|
74
|
+
</SelectContent>
|
|
75
|
+
</Select>
|
|
108
76
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
</Button>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
{/* Table */}
|
|
117
|
-
<div className="rounded-md border">
|
|
118
|
-
<Table>
|
|
119
|
-
<TableHeader>
|
|
120
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
121
|
-
<TableRow key={headerGroup.id}>
|
|
122
|
-
{headerGroup.headers.map((header) => (
|
|
123
|
-
<TableHead key={header.id}>
|
|
124
|
-
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
|
125
|
-
</TableHead>
|
|
126
|
-
))}
|
|
127
|
-
</TableRow>
|
|
128
|
-
))}
|
|
129
|
-
</TableHeader>
|
|
130
|
-
<TableBody>
|
|
131
|
-
{isLoading ? (
|
|
132
|
-
<TableRow>
|
|
133
|
-
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
134
|
-
{t("waitlist.admin.loading")}
|
|
135
|
-
</TableCell>
|
|
136
|
-
</TableRow>
|
|
137
|
-
) : entries.length === 0 ? (
|
|
138
|
-
<TableRow>
|
|
139
|
-
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
140
|
-
{t("waitlist.admin.empty")}
|
|
141
|
-
</TableCell>
|
|
142
|
-
</TableRow>
|
|
143
|
-
) : (
|
|
144
|
-
table.getRowModel().rows.map((row) => (
|
|
145
|
-
<TableRow key={row.id}>
|
|
146
|
-
{row.getVisibleCells().map((cell) => (
|
|
147
|
-
<TableCell key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>
|
|
148
|
-
))}
|
|
149
|
-
</TableRow>
|
|
150
|
-
))
|
|
151
|
-
)}
|
|
152
|
-
</TableBody>
|
|
153
|
-
</Table>
|
|
154
|
-
</div>
|
|
77
|
+
<Button variant="outline" size="icon" onClick={() => void data.refresh()} disabled={!data.isLoaded}>
|
|
78
|
+
<RefreshCw className={`h-4 w-4 ${!data.isLoaded ? "animate-spin" : ""}`} />
|
|
79
|
+
</Button>
|
|
155
80
|
</div>
|
|
156
81
|
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ContentListTable
|
|
85
|
+
data={data}
|
|
86
|
+
fields={[
|
|
87
|
+
WaitlistFields.email,
|
|
88
|
+
WaitlistFields.status,
|
|
89
|
+
WaitlistFields.createdAt,
|
|
90
|
+
WaitlistFields.questionnaire,
|
|
91
|
+
WaitlistFields.actions,
|
|
92
|
+
]}
|
|
93
|
+
tableGeneratorType={Modules.Waitlist}
|
|
94
|
+
title={t("waitlist.admin.title")}
|
|
95
|
+
filters={filters}
|
|
96
|
+
/* The waitlist endpoint takes no search term, so rendering the box would
|
|
97
|
+
give the page a control that silently does nothing. */
|
|
98
|
+
allowSearch={false}
|
|
99
|
+
context={{ onInvite: handleInvite }}
|
|
100
|
+
fullWidth={fullWidth}
|
|
101
|
+
emptyState={t("waitlist.admin.empty")}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
157
104
|
}
|
|
@@ -42,7 +42,7 @@ export class WaitlistService extends AbstractService {
|
|
|
42
42
|
search?: string;
|
|
43
43
|
fetchAll?: boolean;
|
|
44
44
|
next?: NextRef;
|
|
45
|
-
|
|
45
|
+
previous?: PreviousRef;
|
|
46
46
|
}): Promise<WaitlistInterface[]> {
|
|
47
47
|
const endpoint = new EndpointCreator({ endpoint: Modules.Waitlist });
|
|
48
48
|
|
|
@@ -55,7 +55,7 @@ export class WaitlistService extends AbstractService {
|
|
|
55
55
|
method: HttpMethod.GET,
|
|
56
56
|
endpoint: endpoint.generate(),
|
|
57
57
|
next: params?.next,
|
|
58
|
-
previous: params?.
|
|
58
|
+
previous: params?.previous,
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
import { ColumnDef } from "@tanstack/react-table";
|
|
4
4
|
import { Send } from "lucide-react";
|
|
5
5
|
import { useTranslations } from "next-intl";
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
import { TableContent, UseTableStructureHook } from "../../../hooks";
|
|
6
8
|
import { Badge, Button } from "../../../shadcnui";
|
|
9
|
+
import { WaitlistFields } from "../data/waitlist.fields";
|
|
7
10
|
import { WaitlistInterface, WaitlistStatus } from "../data/WaitlistInterface";
|
|
8
11
|
|
|
9
|
-
interface UseWaitlistTableStructureProps {
|
|
10
|
-
onInvite: (entry: WaitlistInterface) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* Parse questionnaire JSON string safely
|
|
15
14
|
*/
|
|
@@ -22,10 +21,25 @@ function parseQuestionnaire(questionnaire: string | undefined): Record<string, a
|
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Columns for the administrative waitlist. The invite action needs the list's
|
|
26
|
+
* own handler, so WaitlistList passes it through ContentListTable's `context`
|
|
27
|
+
* as `onInvite`.
|
|
28
|
+
*/
|
|
29
|
+
export const useWaitlistTableStructure: UseTableStructureHook<WaitlistInterface, WaitlistFields> = (params) => {
|
|
28
30
|
const t = useTranslations();
|
|
31
|
+
const onInvite: ((entry: WaitlistInterface) => void) | undefined = params.context?.onInvite;
|
|
32
|
+
|
|
33
|
+
const tableData = useMemo(() => {
|
|
34
|
+
return params.data.map((entry: WaitlistInterface) => {
|
|
35
|
+
const row: TableContent<WaitlistInterface> = { jsonApiData: entry };
|
|
36
|
+
row[WaitlistFields.waitlistId] = entry.id;
|
|
37
|
+
params.fields.forEach((field) => {
|
|
38
|
+
row[field] = (entry as any)[field as keyof WaitlistInterface];
|
|
39
|
+
});
|
|
40
|
+
return row;
|
|
41
|
+
});
|
|
42
|
+
}, [params.data, params.fields]);
|
|
29
43
|
|
|
30
44
|
const getStatusBadge = (status: WaitlistStatus) => {
|
|
31
45
|
const variants: Record<WaitlistStatus, { variant: "default" | "secondary" | "outline" | "destructive" }> = {
|
|
@@ -39,31 +53,44 @@ export function useWaitlistTableStructure({
|
|
|
39
53
|
return <Badge variant={config.variant}>{t(`waitlist.admin.status.${status}`)}</Badge>;
|
|
40
54
|
};
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
{
|
|
56
|
+
const fieldColumnMap: Partial<Record<WaitlistFields, () => any>> = {
|
|
57
|
+
[WaitlistFields.email]: () => ({
|
|
58
|
+
id: "email",
|
|
44
59
|
accessorKey: "email",
|
|
45
60
|
header: t("waitlist.admin.columns.email"),
|
|
46
|
-
cell: ({ row }
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
cell: ({ row }: { row: TableContent<WaitlistInterface> }) => (
|
|
62
|
+
<span className="font-medium">{row.original.jsonApiData.email}</span>
|
|
63
|
+
),
|
|
64
|
+
enableSorting: false,
|
|
65
|
+
enableHiding: false,
|
|
66
|
+
}),
|
|
67
|
+
[WaitlistFields.status]: () => ({
|
|
68
|
+
id: "status",
|
|
49
69
|
accessorKey: "status",
|
|
50
70
|
header: t("waitlist.admin.columns.status"),
|
|
51
|
-
cell: ({ row }) => getStatusBadge(row.original.status),
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
cell: ({ row }: { row: TableContent<WaitlistInterface> }) => getStatusBadge(row.original.jsonApiData.status),
|
|
72
|
+
enableSorting: false,
|
|
73
|
+
enableHiding: false,
|
|
74
|
+
}),
|
|
75
|
+
[WaitlistFields.createdAt]: () => ({
|
|
76
|
+
id: "createdAt",
|
|
54
77
|
accessorKey: "createdAt",
|
|
55
78
|
header: t("waitlist.admin.columns.submitted"),
|
|
56
|
-
cell: ({ row }) => {
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
cell: ({ row }: { row: TableContent<WaitlistInterface> }) => {
|
|
80
|
+
const entry: WaitlistInterface = row.original.jsonApiData;
|
|
81
|
+
if (!entry.createdAt) return "-";
|
|
82
|
+
return new Date(entry.createdAt).toLocaleDateString();
|
|
59
83
|
},
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
enableSorting: false,
|
|
85
|
+
enableHiding: false,
|
|
86
|
+
}),
|
|
87
|
+
[WaitlistFields.questionnaire]: () => ({
|
|
88
|
+
id: "questionnaire",
|
|
62
89
|
accessorKey: "questionnaire",
|
|
63
90
|
header: t("waitlist.admin.columns.questionnaire"),
|
|
64
|
-
cell: ({ row }) => {
|
|
91
|
+
cell: ({ row }: { row: TableContent<WaitlistInterface> }) => {
|
|
65
92
|
// IMPORTANT: Parse JSON string from backend
|
|
66
|
-
const questionnaire = parseQuestionnaire(row.original.questionnaire);
|
|
93
|
+
const questionnaire = parseQuestionnaire(row.original.jsonApiData.questionnaire);
|
|
67
94
|
if (!questionnaire || Object.keys(questionnaire).length === 0) {
|
|
68
95
|
return <span className="text-muted-foreground">-</span>;
|
|
69
96
|
}
|
|
@@ -84,16 +111,18 @@ export function useWaitlistTableStructure({
|
|
|
84
111
|
</details>
|
|
85
112
|
);
|
|
86
113
|
},
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
enableSorting: false,
|
|
115
|
+
enableHiding: false,
|
|
116
|
+
}),
|
|
117
|
+
[WaitlistFields.actions]: () => ({
|
|
89
118
|
id: "actions",
|
|
90
119
|
header: t("waitlist.admin.columns.actions"),
|
|
91
|
-
cell: ({ row }) => {
|
|
92
|
-
const entry = row.original;
|
|
120
|
+
cell: ({ row }: { row: TableContent<WaitlistInterface> }) => {
|
|
121
|
+
const entry: WaitlistInterface = row.original.jsonApiData;
|
|
93
122
|
|
|
94
123
|
if (entry.status === "confirmed") {
|
|
95
124
|
return (
|
|
96
|
-
<Button size="sm" variant="outline" onClick={() => onInvite(entry)}>
|
|
125
|
+
<Button size="sm" variant="outline" onClick={() => onInvite?.(entry)}>
|
|
97
126
|
<Send className="me-2 h-4 w-4" />
|
|
98
127
|
{t("waitlist.admin.actions.invite")}
|
|
99
128
|
</Button>
|
|
@@ -116,6 +145,16 @@ export function useWaitlistTableStructure({
|
|
|
116
145
|
<span className="text-muted-foreground text-sm">{t("waitlist.admin.actions.awaiting_confirmation")}</span>
|
|
117
146
|
);
|
|
118
147
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
148
|
+
enableSorting: false,
|
|
149
|
+
enableHiding: false,
|
|
150
|
+
}),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const columns = useMemo(() => {
|
|
154
|
+
return params.fields.map((field) => fieldColumnMap[field]?.()).filter((col) => col !== undefined) as ColumnDef<
|
|
155
|
+
TableContent<WaitlistInterface>
|
|
156
|
+
>[];
|
|
157
|
+
}, [params.fields, fieldColumnMap, t]);
|
|
158
|
+
|
|
159
|
+
return useMemo(() => ({ data: tableData, columns: columns }), [tableData, columns]);
|
|
160
|
+
};
|
|
@@ -21,7 +21,7 @@ const buttonVariants = cva(
|
|
|
21
21
|
},
|
|
22
22
|
size: {
|
|
23
23
|
default:
|
|
24
|
-
"h-7 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
24
|
+
"h-7 max-md:h-9 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
25
25
|
xs: "h-5 gap-1 rounded-sm px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-2.5",
|
|
26
26
|
sm: "h-6 gap-1 px-2 text-xs/relaxed has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
27
27
|
lg: "h-8 gap-1 px-2.5 text-xs/relaxed has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-4",
|
|
@@ -10,7 +10,7 @@ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
|
|
10
10
|
<CheckboxPrimitive.Root
|
|
11
11
|
data-slot="checkbox"
|
|
12
12
|
className={cn(
|
|
13
|
-
"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-shadow group-has-disabled/field:opacity-50 focus-visible:ring-[2px] aria-invalid:ring-[2px] peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 cursor-pointer data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
13
|
+
"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-shadow group-has-disabled/field:opacity-50 focus-visible:ring-[2px] aria-invalid:ring-[2px] peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 max-md:after:-inset-y-3 cursor-pointer data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
14
14
|
className,
|
|
15
15
|
)}
|
|
16
16
|
{...props}
|