From 7e8c13959043d1af5567b3cd65c3eac8dbc8cf03 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Thu, 20 Aug 2026 18:33:43 +0200 Subject: [PATCH 01/11] test(e2e): stabilize lazy widget login --- e2e/lazy-widgets.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/e2e/lazy-widgets.spec.ts b/e2e/lazy-widgets.spec.ts index 4ae7d4f72..570e260aa 100644 --- a/e2e/lazy-widgets.spec.ts +++ b/e2e/lazy-widgets.spec.ts @@ -78,11 +78,15 @@ describe("lazy widget application graph", () => { await page.goto(`${baseUrl}/auth/login`); await page.getByLabel("Username").fill(adminCredentials.username); await page.locator("#password").fill(adminCredentials.password); - const signedIn = page.waitForURL(baseUrl, { waitUntil: "commit", timeout: 60_000 }); + const signedIn = page.waitForURL((url) => url.origin === baseUrl && url.pathname === "/", { + waitUntil: "commit", + timeout: 60_000, + }); await page.locator("css=button[type='submit']").click(); await signedIn; - await expect(page.locator("[data-homarr-dev-benchmark-board]")).toBeVisible({ timeout: 30_000 }); + const visibleBoard = page.locator("[data-homarr-dev-benchmark-board]").filter({ visible: true }).first(); + await expect(visibleBoard).toBeVisible({ timeout: 30_000 }); const clockWidget = page.locator( `[data-id="${itemId}"] .clock-widget-container, [data-grid-item-id="${itemId}"] .clock-widget-container`, ); From bd4f6a92c6cc35c2d35ca3562daf850c4a9d402a Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Thu, 20 Aug 2026 18:48:33 +0200 Subject: [PATCH 02/11] fix(onboarding): keep the account submit disabled until the step advances (#6641) --- packages/onboarding/src/account.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/onboarding/src/account.tsx b/packages/onboarding/src/account.tsx index fe31c6eaa..354711c17 100644 --- a/packages/onboarding/src/account.tsx +++ b/packages/onboarding/src/account.tsx @@ -104,7 +104,7 @@ const CredentialsSetup = () => { return ( -
void submitAsync(values))}> + submitAsync(values))}> { {submitError} ) : null} - From f3014fe2712ed11d35d5398bd2fe6727133e20c4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Aug 2026 03:19:18 +0200 Subject: [PATCH 03/11] refactor(board): modernize item and application select modals with SelectableCard (#6642) Modernize board item, application, and integration selection flows with shared Mantine components, batch board actions, improved accessibility, and hardened persistence. --- apps/docs/docs/management/boards/index.mdx | 17 +- apps/docs/docs/widgets/today/index.mdx | 34 -- apps/docs/docs/widgets/today/index.ts | 43 -- apps/docs/static/api/open-api-schema.json | 1 - .../app/[locale]/boards/(content)/_client.tsx | 127 ++--- .../boards/(content)/_edit-actions.tsx | 93 +++- .../components/board/audio/board-sounds.ts | 123 +++++ .../board/items/item-content.module.css | 19 + .../src/components/board/items/item-menu.tsx | 7 +- .../board/items/item-select-data.spec.ts | 277 +++++++--- .../board/items/item-select-modal.module.css | 20 - .../board/items/item-select-modal.spec.tsx | 211 ++++++++ .../board/items/item-select-modal.tsx | 483 +++++++++--------- .../board/items/lazy-widget-edit-modal.tsx | 41 +- .../board/sections/grid/fixed-grid-item.tsx | 14 + .../board/sections/grid/grid-editor.css | 144 +++--- .../board/sections/grid/grid-editor.tsx | 48 +- .../grid/test/fixed-grid-item.spec.ts | 9 + .../selection/board-selection-context.tsx | 270 ++++++++++ .../selection/board-selection-toolbar.tsx | 255 +++++++++ .../board/selection/board-selection.spec.tsx | 251 +++++++++ .../src/components/create/setup-analytics.ts | 2 +- .../create/universal-create-actions.spec.ts | 77 --- .../create/universal-create-actions.ts | 120 ----- .../create/universal-create-modal.module.css | 21 - .../universal-create-modal.module.d.css.ts | 5 - .../create/universal-create-modal.tsx | 162 ------ .../create/universal-create-spotlight.tsx | 35 -- .../create/universal-create-trigger.tsx | 49 -- .../integration/integration-select-modal.tsx | 52 +- apps/nextjs/src/components/layout/header.tsx | 5 - .../src/docs/homarr-docs-sitemap.ts | 1 - .../definitions/src/docs/widget-doc-slugs.ts | 1 - packages/definitions/src/widget.ts | 2 - .../src/apps/app-select-modal.spec.tsx | 438 ++++++++++++++++ .../src/apps/app-select-modal.tsx | 266 ++++++---- packages/modals/src/index.tsx | 10 +- packages/modals/src/reducer.tsx | 11 + packages/modals/src/type.ts | 10 +- packages/translation/src/lang/en.json | 129 +---- packages/ui/src/components/index.tsx | 7 + .../src/components/selectable-card/index.ts | 7 + .../selectable-card.module.css | 79 +++ .../selectable-card.module.d.css.ts | 7 + .../selectable-card/selectable-card.spec.tsx | 370 ++++++++++++++ .../selectable-card/selectable-card.tsx | 177 +++++++ .../integration-select-grid.spec.tsx | 279 ++++++++++ .../integration-select-grid.tsx | 294 ++++++++--- packages/widgets/package.json | 4 +- .../widgets/src/_inputs/widget-app-input.tsx | 14 +- packages/widgets/src/app/component.tsx | 3 +- packages/widgets/src/catalog.ts | 2 - packages/widgets/src/index.tsx | 2 - packages/widgets/src/manifest.ts | 2 - .../widgets/src/modals/widget-edit-modal.tsx | 14 +- packages/widgets/src/today/component.tsx | 223 -------- packages/widgets/src/today/date-stats.spec.ts | 47 -- packages/widgets/src/today/date-stats.ts | 172 ------- packages/widgets/src/today/index.ts | 24 - .../widgets/src/widget-integration-select.tsx | 20 +- 60 files changed, 3791 insertions(+), 1839 deletions(-) delete mode 100644 apps/docs/docs/widgets/today/index.mdx delete mode 100644 apps/docs/docs/widgets/today/index.ts create mode 100644 apps/nextjs/src/components/board/audio/board-sounds.ts delete mode 100644 apps/nextjs/src/components/board/items/item-select-modal.module.css create mode 100644 apps/nextjs/src/components/board/items/item-select-modal.spec.tsx create mode 100644 apps/nextjs/src/components/board/selection/board-selection-context.tsx create mode 100644 apps/nextjs/src/components/board/selection/board-selection-toolbar.tsx create mode 100644 apps/nextjs/src/components/board/selection/board-selection.spec.tsx delete mode 100644 apps/nextjs/src/components/create/universal-create-actions.spec.ts delete mode 100644 apps/nextjs/src/components/create/universal-create-actions.ts delete mode 100644 apps/nextjs/src/components/create/universal-create-modal.module.css delete mode 100644 apps/nextjs/src/components/create/universal-create-modal.module.d.css.ts delete mode 100644 apps/nextjs/src/components/create/universal-create-modal.tsx delete mode 100644 apps/nextjs/src/components/create/universal-create-spotlight.tsx delete mode 100644 apps/nextjs/src/components/create/universal-create-trigger.tsx create mode 100644 packages/modals-collection/src/apps/app-select-modal.spec.tsx create mode 100644 packages/ui/src/components/selectable-card/index.ts create mode 100644 packages/ui/src/components/selectable-card/selectable-card.module.css create mode 100644 packages/ui/src/components/selectable-card/selectable-card.module.d.css.ts create mode 100644 packages/ui/src/components/selectable-card/selectable-card.spec.tsx create mode 100644 packages/ui/src/components/selectable-card/selectable-card.tsx create mode 100644 packages/ui/src/integration-select-grid/integration-select-grid.spec.tsx delete mode 100644 packages/widgets/src/today/component.tsx delete mode 100644 packages/widgets/src/today/date-stats.spec.ts delete mode 100644 packages/widgets/src/today/date-stats.ts delete mode 100644 packages/widgets/src/today/index.ts diff --git a/apps/docs/docs/management/boards/index.mdx b/apps/docs/docs/management/boards/index.mdx index 6f1710e3b..5c1407572 100644 --- a/apps/docs/docs/management/boards/index.mdx +++ b/apps/docs/docs/management/boards/index.mdx @@ -204,10 +204,13 @@ changes, Homarr asks whether to discard them. If the editor cannot load, the boa style={{ maxWidth: "100%" }} /> -While edit mode is active, **Add board content** opens the same Universal Create surface as the global header action and -Spotlight. It lists the available actions directly and can add a widget, app, integration, Container, board, Workshop item, or Custom Widget when the -current user and context allow it. Widget setup offers the main canvas, enabled rails, and existing Containers as -destinations before the widget is created, so the common flow does not require a second move step. +While edit mode is active, **Add board content** opens a menu for adding a widget, app, integration, or Container. The +available actions respect the current user's permissions. Widget setup offers the main canvas, enabled rails, and existing +Containers as destinations before the widget is created, so the common flow does not require a second move step. + +Hold `Ctrl` on Windows or Linux, or `⌘` on macOS, while clicking board items to select several at once. The selection +toolbar can move the complete selection to another canvas, rail, or Container, delete it with an undo option, or clear the +selection. A move is applied only when every selected item fits in the destination. The widget editor also lists its selected integrations. Users with full access can choose **Edit connection** to test, diagnose, and repair an integration without leaving the board. A successful repair refreshes the affected widget; the @@ -227,11 +230,7 @@ During a pointer or touch interaction, Escape cancels the move or resize and res Invalid, outside, or otherwise forbidden drops are cancelled in the same way. The **Move / resize item** action in the edit menu remains available for precise numeric input. -A focused board tile in keyboard editing mode +A focused board tile in keyboard editing mode #### Preview diff --git a/apps/docs/docs/widgets/today/index.mdx b/apps/docs/docs/widgets/today/index.mdx deleted file mode 100644 index 000861d20..000000000 --- a/apps/docs/docs/widgets/today/index.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "Today" -description: "Displays the current date and progress through the calendar." -hide_title: true ---- - -import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions"; - -import { AddingWidget } from "@site/src/components/widgets/adding"; -import { WidgetConfig } from "@site/src/components/widgets/configuration"; -import { WidgetHeader } from "@site/src/components/widgets/header"; -import { todayWidget } from "."; - - - -The Today widget presents the current local date without duplicating a live clock. It progressively adds the week -number, day of year, quarter, remaining days, and calendar progress as more space becomes available. - -The advanced view shows week, month, and year progress together with a twelve-month overview. Week numbers can follow -your locale or the ISO 8601 convention. “Days after today” excludes the current day to keep the remaining-day count -unambiguous. - -The widget runs entirely in your browser and does not send date or location data to an external service. It follows -the timezone and locale of the device viewing the board, so viewers in different regions can see their own local date. - - - -### Adding the widget - - - -### Configuration - - diff --git a/apps/docs/docs/widgets/today/index.ts b/apps/docs/docs/widgets/today/index.ts deleted file mode 100644 index 8922d4b6e..000000000 --- a/apps/docs/docs/widgets/today/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { WidgetDefinition } from "@site/src/types"; -import { IconCalendarWeek } from "@tabler/icons-react"; - -export const todayWidget: WidgetDefinition = { - icon: IconCalendarWeek, - name: "Today", - description: "Displays the current date and progress through the calendar.", - path: "../../widgets/today", - configuration: { - items: [ - { - name: "Week numbering", - description: "Use locale-specific week numbering or ISO 8601 week numbers", - values: { type: "select", options: ["Use locale rules", "ISO 8601"] }, - defaultValue: "Use locale rules", - }, - { - name: "Show week number", - description: "Display the current week number when the widget has room", - values: { type: "boolean" }, - defaultValue: "yes", - }, - { - name: "Show day of year", - description: "Display the current day number within the year", - values: { type: "boolean" }, - defaultValue: "yes", - }, - { - name: "Show quarter", - description: "Display the current calendar quarter", - values: { type: "boolean" }, - defaultValue: "yes", - }, - { - name: "Show year progress", - description: "Display progress through the current year", - values: { type: "boolean" }, - defaultValue: "yes", - }, - ], - }, -}; diff --git a/apps/docs/static/api/open-api-schema.json b/apps/docs/static/api/open-api-schema.json index 8e5da38c1..83b48a665 100644 --- a/apps/docs/static/api/open-api-schema.json +++ b/apps/docs/static/api/open-api-schema.json @@ -2106,7 +2106,6 @@ "enum": [ "clock", "weather", - "today", "airQuality", "countdown", "timer", diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx index bfd8d33ec..9fa4b7c5d 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx @@ -23,6 +23,8 @@ import { BoardGridEditorBoundary } from "~/components/board/sections/grid/board- import { BoardGridPortalHost, BoardGridPortalRenderer } from "~/components/board/sections/grid/grid-portal-host"; import { BoardSectionCollapseProvider } from "~/components/board/sections/section-collapse"; import { BoardBackgroundVideo } from "~/components/layout/background"; +import { BoardSelectionProvider } from "~/components/board/selection/board-selection-context"; +import { BoardSelectionToolbar } from "~/components/board/selection/board-selection-toolbar"; import classes from "./_client.module.css"; const APP_SHELL_INLINE_PADDING = 32; @@ -63,71 +65,74 @@ export const ClientBoard = () => { .join(" "); const content = ( - - - - - - - - - - -
- {leftColumnCount > 0 && leftSection && ( - - )} + + + + + + + + + + + +
+ {leftColumnCount > 0 && leftSection && ( + + )} -
- -
- - {rightColumnCount > 0 && rightSection && ( - - )} -
-
-
-
-
-
-
+ + + {rightColumnCount > 0 && rightSection && ( + + )} +
+
+
+
+
+ +
+
+ ); if (representativeWidth === null) return content; diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx index 87eda8813..0c9404897 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx @@ -2,14 +2,19 @@ import { useEffect, useRef } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { IconPlus } from "@tabler/icons-react"; +import { Group, Menu } from "@mantine/core"; +import { IconBox, IconChevronDown, IconLayoutGridAdd, IconPlug, IconPlus, IconResize } from "@tabler/icons-react"; +import { useSession } from "@homarr/auth/client"; import { useRequiredBoard } from "@homarr/boards/context"; import { useModalAction } from "@homarr/modals"; +import { AppSelectModal } from "@homarr/modals-collection"; import { useI18n } from "@homarr/translation/client"; -import { useBoardAddActions } from "~/components/board/use-board-add-actions"; -import { UniversalCreateModal } from "~/components/create/universal-create-modal"; +import { useItemActions } from "~/components/board/items/item-actions"; +import { ItemSelectModal } from "~/components/board/items/item-select-modal"; +import { useContainerActions } from "~/components/board/sections/container/container-actions"; +import { IntegrationSelectModal } from "~/components/integration/integration-select-modal"; import { HeaderButton } from "~/components/layout/header/button"; export default function BoardEditActions() { @@ -17,9 +22,13 @@ export default function BoardEditActions() { } const AddMenu = () => { + const { data: session } = useSession(); const board = useRequiredBoard(); - const { addWidget, addApp, connectService, addContainer, canConnectService } = useBoardAddActions(); - const { openModal } = useModalAction(UniversalCreateModal); + const { openModal: openItemSelectModal } = useModalAction(ItemSelectModal); + const { openModal: openAppSelectModal } = useModalAction(AppSelectModal); + const { openModal: openIntegrationSelectModal } = useModalAction(IntegrationSelectModal); + const { addContainer } = useContainerActions(); + const { createItem } = useItemActions(); const t = useI18n(); const pathname = usePathname(); const router = useRouter(); @@ -29,29 +38,69 @@ const AddMenu = () => { useEffect(() => { if (handledInitialAdd.current || searchParams.get("add") !== "true") return; handledInitialAdd.current = true; - addWidget(); + openItemSelectModal({ boardId: board.id }); const nextSearchParams = new URLSearchParams(searchParams.toString()); nextSearchParams.delete("add"); const query = nextSearchParams.toString(); router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false }); - }, [addWidget, board.id, pathname, router, searchParams]); + }, [board.id, openItemSelectModal, pathname, router, searchParams]); + + const handleSelectItem = () => { + openItemSelectModal({ boardId: board.id }); + }; + + const handleSelectApp = () => { + openAppSelectModal({ + onSelect: (app) => { + createItem({ + kind: "app", + options: { appId: app.id }, + }); + }, + onSelectMany: (apps) => { + for (const app of apps) { + createItem({ + kind: "app", + options: { appId: app.id }, + }); + } + }, + withCreate: session?.user.permissions.includes("app-create") ?? false, + }); + }; + + const handleAddIntegration = () => { + openIntegrationSelectModal({ completionBoardId: board.id }); + }; return ( - - openModal({ - boardActions: { - widget: addWidget, - app: addApp, - integration: canConnectService ? connectService : undefined, - container: addContainer, - }, - }) - } - > - - + + + + + + + + + + + } onClick={handleSelectItem}> + {t("item.action.create")} + + } onClick={handleSelectApp}> + {t("app.action.add")} + + {session?.user.permissions.includes("integration-create") && ( + } onClick={handleAddIntegration}> + {t("integration.action.create")} + + )} + + } onClick={addContainer}> + {t("section.container.action.create")} + + + ); }; diff --git a/apps/nextjs/src/components/board/audio/board-sounds.ts b/apps/nextjs/src/components/board/audio/board-sounds.ts new file mode 100644 index 000000000..3d8c4bce8 --- /dev/null +++ b/apps/nextjs/src/components/board/audio/board-sounds.ts @@ -0,0 +1,123 @@ +/** + * Procedural UI sounds for Homarr using the Web Audio API. + * Zero audio files shipped. Pure runtime synthesis with oscillators, noise buffers, and gain envelopes. + */ + +let audioContext: AudioContext | null = null; + +const getAudioContext = (): AudioContext | null => { + if (typeof window === "undefined") return null; + + try { + if (!audioContext) { + const AudioContextClass = + window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext; + if (AudioContextClass) audioContext = new AudioContextClass(); + } + if (audioContext?.state === "suspended") void audioContext.resume().catch(() => undefined); + return audioContext; + } catch { + return null; + } +}; + +/** + * Creates a white noise audio buffer for percussive textures + */ +const createNoiseBuffer = (ctx: AudioContext, durationSeconds = 0.2): AudioBuffer => { + const bufferSize = Math.floor(ctx.sampleRate * durationSeconds); + const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate); + const data = buffer.getChannelData(0); + for (let i = 0; i < bufferSize; i++) { + data[i] = Math.random() * 2 - 1; + } + return buffer; +}; + +/** + * Procedural trash / delete crunch sound. + * Synthesizes a crunchy bin/paper crumple noise burst and downward pitch thud. + */ +export const playTrashSound = (volume = 0.35) => { + const ctx = getAudioContext(); + if (!ctx) return; + + try { + const now = ctx.currentTime; + + // Master Gain + const masterGain = ctx.createGain(); + masterGain.gain.setValueAtTime(volume, now); + masterGain.connect(ctx.destination); + + // 1. Downward sweep sub-oscillator (physical thud) + const osc = ctx.createOscillator(); + const oscGain = ctx.createGain(); + osc.type = "triangle"; + osc.frequency.setValueAtTime(260, now); + osc.frequency.exponentialRampToValueAtTime(35, now + 0.18); + oscGain.gain.setValueAtTime(0.7, now); + oscGain.gain.exponentialRampToValueAtTime(0.001, now + 0.18); + osc.connect(oscGain); + oscGain.connect(masterGain); + osc.start(now); + osc.stop(now + 0.2); + + // 2. High-crunch noise burst (crush / crumple texture) + try { + const noiseBuffer = createNoiseBuffer(ctx, 0.22); + const noiseSource = ctx.createBufferSource(); + noiseSource.buffer = noiseBuffer; + + const noiseFilter = ctx.createBiquadFilter(); + noiseFilter.type = "bandpass"; + noiseFilter.frequency.setValueAtTime(1400, now); + noiseFilter.frequency.exponentialRampToValueAtTime(320, now + 0.2); + noiseFilter.Q.setValueAtTime(2.2, now); + + const noiseGain = ctx.createGain(); + noiseGain.gain.setValueAtTime(0.9, now); + noiseGain.gain.exponentialRampToValueAtTime(0.001, now + 0.2); + + noiseSource.connect(noiseFilter); + noiseFilter.connect(noiseGain); + noiseGain.connect(masterGain); + + noiseSource.start(now); + noiseSource.stop(now + 0.22); + } catch { + // The noise layer is optional; retain the oscillator when buffers are constrained. + } + } catch { + // Sound feedback must never interrupt deleting board items. + } +}; + +/** + * Procedural select / pop sound. + */ +export const playPopSound = (volume = 0.25) => { + const ctx = getAudioContext(); + if (!ctx) return; + + try { + const now = ctx.currentTime; + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = "sine"; + osc.frequency.setValueAtTime(460, now); + osc.frequency.exponentialRampToValueAtTime(780, now + 0.05); + + gain.gain.setValueAtTime(volume, now); + gain.gain.exponentialRampToValueAtTime(0.001, now + 0.06); + + osc.connect(gain); + gain.connect(ctx.destination); + + osc.start(now); + osc.stop(now + 0.07); + } catch { + // Sound feedback must never interrupt selecting board items. + } +}; diff --git a/apps/nextjs/src/components/board/items/item-content.module.css b/apps/nextjs/src/components/board/items/item-content.module.css index 7100f3197..7ec4b3a16 100644 --- a/apps/nextjs/src/components/board/items/item-content.module.css +++ b/apps/nextjs/src/components/board/items/item-content.module.css @@ -11,6 +11,19 @@ border-color: var(--border-color) !important; } +.settingsButton { + opacity: 0; + pointer-events: none; + transition: opacity 120ms ease; +} + +:global([data-type="item"]:hover) .settingsButton, +:global([data-type="item"]:focus-within) .settingsButton, +.settingsButton[data-menu-open="true"] { + opacity: 1; + pointer-events: auto; +} + .editModeInertContent { display: contents; } @@ -39,3 +52,9 @@ opacity: 1; pointer-events: auto; } + +@media (prefers-reduced-motion: reduce) { + .settingsButton { + transition: none; + } +} diff --git a/apps/nextjs/src/components/board/items/item-menu.tsx b/apps/nextjs/src/components/board/items/item-menu.tsx index 0679edde3..55e998125 100644 --- a/apps/nextjs/src/components/board/items/item-menu.tsx +++ b/apps/nextjs/src/components/board/items/item-menu.tsx @@ -14,6 +14,7 @@ import { BoardRemoveConfirmationMenuItem } from "../remove-confirmation-menu-ite import { useSectionContext } from "../sections/section-context"; import { useItemActions } from "./item-actions"; import { useOpenItemMoveModal } from "./item-move-modal"; +import itemContentClasses from "./item-content.module.css"; import { LazyWidgetEditModal, preloadWidgetEditModal } from "./lazy-widget-edit-modal"; interface BoardItemMenuProps { @@ -105,9 +106,11 @@ const BoardItemMenuInner = ({ item, definition, resetErrorBoundary }: BoardItemM size={24} radius="sm" pos="absolute" - top={12} - right={8} + top={4} + right={4} style={{ zIndex: 30 }} + className={itemContentClasses.settingsButton} + data-menu-open={isMenuOpen || undefined} data-board-widget-settings aria-label={tItem("menu.label.settingsFor", { name: label })} > diff --git a/apps/nextjs/src/components/board/items/item-select-data.spec.ts b/apps/nextjs/src/components/board/items/item-select-data.spec.ts index a9527a3ba..cc269c8b6 100644 --- a/apps/nextjs/src/components/board/items/item-select-data.spec.ts +++ b/apps/nextjs/src/components/board/items/item-select-data.spec.ts @@ -1,5 +1,7 @@ import { describe, expect, it, vi } from "vitest"; +import type { IntegrationKind } from "@homarr/definitions"; + import { getWidgetConnectionStatus, resolveMatchingIntegrationsAsync, @@ -8,95 +10,254 @@ import { } from "./item-select-data"; describe("getWidgetConnectionStatus", () => { - const availableKinds = new Set(["sonarr" as const]); + const availableKinds = new Set(["sonarr", "plex"]); - it("reports widgets with a configured compatible integration as ready", () => { - expect( - getWidgetConnectionStatus({ - supportedIntegrations: ["sonarr", "radarr"], - availableKinds, - connectionOptional: false, - }), - ).toBe("ready"); + describe("ready status", () => { + it("returns 'ready' when at least one supported integration is in availableKinds", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["sonarr", "radarr"], + availableKinds, + connectionOptional: false, + }), + ).toBe("ready"); + }); + + it("returns 'ready' when all supported integrations are in availableKinds", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["sonarr", "plex"], + availableKinds, + connectionOptional: false, + }), + ).toBe("ready"); + }); + + it("returns 'ready' even if connectionOptional is true as long as a matching integration exists", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["sonarr"], + availableKinds, + connectionOptional: true, + }), + ).toBe("ready"); + }); }); - it("reports required integrations that are not configured as needing setup", () => { - expect( - getWidgetConnectionStatus({ - supportedIntegrations: ["radarr"], - availableKinds, - connectionOptional: false, - }), - ).toBe("needsSetup"); + describe("needsSetup status", () => { + it("returns 'needsSetup' when supported integrations are non-empty, none are available, and connectionOptional is false", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["radarr", "sabNzbd"], + availableKinds, + connectionOptional: false, + }), + ).toBe("needsSetup"); + }); + + it("returns 'needsSetup' when availableKinds is empty and connection is required", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["sonarr"], + availableKinds: new Set(), + connectionOptional: false, + }), + ).toBe("needsSetup"); + }); }); - it.each([ - { supportedIntegrations: [], connectionOptional: false }, - { supportedIntegrations: ["radarr" as const], connectionOptional: true }, - ])("reports a widget without a required connection", ({ supportedIntegrations, connectionOptional }) => { - expect(getWidgetConnectionStatus({ supportedIntegrations, availableKinds, connectionOptional })).toBe( - "noConnectionRequired", - ); + describe("noConnectionRequired status", () => { + it("returns 'noConnectionRequired' when supportedIntegrations is empty regardless of connectionOptional", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: [], + availableKinds, + connectionOptional: false, + }), + ).toBe("noConnectionRequired"); + + expect( + getWidgetConnectionStatus({ + supportedIntegrations: [], + availableKinds: new Set(), + connectionOptional: true, + }), + ).toBe("noConnectionRequired"); + }); + + it("returns 'noConnectionRequired' when connectionOptional is true and no supported integrations are available", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["radarr", "jellyfin"], + availableKinds, + connectionOptional: true, + }), + ).toBe("noConnectionRequired"); + }); + + it("returns 'noConnectionRequired' when connectionOptional is true and availableKinds is empty", () => { + expect( + getWidgetConnectionStatus({ + supportedIntegrations: ["sonarr"], + availableKinds: new Set(), + connectionOptional: true, + }), + ).toBe("noConnectionRequired"); + }); }); }); -describe("widget selection lock", () => { - it("rejects repeated selection synchronously until the active selection finishes", () => { +describe("selection concurrency locks", () => { + it("acquires lock on unlocked ref and sets current to true", () => { + const lock = { current: false }; + + const acquired = tryLockSelection(lock); + expect(acquired).toBe(true); + expect(lock.current).toBe(true); + }); + + it("rejects lock acquisition on an already locked ref", () => { + const lock = { current: true }; + + const acquired = tryLockSelection(lock); + expect(acquired).toBe(false); + expect(lock.current).toBe(true); + }); + + it("unlocks lock and allows subsequent acquisition", () => { const lock = { current: false }; expect(tryLockSelection(lock)).toBe(true); expect(tryLockSelection(lock)).toBe(false); unlockSelection(lock); + expect(lock.current).toBe(false); + expect(tryLockSelection(lock)).toBe(true); }); + + it("handles repeated unlock safely without throwing", () => { + const lock = { current: false }; + expect(() => { + unlockSelection(lock); + unlockSelection(lock); + }).not.toThrow(); + expect(lock.current).toBe(false); + }); + + it("prevents multiple concurrent callers from proceeding simultaneously", () => { + const lock = { current: false }; + const results: boolean[] = []; + + // Simulate 5 simultaneous selection attempts + for (let i = 0; i < 5; i++) { + results.push(tryLockSelection(lock)); + } + + expect(results).toEqual([true, false, false, false, false]); + }); }); describe("resolveMatchingIntegrationsAsync", () => { - it("waits for the shared integration query before a cold integration-backed selection", async () => { - const ensureDataAsync = vi.fn(async () => [ - { id: "matching", kind: "sonarr" as const }, - { id: "other", kind: "radarr" as const }, - ]); + it("returns empty array immediately when widget does not support integrations without calling ensureDataAsync", async () => { + const ensureDataAsync = vi.fn(async () => [{ id: "1", kind: "sonarr" as IntegrationKind }]); - await expect( - resolveMatchingIntegrationsAsync({ - hasIntegrationSupport: true, - supportedIntegrations: ["sonarr"], - currentData: undefined, - ensureDataAsync, - }), - ).resolves.toEqual([{ id: "matching", kind: "sonarr" }]); - expect(ensureDataAsync).toHaveBeenCalledOnce(); - }); + const result = await resolveMatchingIntegrationsAsync({ + hasIntegrationSupport: false, + supportedIntegrations: ["sonarr"], + currentData: undefined, + ensureDataAsync, + }); - it("does not wait for integrations when the widget has no integration support", async () => { - const ensureDataAsync = vi.fn(async () => [{ id: "unused", kind: "sonarr" as const }]); - - await expect( - resolveMatchingIntegrationsAsync({ - hasIntegrationSupport: false, - supportedIntegrations: [], - currentData: undefined, - ensureDataAsync, - }), - ).resolves.toEqual([]); + expect(result).toEqual([]); expect(ensureDataAsync).not.toHaveBeenCalled(); }); - it("excludes matching integrations the user cannot use", async () => { + it("uses currentData synchronously when available without calling ensureDataAsync", async () => { const currentData = [ - { id: "allowed", kind: "sonarr" as const, permissions: { hasUseAccess: true } }, - { id: "blocked", kind: "sonarr" as const, permissions: { hasUseAccess: false } }, + { id: "1", kind: "sonarr" as IntegrationKind, permissions: { hasUseAccess: true } }, + { id: "2", kind: "radarr" as IntegrationKind, permissions: { hasUseAccess: true } }, + { id: "3", kind: "plex" as IntegrationKind, permissions: { hasUseAccess: true } }, ]; + const ensureDataAsync = vi.fn(); + + const result = await resolveMatchingIntegrationsAsync({ + hasIntegrationSupport: true, + supportedIntegrations: ["sonarr", "radarr"], + currentData, + ensureDataAsync, + }); + + expect(result).toEqual([currentData[0], currentData[1]]); + expect(ensureDataAsync).not.toHaveBeenCalled(); + }); + + it("calls ensureDataAsync when currentData is undefined and filters by supported kinds", async () => { + const fetchedData = [ + { id: "1", kind: "sonarr" as IntegrationKind }, + { id: "2", kind: "radarr" as IntegrationKind }, + { id: "3", kind: "sabNzbd" as IntegrationKind }, + ]; + const ensureDataAsync = vi.fn(async () => fetchedData); + + const result = await resolveMatchingIntegrationsAsync({ + hasIntegrationSupport: true, + supportedIntegrations: ["radarr", "sabNzbd"], + currentData: undefined, + ensureDataAsync, + }); + + expect(result).toEqual([fetchedData[1], fetchedData[2]]); + expect(ensureDataAsync).toHaveBeenCalledOnce(); + }); + + it("excludes integrations where permissions.hasUseAccess is explicitly false", async () => { + const currentData = [ + { id: "allowed-1", kind: "sonarr" as IntegrationKind, permissions: { hasUseAccess: true } }, + { id: "blocked-1", kind: "sonarr" as IntegrationKind, permissions: { hasUseAccess: false } }, + { id: "allowed-no-perms", kind: "sonarr" as IntegrationKind }, + ]; + + const result = await resolveMatchingIntegrationsAsync({ + hasIntegrationSupport: true, + supportedIntegrations: ["sonarr"], + currentData, + ensureDataAsync: vi.fn(), + }); + + expect(result).toEqual([currentData[0], currentData[2]]); + }); + + it("returns empty array when none of the fetched integrations match supported kinds", async () => { + const fetchedData = [ + { id: "1", kind: "plex" as IntegrationKind }, + { id: "2", kind: "jellyfin" as IntegrationKind }, + ]; + const ensureDataAsync = vi.fn(async () => fetchedData); + + const result = await resolveMatchingIntegrationsAsync({ + hasIntegrationSupport: true, + supportedIntegrations: ["sonarr", "radarr"], + currentData: undefined, + ensureDataAsync, + }); + + expect(result).toEqual([]); + expect(ensureDataAsync).toHaveBeenCalledOnce(); + }); + + it("propagates error when ensureDataAsync rejects", async () => { + const ensureDataAsync = vi.fn(async () => { + throw new Error("Network timeout"); + }); await expect( resolveMatchingIntegrationsAsync({ hasIntegrationSupport: true, supportedIntegrations: ["sonarr"], - currentData, - ensureDataAsync: vi.fn(), + currentData: undefined, + ensureDataAsync, }), - ).resolves.toEqual([currentData[0]]); + ).rejects.toThrow("Network timeout"); }); }); diff --git a/apps/nextjs/src/components/board/items/item-select-modal.module.css b/apps/nextjs/src/components/board/items/item-select-modal.module.css deleted file mode 100644 index 1a53dd49b..000000000 --- a/apps/nextjs/src/components/board/items/item-select-modal.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.action { - opacity: 0; - transition: opacity 150ms ease; -} -.card:hover .action, -.card:focus-within .action { - opacity: 1; -} - -@media (hover: none), (pointer: coarse) { - .action { - opacity: 1; - } -} - -@media (prefers-reduced-motion: reduce) { - .action { - transition: none; - } -} diff --git a/apps/nextjs/src/components/board/items/item-select-modal.spec.tsx b/apps/nextjs/src/components/board/items/item-select-modal.spec.tsx new file mode 100644 index 000000000..ee589bf57 --- /dev/null +++ b/apps/nextjs/src/components/board/items/item-select-modal.spec.tsx @@ -0,0 +1,211 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import type { Root } from "react-dom/client"; +import { MantineProvider } from "@mantine/core"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import type { IntegrationKind, WidgetKind } from "@homarr/definitions"; + +import { ConnectionStatusBadge, SupportedIntegrations, WidgetItem } from "./item-select-modal"; + +vi.mock("@homarr/translation/client", () => ({ + useI18n: () => (key: string) => { + const translations: Record = { + "item.create.connectionStatus.ready": "Ready", + "item.create.connectionStatus.needsSetup": "Needs setup", + "item.create.connectionStatus.noConnectionRequired": "No connection required", + "item.create.standalone": "Standalone", + "widget.mediaServer.name": "Media Server", + "widget.mediaServer.description": "Stream your media", + "widget.clock.name": "Clock", + "widget.clock.description": "Displays current time", + }; + return translations[key] ?? key; + }, + useScopedI18n: () => (key: string) => key, +})); + +vi.mock("@homarr/definitions", async () => { + const actual = await vi.importActual>("@homarr/definitions"); + return { + ...actual, + getIntegrationName: (kind: string) => kind.charAt(0).toUpperCase() + kind.slice(1), + }; +}); + +let root: Root; +let host: HTMLDivElement; + +beforeEach(() => { + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + window.matchMedia = vi.fn().mockImplementation(() => ({ + matches: false, + media: "", + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })); + host = document.createElement("div"); + document.body.append(host); + root = createRoot(host); +}); + +afterEach(async () => { + await act(async () => root.unmount()); + host.remove(); +}); + +describe("Advanced Add App - Modal Subcomponents", () => { + describe("ConnectionStatusBadge", () => { + it("renders 'Ready' badge with teal color when status is 'ready'", async () => { + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("Ready"); + const badge = host.querySelector("span, div"); + expect(badge).not.toBeNull(); + }); + + it("renders 'Needs setup' badge with orange color when status is 'needsSetup'", async () => { + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("Needs setup"); + }); + + it("renders null when status is 'noConnectionRequired'", async () => { + await act(async () => + root.render( + + + , + ), + ); + + expect(host.querySelector("[class*='Badge-root']")).toBeNull(); + }); + }); + + describe("SupportedIntegrations", () => { + it("renders up to 5 avatars for supported integrations", async () => { + const integrations: IntegrationKind[] = ["sonarr", "radarr", "plex"]; + + await act(async () => + root.render( + + + , + ), + ); + + // Avatars should be rendered for the 3 items + expect(host.querySelectorAll("img, svg, div").length).toBeGreaterThan(0); + expect(host.textContent).not.toContain("+"); + }); + + it("renders overflow indicator when 6 or more integrations are supported", async () => { + const integrations: IntegrationKind[] = ["sonarr", "radarr", "plex", "jellyfin", "emby", "deluge"]; + + await act(async () => + root.render( + + + , + ), + ); + + // 6 integrations total: 4 shown, +2 in badge + expect(host.textContent).toContain("+2"); + }); + + it("renders Standalone label when integrations list is empty", async () => { + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("Standalone"); + }); + }); + + describe("WidgetItem", () => { + const FakeIcon = () => Icon; + + it("renders widget card with icon, title, description, and connection status", async () => { + const onSelect = vi.fn(); + const item = { + kind: "mediaServer" as WidgetKind, + name: "Media Server", + description: "Stream your media collection effortlessly", + icon: FakeIcon as never, + supportedIntegrations: ["plex" as IntegrationKind, "jellyfin" as IntegrationKind], + }; + + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button).not.toBeNull(); + expect(host.textContent).toContain("Media Server"); + expect(host.textContent).toContain("Stream your media collection effortlessly"); + expect(host.textContent).toContain("Ready"); + expect(host.querySelector("[data-testid='widget-icon']")).not.toBeNull(); + + await act(async () => { + button?.click(); + }); + + expect(onSelect).toHaveBeenCalledOnce(); + }); + + it("does not mark widget cards as selected", async () => { + const item = { + kind: "clock" as WidgetKind, + name: "Clock", + description: "Displays current time", + icon: FakeIcon as never, + supportedIntegrations: [] as IntegrationKind[], + }; + + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.hasAttribute("data-selected")).toBe(false); + }); + }); +}); diff --git a/apps/nextjs/src/components/board/items/item-select-modal.tsx b/apps/nextjs/src/components/board/items/item-select-modal.tsx index 2d163c89d..1b53094a9 100644 --- a/apps/nextjs/src/components/board/items/item-select-modal.tsx +++ b/apps/nextjs/src/components/board/items/item-select-modal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useState } from "react"; +import { useMemo, useRef, useState } from "react"; import { Avatar, Badge, @@ -6,16 +6,18 @@ import { Button, Center, Divider, - Flex, Group, Image, + Input, Select, + SimpleGrid, Stack, Text, + ThemeIcon, Tooltip, } from "@mantine/core"; import { notifications } from "@mantine/notifications"; -import { IconApi, IconBuildingStore } from "@tabler/icons-react"; +import { IconApi, IconBuildingStore, IconSearch } from "@tabler/icons-react"; import type { RouterOutputs } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; @@ -23,8 +25,8 @@ import { useSession } from "@homarr/auth/client"; import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context"; import { createId } from "@homarr/common"; import { - getIconUrl, getBoardLaneColumnCount, + getIconUrl, getIntegrationName, getRootSectionLane, widgetIntegrationSupport, @@ -36,14 +38,14 @@ import { createModal, modalSizeSelect, useModalAction } from "@homarr/modals"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; import { useSettings } from "@homarr/settings"; import { useI18n } from "@homarr/translation/client"; -import { CatalogItem, SelectGridLayout, selectGridCardHeight } from "@homarr/ui"; +import { SelectableCard } from "@homarr/ui"; import type { TablerIcon } from "@homarr/ui"; import { widgetCatalogIcons } from "@homarr/widgets/catalog"; import { loadWidgetDefinition, reduceWidgetOptionsWithDefinition } from "@homarr/widgets/manifest"; -import { IntegrationSelectModal } from "~/components/integration/integration-select-modal"; -import { useSetupAnalytics } from "~/components/create/setup-analytics"; import type { EmptySection } from "~/app/[locale]/boards/_types"; +import { useSetupAnalytics } from "~/components/create/setup-analytics"; +import { IntegrationSelectModal } from "~/components/integration/integration-select-modal"; import { useItemActions } from "./item-actions"; import type { WidgetConnectionStatus } from "./item-select-data"; import { @@ -64,6 +66,16 @@ interface ItemSelectModalContentProps { initialSearch?: string; } +export interface WidgetItemData { + kind: WidgetKind; + supportedIntegrations: IntegrationKind[]; + icon: TablerIcon; + name: string; + description: string; +} + +const selectGridCols = { base: 1, xs: 2, sm: 2, md: 3, lg: 4 }; + const ItemSelectModalContent = ({ actions, integrationData, @@ -108,15 +120,14 @@ const ItemSelectModalContent = ({ [board.sections, currentLayout, mainCanvasSection, t], ); const [targetSectionId, setTargetSectionId] = useState(placementOptions[0]?.value ?? null); + const effectiveSectionId = placementOptions.some(({ value }) => value === targetSectionId) + ? targetSectionId + : (placementOptions[0]?.value ?? null); + const [loadingSelection, setLoadingSelection] = useState(null); const selectionLock = useRef(false); const { createItem, removeItem } = useItemActions(); - useEffect(() => { - if (placementOptions.some(({ value }) => value === targetSectionId)) return; - setTargetSectionId(placementOptions[0]?.value ?? null); - }, [placementOptions, targetSectionId]); - const { openModal: openEditModal } = useModalAction(LazyWidgetEditModal); const { openModal: openIntegrationModal } = useModalAction(IntegrationSelectModal); const settings = useSettings(); @@ -128,7 +139,7 @@ const ItemSelectModalContent = ({ [integrationData], ); - const items = useMemo( + const items = useMemo( () => widgetKinds .filter((kind) => kind !== "customApi") @@ -188,7 +199,7 @@ const ItemSelectModalContent = ({ createdItem.layouts.find(({ layoutId }) => layoutId === currentLayoutId)?.sectionId ?? createdItem.layouts[0]?.sectionId; const actualDestination = placementOptions.find(({ value }) => value === actualSectionId)?.label; - const wasRelocated = targetSectionId !== null && actualSectionId !== targetSectionId && actualDestination; + const wasRelocated = effectiveSectionId !== null && actualSectionId !== effectiveSectionId && actualDestination; const notificationId = `board-item-created:${itemId}`; showSuccessNotification({ id: notificationId, @@ -247,7 +258,7 @@ const ItemSelectModalContent = ({ kind: "customApi", integrationIds: [], options: configuredOptions, - targetSectionId: targetSectionId ?? undefined, + targetSectionId: effectiveSectionId ?? undefined, advancedOptions, }); notifyCreated(updatedBoard, itemId, customWidgetDefinition.name); @@ -310,7 +321,7 @@ const ItemSelectModalContent = ({ kind, options, integrationIds: newIntegrationIds, - targetSectionId: targetSectionId ?? undefined, + targetSectionId: effectiveSectionId ?? undefined, advancedOptions, }); if (!notifyCreated(updatedBoard, itemId, t(`widget.${kind}.name`))) return; @@ -377,125 +388,233 @@ const ItemSelectModalContent = ({ }; return ( - - - { - if (event.key === "Enter" && loadingSelection === null && filteredItems.length === 1) { - const [item] = filteredItems; - if (item) void handleAdd(item.kind); - } - }} - > - {placementOptions.length > 1 && ( - - setSearch(event.currentTarget.value)} + leftSection={} + placeholder={`${t("item.create.search")}...`} + aria-label={t("item.create.search")} + data-autofocus + onKeyDown={(event) => { + if ( + event.key === "Enter" && + loadingSelection === null && + filteredItems.length === 1 && + filteredItems[0] + ) { + void handleAdd(filteredItems[0].kind); + } + }} + /> + + + {placementOptions.length > 1 && ( + + setSearch(event.currentTarget.value)} + leftSection={} + placeholder={`${t("app.action.select.search")}...`} + aria-label={t("app.action.select.search")} + data-autofocus + onKeyDown={(event) => { + if (event.key === "Enter" && filteredApps.length === 1 && filteredApps[0]) { + handleSelect(filteredApps[0]); } - selected={multiSelect ? selectedAppIds.has(app.id) : undefined} - onSelect={() => handleSelect(app)} - > - - - {app.name} - - {app.name} - - {multiSelect && selectedAppIds.has(app.id) && ( - - )} - - - - {app.description ?? ""} - - - - {multiSelect - ? selectedAppIds.has(app.id) - ? t("app.action.select.selected") - : t("app.action.select.toggle") - : t("app.action.select.action", { app: app.name })} - - - - ))} - - {filteredApps.length === 0 && !isPending && ( -
- {t("app.action.select.noResults")} -
+ }} + /> + {multiSelect && ( + + + {t("app.action.select.multiSelectTip")} + + {selectedAppIds.size > 0 && ( + + {t("app.action.select.selectedCount", { count: selectedAppIds.size })} + + )} + )} -
- {multiSelect && ( - - - + + + {/* Scrollable Container with App Cards */} + + + + {innerProps.withCreate && ( + + + + } + title={t("app.action.create.title")} + description={t("app.action.create.description")} + footerLeft={ + + {t("app.action.select.customApplication")} + + } + /> + )} + + {filteredApps.map((app) => ( + + ))} + + + {filteredApps.length === 0 && !isPending && ( +
+ {t("app.action.select.noResults")} +
+ )} +
+
+ + {/* Multi-Select Action Footer */} + {multiSelect && selectedApps.length > 0 && ( + + + + {t("app.action.select.appsSelected", { count: selectedApps.length })} + + + + + + + )} - + ); }).withOptions({ defaultTitle: (t) => t("app.action.select.title"), size: modalSizeSelect, }); + +// ========================================================================= +// AppCard: Variant 3 (Dashboard Inset) with Medium Title & Large App Icon +// ========================================================================= +const AppCard = ({ + app, + isSelected, + multiSelect, + onSelect, +}: { + app: SelectableApp; + isSelected: boolean; + multiSelect: boolean; + onSelect: (app: SelectableApp, event?: React.MouseEvent) => void; +}) => { + const t = useI18n(); + + return ( + onSelect(app, event)} + aria-label={app.name} + selected={isSelected} + icon={{app.name}} + title={app.name} + topRight={ + isSelected ? ( + + {t("app.action.select.selected")} + + ) : null + } + description={app.description} + footerLeft={ + + {multiSelect && isSelected ? t("app.action.select.selected") : t("app.action.select.application")} + + } + /> + ); +}; diff --git a/packages/modals/src/index.tsx b/packages/modals/src/index.tsx index 884182462..fa25dd79b 100644 --- a/packages/modals/src/index.tsx +++ b/packages/modals/src/index.tsx @@ -102,6 +102,13 @@ const ActiveModal = ({ modal, state, handleCloseModal }: ActiveModalProps) => { content: presentationClasses.inspectorContent, }); + const handleClose = useCallback(() => { + const canClose = modal.reference.onCloseRequest ? modal.reference.onCloseRequest() : true; + if (canClose) { + handleCloseModal(); + } + }, [handleCloseModal, modal.reference]); + return ( { {...otherModalProps} title={translateIfNecessary(t, modal.props.defaultTitle)} opened={opened} - onClose={handleCloseModal} + onClose={handleClose} > {modal.reference.content} @@ -144,7 +151,6 @@ export const useModalAction = (modal: TModal) => return { openModal: (innerProps: inferInnerProps, options: OpenModalOptions | void) => { // void actually is undefined - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition context.openModalInner({ modal, innerProps, options: options ?? {} }); }, }; diff --git a/packages/modals/src/reducer.tsx b/packages/modals/src/reducer.tsx index ba22f7297..039f07e52 100644 --- a/packages/modals/src/reducer.tsx +++ b/packages/modals/src/reducer.tsx @@ -94,6 +94,8 @@ const getModal = (modal: ModalState) => const ModalContent = modal.modal.component; const { innerProps, ...rest } = modal.props; + let closeInterceptor: (() => boolean) | null = null; + const FullModal = () => { const context = useContext(ModalContext); @@ -106,6 +108,9 @@ const getModal = (modal: ModalState) => innerProps={innerProps} actions={{ closeModal: () => context.closeModal(modal.id), + setCloseInterceptor: (interceptor: (() => boolean) | null) => { + closeInterceptor = interceptor; + }, }} /> ); @@ -114,5 +119,11 @@ const getModal = (modal: ModalState) => return { modalProps: rest, content: , + onCloseRequest: () => { + if (closeInterceptor) { + return closeInterceptor(); + } + return true; + }, }; }; diff --git a/packages/modals/src/type.ts b/packages/modals/src/type.ts index cd553ac1a..5faa421d2 100644 --- a/packages/modals/src/type.ts +++ b/packages/modals/src/type.ts @@ -5,10 +5,12 @@ import type { stringOrTranslation } from "@homarr/translation"; import type { ModalPresentation } from "./modal-presentation"; -export type ModalComponent = (props: { - actions: { closeModal: () => void }; - innerProps: TInnerProps; -}) => ReactNode; +export interface ModalActions { + closeModal: () => void; + setCloseInterceptor?: (interceptor: (() => boolean) | null) => void; +} + +export type ModalComponent = (props: { actions: ModalActions; innerProps: TInnerProps }) => ReactNode; export type CreateModalOptions = Pick< ModalOptions, diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 19e7c1ddd..492bd1880 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -236,61 +236,6 @@ } } }, - "universalCreate": { - "title": "Create", - "analyticsError": "Usage metrics could not be recorded. Your action was not affected.", - "trigger": { - "label": "Create something", - "description": "Add content, connect a service, or create a board from one place." - }, - "search": { - "label": "Search create actions", - "placeholder": "What do you want to create?", - "noResults": "No matching actions" - }, - "group": { - "currentBoard": "Current board", - "library": "Apps and services", - "boards": "Boards" - }, - "action": { - "widget": { - "label": "Add widget", - "description": "Add live information or controls to this board.", - "keywords": "item dashboard content" - }, - "app": { - "label": "Add or create app", - "description": "Place an app shortcut on this board or create one for your library.", - "keywords": "shortcut bookmark application" - }, - "integration": { - "label": "Connect service", - "description": "Connect a service so widgets can use its data.", - "keywords": "integration provider connection" - }, - "container": { - "label": "Add container", - "description": "Group related board content into a resizable section.", - "keywords": "section group layout" - }, - "board": { - "label": "Create board", - "description": "Start a new dashboard and add its first widget.", - "keywords": "dashboard page" - }, - "workshop": { - "label": "Browse Workshop", - "description": "Discover community-built widgets and install them.", - "keywords": "community discover install" - }, - "customWidget": { - "label": "Create custom widget", - "description": "Build a widget tailored to your own workflow.", - "keywords": "code jsx developer" - } - } - }, "user": { "title": "Users", "name": "User", @@ -876,7 +821,12 @@ "title": "Select an app to add to this board", "toggle": "Click to select", "selected": "Selected", - "multiple": "Add {count}" + "multiple": "Add {count}", + "multiSelectTip": "Tip: Hold Ctrl / ⌘ to select multiple apps at once", + "selectedCount": "{count, plural, one {# selected} other {# selected}}", + "appsSelected": "{count, plural, one {# app selected} other {# apps selected}}", + "customApplication": "Custom application", + "application": "Application" }, "create": { "title": "Create new app", @@ -1721,7 +1671,11 @@ "grid": { "widgetCount": "{count} widgets", "noWidgets": "No widgets", - "detected": "Detected via Docker" + "detected": "Detected via Docker", + "connected": "{count, plural, one {# connected} other {# connected}}", + "tiedWidgets": "Tied widgets", + "more": "+{count} more", + "loadError": "Integrations could not be loaded. Try again before creating a new connection." }, "category": { "dnsHole": "DNS", @@ -2135,6 +2089,7 @@ "success": "Success", "beta": "Beta", "error": "Error", + "analyticsError": "Setup analytics could not be recorded.", "action": { "add": "Add", "apply": "Apply", @@ -2810,6 +2765,8 @@ "needsSetup": "Needs setup", "noConnectionRequired": "No connection required" }, + "customWidget": "Custom widget", + "standalone": "Standalone", "missingIntegration": { "title": "Connect a service first", "noPermission": "This widget needs a compatible service connection. Ask an administrator to connect one, then try again." @@ -2826,6 +2783,20 @@ } } }, + "selection": { + "summary": "{count, plural, one {# selected item} other {# selected items}}", + "selected": "{count, plural, one {# selected} other {# selected}}", + "removed": "{count, plural, one {# item removed} other {# items removed}}", + "move": "Move", + "moveTo": "Move selected items", + "delete": "Delete", + "clear": "Clear selection", + "clearShortcut": "Clear selection (Escape)", + "saveErrorTitle": "Unable to update items", + "saveErrorMessage": "The changes could not be saved. The board was refreshed.", + "moveUnavailableTitle": "Unable to move selected items", + "moveUnavailableMessage": "The destination does not have enough room for the entire selection." + }, "moveResize": { "title": "Move / resize item", "entryLabel": "{name}, column {column}, row {row}", @@ -3465,50 +3436,6 @@ "notificationsDenied": "Notifications are blocked in this browser." } }, - "today": { - "name": "Today", - "description": "Displays the current date and progress through the calendar.", - "option": { - "weekConvention": { - "label": "Week numbering", - "options": { - "locale": "Use locale rules", - "iso": "ISO 8601" - } - }, - "showWeekNumber": { - "label": "Show week number" - }, - "showDayOfYear": { - "label": "Show day of year" - }, - "showQuarter": { - "label": "Show quarter" - }, - "showYearProgress": { - "label": "Show year progress" - } - }, - "weekNumber": "Week {week}", - "dayOfYear": "Day {day} of {total}", - "quarter": "Quarter {quarter}", - "daysAfterToday": "{days, plural, =0 {Last day of the year} one {# day after today} other {# days after today}}", - "weekProgressLabel": "Week progress", - "monthProgressLabel": "Month progress", - "yearProgressLabel": "Year progress", - "yearProgress": "Year progress: {progress}%", - "weekConvention": { - "locale": "Week numbers follow your locale", - "iso": "ISO 8601 week numbers" - }, - "advanced": { - "week": "Week number", - "dayOfYear": "Day of year", - "quarter": "Quarter", - "quarterValue": "Q{quarter}", - "daysRemaining": "Days after today" - } - }, "minecraftServerStatus": { "name": "Minecraft Server Status", "description": "Displays the status of a Minecraft server", diff --git a/packages/ui/src/components/index.tsx b/packages/ui/src/components/index.tsx index a593e1975..e2f8d28dc 100644 --- a/packages/ui/src/components/index.tsx +++ b/packages/ui/src/components/index.tsx @@ -13,6 +13,13 @@ export { CustomPasswordInput } from "./password-input/password-input"; export { IntegrationAvatar } from "./integration-avatar"; export { BetaBadge } from "./beta-badge"; export { CatalogItem } from "./catalog-item"; +export { SelectableCard } from "./selectable-card"; +export type { + SelectableCardProps, + SelectableCardStylesNames, + SelectableCardCssVariables, + SelectableCardFactory, +} from "./selectable-card"; export { MaskedImage } from "./masked-image"; export { MaskedOrNormalImage } from "./masked-or-normal-image"; export { LanguageIcon } from "./language-icon"; diff --git a/packages/ui/src/components/selectable-card/index.ts b/packages/ui/src/components/selectable-card/index.ts new file mode 100644 index 000000000..c71cde19a --- /dev/null +++ b/packages/ui/src/components/selectable-card/index.ts @@ -0,0 +1,7 @@ +export { SelectableCard } from "./selectable-card"; +export type { + SelectableCardProps, + SelectableCardStylesNames, + SelectableCardCssVariables, + SelectableCardFactory, +} from "./selectable-card"; diff --git a/packages/ui/src/components/selectable-card/selectable-card.module.css b/packages/ui/src/components/selectable-card/selectable-card.module.css new file mode 100644 index 000000000..b75f06176 --- /dev/null +++ b/packages/ui/src/components/selectable-card/selectable-card.module.css @@ -0,0 +1,79 @@ +.root { + cursor: pointer; + text-align: start; + display: flex; + flex-direction: column; + position: relative; + width: 100%; + min-height: 175px; + height: 100%; + padding: var(--mantine-spacing-sm); + border-radius: var(--selectable-card-radius, var(--mantine-radius-md)); + background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + border: 1px solid var(--mantine-color-default-border); + box-shadow: var(--mantine-shadow-xs); + transition: + transform 150ms ease, + box-shadow 150ms ease, + border-color 150ms ease; + overflow: hidden; + font-family: inherit; + color: inherit; + text-decoration: none; +} + +.root:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: var(--mantine-shadow-sm); + border-color: var(--mantine-primary-color-filled); +} + +.root:focus-visible { + outline: 2px solid var(--mantine-primary-color-filled); + outline-offset: 2px; +} + +.root[data-selected] { + border-color: var(--mantine-primary-color-filled); + box-shadow: 0 0 0 1px var(--mantine-primary-color-filled); +} + +.root:disabled { + cursor: not-allowed; + opacity: 0.6; +} + +.header { + border-bottom: 1px solid var(--mantine-color-default-border); + background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-8)); + padding-top: var(--mantine-spacing-xs); + padding-bottom: var(--mantine-spacing-xs); + margin: calc(var(--mantine-spacing-sm) * -1); + margin-bottom: 0; + padding-left: var(--mantine-spacing-sm); + padding-right: var(--mantine-spacing-sm); +} + +.body { + flex: 1 1 auto; + display: flex; + flex-direction: column; + gap: var(--mantine-spacing-xs); + margin-top: var(--mantine-spacing-xs); + margin-bottom: var(--mantine-spacing-xs); + min-height: 0; +} + +.footer { + border-top: 1px dashed var(--mantine-color-default-border); + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8)); + padding-top: var(--mantine-spacing-xs); + padding-bottom: var(--mantine-spacing-xs); + margin: calc(var(--mantine-spacing-sm) * -1); + margin-top: auto; + padding-left: var(--mantine-spacing-sm); + padding-right: var(--mantine-spacing-sm); + min-height: 40px; + display: flex; + align-items: center; +} diff --git a/packages/ui/src/components/selectable-card/selectable-card.module.d.css.ts b/packages/ui/src/components/selectable-card/selectable-card.module.d.css.ts new file mode 100644 index 000000000..3aa25b940 --- /dev/null +++ b/packages/ui/src/components/selectable-card/selectable-card.module.d.css.ts @@ -0,0 +1,7 @@ +declare const classes: { + readonly root: string; + readonly header: string; + readonly body: string; + readonly footer: string; +}; +export default classes; diff --git a/packages/ui/src/components/selectable-card/selectable-card.spec.tsx b/packages/ui/src/components/selectable-card/selectable-card.spec.tsx new file mode 100644 index 000000000..fbecf3093 --- /dev/null +++ b/packages/ui/src/components/selectable-card/selectable-card.spec.tsx @@ -0,0 +1,370 @@ +// @vitest-environment jsdom + +import { act, createRef } from "react"; +import { createRoot } from "react-dom/client"; +import type { Root } from "react-dom/client"; +import { Badge, MantineProvider } from "@mantine/core"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { SelectableCard } from "./selectable-card"; +import classes from "./selectable-card.module.css"; + +let root: Root; +let host: HTMLDivElement; + +beforeEach(() => { + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + window.matchMedia = vi.fn().mockImplementation(() => ({ + matches: false, + media: "", + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })); + host = document.createElement("div"); + document.body.append(host); + root = createRoot(host); +}); + +afterEach(async () => { + await act(async () => root.unmount()); + host.remove(); +}); + +describe("SelectableCard", () => { + describe("rendering slots", () => { + it("renders as a native button with default button type and accessible slots", async () => { + const onClick = vi.fn(); + await act(async () => + root.render( + + Icon} + title="Sonarr" + topRight={Media} + description="Manage TV series downloads" + footerLeft={Left Meta} + footerRight={Right Meta} + onClick={onClick} + /> + , + ), + ); + + const button = host.querySelector("button"); + expect(button).not.toBeNull(); + expect(button?.type).toBe("button"); + expect(button?.getAttribute("aria-label")).toBe("Sonarr Card"); + expect(host.querySelector("[data-testid='card-icon']")).not.toBeNull(); + expect(host.textContent).toContain("Sonarr"); + expect(host.querySelector("[data-testid='card-topright']")).not.toBeNull(); + expect(host.textContent).toContain("Manage TV series downloads"); + expect(host.querySelector("[data-testid='card-footer-left']")).not.toBeNull(); + expect(host.querySelector("[data-testid='card-footer-right']")).not.toBeNull(); + + await act(async () => button?.click()); + expect(onClick).toHaveBeenCalledOnce(); + }); + + it("renders only header when only header slots (icon, title, topRight) are provided", async () => { + await act(async () => + root.render( + + I} + title="Only Header" + topRight={TR} + /> + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.querySelector(`.${classes.header}`)).not.toBeNull(); + expect(button?.querySelector(`.${classes.body}`)).toBeNull(); + expect(button?.querySelector(`.${classes.footer}`)).toBeNull(); + expect(host.querySelector("[data-testid='only-icon']")).not.toBeNull(); + expect(host.querySelector("[data-testid='only-topright']")).not.toBeNull(); + }); + + it("renders footer when only footerLeft or only footerRight is provided", async () => { + await act(async () => + root.render( + + Left} /> + , + ), + ); + + expect(host.querySelector(`.${classes.footer}`)).not.toBeNull(); + expect(host.querySelector("[data-testid='left-only']")).not.toBeNull(); + + await act(async () => + root.render( + + Right} /> + , + ), + ); + + expect(host.querySelector(`.${classes.footer}`)).not.toBeNull(); + expect(host.querySelector("[data-testid='right-only']")).not.toBeNull(); + }); + + it("applies Styles API classes for root, header, body, and footer", async () => { + await act(async () => + root.render( + + Footer} + classNames={{ + root: "custom-root", + header: "custom-header", + body: "custom-body", + footer: "custom-footer", + }} + /> + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.classList.contains(classes.root)).toBe(true); + expect(button?.classList.contains("custom-root")).toBe(true); + + const header = button?.querySelector(`.${classes.header}`); + expect(header).not.toBeNull(); + expect(header?.classList.contains("custom-header")).toBe(true); + + const body = button?.querySelector(`.${classes.body}`); + expect(body).not.toBeNull(); + expect(body?.classList.contains("custom-body")).toBe(true); + + const footer = button?.querySelector(`.${classes.footer}`); + expect(footer).not.toBeNull(); + expect(footer?.classList.contains("custom-footer")).toBe(true); + }); + + it("prioritizes title without wrapping and constrains topRight badge with ellipsis", async () => { + await act(async () => + root.render( + + Category Badge} + /> + , + ), + ); + + const button = host.querySelector("button"); + const header = button?.querySelector(`.${classes.header}`); + expect(header).not.toBeNull(); + + const titleText = header?.querySelector("p, span, div"); + expect(titleText?.textContent).toContain("Very Long Title Name That Should Not Wrap"); + + await act(async () => + root.render( + + Custom Title Element} + topRight={Top Right} + /> + , + ), + ); + + expect(host.querySelector("[data-testid='custom-title-node']")).not.toBeNull(); + expect(host.querySelector("[data-testid='custom-top-right']")).not.toBeNull(); + }); + + it("renders custom children in the body slot over description", async () => { + await act(async () => + root.render( + + +
Custom Child Content
+
+
, + ), + ); + + expect(host.querySelector("[data-testid='custom-body-children']")).not.toBeNull(); + expect(host.textContent).toContain("Custom Child Content"); + expect(host.textContent).not.toContain("This should not appear"); + }); + + it("renders an empty description without fallback copy", async () => { + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("No Desc Item"); + expect(host.textContent).not.toContain("No description provided"); + }); + + it("omits header, body, or footer when corresponding props are omitted", async () => { + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.querySelector(`.${classes.header}`)).toBeNull(); + expect(button?.querySelector(`.${classes.body}`)).toBeNull(); + expect(button?.querySelector(`.${classes.footer}`)).toBeNull(); + }); + }); + + describe("states", () => { + it("handles selected state via data-selected attribute", async () => { + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.getAttribute("data-selected")).toBe("true"); + + await act(async () => + root.render( + + + , + ), + ); + + expect(button?.hasAttribute("data-selected")).toBe(false); + }); + + it("handles disabled state and prevents click handlers", async () => { + const onClick = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.disabled).toBe(true); + + await act(async () => button?.click()); + expect(onClick).not.toHaveBeenCalled(); + }); + + it("handles loading state by disabling the button and showing LoadingOverlay", async () => { + const onClick = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.disabled).toBe(true); + + await act(async () => button?.click()); + expect(onClick).not.toHaveBeenCalled(); + }); + }); + + describe("interactions and accessibility", () => { + it("supports focus and click activation", async () => { + const onClick = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + button?.focus(); + expect(document.activeElement).toBe(button); + + await act(async () => { + button?.click(); + }); + expect(onClick).toHaveBeenCalledOnce(); + }); + + it("forwards ref to the underlying HTMLButtonElement", async () => { + const ref = createRef(); + await act(async () => + root.render( + + + , + ), + ); + + expect(ref.current).toBeInstanceOf(HTMLButtonElement); + expect(ref.current?.tagName).toBe("BUTTON"); + }); + + it("resolves radius CSS variables via varsResolver", async () => { + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.style.getPropertyValue("--selectable-card-radius")).toBe("var(--mantine-radius-lg)"); + + await act(async () => + root.render( + + + , + ), + ); + + expect(button?.style.getPropertyValue("--selectable-card-radius")).toBe("var(--mantine-radius-sm)"); + }); + + it("passes arbitrary HTML attributes down to the button element", async () => { + await act(async () => + root.render( + + + , + ), + ); + + const button = host.querySelector("button"); + expect(button?.getAttribute("data-testid")).toBe("my-selectable-card"); + expect(button?.getAttribute("data-custom")).toBe("custom-value"); + expect(button?.tabIndex).toBe(0); + }); + }); +}); diff --git a/packages/ui/src/components/selectable-card/selectable-card.tsx b/packages/ui/src/components/selectable-card/selectable-card.tsx new file mode 100644 index 000000000..c32944917 --- /dev/null +++ b/packages/ui/src/components/selectable-card/selectable-card.tsx @@ -0,0 +1,177 @@ +"use client"; + +import type { ReactNode } from "react"; +import { + Box, + createVarsResolver, + factory, + Group, + LoadingOverlay, + Text, + Tooltip, + useProps, + useStyles, +} from "@mantine/core"; +import type { BoxProps, ElementProps, Factory, MantineRadius, StylesApiProps } from "@mantine/core"; + +import classes from "./selectable-card.module.css"; + +export type SelectableCardStylesNames = "root" | "header" | "body" | "footer"; +export type SelectableCardCssVariables = { + root: "--selectable-card-radius"; +}; + +export interface SelectableCardProps + extends BoxProps, StylesApiProps, Omit, "title"> { + icon?: ReactNode; + title?: ReactNode; + topRight?: ReactNode; + description?: string | null; + footerLeft?: ReactNode; + footerRight?: ReactNode; + selected?: boolean; + loading?: boolean; + disabled?: boolean; + radius?: MantineRadius; + children?: ReactNode; +} + +export type SelectableCardFactory = Factory<{ + props: SelectableCardProps; + ref: HTMLButtonElement; + stylesNames: SelectableCardStylesNames; + vars: SelectableCardCssVariables; +}>; + +const defaultProps: Partial = { + type: "button", +}; + +const varsResolver = createVarsResolver((_theme, { radius }) => ({ + root: { + "--selectable-card-radius": radius ? `var(--mantine-radius-${radius})` : undefined, + }, +})); + +export const SelectableCard = factory((_props) => { + const props = useProps("SelectableCard", defaultProps, _props); + const { + ref, + classNames, + className, + style, + styles, + unstyled, + vars, + attributes, + icon, + title, + topRight, + description, + footerLeft, + footerRight, + selected, + loading = false, + disabled = false, + children, + type = "button", + ...others + } = props; + + const getStyles = useStyles({ + name: "SelectableCard", + classes, + props, + className, + style, + classNames, + styles, + unstyled, + vars, + attributes, + varsResolver, + }); + + return ( + + + + {/* Header Section: Inset background with Title that never elides before the category */} + {(icon || title || topRight) && ( + + + + {icon} + {typeof title === "string" ? ( + + {title} + + ) : ( + title + )} + + {topRight && ( + + {topRight} + + )} + + + )} + + {/* Body: Custom Children or Default Description */} + {children ? ( + {children} + ) : description !== undefined ? ( + + + + {description} + + + + ) : null} + + {/* Footer Section: Inset Dashed Bottom Meta */} + {(footerLeft || footerRight) && ( + + + {footerLeft} + {footerRight} + + + )} + + ); +}); + +SelectableCard.displayName = "@homarr/ui/SelectableCard"; +SelectableCard.classes = classes; diff --git a/packages/ui/src/integration-select-grid/integration-select-grid.spec.tsx b/packages/ui/src/integration-select-grid/integration-select-grid.spec.tsx new file mode 100644 index 000000000..dfd52e5b6 --- /dev/null +++ b/packages/ui/src/integration-select-grid/integration-select-grid.spec.tsx @@ -0,0 +1,279 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import type { Root } from "react-dom/client"; +import { MantineProvider } from "@mantine/core"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import type { IntegrationKind } from "@homarr/definitions"; + +import { IntegrationSelectGrid } from "./integration-select-grid"; + +vi.mock("@homarr/definitions", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getWidgetKindsForIntegration: (kind: IntegrationKind) => { + if (kind === "wud") return []; + return actual.getWidgetKindsForIntegration(kind); + }, + }; +}); + +vi.mock("@homarr/translation/client", () => ({ + useI18n: () => (key: string, values?: { count?: number }) => { + const translations: Record = { + "integration.page.list.search": "Search integrations", + "common.noResults": "No results found", + "integration.grid.noWidgets": "No widgets tied to this service", + "integration.grid.tiedWidgets": "Tied Widgets", + "integration.grid.connected": `${values?.count ?? 0} connected`, + "integration.grid.more": `+${values?.count ?? 0} more`, + "widget.mediaServer.name": "Media Server", + "widget.calendar.name": "Calendar", + "widget.downloads.name": "Downloads", + "integration.category.mediaService": "Media Service", + "integration.category.downloadClient": "Download Client", + "integration.category.calendar": "Calendar", + "integration.category.smartHomeServer": "Smart Home", + "integration.category.usenet": "Usenet", + "integration.category.torrent": "Torrent", + }; + return translations[key] ?? key; + }, +})); + +let root: Root; +let host: HTMLDivElement; + +beforeEach(() => { + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + window.matchMedia = vi.fn().mockImplementation(() => ({ + matches: false, + media: "", + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })); + vi.stubGlobal( + "ResizeObserver", + class { + observe() {} + unobserve() {} + disconnect() {} + }, + ); + host = document.createElement("div"); + document.body.append(host); + root = createRoot(host); +}); + +afterEach(async () => { + await act(async () => root.unmount()); + host.remove(); +}); + +describe("IntegrationSelectGrid", () => { + it("renders a 4-column responsive grid of selectable integration cards", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const cards = host.querySelectorAll("button"); + expect(cards.length).toBe(4); + expect(host.textContent).toContain("Jellyfin"); + expect(host.textContent).toContain("Sonarr"); + expect(host.textContent).toContain("Radarr"); + expect(host.textContent).toContain("SABnzbd"); + }); + + it("renders tied widgets with icons and labels inside integration cards", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("Tied Widgets"); + // Jellyfin is tied to Media Server widget + expect(host.textContent).toContain("Media Server"); + }); + + it("displays fallback message when integration has no tied widgets", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + expect(host.querySelector("button")).not.toBeNull(); + expect(host.textContent).toContain("No widgets tied to this service"); + }); + + it("displays '{count} connected' in footerLeft when integration instances exist", async () => { + const onSelect = vi.fn(); + const integrationData = [ + { kind: "jellyfin" as IntegrationKind, name: "Home Jellyfin" }, + { kind: "jellyfin" as IntegrationKind, name: "Remote Jellyfin" }, + { kind: "sonarr" as IntegrationKind, name: "Sonarr Instance" }, + ]; + + await act(async () => + root.render( + + + , + ), + ); + + // Jellyfin has 2 connected + expect(host.textContent).toContain("2 connected"); + + // Radarr has 0 connected, so it doesn't show "0 connected" + expect(host.textContent).not.toContain("0 connected"); + }); + + it("filters integrations dynamically by search input", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const searchInput = host.querySelector("input"); + expect(searchInput).not.toBeNull(); + + // Type "jelly" + await act(async () => { + if (searchInput) { + const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set; + nativeInputValueSetter?.call(searchInput, "jelly"); + searchInput.dispatchEvent(new Event("input", { bubbles: true })); + searchInput.dispatchEvent(new Event("change", { bubbles: true })); + } + }); + + const cards = host.querySelectorAll("button"); + expect(cards.length).toBe(1); + expect(host.textContent).toContain("Jellyfin"); + expect(host.textContent).not.toContain("Sonarr"); + expect(host.textContent).not.toContain("Radarr"); + }); + + it("displays empty state when no integrations match search query", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const searchInput = host.querySelector("input"); + await act(async () => { + if (searchInput) { + const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set; + nativeInputValueSetter?.call(searchInput, "nonexistent-integration-query"); + searchInput.dispatchEvent(new Event("input", { bubbles: true })); + searchInput.dispatchEvent(new Event("change", { bubbles: true })); + } + }); + + expect(host.textContent).toContain("No results found"); + expect(host.querySelectorAll("button").length).toBe(0); + }); + + it("calls onSelect when Enter is pressed with exactly 1 search match", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const searchInput = host.querySelector("input"); + await act(async () => { + if (searchInput) { + const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set; + nativeInputValueSetter?.call(searchInput, "jelly"); + searchInput.dispatchEvent(new Event("input", { bubbles: true })); + searchInput.dispatchEvent(new Event("change", { bubbles: true })); + } + }); + + await act(async () => { + searchInput?.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true })); + }); + + expect(onSelect).toHaveBeenCalledWith("jellyfin"); + }); + + it("calls onSelect when an integration card is clicked", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const cardButton = host.querySelector("button"); + await act(async () => cardButton?.click()); + expect(onSelect).toHaveBeenCalledWith("sonarr"); + }); + + it("includes mock integration when enableMockIntegration is true", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + expect(host.textContent).toContain("Mock"); + expect(host.textContent).toContain("Sonarr"); + }); + + it("excludes mock integration when enableMockIntegration is false", async () => { + const onSelect = vi.fn(); + await act(async () => + root.render( + + + , + ), + ); + + const buttons = Array.from(host.querySelectorAll("button")); + const mockButton = buttons.find((btn) => btn.getAttribute("aria-label")?.toLowerCase() === "mock"); + expect(mockButton).toBeUndefined(); + }); +}); diff --git a/packages/ui/src/integration-select-grid/integration-select-grid.tsx b/packages/ui/src/integration-select-grid/integration-select-grid.tsx index a0cfec6d8..18d10d8e6 100644 --- a/packages/ui/src/integration-select-grid/integration-select-grid.tsx +++ b/packages/ui/src/integration-select-grid/integration-select-grid.tsx @@ -1,27 +1,68 @@ "use client"; import { useMemo, useState } from "react"; -import { Badge, Card, Center, Group, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core"; -import { IconPuzzle } from "@tabler/icons-react"; +import { Badge, Center, Group, Input, ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core"; +import { + IconAd, + IconApps, + IconBinaryTree, + IconBookmark, + IconBrandDocker, + IconBrandMinecraft, + IconBrowser, + IconBuildingBank, + IconBusStop, + IconCalendar, + IconChartBar, + IconClock, + IconClockPlay, + IconCloud, + IconDeviceCctv, + IconDeviceGamepad, + IconDownload, + IconGraphFilled, + IconHeartRateMonitor, + IconHourglass, + IconMessage, + IconMovie, + IconNotes, + IconPuzzle, + IconReportSearch, + IconRobot, + IconRocket, + IconRss, + IconSearch, + IconServer2, + IconTicket, + IconTopologyFull, + IconTransform, + IconVideo, + IconWall, + IconWind, + IconZoomQuestion, +} from "@tabler/icons-react"; -import type { IntegrationKind } from "@homarr/definitions"; +import type { IntegrationKind, WidgetKind } from "@homarr/definitions"; import { useI18n } from "@homarr/translation/client"; import { IntegrationAvatar } from "../components/integration-avatar"; -import { SelectGridLayout, selectGridCardHeight } from "../select-grid-layout"; +import { SelectableCard } from "../components/selectable-card"; +import { selectGridCols } from "../select-grid-layout"; import { buildSortedIntegrations, categoryTranslationKeys, filterIntegrations } from "./integration-grid-shared"; -import classes from "./integration-select-grid.module.css"; +import type { IntegrationGridItem } from "./integration-grid-shared"; export interface IntegrationSelectGridProps { onSelect: (kind: IntegrationKind) => void; enableMockIntegration?: boolean; allowedKinds?: readonly IntegrationKind[]; + integrationData?: { kind: IntegrationKind; name?: string }[]; } export const IntegrationSelectGrid = ({ onSelect, enableMockIntegration = false, allowedKinds, + integrationData, }: IntegrationSelectGridProps) => { const [search, setSearch] = useState(""); const t = useI18n(); @@ -32,75 +73,182 @@ export const IntegrationSelectGrid = ({ const filtered = useMemo(() => filterIntegrations(integrations, search), [integrations, search]); return ( - - {filtered.map((integration) => ( - onSelect(integration.kind)} - aria-label={integration.name} - > - - - - - - {integration.name} - - - - - - - ))} - {filtered.length === 0 && ( -
- {t("common.noResults")} -
- )} -
- ); -}; - -const IntegrationGridMeta = ({ integration }: { integration: ReturnType[number] }) => { - const t = useI18n(); - return ( - - - {integration.categories.slice(0, 2).map((category) => ( - - {categoryTranslationKeys[category] ? t(categoryTranslationKeys[category] as never) : category} - - ))} - - {integration.widgets.length > 0 ? ( - - {integration.widgets.map((widgetKind) => ( - - {t(`widget.${widgetKind}.name`)} - - ))} - + + {/* Top Search Input */} + setSearch(event.currentTarget.value)} + leftSection={} + placeholder={`${t("integration.page.list.search")}...`} + aria-label={t("integration.page.list.search")} + data-autofocus + onKeyDown={(event) => { + if (event.key === "Enter" && filtered.length === 1 && filtered[0]) { + onSelect(filtered[0].kind); } - > - }> - {t("integration.grid.widgetCount", { count: String(integration.widgets.length) })} - - - ) : ( - - {t("integration.grid.noWidgets")} - - )} + }} + /> + + {/* Scrollable Container with Integration Cards */} + + + + {filtered.map((integration) => ( + i.kind === integration.kind).length} + /> + ))} + + + {filtered.length === 0 && ( +
+ {t("common.noResults")} +
+ )} +
+
); }; + +// ========================================================================= +// IntegrationCard: Shared Modular SelectableCard with Tied Widgets +// ========================================================================= +const IntegrationCard = ({ + integration, + onSelect, + connectedCount = 0, +}: { + integration: IntegrationGridItem; + onSelect: (kind: IntegrationKind) => void; + connectedCount?: number; +}) => { + const t = useI18n(); + + return ( + onSelect(integration.kind)} + aria-label={integration.name} + icon={} + title={integration.name} + topRight={} + footerLeft={ + connectedCount > 0 ? ( + + {t("integration.grid.connected", { count: connectedCount })} + + ) : null + } + > + + {t("integration.grid.tiedWidgets")} + + + + ); +}; + +const widgetIconsMap: Partial> = { + clock: IconClock, + weather: IconCloud, + airQuality: IconWind, + countdown: IconHourglass, + timer: IconClockPlay, + app: IconApps, + assistant: IconRobot, + iframe: IconBrowser, + video: IconDeviceCctv, + notebook: IconNotes, + anchorNote: IconNotes, + dnsHoleSummary: IconAd, + dnsHoleControls: IconDeviceGamepad, + "smartHome-entityState": IconBinaryTree, + "smartHome-executeAutomation": IconBinaryTree, + stockPrice: IconBuildingBank, + mediaServer: IconVideo, + calendar: IconCalendar, + downloads: IconDownload, + "mediaRequests-requestList": IconZoomQuestion, + "mediaRequests-requestStats": IconChartBar, + mediaTranscoding: IconTransform, + mediaMissing: IconMovie, + minecraftServerStatus: IconBrandMinecraft, + networkControllerSummary: IconTopologyFull, + networkControllerStatus: IconTopologyFull, + rssFeed: IconRss, + bookmarks: IconBookmark, + indexerManager: IconReportSearch, + healthMonitoring: IconHeartRateMonitor, + releases: IconRocket, + mediaReleases: IconTicket, + dockerContainers: IconBrandDocker, + firewall: IconWall, + notifications: IconMessage, + systemResources: IconGraphFilled, + coolify: IconCloud, + systemDisks: IconServer2, + timetable: IconBusStop, +}; + +const IntegrationTiedWidgets = ({ widgets, limit }: { widgets: WidgetKind[]; limit?: number }) => { + const t = useI18n(); + if (widgets.length === 0) { + return ( + + {t("integration.grid.noWidgets")} + + ); + } + + const items = limit ? widgets.slice(0, limit) : widgets; + const moreCount = limit ? widgets.length - limit : 0; + + return ( + + {items.map((widgetKind) => { + const WidgetIcon = widgetIconsMap[widgetKind] ?? IconPuzzle; + return ( + } + style={{ fontWeight: 500 }} + > + {t(`widget.${widgetKind}.name`)} + + ); + })} + {moreCount > 0 && ( + + {t("integration.grid.more", { count: moreCount })} + + )} + + ); +}; + +const IntegrationCategoryBadges = ({ + categories, + limit, +}: { + categories: IntegrationGridItem["categories"]; + limit?: number; +}) => { + const t = useI18n(); + const items = limit ? categories.slice(0, limit) : categories; + + return ( + + {items.map((category) => ( + + {categoryTranslationKeys[category] ? t(categoryTranslationKeys[category] as never) : category} + + ))} + + ); +}; diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 034197e44..8580c9ed8 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -13,7 +13,9 @@ }, "exports": { ".": "./index.ts", - "./_inputs/json-path-tree-picker": "./src/_inputs/json-path-tree-picker.tsx", + "./inputs": "./src/_inputs/index.ts", + "./inputs/form": "./src/_inputs/form.ts", + "./inputs/json-path-tree-picker": "./src/_inputs/json-path-tree-picker.tsx", "./custom-api/component": "./src/custom-api/component.tsx", "./custom-api/custom-jsx-display": "./src/custom-api/custom-jsx-display.tsx", "./custom-api/extract-display-data": "./src/custom-api/extract-display-data.ts", diff --git a/packages/widgets/src/_inputs/widget-app-input.tsx b/packages/widgets/src/_inputs/widget-app-input.tsx index 933f7daf9..313d7243f 100644 --- a/packages/widgets/src/_inputs/widget-app-input.tsx +++ b/packages/widgets/src/_inputs/widget-app-input.tsx @@ -17,6 +17,12 @@ import type { CommonWidgetInputProps } from "./common"; import { useWidgetInputTranslation } from "./common"; import { useFormContext } from "./form"; +const renderHereLink = (children: React.ReactNode) => ( + + {children} + +); + export const WidgetAppInput = ({ property, kind }: CommonWidgetInputProps<"app">) => { const t = useI18n(); const tInput = useWidgetInputTranslation(kind, property); @@ -49,13 +55,9 @@ export const WidgetAppInput = ({ property, kind }: CommonWidgetInputProps<"app"> } inputWrapperOrder={["label", "input", "description", "error"]} description={ - + {t.rich("widget.common.app.description", { - here: () => ( - - {t("common.here")} - - ), + here: renderHereLink, })} } diff --git a/packages/widgets/src/app/component.tsx b/packages/widgets/src/app/component.tsx index 8475de4e0..d78be5d24 100644 --- a/packages/widgets/src/app/component.tsx +++ b/packages/widgets/src/app/component.tsx @@ -26,7 +26,7 @@ export default function AppWidget({ options, isEditMode, height, width }: Widget const t = useI18n(); const settings = useSettings(); const board = useRequiredBoard(); - const appQuery = clientApi.app.byId.useQuery({ id: options.appId }); + const appQuery = clientApi.app.byId.useQuery({ id: options.appId }, { enabled: Boolean(options.appId) }); const app = getUsableWidgetQueryData(appQuery); const href = getSafeAppHref(app?.href); useRegisterSpotlightContextResults( @@ -50,6 +50,7 @@ export default function AppWidget({ options, isEditMode, height, width }: Widget [app, href, options.openInNewTab], ); + if (!options.appId) return ; if (isInitialWidgetQueryPending(appQuery)) return ; if (!app) return ; diff --git a/packages/widgets/src/catalog.ts b/packages/widgets/src/catalog.ts index 6ca945d47..3287a15d5 100644 --- a/packages/widgets/src/catalog.ts +++ b/packages/widgets/src/catalog.ts @@ -15,7 +15,6 @@ import { IconBuildingBank, IconBusStop, IconCalendar, - IconCalendarWeek, IconChartAreaLine, IconChartBar, IconClock, @@ -60,7 +59,6 @@ import type { TablerIcon } from "@homarr/ui"; export const widgetCatalogIcons: Record = { clock: IconClock, weather: IconCloud, - today: IconCalendarWeek, airQuality: IconWind, countdown: IconHourglass, timer: IconClockPlay, diff --git a/packages/widgets/src/index.tsx b/packages/widgets/src/index.tsx index 060185a39..86ea2599d 100644 --- a/packages/widgets/src/index.tsx +++ b/packages/widgets/src/index.tsx @@ -63,7 +63,6 @@ import * as beszelAlerts from "./beszel-alerts"; import * as beszelSystemStats from "./beszel-system-stats"; import * as video from "./video"; import * as weather from "./weather"; -import * as today from "./today"; import * as airQuality from "./air-quality"; import * as countdown from "./countdown"; import * as timer from "./timer"; @@ -98,7 +97,6 @@ export { AssistantWidgetRendererProvider } from "./assistant/context"; export const widgetImports = { clock, weather, - today, airQuality, countdown, timer, diff --git a/packages/widgets/src/manifest.ts b/packages/widgets/src/manifest.ts index 9d1cb6e6d..09795db10 100644 --- a/packages/widgets/src/manifest.ts +++ b/packages/widgets/src/manifest.ts @@ -24,7 +24,6 @@ export type WidgetResources = { const moduleLoaders: Record Promise> = { clock: () => import("./clock") as Promise, weather: () => import("./weather") as Promise, - today: () => import("./today") as Promise, airQuality: () => import("./air-quality") as Promise, countdown: () => import("./countdown") as Promise, timer: () => import("./timer") as Promise, @@ -89,7 +88,6 @@ const moduleLoaders: Record Promise> = { const componentLoaders: Record Promise> = { clock: () => import("./clock/component") as Promise, weather: () => import("./weather/component") as Promise, - today: () => import("./today/component") as Promise, airQuality: () => import("./air-quality/component") as Promise, countdown: () => import("./countdown/component") as Promise, timer: () => import("./timer/component") as Promise, diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx index 36ec3815b..bd0d9c835 100644 --- a/packages/widgets/src/modals/widget-edit-modal.tsx +++ b/packages/widgets/src/modals/widget-edit-modal.tsx @@ -58,6 +58,7 @@ export interface WidgetEditModalProps { appId?: string; integrationEditForm?: ComponentType; onIntegrationSaved?: () => void; + onOpenNewIntegration?: (onCreated?: (id: string) => void) => void; } export const getSelectedWidgetIntegrations = ( @@ -217,6 +218,17 @@ export const WidgetEditModal = createModal>(({ handleSubmit(event); }; + const canCreateIntegration = maxIntegrations === 1 || form.values.integrationIds.length < maxIntegrations; + const handleOpenNewIntegration = innerProps.onOpenNewIntegration + ? () => { + innerProps.onOpenNewIntegration?.((newId) => { + if (newId) { + form.setFieldValue("integrationIds", [...(maxIntegrations > 1 ? form.values.integrationIds : []), newId]); + } + }); + } + : undefined; + const widgetFormContent = ( {canConfigureWidget && innerProps.integrationSupport && ( @@ -225,6 +237,7 @@ export const WidgetEditModal = createModal>(({ data={innerProps.integrationData} canSelectMultiple={maxIntegrations > 1} withAsterisk={integrationsRequired} + onOpenNewIntegration={canCreateIntegration ? handleOpenNewIntegration : undefined} {...form.getInputProps("integrationIds")} /> )} @@ -413,6 +426,5 @@ export const WidgetEditModal = createModal>(({ return t("item.edit.title"); }, size: modalSizeForm, - presentation: "inspector", closeOnClickOutside: false, }); diff --git a/packages/widgets/src/today/component.tsx b/packages/widgets/src/today/component.tsx deleted file mode 100644 index 9b7493577..000000000 --- a/packages/widgets/src/today/component.tsx +++ /dev/null @@ -1,223 +0,0 @@ -"use client"; - -import { - Badge, - Box, - Group, - Paper, - Progress, - RingProgress, - ScrollArea, - SimpleGrid, - Stack, - Text, - VisuallyHidden, -} from "@mantine/core"; - -import { useCurrentIntlLocale, useScopedI18n } from "@homarr/translation/client"; - -import { formatLocalizedDate } from "../common/locale"; -import { useWidgetNow } from "../common/use-widget-now"; -import type { WidgetComponentProps } from "../definition"; -import { getTodayLayout, getTodayStats } from "./date-stats"; - -const formatProgress = (value: number) => Math.round(value); - -export default function TodayWidget({ options, width, height, displayMode }: WidgetComponentProps<"today">) { - const now = useWidgetNow("minute"); - const locale = useCurrentIntlLocale(); - const t = useScopedI18n("widget.today"); - - if (now === null) { - return ( - - — - - ); - } - - const stats = getTodayStats(now, options.weekConvention, locale); - if (displayMode === "advanced") { - return ; - } - - const layout = getTodayLayout(width, height); - const weekday = formatLocalizedDate(now, locale, { weekday: "long" }); - const month = formatLocalizedDate(now, locale, { month: "long" }); - const fullDate = formatLocalizedDate(now, locale, { dateStyle: "long" }); - - if (layout.tiny) { - return ( - - - {weekday} · {month} - - - {now.getDate()} - - - ); - } - - return ( - - - - - {weekday} - - - {layout.showFullDate ? fullDate : `${month} ${now.getDate()}`} - - - {options.showYearProgress && ( - - - {formatProgress(stats.yearProgress)}% - - } - /> - - - {t("yearProgress", { progress: formatProgress(stats.yearProgress) })} - - -
- )} - - - {layout.showStats && ( - - {options.showWeekNumber && {t("weekNumber", { week: stats.weekNumber })}} - {options.showDayOfYear && ( - {t("dayOfYear", { day: stats.dayOfYear, total: stats.daysInYear })} - )} - {layout.showQuarter && options.showQuarter && ( - {t("quarter", { quarter: stats.quarter })} - )} - {layout.showQuarter && ( - - {t("daysAfterToday", { days: stats.daysAfterToday })} - - )} - - )} - - {layout.showMonthProgress && } - - ); -} - -interface AdvancedTodayProps { - now: Date; - locale: string; - options: WidgetComponentProps<"today">["options"]; - stats: ReturnType; -} - -const AdvancedToday = ({ now, locale, options, stats }: AdvancedTodayProps) => { - const t = useScopedI18n("widget.today"); - const numberFormatter = new Intl.NumberFormat(locale); - const visibleMetrics = [ - options.showWeekNumber && { label: t("advanced.week"), value: numberFormatter.format(stats.weekNumber) }, - options.showDayOfYear && { - label: t("advanced.dayOfYear"), - value: `${numberFormatter.format(stats.dayOfYear)} / ${numberFormatter.format(stats.daysInYear)}`, - }, - options.showQuarter && { - label: t("advanced.quarter"), - value: t("advanced.quarterValue", { quarter: stats.quarter }), - }, - { label: t("advanced.daysRemaining"), value: numberFormatter.format(stats.daysAfterToday) }, - ].filter((metric): metric is { label: string; value: string } => Boolean(metric)); - - return ( - - - - - {formatLocalizedDate(now, locale, { dateStyle: "full" })} - - {options.showWeekNumber && ( - - {t(`weekConvention.${options.weekConvention}`)} - - )} - - - - {visibleMetrics.map((metric) => ( - - - {metric.label} - - - {metric.value} - - - ))} - - - - - - {options.showYearProgress && } - - - - {Array.from({ length: 12 }, (_, monthIndex) => { - const date = new Date(now.getFullYear(), monthIndex, 1); - const isCurrent = monthIndex === stats.monthIndex; - return ( - - - {formatLocalizedDate(date, locale, { month: "short" })} - - {isCurrent && ( - - {now.getDate()} - - )} - - ); - })} - - - - ); -}; - -const ProgressMetric = ({ label, value }: { label: string; value: number }) => { - const roundedValue = formatProgress(value); - return ( - - - - {label} - - - {roundedValue}% - - - - - ); -}; - -const toDateTime = (date: Date) => { - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, "0"); - const day = String(date.getDate()).padStart(2, "0"); - return `${year}-${month}-${day}`; -}; diff --git a/packages/widgets/src/today/date-stats.spec.ts b/packages/widgets/src/today/date-stats.spec.ts deleted file mode 100644 index 414732dfa..000000000 --- a/packages/widgets/src/today/date-stats.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { getTodayLayout, getTodayStats } from "./date-stats"; - -describe("today date statistics", () => { - it("accounts for leap years", () => { - const stats = getTodayStats(new Date(2024, 11, 31, 12), "iso"); - expect(stats.dayOfYear).toBe(366); - expect(stats.daysInYear).toBe(366); - expect(stats.daysAfterToday).toBe(0); - }); - - it("uses the requested week convention", () => { - const date = new Date(2023, 0, 1, 12); - expect(getTodayStats(date, "iso").weekNumber).toBe(52); - expect(getTodayStats(date, "locale", "en-US").weekNumber).toBe(1); - expect(getTodayStats(date, "locale", "de-DE").weekNumber).toBe(52); - }); - - it("returns bounded calendar progress", () => { - const stats = getTodayStats(new Date(2026, 7, 20, 12), "iso"); - expect(stats.quarter).toBe(3); - expect(stats.weekProgress).toBeGreaterThanOrEqual(0); - expect(stats.weekProgress).toBeLessThanOrEqual(100); - expect(stats.monthProgress).toBeGreaterThanOrEqual(0); - expect(stats.monthProgress).toBeLessThanOrEqual(100); - expect(stats.yearProgress).toBeGreaterThanOrEqual(0); - expect(stats.yearProgress).toBeLessThanOrEqual(100); - }); -}); - -describe("today compact layout", () => { - it("keeps tiny widgets focused on the date", () => { - expect(getTodayLayout(140, 120)).toMatchObject({ tiny: true, showStats: false }); - }); - - it("reveals secondary information only when both axes have room", () => { - expect(getTodayLayout(240, 120)).toMatchObject({ showStats: true, showQuarter: false }); - expect(getTodayLayout(300, 180)).toEqual({ - tiny: false, - showFullDate: true, - showStats: true, - showQuarter: true, - showMonthProgress: true, - }); - }); -}); diff --git a/packages/widgets/src/today/date-stats.ts b/packages/widgets/src/today/date-stats.ts deleted file mode 100644 index 790d41016..000000000 --- a/packages/widgets/src/today/date-stats.ts +++ /dev/null @@ -1,172 +0,0 @@ -import dayjs from "dayjs"; -import dayOfYear from "dayjs/plugin/dayOfYear"; -import quarterOfYear from "dayjs/plugin/quarterOfYear"; - -dayjs.extend(dayOfYear); -dayjs.extend(quarterOfYear); - -export type WeekConvention = "locale" | "iso"; - -export interface TodayStats { - dayOfYear: number; - daysInYear: number; - daysAfterToday: number; - monthIndex: number; - quarter: number; - weekNumber: number; - weekProgress: number; - monthProgress: number; - yearProgress: number; -} - -const getProgress = (value: number, start: number, end: number) => { - if (end <= start) return 0; - return Math.min(100, Math.max(0, ((value - start) / (end - start)) * 100)); -}; - -export const getTodayStats = (date: Date, weekConvention: WeekConvention, locale = "en-US"): TodayStats => { - const value = dayjs(date); - const yearStart = value.startOf("year"); - const nextYearStart = yearStart.add(1, "year"); - const monthStart = value.startOf("month"); - const nextMonthStart = monthStart.add(1, "month"); - const weekRules = getWeekRules(weekConvention, locale); - const weekDay = value.day() === 0 ? 7 : value.day(); - const daysSinceWeekStart = (weekDay - weekRules.firstDay + 7) % 7; - const weekStart = new Date(date.getFullYear(), date.getMonth(), date.getDate() - daysSinceWeekStart); - const nextWeekStart = new Date(weekStart.getFullYear(), weekStart.getMonth(), weekStart.getDate() + 7); - const currentDayOfYear = value.dayOfYear(); - const daysInYear = yearStart.endOf("year").dayOfYear(); - - return { - dayOfYear: currentDayOfYear, - daysInYear, - daysAfterToday: daysInYear - currentDayOfYear, - monthIndex: value.month(), - quarter: value.quarter(), - weekNumber: getWeekNumber(date, weekRules), - weekProgress: getProgress(value.valueOf(), weekStart.getTime(), nextWeekStart.getTime()), - monthProgress: getProgress(value.valueOf(), monthStart.valueOf(), nextMonthStart.valueOf()), - yearProgress: getProgress(value.valueOf(), yearStart.valueOf(), nextYearStart.valueOf()), - }; -}; - -interface WeekRules { - firstDay: number; - minimalDays: number; -} - -interface LocaleWeekInfo { - firstDay: number; - minimalDays?: number; -} - -type LocaleWithWeekInfo = Intl.Locale & { - weekInfo?: LocaleWeekInfo; - getWeekInfo?: () => LocaleWeekInfo; -}; - -const fourDayMinimumRegions = new Set([ - "AD", - "AN", - "AT", - "AX", - "BE", - "BG", - "CH", - "CZ", - "DE", - "DK", - "EE", - "ES", - "FI", - "FJ", - "FO", - "FR", - "GF", - "GB", - "GG", - "GI", - "GP", - "GR", - "IE", - "IM", - "IS", - "IT", - "LI", - "LT", - "LU", - "MC", - "MQ", - "NL", - "NO", - "PL", - "PT", - "RE", - "RU", - "SE", - "SJ", - "SK", - "VA", -]); - -const getWeekRules = (convention: WeekConvention, locale: string): WeekRules => { - if (convention === "iso") return { firstDay: 1, minimalDays: 4 }; - try { - const localeInfo = new Intl.Locale(locale) as LocaleWithWeekInfo; - const weekInfo = localeInfo.weekInfo ?? localeInfo.getWeekInfo?.(); - if (weekInfo) { - const region = localeInfo.maximize().region; - const fallbackMinimalDays = region && fourDayMinimumRegions.has(region) ? 4 : 1; - return { firstDay: weekInfo.firstDay, minimalDays: weekInfo.minimalDays ?? fallbackMinimalDays }; - } - } catch { - // Invalid locales fall back to the common US week convention. - } - return { firstDay: 7, minimalDays: 1 }; -}; - -const getWeekNumber = (date: Date, rules: WeekRules): number => { - const dateDay = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()); - let weekYear = date.getFullYear(); - let firstWeekStart = getFirstWeekStart(weekYear, rules); - - if (dateDay < firstWeekStart) { - weekYear -= 1; - firstWeekStart = getFirstWeekStart(weekYear, rules); - } else { - const nextFirstWeekStart = getFirstWeekStart(weekYear + 1, rules); - if (dateDay >= nextFirstWeekStart) return 1; - } - - return Math.floor((dateDay - firstWeekStart) / (7 * 86_400_000)) + 1; -}; - -const getFirstWeekStart = (year: number, rules: WeekRules): number => { - const januaryFirst = Date.UTC(year, 0, 1); - const sundayBasedWeekDay = new Date(januaryFirst).getUTCDay(); - const weekDay = sundayBasedWeekDay === 0 ? 7 : sundayBasedWeekDay; - const daysBeforeJanuary = (weekDay - rules.firstDay + 7) % 7; - const daysInFirstWeek = 7 - daysBeforeJanuary; - if (daysInFirstWeek >= rules.minimalDays) return januaryFirst - daysBeforeJanuary * 86_400_000; - return januaryFirst + daysInFirstWeek * 86_400_000; -}; - -export interface TodayLayout { - tiny: boolean; - showFullDate: boolean; - showStats: boolean; - showQuarter: boolean; - showMonthProgress: boolean; -} - -export const getTodayLayout = (width: number, height: number): TodayLayout => { - const tiny = width < 150 || height < 96; - return { - tiny, - showFullDate: !tiny && width >= 180, - showStats: !tiny && width >= 210 && height >= 115, - showQuarter: width >= 240 && height >= 145, - showMonthProgress: width >= 300 && height >= 170, - }; -}; diff --git a/packages/widgets/src/today/index.ts b/packages/widgets/src/today/index.ts deleted file mode 100644 index e494df35b..000000000 --- a/packages/widgets/src/today/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IconCalendarWeek } from "@tabler/icons-react"; - -import { createWidgetDefinition } from "../definition"; -import { optionsBuilder } from "../options"; - -export const { definition, componentLoader } = createWidgetDefinition("today", { - icon: IconCalendarWeek, - supportsAdvancedFocus: true, - createOptions() { - return optionsBuilder.from((factory) => ({ - weekConvention: factory.select({ - defaultValue: "locale", - options: [ - { value: "locale", label: (t) => t("widget.today.option.weekConvention.options.locale") }, - { value: "iso", label: (t) => t("widget.today.option.weekConvention.options.iso") }, - ], - }), - showWeekNumber: factory.switch({ defaultValue: true }), - showDayOfYear: factory.switch({ defaultValue: true }), - showQuarter: factory.switch({ defaultValue: true }), - showYearProgress: factory.switch({ defaultValue: true }), - })); - }, -}).withDynamicImport(() => import("./component")); diff --git a/packages/widgets/src/widget-integration-select.tsx b/packages/widgets/src/widget-integration-select.tsx index ed82c4099..e24418395 100644 --- a/packages/widgets/src/widget-integration-select.tsx +++ b/packages/widgets/src/widget-integration-select.tsx @@ -32,6 +32,7 @@ interface WidgetIntegrationSelectProps { canSelectMultiple?: boolean; data: IntegrationSelectOption[]; withAsterisk?: boolean; + onOpenNewIntegration?: () => void; } export const WidgetIntegrationSelect = ({ data, @@ -40,6 +41,7 @@ export const WidgetIntegrationSelect = ({ label, canSelectMultiple = true, withAsterisk = false, + onOpenNewIntegration, ...props }: WidgetIntegrationSelectProps) => { const t = useI18n(); @@ -109,11 +111,19 @@ export const WidgetIntegrationSelect = ({ - - {t("widget.common.integration.manage")} - - + onOpenNewIntegration ? ( + + + {t("integration.action.create")} + + + ) : ( + + + {t("widget.common.integration.manage")} + + + ) } pointer onClick={() => combobox.openDropdown()} From 4c475b8c0a15f32cf6a86169f4c29a6aa53788d2 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 21 Aug 2026 12:49:03 +0200 Subject: [PATCH 04/11] ci: publish v2 images reliably --- .github/workflows/ci.yml | 26 ++++++--- .github/workflows/workshop.yml | 96 ++++++++++++++++++++++++++++------ 2 files changed, 99 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index faf08f5bc..89aa72bba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -374,21 +374,31 @@ jobs: AMD64_DIGEST: ${{ needs.preview-amd64.outputs.digest }} ARM64_DIGEST: ${{ needs.preview-arm64.outputs.digest }} run: | - docker buildx imagetools create -t "${PREVIEW_IMAGE}:${PREVIEW_TAG}" \ + tags=(-t "${PREVIEW_IMAGE}:${PREVIEW_TAG}") + if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then + tags+=(-t "${PREVIEW_IMAGE}:pr-6545") + fi + docker buildx imagetools create "${tags[@]}" \ "${PREVIEW_IMAGE}@${AMD64_DIGEST}" \ "${PREVIEW_IMAGE}@${ARM64_DIGEST}" - name: Verify preview manifest platforms if: "!env.ACT" run: | - image="${PREVIEW_IMAGE}:${PREVIEW_TAG}" - manifest="$(docker buildx imagetools inspect "$image" --raw)" - for architecture in amd64 arm64; do - jq --exit-status --arg architecture "$architecture" \ - 'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \ - <<<"$manifest" >/dev/null + tags=("${PREVIEW_TAG}") + if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then + tags+=("pr-6545") + fi + for tag in "${tags[@]}"; do + image="${PREVIEW_IMAGE}:${tag}" + manifest="$(docker buildx imagetools inspect "$image" --raw)" + for architecture in amd64 arm64; do + jq --exit-status --arg architecture "$architecture" \ + 'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \ + <<<"$manifest" >/dev/null + done + docker buildx imagetools inspect "$image" done - docker buildx imagetools inspect "$image" cleanup: name: Clean up preview image diff --git a/.github/workflows/workshop.yml b/.github/workflows/workshop.yml index b8e1e34f5..3fec8610d 100644 --- a/.github/workflows/workshop.yml +++ b/.github/workflows/workshop.yml @@ -27,37 +27,103 @@ jobs: - run: pnpm test:workshop - run: pnpm test:workshop-image - publish: + publish-amd64: if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main') needs: validate runs-on: ubuntu-24.04 - timeout-minutes: 30 + timeout-minutes: 20 permissions: contents: read packages: write + outputs: + digest: ${{ steps.build.outputs.digest }} steps: - uses: actions/checkout@v7 - - uses: docker/setup-qemu-action@v4 - uses: docker/setup-buildx-action@v4 - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/metadata-action@v6 - id: metadata - with: - images: ghcr.io/homarr-labs/workshop - tags: | - type=sha,prefix=sha-,format=long - type=raw,value=v2,enable=${{ github.ref_name == 'v2' || github.ref_name == 'release/v2' }} - type=raw,value=latest,enable=${{ github.ref_name == 'main' }} - uses: docker/build-push-action@v7 + id: build with: context: . file: apps/workshop/Dockerfile target: production - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.metadata.outputs.tags }} - labels: ${{ steps.metadata.outputs.labels }} + platforms: linux/amd64 + outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=workshop-linux-amd64 + cache-to: type=gha,scope=workshop-linux-amd64,mode=max + + publish-arm64: + if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main') + needs: validate + runs-on: ubuntu-24.04-arm + timeout-minutes: 20 + permissions: + contents: read + packages: write + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - uses: actions/checkout@v7 + - uses: docker/setup-buildx-action@v4 + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v7 + id: build + with: + context: . + file: apps/workshop/Dockerfile + target: production + platforms: linux/arm64 + outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=workshop-linux-arm64 + cache-to: type=gha,scope=workshop-linux-arm64,mode=max + + publish: + if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main') + needs: [publish-amd64, publish-arm64] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read + packages: write + env: + WORKSHOP_IMAGE: ghcr.io/homarr-labs/workshop + steps: + - uses: docker/setup-buildx-action@v4 + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Publish multi-platform image + env: + AMD64_DIGEST: ${{ needs.publish-amd64.outputs.digest }} + ARM64_DIGEST: ${{ needs.publish-arm64.outputs.digest }} + run: | + tags=(-t "${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}") + if [[ "${GITHUB_REF_NAME}" == "v2" || "${GITHUB_REF_NAME}" == "release/v2" ]]; then + tags+=(-t "${WORKSHOP_IMAGE}:v2") + fi + if [[ "${GITHUB_REF_NAME}" == "main" ]]; then + tags+=(-t "${WORKSHOP_IMAGE}:latest") + fi + docker buildx imagetools create "${tags[@]}" \ + "${WORKSHOP_IMAGE}@${AMD64_DIGEST}" \ + "${WORKSHOP_IMAGE}@${ARM64_DIGEST}" + - name: Verify multi-platform image + run: | + image="${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}" + manifest="$(docker buildx imagetools inspect "$image" --raw)" + for architecture in amd64 arm64; do + jq --exit-status --arg architecture "$architecture" \ + 'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \ + <<<"$manifest" >/dev/null + done + docker buildx imagetools inspect "$image" From 9b406d5d83c8c44c87b1ec0f8e2d7890648a3d40 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Aug 2026 13:11:40 +0200 Subject: [PATCH 05/11] fix(beszel): restore balanced system grid layout (#6647) --- .../docs/widgets/beszel-system-grid/index.mdx | 2 +- .../widgets/src/beszel-system-grid/component.tsx | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/docs/docs/widgets/beszel-system-grid/index.mdx b/apps/docs/docs/widgets/beszel-system-grid/index.mdx index 137d3d003..1748521e4 100644 --- a/apps/docs/docs/widgets/beszel-system-grid/index.mdx +++ b/apps/docs/docs/widgets/beszel-system-grid/index.mdx @@ -16,7 +16,7 @@ import beszelSystemGrid from "./img/beszel-system-grid.png"; -This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Cards adapt their layout based on available space. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount. +This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Resizing the widget adjusts its rows and columns to keep the cards balanced. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount. Select a system card to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts. diff --git a/packages/widgets/src/beszel-system-grid/component.tsx b/packages/widgets/src/beszel-system-grid/component.tsx index d476371ce..e1bdaee80 100644 --- a/packages/widgets/src/beszel-system-grid/component.tsx +++ b/packages/widgets/src/beszel-system-grid/component.tsx @@ -123,19 +123,12 @@ const MIN_CELL_HEIGHT = 80; const ADVANCED_MIN_CELL_WIDTH = 320; const ADVANCED_MIN_CELL_HEIGHT = 420; -const getColCount = (width: number, _height: number, itemCount: number, minCellWidth = MIN_CELL_WIDTH): number => { +const getColCount = (width: number, height: number, itemCount: number, minCellWidth = MIN_CELL_WIDTH): number => { if (itemCount <= 1) return 1; const maxCols = Math.min(itemCount, Math.max(1, Math.floor(width / minCellWidth))); - - let best = 1; - for (let c = 1; c <= maxCols; c++) { - const emptyCells = (c - (itemCount % c)) % c; - const bestEmpty = (best - (itemCount % best)) % best; - if (emptyCells < bestEmpty || (emptyCells === bestEmpty && c > best)) { - best = c; - } - } - return best; + const aspectRatio = width / Math.max(height, MIN_CELL_HEIGHT); + const idealCols = Math.round(Math.sqrt(itemCount * aspectRatio)); + return Math.min(maxCols, Math.max(1, idealCols)); }; interface MetricRowProps { From 002381d294cdf301e3906d57525d87f67c5cb9dc Mon Sep 17 00:00:00 2001 From: Bergasha Date: Fri, 21 Aug 2026 23:12:31 +1000 Subject: [PATCH 06/11] feat(v2 docker widget): Fixes column-resize squashing (#6646) * fix(dataTable): resizing a column no longer squashes the others mantine-datatable's built-in column resize keeps the table's total width fixed by shrinking every other column to compensate, which collapses columns toward zero in a narrow widget (open upstream issue, no fix: icflorescu/mantine-datatable#discussions-757). Replaced it with our own resize handle that only ever changes the width of the column being dragged - the table grows and scrolls horizontally instead. Shared via usePersistedTableLayout, so docker, downloads, and beszel-system-table all get it. * feat(docker): enable hold-Shift advanced focus preview The widget already renders differently for displayMode="advanced", but was missing supportsAdvancedFocus: true, so the board never offered the hold-Shift preview for it - matching downloads and media-server, which both set this flag. * style(docker): tighten row density, decouple icon size from row height Circular avatar, denser row/footer padding, and action icons rendered larger than their button via absolute positioning so they don't inflate row height (the button stays small and fixed - it repeats once per row, so any growth there multiplies across every visible container). * fix(data-table): harden shared column resizing * fix(data-table): account for board scaling * fix(data-table): compact shared headers * fix(data-table): fit columns to widget * fix(downloads): simplify advanced details * test(widgets): align Docker advanced focus * fix(downloads): widen expanded details --------- Co-authored-by: ajnart --- packages/translation/src/lang/en.json | 46 ++++---- .../widgets/src/common/homarr-data-table.css | 33 ++++++ .../widgets/src/common/homarr-data-table.tsx | 105 +++++++++++++++++- .../src/common/use-persisted-table-layout.ts | 2 +- packages/widgets/src/definition.spec.ts | 2 +- packages/widgets/src/docker/component.tsx | 65 ++++++++--- packages/widgets/src/docker/index.ts | 1 + packages/widgets/src/downloads/component.tsx | 34 +++--- .../widgets/src/downloads/helpers.spec.ts | 6 +- packages/widgets/src/downloads/helpers.ts | 13 ++- patches/mantine-datatable@9.3.1.patch | 17 +++ pnpm-lock.yaml | 7 +- pnpm-workspace.yaml | 1 + 13 files changed, 270 insertions(+), 62 deletions(-) create mode 100644 patches/mantine-datatable@9.3.1.patch diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 492bd1880..ce842242d 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -4836,70 +4836,70 @@ }, "items": { "actions": { - "columnTitle": "Controls" + "columnTitle": "Actions" }, "added": { "columnTitle": "Added", - "detailsTitle": "Date Added" + "detailsTitle": "Added" }, "category": { - "columnTitle": "Extras", - "detailsTitle": "Categories (Or extra information)" + "columnTitle": "Category", + "detailsTitle": "Category" }, "downSpeed": { "columnTitle": "Down", - "detailsTitle": "Download Speed" + "detailsTitle": "Down" }, "index": { "columnTitle": "#", - "detailsTitle": "Current index within client" + "detailsTitle": "Index" }, "id": { - "columnTitle": "Id", - "detailsTitle": "Item ID" + "columnTitle": "ID", + "detailsTitle": "ID" }, "integration": { - "columnTitle": "Integration" + "columnTitle": "Source" }, "name": { - "columnTitle": "Job name" + "columnTitle": "Name" }, "progress": { "columnTitle": "Progress", - "detailsTitle": "Download Progress" + "detailsTitle": "Progress" }, "ratio": { "columnTitle": "Ratio", - "detailsTitle": "Torrent ratio (received/sent)" + "detailsTitle": "Ratio" }, "received": { - "columnTitle": "Total down", - "detailsTitle": "Total downloaded" + "columnTitle": "Down", + "detailsTitle": "Downloaded" }, "sent": { - "columnTitle": "Total up", - "detailsTitle": "Total Uploaded" + "columnTitle": "Up", + "detailsTitle": "Uploaded" }, "size": { - "columnTitle": "File Size", - "detailsTitle": "Total Size of selection/files" + "columnTitle": "Size", + "detailsTitle": "Size" }, "state": { "columnTitle": "State", - "detailsTitle": "Job State" + "detailsTitle": "State" }, "time": { - "columnTitle": "Finish time", - "detailsTitle": "Time since/to completion", + "columnTitle": "ETA", + "detailsTitle": "ETA", "eta": "ETA" }, "type": { "columnTitle": "Type", - "detailsTitle": "Download Client type" + "detailsTitle": "Type" }, "upSpeed": { "columnTitle": "Up", - "detailsTitle": "Upload Speed" + "detailsTitle": "Up" } }, "states": { diff --git a/packages/widgets/src/common/homarr-data-table.css b/packages/widgets/src/common/homarr-data-table.css index 0e9d7d7c5..22b6b358d 100644 --- a/packages/widgets/src/common/homarr-data-table.css +++ b/packages/widgets/src/common/homarr-data-table.css @@ -12,11 +12,18 @@ background-color: transparent; } +.homarr-data-table .mantine-datatable-table { + width: 100% !important; + max-width: 100%; + table-layout: fixed !important; +} + .homarr-data-table .mantine-datatable-table tbody tr:hover { background-color: color-mix(in srgb, var(--mantine-color-default-hover) 40%, transparent) !important; } .homarr-data-table th { + padding: 0; white-space: nowrap; background-color: color-mix(in srgb, var(--mantine-color-body) 60%, transparent) !important; backdrop-filter: blur(8px); @@ -28,6 +35,13 @@ text-overflow: ellipsis; } +/* Lets a cell's content (e.g. an oversized action icon) visually overflow its own box without + growing it - used to make a per-row icon look bigger without inflating row height, since the + icon is pulled out of flow via absolute positioning rather than actually being a bigger cell. */ +.homarr-data-table td.docker-actions-cell { + overflow: visible; +} + .homarr-data-table td .mantine-Text-root, .homarr-data-table td .mantine-Group-root { white-space: nowrap; @@ -40,6 +54,25 @@ transition: opacity 150ms ease-out; } +.homarr-data-table .mantine-datatable-header-cell-sortable-group { + gap: 0; +} + +.homarr-data-table .mantine-datatable-header-cell-draggable-action-icon { + margin: 0; +} + +.homarr-data-table .mantine-datatable-header-cell-sortable-unsorted-icon { + display: none; +} + +.homarr-data-table .mantine-datatable-header-cell-draggable-action-icon svg, +.homarr-data-table .mantine-datatable-header-cell-sortable-text svg, +.homarr-data-table .mantine-datatable-header-cell-sortable-icon svg { + width: var(--mantine-font-size-md); + height: var(--mantine-font-size-md); +} + .homarr-data-table th:hover .mantine-datatable-header-cell-sortable-icon, .homarr-data-table th:hover .mantine-datatable-header-cell-draggable-action-icon, .homarr-data-table .mantine-datatable-header-cell-draggable-action-icon:focus-visible, diff --git a/packages/widgets/src/common/homarr-data-table.tsx b/packages/widgets/src/common/homarr-data-table.tsx index 0941a9305..e3f234853 100644 --- a/packages/widgets/src/common/homarr-data-table.tsx +++ b/packages/widgets/src/common/homarr-data-table.tsx @@ -2,10 +2,24 @@ import "./homarr-data-table.css"; -import type { DataTableProps } from "mantine-datatable"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import type { DataTableColumn, DataTableProps } from "mantine-datatable"; import { DataTable } from "mantine-datatable"; +const fallbackColumnWidth = 60; + +const getColumnWidth = (width: string | number | undefined): number => { + if (typeof width === "number" && Number.isFinite(width) && width > 0) return width; + if (typeof width === "string") { + const parsedWidth = Number.parseFloat(width); + if (Number.isFinite(parsedWidth) && parsedWidth > 0) return parsedWidth; + } + return fallbackColumnWidth; +}; + type HomarrDataTableProps = DataTableProps & { + columns: DataTableColumn[]; + groups?: never; isEditMode: boolean; cellPadding: string; rowCursor?: "auto" | "default"; @@ -16,11 +30,78 @@ export function HomarrDataTable({ cellPadding, rowCursor = "auto", className, + columns, defaultColumnProps, + onMouseDownCapture, scrollAreaProps, style, ...props }: HomarrDataTableProps) { + const [resizePreviewWidths, setResizePreviewWidths] = useState>(); + const removeResizeListeners = useRef<() => void>(() => undefined); + useEffect(() => () => removeResizeListeners.current(), []); + + const handleMouseDownCapture = useCallback( + (event: React.MouseEvent) => { + onMouseDownCapture?.(event); + const target = event.target; + if (!(target instanceof Element)) return; + + const handle = target.closest(".mantine-datatable-header-resizable-handle"); + const currentCell = handle?.closest("th[data-accessor]"); + const headerRow = currentCell?.parentElement; + if (!currentCell || !headerRow) return; + + const cells = Array.from(headerRow.querySelectorAll("th[data-accessor]")); + const currentIndex = cells.indexOf(currentCell); + const nextCell = cells[currentIndex + 1]; + const currentAccessor = currentCell.dataset.accessor; + const nextAccessor = nextCell?.dataset.accessor; + if (!nextCell || !currentAccessor || !nextAccessor) return; + + const widths = Object.fromEntries( + cells.flatMap((cell) => { + const accessor = cell.dataset.accessor; + if (!accessor) return []; + return [[accessor, cell.getBoundingClientRect().width]]; + }), + ); + const currentWidth = widths[currentAccessor]; + const nextWidth = widths[nextAccessor]; + if (currentWidth === undefined || nextWidth === undefined) return; + + const visualScale = currentCell.getBoundingClientRect().width / currentCell.offsetWidth; + const minimumWidth = fallbackColumnWidth * visualScale; + const startX = event.clientX; + + removeResizeListeners.current(); + const handleMouseMove = (moveEvent: MouseEvent) => { + const delta = Math.min( + nextWidth - minimumWidth, + Math.max(minimumWidth - currentWidth, moveEvent.clientX - startX), + ); + setResizePreviewWidths({ + ...widths, + [currentAccessor]: currentWidth + delta, + [nextAccessor]: nextWidth - delta, + }); + }; + const handleResizeEnd = () => { + removeResizeListeners.current(); + requestAnimationFrame(() => requestAnimationFrame(() => setResizePreviewWidths(undefined))); + }; + removeResizeListeners.current = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleResizeEnd); + window.removeEventListener("blur", handleResizeEnd); + }; + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleResizeEnd); + window.addEventListener("blur", handleResizeEnd); + }, + [onMouseDownCapture], + ); + const mergedClassName = [ "homarr-data-table", rowCursor === "default" ? "homarr-data-table-row-cursor-default" : undefined, @@ -28,6 +109,26 @@ export function HomarrDataTable({ ] .filter(Boolean) .join(" "); + const fittedColumns = useMemo(() => { + const visibleColumns = columns.filter(({ hidden }) => !hidden); + const getRenderedWidth = (column: DataTableColumn) => + resizePreviewWidths?.[String(column.accessor)] ?? getColumnWidth(column.width); + const totalWidth = visibleColumns.reduce((total, column) => total + getRenderedWidth(column), 0); + if (totalWidth <= 0) return columns; + + return columns.map((column) => { + if (column.hidden) return column; + + const width = `${(getRenderedWidth(column) / totalWidth) * 100}%`; + const fittedWidth = { width, minWidth: width, maxWidth: width }; + const cellsStyle = column.cellsStyle ?? defaultColumnProps?.cellsStyle; + return { + ...column, + titleStyle: [column.titleStyle, fittedWidth], + cellsStyle: (record: T, index: number) => [cellsStyle?.(record, index), fittedWidth, { padding: cellPadding }], + }; + }); + }, [cellPadding, columns, defaultColumnProps?.cellsStyle, resizePreviewWidths]); return ( @@ -42,6 +143,8 @@ export function HomarrDataTable({ textSelectionDisabled height="100%" {...props} + columns={fittedColumns} + onMouseDownCapture={handleMouseDownCapture} className={mergedClassName} style={[{ pointerEvents: isEditMode ? "none" : undefined }, style]} defaultColumnProps={{ diff --git a/packages/widgets/src/common/use-persisted-table-layout.ts b/packages/widgets/src/common/use-persisted-table-layout.ts index 9a126f076..cdfcf289c 100644 --- a/packages/widgets/src/common/use-persisted-table-layout.ts +++ b/packages/widgets/src/common/use-persisted-table-layout.ts @@ -107,7 +107,7 @@ export const parseColumnWidths = (value: string, columnAccessors: readonly strin } }; -export const usePersistedTableLayout = ({ +export const usePersistedTableLayout = ({ columns, columnAccessors, columnOrder, diff --git a/packages/widgets/src/definition.spec.ts b/packages/widgets/src/definition.spec.ts index 4fabb1691..94d94e0b9 100644 --- a/packages/widgets/src/definition.spec.ts +++ b/packages/widgets/src/definition.spec.ts @@ -39,7 +39,6 @@ describe("supportsAdvancedFocus", () => { beszelSystemStatsDefinition, beszelSystemTableDefinition, customApiDefinition, - dockerDefinition, indexerManagerDefinition, notebookDefinition, ])("keeps non-enhanced widgets compact", (widgetDefinition) => { @@ -49,6 +48,7 @@ describe("supportsAdvancedFocus", () => { test("requires an explicit opt-in", () => { expect(supportsAdvancedFocus({})).toBe(false); expect(supportsAdvancedFocus(clockDefinition)).toBe(true); + expect(supportsAdvancedFocus(dockerDefinition)).toBe(true); expect(supportsAdvancedFocus(downloadsDefinition)).toBe(true); }); }); diff --git a/packages/widgets/src/docker/component.tsx b/packages/widgets/src/docker/component.tsx index f10f40666..73245a4d6 100644 --- a/packages/widgets/src/docker/component.tsx +++ b/packages/widgets/src/docker/component.tsx @@ -69,6 +69,18 @@ interface ContainerActionHandlers { const columnAccessors = ["name", "state", "host", "cpuUsage", "memoryUsage", "actions"] as const; const containerMenuWidth = 240; +// The *button* sizes stay small and fixed - row height is set by the tallest cell, and the +// actions button is repeated once per row, so any growth here multiplies across every row and +// tanks how many containers fit on screen. The *icon* sizes are rendered larger than their +// button via absolute positioning (see ContainerMenuButton/footer refresh below) - an +// absolutely-positioned element is removed from normal flow, so it doesn't count toward its +// ancestor's height even though it visually overflows the button. This lets the icon look +// properly sized without the button (and therefore the row) growing to match. +const rowActionButtonSize = 22; +const rowActionIconVisualSize = 32; +const footerRefreshButtonSize = 24; +const footerRefreshIconVisualSize = 30; + const createContainerLogsPath = (container: Pick) => `/manage/tools/docker/logs/${container.id}?name=${encodeURIComponent(container.name)}&endpointId=${encodeURIComponent(container.endpointId)}`; @@ -83,7 +95,13 @@ const ContainerStateBadge = ({ state }: { state: ContainerState }) => { const t = useScopedI18n("docker.field.state.option"); return ( - + {t(state)} ); @@ -126,16 +144,15 @@ const createColumns = ( {inlineState && } {container.name.at(0)?.toUpperCase()} - + {container.name} @@ -191,8 +208,12 @@ const createColumns = ( { accessor: "actions", title: "", - width: 44, + width: rowActionButtonSize + 18, textAlign: "right", + // The default `.homarr-data-table td { overflow: hidden }` (needed for name/host ellipsis + // truncation) would clip the actions icon's intentional visual overflow - opt this one + // cell out of it. + cellsClassName: "docker-actions-cell", render: (container) => , }, ]; @@ -407,7 +428,7 @@ export default function DockerWidget({ - - + + {t("table.footer", { count: containers.length.toString() })} {footerVisibility.cpu && ( - + {t("table.totalCpu", { cpu: totals.cpu.toFixed(2) })} )} {footerVisibility.memory && ( - + {t("table.totalMemory", { memory: formatBytes(totals.memory) })} )} refreshInventory.mutate()} aria-label={t("table.refresh.lastUpdated", { when: relativeTime })} + style={{ position: "relative", overflow: "visible" }} > - + @@ -492,11 +518,18 @@ function ContainerMenuButton({ className={actionTargetClasses.root} variant="subtle" color="gray" - size="sm" + size={rowActionButtonSize} aria-label={t("title")} onClick={(event) => event.stopPropagation()} + style={{ position: "relative", overflow: "visible" }} > - + {/* Rendered larger than the button and pulled out of flow via absolute positioning, + so it looks properly sized without the button - and therefore the row - growing + to match (row height is set by the tallest cell, and this button repeats per row). */} + diff --git a/packages/widgets/src/docker/index.ts b/packages/widgets/src/docker/index.ts index 8a879cd4d..3a453c670 100644 --- a/packages/widgets/src/docker/index.ts +++ b/packages/widgets/src/docker/index.ts @@ -26,6 +26,7 @@ const columnTranslationKeyMap = { export const { definition, componentLoader } = createWidgetDefinition("dockerContainers", { icon: IconBrandDocker, + supportsAdvancedFocus: true, queryKey: [["docker", "getContainers"]], refetchInterval: 30, createOptions() { diff --git a/packages/widgets/src/downloads/component.tsx b/packages/widgets/src/downloads/component.tsx index a50685460..e0874719f 100644 --- a/packages/widgets/src/downloads/component.tsx +++ b/packages/widgets/src/downloads/component.tsx @@ -793,7 +793,9 @@ export default function DownloadClientsWidget({ animateOpacity: true, transitionTimingFunction: "ease-out", }, - content: ({ record, collapse }) => , + content: ({ record, collapse }) => ( + + ), }} onScroll={() => { if (contextMenu) closeContextMenu(); @@ -869,7 +871,13 @@ function buildHoverTooltip(record: ExtendedDownloadClientItem, t: DownloadsT): R ); } -function ExpandedRow({ item, collapse }: { item: ExtendedDownloadClientItem; collapse: () => void }) { +function ExpandedRow({ + item, + collapse, +}: { + item: ExtendedDownloadClientItem; + collapse: () => void; +}) { const t = useScopedI18n("widget.downloads"); const locale = useCurrentIntlLocale(); const progressPercent = Math.floor(item.progress * 100); @@ -891,13 +899,13 @@ function ExpandedRow({ item, collapse }: { item: ExtendedDownloadClientItem; col thickness={5} roundCaps sections={[{ value: progressPercent, color: progressColor(item.state, item.progress) }]} - label={{`${progressPercent}%`}} + label={{`${progressPercent}%`}} /> - - - - + + + + {item.name} @@ -905,7 +913,7 @@ function ExpandedRow({ item, collapse }: { item: ExtendedDownloadClientItem; col - + - + + {label}: - + {icon} - + {value} - + ); } diff --git a/packages/widgets/src/downloads/helpers.spec.ts b/packages/widgets/src/downloads/helpers.spec.ts index 6a0cbf4b8..26ed262cc 100644 --- a/packages/widgets/src/downloads/helpers.spec.ts +++ b/packages/widgets/src/downloads/helpers.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "vitest"; import { - DOWNLOAD_COLUMN_ACCESSORS, + ADVANCED_DOWNLOAD_COLUMN_ACCESSORS, filterDownloadItemsByStatus, getAvailableDownloadStates, getDownloadColumnAccessors, @@ -25,8 +25,8 @@ describe("downloads filters", () => { }); describe("downloads advanced columns", () => { - test("uses every column in advanced mode without changing the compact selection", () => { - expect(getDownloadColumnAccessors(["name"], true)).toEqual(DOWNLOAD_COLUMN_ACCESSORS); + test("uses summary columns in advanced mode without changing the compact selection", () => { + expect(getDownloadColumnAccessors(["name"], true)).toEqual(ADVANCED_DOWNLOAD_COLUMN_ACCESSORS); expect(getDownloadColumnAccessors(["name"], false)).toEqual(["name"]); }); }); diff --git a/packages/widgets/src/downloads/helpers.ts b/packages/widgets/src/downloads/helpers.ts index 99a670f46..6e7cce778 100644 --- a/packages/widgets/src/downloads/helpers.ts +++ b/packages/widgets/src/downloads/helpers.ts @@ -20,6 +20,17 @@ export const DOWNLOAD_COLUMN_ACCESSORS = [ "type", ] as const satisfies readonly (keyof ExtendedDownloadClientItem)[]; +export const ADVANCED_DOWNLOAD_COLUMN_ACCESSORS = [ + "name", + "progress", + "size", + "downSpeed", + "upSpeed", + "time", + "state", + "integration", +] as const satisfies readonly (typeof DOWNLOAD_COLUMN_ACCESSORS)[number][]; + type DownloadColumnAccessor = (typeof DOWNLOAD_COLUMN_ACCESSORS)[number]; export function getDownloadsStatsDisplay(showCompactStats: boolean, isAdvanced: boolean) { @@ -33,7 +44,7 @@ export function getDownloadColumnAccessors( configuredColumns: readonly DownloadColumnAccessor[], isAdvanced: boolean, ): readonly DownloadColumnAccessor[] { - return isAdvanced ? DOWNLOAD_COLUMN_ACCESSORS : configuredColumns; + return isAdvanced ? ADVANCED_DOWNLOAD_COLUMN_ACCESSORS : configuredColumns; } export function filterDownloadItemsByStatus( diff --git a/patches/mantine-datatable@9.3.1.patch b/patches/mantine-datatable@9.3.1.patch new file mode 100644 index 000000000..bcfcce0d5 --- /dev/null +++ b/patches/mantine-datatable@9.3.1.patch @@ -0,0 +1,17 @@ +diff --git a/dist/index.js b/dist/index.js +index 1ac8241b321236fb9f43feb9405151da1fd3d975..9fb5fff13b2bfd2d1db4a7d16003f1fa9e0fb699 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -1,2 +1,2 @@ +-"use strict";"use client";var pr=Object.create;var Dt=Object.defineProperty;var fr=Object.getOwnPropertyDescriptor;var br=Object.getOwnPropertyNames;var gr=Object.getPrototypeOf,Tr=Object.prototype.hasOwnProperty;var hr=(e,t)=>{for(var n in t)Dt(e,n,{get:t[n],enumerable:!0})},Rn=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of br(t))!Tr.call(e,a)&&a!==n&&Dt(e,a,{get:()=>t[a],enumerable:!(o=fr(t,a))||o.enumerable});return e};var q=(e,t,n)=>(n=e!=null?pr(gr(e)):{},Rn(t||!e||!e.__esModule?Dt(n,"default",{value:e,enumerable:!0}):n,e)),Cr=e=>Rn(Dt({},"__esModule",{value:!0}),e);var xr={};hr(xr,{DataTable:()=>ra,DataTableDraggableRow:()=>sa,calculateColSpan:()=>it,differenceBy:()=>an,flattenColumns:()=>Xe,getGroupsAtDepth:()=>vt,getMaxGroupDepth:()=>xt,getRecordId:()=>F,getValueAtPath:()=>Rt,hasStringAccessor:()=>rt,humanize:()=>Ie,needsRightBorder:()=>rn,reorderRecords:()=>yr,sanitizeStoredArray:()=>pe,swapRecords:()=>Pr,uniqBy:()=>lt,useDataTableColumns:()=>ln});module.exports=Cr(xr);var Bt=require("@mantine/core"),Wt=require("@mantine/hooks"),Tn=q(require("clsx")),J=require("react");var xn=require("@mantine/core");function V(e,t,n){return e?(0,xn.parseThemeColor)({color:typeof e=="object"?e[n]:e,theme:t}).value:void 0}function vn({theme:e,c:t,backgroundColor:n,borderColor:o,rowBorderColor:a,stripedColor:r,highlightOnHoverColor:l}){return{"--mantine-datatable-color-light":V(t,e,"light"),"--mantine-datatable-color-dark":V(t,e,"dark"),"--mantine-datatable-background-color-light":V(n,e,"light"),"--mantine-datatable-background-color-dark":V(n,e,"dark"),"--mantine-datatable-border-color-light":V(o,e,"light"),"--mantine-datatable-border-color-dark":V(o,e,"dark"),"--mantine-datatable-row-border-color-light":V(a,e,"light"),"--mantine-datatable-row-border-color-dark":V(a,e,"dark"),"--mantine-datatable-striped-color-light":V(r,e,"light"),"--mantine-datatable-striped-color-dark":V(r,e,"dark"),"--mantine-datatable-highlight-on-hover-color-light":V(l,e,"light"),"--mantine-datatable-highlight-on-hover-color-dark":V(l,e,"dark")}}function wt({theme:e,paginationActiveTextColor:t,paginationActiveBackgroundColor:n}){return{"--mantine-datatable-pagination-active-text-color-light":V(t,e,"light"),"--mantine-datatable-pagination-active-text-color-dark":V(t,e,"dark"),"--mantine-datatable-pagination-active-background-color-light":V(n,e,"light"),"--mantine-datatable-pagination-active-background-color-dark":V(n,e,"dark")}}function Mn({theme:e,color:t,backgroundColor:n}){return{"--mantine-datatable-row-color-light":V(t,e,"light"),"--mantine-datatable-row-color-dark":V(t,e,"dark"),"--mantine-datatable-row-background-color-light":V(n,e,"light"),"--mantine-datatable-row-background-color-dark":V(n,e,"dark")}}var en=require("react");var kn=require("@mantine/core"),[En,$e]=(0,kn.createSafeContext)("useDataTableColumnsContext must be used within DataTableColumnProvider");var Ln=require("react/jsx-runtime"),tn=e=>{let{children:t,columnsOrder:n,setColumnsOrder:o,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S,pinnedMap:b}=e,[C,E]=(0,en.useState)(""),[D,L]=(0,en.useState)("");return(0,Ln.jsx)(En,{value:{sourceColumn:C,setSourceColumn:E,targetColumn:D,setTargetColumn:L,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,swapColumns:()=>{if(!n||!o||!C||!D)return;let f=b.get(C),h=b.get(D);if(f?.logicalSide!==h?.logicalSide)return;let w=n.indexOf(C),M=n.indexOf(D);if(w!==-1&&M!==-1){let g=[...n],[R]=g.splice(w,1);g.splice(M,0,R),o(g)}},resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S},children:t})};var nn=require("react/jsx-runtime");function In(){return(0,nn.jsx)("tr",{className:"mantine-datatable-empty-row",children:(0,nn.jsx)("td",{})})}var St=require("@mantine/core");var be=require("react/jsx-runtime");function Nn(){return(0,be.jsxs)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,be.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,be.jsx)("path",{d:"M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74"}),(0,be.jsx)("path",{d:"M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6"}),(0,be.jsx)("path",{d:"M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4"}),(0,be.jsx)("path",{d:"M3 3l18 18"})]})}var me=require("react/jsx-runtime");function Hn({icon:e,text:t,active:n,children:o}){return(0,me.jsx)(St.Center,{className:"mantine-datatable-empty-state","data-active":n||void 0,children:o||(0,me.jsxs)(me.Fragment,{children:[e||(0,me.jsx)("div",{className:"mantine-datatable-empty-state-icon",children:(0,me.jsx)(Nn,{})}),(0,me.jsx)(St.Text,{component:"div",size:"sm",c:"dimmed",children:t})]})})}var It=require("@mantine/core"),bo=q(require("clsx"));var io=require("@mantine/core"),so=q(require("clsx"));var on=require("react");var An=require("@mantine/hooks"),st=require("react");function Ie(e){let t=e.replace(/([a-z\d])([A-Z]+)/g,"$1 $2").replace(/\W|_/g," ").trim().toLowerCase();return`${t.charAt(0).toUpperCase()}${t.slice(1)}`}function pe(e,t,n){return!Array.isArray(e)||n&&!e.every(n)?t:e}function rt(e){return typeof e=="object"&&e!==null&&typeof e.accessor=="string"}function an(e,t,n){return e.filter(o=>!t.map(n).includes(n(o)))}function lt(e,t){return e.filter((n,o,a)=>o===a.findIndex(r=>t(n)===t(r)))}function Rt(e,t){return t?t.match(/([^[.\]])+/g).reduce((o,a)=>o&&o[a],e):void 0}function F(e,t){return typeof t=="string"?Rt(e,t):t(e)}function yr(e,t){let n=structuredClone(t),o=n[e.source.index];return e.destination&&(n.splice(e.source.index,1),n.splice(e.destination.index,0,o)),n}function Pr(e,t){let n=structuredClone(t),o=e.destination;if(!o)return n;let a=n[e.source.index],r=n[o.index];return n.splice(o.index,1,a),n.splice(e.source.index,1,r),n}function xt(e){return!e||e.length===0?0:Math.max(...e.map(t=>t.groups&&t.groups.length>0?1+xt(t.groups):1))}function Xe(e){let t=[];for(let n of e)n.columns&&n.columns.length>0?t.push(...n.columns.filter(o=>o!=null)):n.groups&&n.groups.length>0&&t.push(...Xe(n.groups));return t.filter(n=>n!=null)}function it(e,t){return e.columns&&e.columns.length>0?e.columns.filter((n,o)=>n.hidden?!1:t?t[o]!==!1:!0).length:e.groups&&e.groups.length>0?e.groups.reduce((n,o)=>n+it(o,t),0):0}function vt(e,t,n=0){if(n===t)return[...e];let o=[];for(let a of e)a.groups&&a.groups.length>0&&o.push(...vt(a.groups,t,n+1));return o}function rn(e,t,n){return n?!e||t:!1}function On({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,st.useMemo)(()=>t.map(i=>({accessor:i.accessor,defaultPinned:i.pinned,pinnable:!!i.pinnable,pinned:i.pinned})),[t]),[a,r]=(0,An.useLocalStorage)({key:e?`${e}-columns-pinning`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,rt);function s(i){e&&r(typeof i=="function"?u=>i(u??o):i)}let c=(0,st.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultPinned:u.pinned,pinnable:!!u.pinnable,pinned:u.pinned})}),i},[l,t,o]);(0,st.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsPinning:c,setColumnsPinning:s,resetColumnsPinning:m}:{columnsPinning:o,setColumnsPinning:s,resetColumnsPinning:m}}var zn=require("@mantine/hooks"),ct=require("react");function _n({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,ct.useMemo)(()=>t?t.map(i=>i.accessor):[],[t]),[a,r]=(0,zn.useLocalStorage)({key:e?`${e}-columns-order`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,i=>typeof i=="string");function s(i){e&&r(i)}let c=(0,ct.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u)&&i.push(u)}),t.forEach(u=>{i.includes(u.accessor)||i.push(u.accessor)}),i},[l,t,o]);(0,ct.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsOrder:c,setColumnsOrder:s,resetColumnsOrder:m}:{columnsOrder:l,setColumnsOrder:s,resetColumnsOrder:m}}var Wn=require("@mantine/hooks"),_=require("react");function Bn({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o}){let a=(0,_.useMemo)(()=>t.some(f=>f.resizable&&!f.hidden&&f.accessor!=="__selection__"),[t]),r=(0,_.useCallback)(()=>t.filter(f=>f.accessor!=="__selection__").map(f=>({[f.accessor]:f.width??"auto"})),[t]),[l,s]=(0,Wn.useLocalStorage)({key:e?`${e}-columns-width`:"",defaultValue:e?r():void 0,getInitialValueInEffect:n}),c=pe(l,[],f=>typeof f=="object"&&f!==null),[m,i]=(0,_.useState)(()=>e&&c.length>0?c:r()),[u,p]=(0,_.useState)(!1),T=(0,_.useRef)(!1);(0,_.useEffect)(()=>{e&&(!c||c.length===0||i(c))},[e,c]),(0,_.useEffect)(()=>{i(f=>{let h=new Set(t.filter(y=>y.accessor!=="__selection__").map(y=>String(y.accessor))),w=f.filter(y=>h.has(Object.keys(y)[0])),M=new Set(w.map(y=>Object.keys(y)[0])),g=!1;for(let y of t){if(y.accessor==="__selection__")continue;let v=String(y.accessor);M.has(v)||(w.push({[v]:y.width??"auto"}),g=!0)}return w.length!==f.length||g?w:f})},[t]);let d=(0,_.useCallback)(f=>{let h=f.filter(w=>w.accessor!=="__selection__");h.length!==0&&(T.current=!0,i(w=>w.map(M=>{let g=Object.keys(M)[0],R=h.find(y=>y.accessor===g);return R?{[g]:R.width}:M})))},[]);(0,_.useEffect)(()=>{!e||!T.current||u||(T.current=!1,s(m))},[e,m,u,s]);let S=(0,_.useCallback)((f,h)=>{d([{accessor:f,width:h}])},[d]),b=(0,_.useCallback)(f=>{d(f)},[d]),C=(0,_.useCallback)(()=>{p(!0);let f=o?.current;if(!f)return;let h=Array.from(f.querySelectorAll("th[data-accessor]"));if(h.length===0)return;let w=[];for(let M of h){let g=M.getAttribute("data-accessor");if(!g||g==="__selection__")continue;let R=Math.round(M.getBoundingClientRect().width);w.push({[g]:`${R}px`})}T.current=!0,i(w)},[o]),E=(0,_.useCallback)(()=>{p(!1)},[]),D=(0,_.useCallback)(()=>{let f=r();T.current=!0,i(f),p(!1)},[r]),L=(0,_.useMemo)(()=>{if(!a)return!1;let f=t.filter(h=>h.resizable&&!h.hidden&&h.accessor!=="__selection__");return m.filter(h=>{let w=Object.keys(h)[0];return f.some(M=>M.accessor===w)}).every(h=>{let w=Object.values(h)[0];return w==="auto"||w==="initial"})},[a,t,m]),N=(0,_.useCallback)(()=>{},[]);return{columnsWidth:m,setColumnsWidth:d,setColumnWidth:S,setMultipleColumnWidths:b,resetColumnsWidth:D,hasResizableColumns:a,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:u,beginResize:C,endResize:E}}var Mt=require("react");var Vn=require("@mantine/hooks"),ut=require("react");function Gn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,ut.useMemo)(()=>t?.map(i=>({accessor:i.accessor,defaultToggle:i.defaultToggle||!0,toggleable:i.toggleable,toggled:i.defaultToggle===void 0?!0:i.defaultToggle})),[t]),[a,r]=(0,Vn.useLocalStorage)({key:e?`${e}-columns-toggle`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,rt);function s(i){e&&r(i)}let c=(0,ut.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultToggle:u.defaultToggle||!0,toggleable:u.toggleable,toggled:u.defaultToggle===void 0?!0:u.defaultToggle})}),i},[l,t,o]);(0,ut.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsToggle:c,setColumnsToggle:s,resetColumnsToggle:m}:{columnsToggle:l,setColumnsToggle:s,resetColumnsToggle:m}}var ln=({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o,scrollViewportRef:a})=>{let{columnsOrder:r,setColumnsOrder:l,resetColumnsOrder:s}=_n({key:e,columns:t,getInitialValueInEffect:n}),{columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i}=Gn({key:e,columns:t,getInitialValueInEffect:n}),{columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T}=On({key:e,columns:t,getInitialValueInEffect:n}),{columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,beginResize:h,endResize:w}=Bn({key:e,columns:t,getInitialValueInEffect:n,headerRef:o,scrollViewportRef:a}),M=(0,Mt.useMemo)(()=>{let y;r?y=r.map(P=>t.find(A=>A.accessor===P)).map(P=>{let A=u.find(z=>z.accessor===P?.accessor),B=P?.pinnable?A?.pinned:P?.pinned;return{...P,hidden:P?.hidden||!c.find(z=>z.accessor===P?.accessor)?.toggled,pinned:B}}):y=t;let v=y.filter(P=>P.pinned==="left"),H=y.filter(P=>!P.pinned),k=y.filter(P=>P.pinned==="right");return[...v,...H,...k].map(P=>{if(P?.accessor==="__selection__")return P;let A=P?.accessor,B=d.find(se=>A in se);if(!B)return P;let z=B[A];return z===void 0||z==="auto"||z==="initial"?P:{...P,width:z}})},[t,r,c,u,d]),g=(0,Mt.useMemo)(()=>{let y=M.filter(v=>!v.hidden&&v.accessor!=="__selection__");return y.length===0?!1:y.every(v=>typeof v.width=="string"&&/px$/.test(v.width))},[M]),R=(0,Mt.useMemo)(()=>{if(!g)return null;let y=0;for(let v of M){if(v.hidden||v.accessor==="__selection__")continue;let H=v.width;if(typeof H!="string")continue;let k=parseInt(H,10);Number.isNaN(k)||(y+=k)}return y>0?y:null},[g,M]);return{effectiveColumns:M,setColumnsOrder:l,columnsOrder:r,resetColumnsOrder:s,columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i,columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T,columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,isLocked:g,tableWidth:R,beginResize:h,endResize:w}};var Kn=require("@mantine/core"),K=require("react");var kt=require("react"),Ne=typeof window<"u"?kt.useLayoutEffect:kt.useEffect;var Fn=require("react");function sn(e){let t=(0,Fn.useRef)(e);return t.current=e,t}var $n="--mantine-datatable-header-height",Xn="--mantine-datatable-footer-height",Un="--mantine-datatable-selection-column-width",Qn="--mantine-datatable-last-row-border-bottom";function ne(e,t,n){e?.style.setProperty(t,n)}function un(e){let t=e.borderBoxSize?.[0]||e.contentBoxSize?.[0];return t?{width:t.inlineSize,height:t.blockSize}:{width:e.contentRect.width,height:e.contentRect.height}}function cn(e,t,n){if(e){let o=new ResizeObserver(a=>{let r=a[0];r&&t(un(r))});return o.observe(e),()=>{o.disconnect(),n()}}}function jn({scrollCallbacks:e,withRowBorders:t}){let n={root:(0,K.useRef)(null),table:(0,K.useRef)(null),scrollViewport:(0,K.useRef)(null),header:(0,K.useRef)(null),footer:(0,K.useRef)(null),selectionColumnHeader:(0,K.useRef)(null)},{root:o,table:a,scrollViewport:r,header:l,footer:s,selectionColumnHeader:c}=n,m=sn({withRowBorders:t}),i=sn(e),u=(0,K.useRef)(()=>{}),p=(0,K.useRef)(()=>{}),T=(0,K.useCallback)(d=>{i.current.onScroll?.(d),u.current()},[]);return(0,K.useEffect)(()=>cn(l.current,d=>{ne(o.current,$n,`${d.height}px`)},()=>ne(o.current,$n,"0")),[l.current]),(0,K.useEffect)(()=>cn(s.current,d=>{ne(o.current,Xn,`${d.height}px`)},()=>ne(o.current,Xn,"0")),[s.current]),(0,K.useEffect)(()=>cn(c.current,d=>{ne(o.current,Un,`${d.width}px`)},()=>ne(o.current,Un,"0")),[c.current]),Ne(()=>{if(typeof window>"u")return;let d={top:!0,bottom:!0,left:!0,right:!0},S={width:0,height:0},b={width:0,height:0};function C(f,h){let w=d[f];return d[f]=h,ne(o.current,`--mantine-datatable-${f}-shadow-opacity`,h?"0":"1"),w}function E(){let f=S.height-b.height,h=f<0;ne(o.current,"--mantine-datatable-footer-position",h?"relative":"sticky"),ne(o.current,"--mantine-datatable-footer-bottom",h?`${f}px`:"0")}function D(){m.current.withRowBorders&&S.height{for(let h of f)switch(h.target.tagName){case"TABLE":{S=un(h);break}case"DIV":{b=un(h);break}}L(),E(),D()});return N.observe(a.current),N.observe(r.current),()=>{N.disconnect()}},[]),Ne(()=>{p.current()},[t]),{refs:n,onScroll:T}}var Ue=require("react");function Jn({columns:e,theadRef:t,tbodyRef:n,selectionColumnHeaderRef:o,selectionVisible:a,pinFirstColumn:r,pinLastColumn:l}){let[s,c]=(0,Ue.useState)(new Map),m=(0,Ue.useCallback)(()=>{if(!r&&!l&&!e.some(g=>g.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=getComputedStyle(p).direction==="rtl",b=new Map;if(T)d.querySelectorAll("th[data-accessor]").forEach(g=>{let R=g.dataset.accessor;if(R){let y=g.getBoundingClientRect().width;y>0&&b.set(R,y)}});else{let g=e.filter(y=>!y.hidden);d.querySelectorAll("td:not(.mantine-datatable-row-selector-cell):not(.mantine-datatable-row-expansion-cell)").forEach((y,v)=>{if(g[v]){let H=y.getBoundingClientRect().width;H>0&&b.set(String(g[v].accessor),H)}})}let C=0;if(a){let g=o.current??p.querySelector(T?".mantine-datatable-header-selector-cell":".mantine-datatable-row-selector-cell");g&&(C=g.getBoundingClientRect().width)}let E=e.filter(g=>!g.hidden&&b.has(String(g.accessor))).map(g=>String(g.accessor)),D=new Map;for(let g of e){let R=String(g.accessor);E.includes(R)&&(g.pinned?D.set(R,g.pinned):r&&R===E[0]&&!D.has(R)?D.set(R,"left"):l&&R===E[E.length-1]&&!D.has(R)&&D.set(R,"right"))}let L=E.filter(g=>D.get(g)==="left"),N=E.filter(g=>D.get(g)==="right"),f=new Map,h=C;L.forEach((g,R)=>{f.set(g,{position:S?"right":"left",logicalSide:"left",offset:h,isBoundary:R===L.length-1}),h+=b.get(g)||0});let w=-.4,M=[...N].reverse();M.forEach((g,R)=>{f.set(g,{position:S?"left":"right",logicalSide:"right",offset:w,isBoundary:R===M.length-1}),w+=b.get(g)||0}),c(g=>{if(g.size!==f.size)return f;for(let[R,y]of f){let v=g.get(R);if(!v||v.position!==y.position||v.logicalSide!==y.logicalSide||v.offset!==y.offset||v.isBoundary!==y.isBoundary)return f}return g})},[e,a,t,n,o,r,l]);(0,Ue.useEffect)(()=>{if(!r&&!l&&!e.some(b=>b.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=new ResizeObserver(m);return d.querySelectorAll(T?"th":"td").forEach(b=>{S.observe(b)}),()=>S.disconnect()},[t,n,m]),Ne(m,[m]);let i=[...s.values()].some(p=>p.logicalSide==="left"),u=[...s.values()].some(p=>p.logicalSide==="right");return{pinnedMap:s,hasLeftPinned:i,hasRightPinned:u}}var Et=require("react");function qn(e){let[t,n]=(0,Et.useState)(null),o=e?.join(":")||"";return(0,Et.useEffect)(()=>{n(null)},[o]),{lastSelectionChangeIndex:t,setLastSelectionChangeIndex:n}}var Qe=require("react");function Dr(e,t){let n=()=>{t(e.map(a=>a.matches))},o=e.map(a=>{try{return a.addEventListener("change",n),()=>a.removeEventListener("change",n)}catch{return a.addListener(n),()=>a.removeListener(n)}});return()=>{o.forEach(a=>{a()})}}function wr(e,t){return t||(typeof window<"u"&&"matchMedia"in window?e.map(n=>window.matchMedia(n).matches):e.map(()=>!1))}function Zn(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[o,a]=(0,Qe.useState)(n?t:wr(e,t)),r=(0,Qe.useRef)(null);return(0,Qe.useEffect)(()=>{if("matchMedia"in window)return r.current=e.map(l=>window.matchMedia(l)),a(r.current.map(l=>l.matches)),Dr(r.current,l=>{a(l)})},[e]),o}var Yn=require("@mantine/core"),dn=require("react");function eo(e){let t=(0,Yn.useMantineTheme)(),n=(0,dn.useMemo)(()=>e.map(a=>(typeof a=="function"?a(t):a)??""),[e,t]),o=(0,dn.useMemo)(()=>e.map(()=>!0),[e]);return Zn(n,o)}var to=require("@mantine/core"),no=require("@mantine/hooks");function ge(e){let t=(0,to.useMantineTheme)(),n=typeof e=="function"?e(t):e;return(0,no.useMediaQuery)(n||"",!0)}var oo=require("react");function ao({rowExpansion:e,records:t,idAccessor:n}){let o=[];if(e&&t){let{trigger:s,allowMultiple:c,initiallyExpanded:m}=e;t&&s==="always"?o=t.map(i=>F(i,n)):m&&(o=t.filter((i,u)=>m({record:i,index:u})).map(i=>F(i,n)),c||(o=[o[0]]))}let a,r,l=(0,oo.useState)(o);if(e){let{expandable:s,trigger:c,allowMultiple:m,collapseProps:i,content:u}=e;e.expanded?{recordIds:a,onRecordIdsChange:r}=e.expanded:[a,r]=l;let p=T=>r?.(a.filter(d=>d!==F(T,n)));return{expandOnClick:c!=="always"&&c!=="never",isRowExpanded:T=>c==="always"?!0:a.includes(F(T,n)),isExpandable:({record:T,index:d})=>s?s({record:T,index:d}):!0,expandRow:T=>{let d=F(T,n);r?.(m?[...a,d]:[d])},collapseRow:p,collapseProps:i,content:({record:T,index:d})=>()=>u({record:T,index:d,collapse:()=>p(T)})}}}var mn=require("@mantine/hooks"),dt=require("react");function ro(e,t){let[n,o]=(0,dt.useState)(e),[a,r]=(0,dt.useState)(e),l=(0,mn.useTimeout)(()=>o(!0),0),s=(0,mn.useTimeout)(()=>r(!1),t||200);return(0,dt.useEffect)(()=>{e?(s.clear(),r(!0),l.start()):(l.clear(),o(!1),s.start())},[l,s,e]),{expanded:n,visible:a}}var Ke="mantine-datatable-nowrap",je="mantine-datatable-ellipsis",re="mantine-datatable-pointer-cursor",Lt="mantine-datatable-context-menu-cursor",lo="mantine-datatable-text-selection-disabled",Te="mantine-datatable-text-align-left",he="mantine-datatable-text-align-center",Ce="mantine-datatable-text-align-right";var uo=require("react/jsx-runtime");function co({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,title:a,noWrap:r,ellipsis:l,textAlign:s,width:c}){return ge(o)?(0,uo.jsx)(io.TableTh,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,so.default)({[Ke]:r||l,[je]:l,[Te]:s==="left",[he]:s==="center",[Ce]:s==="right"},e),style:[{width:c,minWidth:c,maxWidth:c},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],children:a}):null}var mo=require("@mantine/core"),fo=require("react/jsx-runtime");function po({shadowVisible:e}){return(0,fo.jsx)(mo.TableTh,{className:"mantine-datatable-footer-selector-placeholder-cell","data-shadow-visible":e||void 0})}var Je=require("react/jsx-runtime");function go({className:e,style:t,columns:n,defaultColumnProps:o,pinnedMap:a,selectionVisible:r,selectorCellShadowVisible:l,ref:s}){return(0,Je.jsx)(It.TableTfoot,{ref:s,className:(0,bo.default)("mantine-datatable-footer",e),style:t,children:(0,Je.jsxs)(It.TableTr,{children:[r&&(0,Je.jsx)(po,{shadowVisible:l}),n.map(({hidden:c,...m})=>{if(c)return null;let{accessor:i,visibleMediaQuery:u,textAlign:p,width:T,footer:d,footerClassName:S,footerStyle:b,noWrap:C,ellipsis:E}={...o,...m};return(0,Je.jsx)(co,{pinnedInfo:a.get(String(i)),className:S,style:b,visibleMediaQuery:u,textAlign:p,width:T,title:d,noWrap:C,ellipsis:E},i)})]})})}var $=require("@mantine/core"),_o=q(require("clsx")),Wo=require("react");var To=require("@mantine/core"),ho=q(require("clsx")),mt=require("react");var yo=require("react/jsx-runtime");function Co({group:{id:e,columns:t,groups:n,title:o,textAlign:a,className:r,style:l},pinnedInfo:s,maxDepth:c,currentDepth:m,previousGroups:i,isLastGroup:u,withColumnBorders:p=!1,totalTableColumns:T}){let d=(0,mt.useMemo)(()=>t&&t.length>0?t:n&&n.length>0?Xe([{id:e,columns:t,groups:n}]):[],[t,n,e]),S=(0,mt.useMemo)(()=>d.map(({visibleMediaQuery:h})=>h),[d]),b=eo(S),C=(0,mt.useMemo)(()=>it({id:e,columns:t,groups:n},b),[e,t,n,b]),E=(0,mt.useMemo)(()=>i.reduce((h,w)=>h+it(w,b),0),[i,b]),L=n&&n.length>0?1:c-m,N=E+C0?(0,yo.jsx)(To.TableTh,{colSpan:C,rowSpan:L>1?L:void 0,"data-pinned":s?.position,"data-pinned-shadow":s?.isBoundary?s.position:void 0,className:(0,ho.default)("mantine-datatable-column-group-header-cell",{[Te]:a==="left",[he]:a==="center",[Ce]:a==="right","mantine-datatable-column-group-header-cell--needs-border":f},r),style:[l,s&&{position:"sticky",[s.position]:s.offset,overflow:"visible"}],children:o??Ie(e)}):null}var U=require("@mantine/core"),bt=q(require("clsx")),At=require("react");var ye=require("@mantine/core"),Nt=require("@mantine/hooks");var pt=require("react/jsx-runtime");function Po(){return(0,pt.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,pt.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,pt.jsx)("path",{d:"M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227z"})]})}var ft=require("react/jsx-runtime");function Do(){return(0,ft.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,ft.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,ft.jsx)("path",{d:"M20 3h-16a1 1 0 0 0 -1 1v2.227l.008 .223a3 3 0 0 0 .772 1.795l4.22 4.641v8.114a1 1 0 0 0 1.316 .949l6 -2l.108 -.043a1 1 0 0 0 .576 -.906v-6.586l4.121 -4.12a3 3 0 0 0 .879 -2.123v-2.171a1 1 0 0 0 -1 -1z",strokeWidth:"0",fill:"currentColor"})]})}var He=require("react/jsx-runtime");function wo({children:e,isActive:t,filterPopoverProps:n,filterPopoverDisableClickOutside:o}){let[a,{close:r,toggle:l}]=(0,Nt.useDisclosure)(!1),s=t?Do:Po,c=(0,Nt.useClickOutside)(r);return o&&(c=void 0),(0,He.jsxs)(ye.Popover,{withArrow:!0,shadow:"md",trapFocus:!0,...n,opened:a,onClose:r,onDismiss:r,children:[(0,He.jsx)(ye.PopoverTarget,{children:(0,He.jsx)(ye.ActionIcon,{className:"mantine-datatable-header-cell-filter-action-icon","data-active":t||void 0,size:"sm",variant:"default",onClick:m=>{m.preventDefault(),l()},onKeyDown:m=>m.stopPropagation(),children:(0,He.jsx)(s,{})})}),(0,He.jsx)(ye.PopoverDropdown,{ref:c,onClick:m=>m.stopPropagation(),onKeyDown:m=>m.stopPropagation(),children:typeof e=="function"?e({close:r}):e})]})}var Y=require("@mantine/core"),fe=require("react");var Ae=require("react/jsx-runtime");function So(){return(0,Ae.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Ae.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Ae.jsx)("path",{d:"M5 12l14 0"}),(0,Ae.jsx)("path",{d:"M5 12l6 6"}),(0,Ae.jsx)("path",{d:"M5 12l6 -6"})]})}var Oe=require("react/jsx-runtime");function Ro(){return(0,Oe.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Oe.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Oe.jsx)("path",{d:"M5 12l14 0"}),(0,Oe.jsx)("path",{d:"M19 12l-6 6"}),(0,Oe.jsx)("path",{d:"M19 12l-6 -6"})]})}var ze=require("react/jsx-runtime");function xo(){return(0,ze.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,ze.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,ze.jsx)("path",{d:"M9 4v6l-2 4v2h10v-2l-2-4v-6"}),(0,ze.jsx)("path",{d:"M12 16v5"}),(0,ze.jsx)("path",{d:"M8 4h8"})]})}var qe=require("react/jsx-runtime");function Ht(){return(0,qe.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,qe.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,qe.jsx)("path",{d:"M18 6l-12 12"}),(0,qe.jsx)("path",{d:"M6 6l12 12"})]})}var j=require("react/jsx-runtime");function vo({currentPinned:e,onPinChange:t}){let[n,o]=(0,fe.useState)(!1),a=(0,fe.useRef)(null),[r,l]=(0,fe.useState)(!1),s=(0,fe.useCallback)(()=>{if(a.current){let p=getComputedStyle(a.current).direction;l(p==="rtl")}},[]);Ne(()=>{s()},[s]),(0,fe.useEffect)(()=>{n&&s()},[n,s]);let c=r?"right":"left",m=r?"left":"right",i=e===c,u=e===m;return(0,j.jsxs)(Y.Popover,{opened:n,onChange:o,position:"bottom",withArrow:!0,shadow:"sm",children:[(0,j.jsx)(Y.Popover.Target,{children:(0,j.jsx)(Y.Center,{ref:a,className:"mantine-datatable-header-cell-pinnable-icon",role:"button","aria-label":"Pin column",onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),children:(0,j.jsx)(Y.ActionIcon,{size:"xs",variant:"light",onClick:p=>p.stopPropagation(),children:(0,j.jsx)(xo,{})})})}),(0,j.jsx)(Y.Popover.Dropdown,{onClick:p=>p.stopPropagation(),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),className:"mantine-datatable-pinnable-dropdown",children:(0,j.jsxs)(Y.Group,{gap:4,children:[(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:i?"filled":"light",onClick:()=>t(i?void 0:c),"aria-label":"Pin left",children:(0,j.jsx)(So,{})}),(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:u?"filled":"light",onClick:()=>t(u?void 0:m),"aria-label":"Pin right",children:(0,j.jsx)(Ro,{})}),e&&(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:"light",color:"red",onClick:()=>t(void 0),"aria-label":"Unpin",children:(0,j.jsx)(Ht,{})})]})})]})}var ko=require("@mantine/core"),Pe=require("react");var Lo=require("react/jsx-runtime"),Mo=50,Eo=e=>{let{accessor:t,columnRef:n}=e,[o,a]=(0,Pe.useState)(!1),r=(0,Pe.useRef)(0),l=(0,Pe.useRef)({current:0,next:0}),{dir:s}=(0,ko.useDirection)(),c=s==="rtl",{setMultipleColumnWidths:m,beginResize:i,endResize:u}=$e(),p=(0,Pe.useCallback)(d=>{if(d.preventDefault(),d.stopPropagation(),!n.current)return;let S=n.current,b=S.nextElementSibling;for(;b;){let f=b.getAttribute("data-accessor");if(f&&f!=="__selection__")break;b=b.nextElementSibling}if(!b)return;let C=b.getAttribute("data-accessor");if(!C)return;let E=S.offsetWidth,D=b.offsetWidth;i(),a(!0),r.current=d.clientX,l.current={current:E,next:D};let L=f=>{let h=f.clientX-r.current;c&&(h=-h);let w=l.current.current-Mo,M=l.current.next-Mo,g=Math.max(-w,Math.min(h,M)),R=l.current.current+g,y=l.current.next-g;m([{accessor:t,width:`${R}px`},{accessor:C,width:`${y}px`}])},N=()=>{a(!1),document.body.style.cursor="initial",document.body.style.userSelect="initial",u(),document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",N)};document.body.style.cursor="col-resize",document.body.style.userSelect="none",document.addEventListener("mousemove",L),document.addEventListener("mouseup",N)},[t,n,c,m,i,u]),T=(0,Pe.useCallback)(()=>{if(!n.current)return;let S=n.current.nextElementSibling,b=[{accessor:t,width:"auto"}];if(S){let C=S.getAttribute("data-accessor");C&&C!=="__selection__"&&b.push({accessor:C,width:"auto"})}m(b)},[t,n,m]);return(0,Lo.jsx)("div",{className:"mantine-datatable-header-resizable-handle",style:{cursor:o?"col-resize":void 0},onMouseDown:p,onDoubleClick:T,onClick:d=>d.stopPropagation(),title:"Drag to resize column, double-click to reset"})};var _e=require("react/jsx-runtime");function Io(){return(0,_e.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,_e.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,_e.jsx)("path",{d:"M8 7l4 -4l4 4"}),(0,_e.jsx)("path",{d:"M8 17l4 4l4 -4"}),(0,_e.jsx)("path",{d:"M12 3l0 18"})]})}var We=require("react/jsx-runtime");function No(){return(0,We.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,We.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,We.jsx)("path",{d:"M12 5l0 14"}),(0,We.jsx)("path",{d:"M16 9l-4 -4"}),(0,We.jsx)("path",{d:"M8 9l4 -4"})]})}var le=require("react/jsx-runtime");function Ho(){return(0,le.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,le.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,le.jsx)("path",{d:"M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"})]})}var G=require("react/jsx-runtime");function Ao({className:e,style:t,pinnedInfo:n,accessor:o,visibleMediaQuery:a,title:r,sortable:l,draggable:s,toggleable:c,pinnable:m,resizable:i,sortIcons:u,textAlign:p,width:T,sortStatus:d,onSortStatusChange:S,filter:b,filterPopoverProps:C,filterPopoverDisableClickOutside:E,filtering:D,sortKey:L}){let{setSourceColumn:N,setTargetColumn:f,swapColumns:h,columnsToggle:w,setColumnsToggle:M,columnsPinning:g,setColumnsPinning:R}=$e(),[y,v]=(0,At.useState)(!1),H=(0,At.useRef)(null),x=g?.find(O=>O.accessor===o)?.pinned;if(!ge(a))return null;let P=r??Ie(o),A=typeof P=="string"?P:void 0,B=l&&S?O=>{O?.defaultPrevented||S({sortKey:L,columnAccessor:o,direction:d?.columnAccessor===o?d.direction==="asc"?"desc":"asc":d?.direction??"asc"})}:void 0,z=O=>{O.stopPropagation(),N(o),v(!1)},se=O=>{O.preventDefault(),f(o),v(!0)},we=()=>{f(o),v(!1),h()},Se=()=>{v(!0)},Re=()=>{v(!1)},xe=O=>{O.stopPropagation(),M(w.map(te=>te.accessor===o?{...te,toggled:!1}:te))},ve=O=>{R(te=>te.map(ce=>ce.accessor===o?{...ce,pinned:O}:ce))};return(0,G.jsxs)(U.TableTh,{"data-accessor":o,"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,bt.default)({"mantine-datatable-header-cell-sortable":l,"mantine-datatable-header-cell-toggleable":c,"mantine-datatable-header-cell-pinnable":m,"mantine-datatable-header-cell-resizable":i},e),style:[{width:T,...i?{}:{minWidth:T,maxWidth:T}},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],role:l?"button":void 0,tabIndex:l?0:void 0,onClick:B,onKeyDown:O=>O.key==="Enter"&&B?.(),ref:H,children:[(0,G.jsxs)(U.Group,{className:"mantine-datatable-header-cell-sortable-group",justify:"space-between",wrap:"nowrap",children:[(0,G.jsxs)(U.Flex,{align:"center",w:"100%",className:(0,bt.default)({"mantine-datatable-header-cell-draggable":s,"mantine-datatable-header-cell-drag-over":y}),draggable:s,onDragStart:s?z:void 0,onDragEnter:s?Se:void 0,onDragOver:s?se:void 0,onDrop:s?we:void 0,onDragLeave:s?Re:void 0,children:[s?(0,G.jsx)(U.Center,{role:"img","aria-label":"Drag column",children:(0,G.jsx)(U.ActionIcon,{className:"mantine-datatable-header-cell-draggable-action-icon",variant:"subtle",size:"xs",onClick:O=>{O.stopPropagation()},children:(0,G.jsx)(Ho,{})})}):null,(0,G.jsx)(U.Box,{className:(0,bt.default)("mantine-datatable-header-cell-sortable-text",{[Te]:p==="left",[he]:p==="center",[Ce]:p==="right"},Ke,je),title:A,children:P})]}),m?(0,G.jsx)(vo,{currentPinned:x,onPinChange:ve}):null,c?(0,G.jsx)(U.Center,{className:"mantine-datatable-header-cell-toggleable-icon",role:"img","aria-label":"Toggle column",children:(0,G.jsx)(U.ActionIcon,{size:"xs",variant:"light",onClick:xe,children:(0,G.jsx)(Ht,{})})}):null,l||d?.columnAccessor===o?d?.columnAccessor===o?(0,G.jsx)(U.Center,{className:(0,bt.default)("mantine-datatable-header-cell-sortable-icon",{"mantine-datatable-header-cell-sortable-icon-reversed":d.direction==="desc"}),role:"img","aria-label":`Sorted ${d.direction==="desc"?"descending":"ascending"}`,children:u?.sorted||(0,G.jsx)(No,{})}):(0,G.jsx)(U.Center,{className:"mantine-datatable-header-cell-sortable-unsorted-icon",role:"img","aria-label":"Not sorted",children:u?.unsorted||(0,G.jsx)(Io,{})}):null,b?(0,G.jsx)(wo,{filterPopoverProps:C,isActive:!!D,filterPopoverDisableClickOutside:E,children:b}):null]}),i&&o!=="__selection__"?(0,G.jsx)(Eo,{accessor:o,columnRef:H}):null]})}var Ot=require("@mantine/core"),Oo=q(require("clsx"));var pn=require("react/jsx-runtime");function zo({className:e,style:t,trigger:n,shadowVisible:o,checked:a,indeterminate:r,checkboxProps:l,onChange:s,rowSpan:c,ref:m}){let i=!l.disabled;return(0,pn.jsx)(Ot.TableTh,{ref:m,className:(0,Oo.default)("mantine-datatable-header-selector-cell",{[re]:n==="cell"&&i},e),style:t,rowSpan:c,"data-shadow-visible":o||void 0,"data-accessor":"__selection__",onClick:n==="cell"&&i?s:void 0,children:(0,pn.jsx)(Ot.Checkbox,{classNames:i?{input:re}:void 0,checked:a,indeterminate:r,onChange:s,...l,disabled:!(s||l.onChange)||l.disabled})})}var Z=require("react/jsx-runtime");function Sr(e,t){let o=(e.columns??[]).filter(u=>!u.hidden).map(u=>String(u.accessor)),a=o.map(u=>t.get(u)).filter(Boolean);if(a.length===0||a.length!==o.length)return;let r=new Set(a.map(u=>u.logicalSide));if(r.size>1)return;let l=[...r][0],s=l==="left"?o[0]:o[o.length-1],c=t.get(s);if(!c)return;let m=l==="left"?o[o.length-1]:o[0],i=t.get(m);return{...c,isBoundary:i?.isBoundary??!1}}function Bo({selectionColumnHeaderRef:e,className:t,style:n,sortStatus:o,sortIcons:a,onSortStatusChange:r,columns:l,defaultColumnProps:s,groups:c,pinnedMap:m,selectionTrigger:i,selectionVisible:u,selectionChecked:p,selectionIndeterminate:T,onSelectionChange:d,selectionCheckboxProps:S,selectorCellShadowVisible:b,selectionColumnClassName:C,selectionColumnStyle:E,withColumnBorders:D=!1,ref:L}){let N=c?xt(c):0,f=N>0?N+1:1,h=u?(0,Z.jsx)(zo,{ref:e,className:C,style:E,trigger:i,shadowVisible:b,checked:p,indeterminate:T,checkboxProps:S,onChange:d,rowSpan:c?f:void 0}):null,{columnsToggle:w,setColumnsToggle:M}=$e(),[g,R]=(0,Wo.useState)(!1),y=l.some(k=>k.toggleable),v=y?Object.fromEntries(l.map(({accessor:k,title:x})=>[k,x??Ie(String(k))])):void 0,H=(0,Z.jsxs)($.TableThead,{className:(0,_o.default)("mantine-datatable-header",t),style:n,ref:L,onContextMenu:y?k=>{k.preventDefault(),R(x=>!x)}:void 0,children:[c&&Array.from({length:N},(k,x)=>{let P=vt(c,x);return(0,Z.jsxs)($.TableTr,{children:[x===0&&h,P.map((A,B)=>(0,Z.jsx)(Co,{group:A,pinnedInfo:Sr(A,m),maxDepth:N,currentDepth:x,previousGroups:P.slice(0,B),isLastGroup:B===P.length-1,withColumnBorders:D,totalTableColumns:l.length},A.id))]},`group-depth-${x}`)}),(0,Z.jsxs)($.TableTr,{children:[!c&&h,l.map(({hidden:k,...x},P)=>{if(k)return null;let{accessor:A,visibleMediaQuery:B,textAlign:z,width:se,title:we,sortable:Se,draggable:Re,toggleable:xe,pinnable:ve,resizable:O,titleClassName:te,titleStyle:ce,filter:ue,filterPopoverProps:Gt,filterPopoverDisableClickOutside:Ft,filtering:$t,sortKey:Xt}={...s,...x};return(0,Z.jsx)(Ao,{accessor:A,pinnedInfo:m.get(String(A)),className:te,style:ce,visibleMediaQuery:B,textAlign:z,width:se,title:we,sortable:Se,draggable:Re,toggleable:xe,pinnable:ve,resizable:O&&Pk.toggleable).map(k=>(0,Z.jsx)($.Group,{children:(0,Z.jsx)($.Checkbox,{classNames:{label:"mantine-datatable-header-column-toggle-checkbox-label"},size:"xs",label:v[k.accessor],checked:k.toggled,onChange:x=>{M(w.map(P=>P.accessor===k.accessor?{...P,toggled:x.currentTarget.checked}:P))}})},k.accessor))})})]}):H}var zt=require("@mantine/core"),Vo=q(require("clsx")),fn=require("react/jsx-runtime");function Go({fetching:e,customContent:t,backgroundBlur:n,size:o,type:a,color:r}){return(0,fn.jsx)(zt.Center,{className:(0,Vo.default)("mantine-datatable-loader",{"mantine-datatable-loader-fetching":e}),style:[{backdropFilter:n?`blur(${n}px)`:void 0}],children:e&&(t||(0,fn.jsx)(zt.Loader,{size:o,type:a,color:r}))})}var De=require("@mantine/core"),bn=q(require("clsx"));var W=require("@mantine/core"),Xo=q(require("clsx"));var Ze=require("react/jsx-runtime");function Fo(){return(0,Ze.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Ze.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Ze.jsx)("path",{d:"M8 9l4 -4l4 4"}),(0,Ze.jsx)("path",{d:"M16 15l-4 4l-4 -4"})]})}var oe=require("react/jsx-runtime"),$o={xs:(0,W.rem)(22),sm:(0,W.rem)(26),md:(0,W.rem)(32),lg:(0,W.rem)(38),xl:(0,W.rem)(44)};function Uo({size:e,label:t,values:n,value:o,activeTextColor:a,activeBackgroundColor:r,onChange:l}){return(0,oe.jsxs)(W.Group,{gap:"xs",children:[(0,oe.jsx)(W.Text,{component:"div",size:e,children:t}),(0,oe.jsxs)(W.Menu,{withinPortal:!0,withArrow:!0,offset:2,classNames:{arrow:"mantine-datatable-page-size-selector-menu-arrow"},children:[(0,oe.jsx)(W.MenuTarget,{children:(0,oe.jsx)(W.Button,{size:e,variant:"default",classNames:{section:"mantine-datatable-page-size-selector-button-icon"},rightSection:(0,oe.jsx)(Fo,{}),style:[{fontWeight:"normal"},s=>({height:$o[e],paddingLeft:s.spacing[e],paddingRight:s.spacing[e]})],children:o})}),(0,oe.jsx)(W.MenuDropdown,{children:n.map(s=>{let c=s===o;return(0,oe.jsx)(W.MenuItem,{className:(0,Xo.default)({"mantine-datatable-page-size-selector-active":c}),style:[{height:$o[e]},c&&(a||r)?m=>wt({theme:m,paginationActiveTextColor:a,paginationActiveBackgroundColor:r}):void 0],disabled:c,onClick:()=>l(s),children:(0,oe.jsx)(W.Text,{component:"div",size:e,children:s})},s)})})]})]})}var ee=require("react/jsx-runtime");function Qo({className:e,style:t,fetching:n,page:o,onPageChange:a,paginationWithEdges:r,paginationWithControls:l,paginationActiveTextColor:s,paginationActiveBackgroundColor:c,paginationSize:m,loadingText:i,noRecordsText:u,paginationText:p,totalRecords:T,recordsPerPage:d,onRecordsPerPageChange:S,recordsPerPageLabel:b,recordsPerPageOptions:C,recordsLength:E,horizontalSpacing:D,paginationWrapBreakpoint:L,getPaginationControlProps:N,getPaginationItemProps:f,renderPagination:h}){let w;if(T){let x=(o-1)*d+1,P=x+(E||0)-1;w=p({from:x,to:P,totalRecords:T})}else w=n?i:u;let M=T&&d?Math.max(1,Math.ceil(T/d)):1,g=T?(o-1)*(d??0)+1:void 0,R=T?(g??1)+(E??0)-1:void 0,v=!ge(({breakpoints:x})=>`(min-width: ${typeof L=="number"?`${(0,De.rem)(L)}rem`:x[L]||L})`),H={Text:x=>(0,ee.jsx)(De.Text,{component:"div",className:"mantine-datatable-pagination-text",size:m,...x,children:w}),PageSizeSelector:x=>C?(0,ee.jsx)(Uo,{activeTextColor:s,activeBackgroundColor:c,size:m,label:b,values:C,value:d,onChange:S,...x}):null,Pagination:x=>(0,ee.jsx)(De.Pagination,{classNames:{root:(0,bn.default)("mantine-datatable-pagination-pages",{"mantine-datatable-pagination-pages-fetching":n||!E}),control:"mantine-datatable-pagination-pages-control"},style:s||c?P=>wt({theme:P,paginationActiveTextColor:s,paginationActiveBackgroundColor:c}):void 0,withEdges:r,withControls:l,value:o,onChange:a,size:m,total:M,getControlProps:N,getItemProps:f,...x})},k={state:{paginationSize:m,page:o,totalPages:M,totalRecords:T,recordsPerPage:d,recordsLength:E,fetching:n,from:g,to:R,isWrapped:v},actions:{setPage:x=>a?.(x),setRecordsPerPage:S?x=>S(x):void 0},Controls:H};return(0,ee.jsx)(De.Box,{px:D??"xs",py:"xs",className:(0,bn.default)("mantine-datatable-pagination",e),style:[{flexDirection:v?"column":"row"},t],children:typeof h=="function"?h(k):(0,ee.jsxs)(ee.Fragment,{children:[(0,ee.jsx)(H.Text,{}),(0,ee.jsx)(H.PageSizeSelector,{}),(0,ee.jsx)(H.Pagination,{})]})})}var ta=require("@mantine/core"),na=q(require("clsx"));var Ko=require("@mantine/core"),jo=q(require("clsx"));var qo=require("react/jsx-runtime");function Jo({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,record:a,index:r,onClick:l,onDoubleClick:s,onContextMenu:c,noWrap:m,ellipsis:i,textAlign:u,width:p,accessor:T,render:d,defaultRender:S,customCellAttributes:b}){return ge(o)?(0,qo.jsx)(Ko.TableTd,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,jo.default)({[Ke]:m||i,[je]:i,[re]:l||s,[Lt]:c,[Te]:u==="left",[he]:u==="center",[Ce]:u==="right"},e),style:[{width:p,minWidth:p,maxWidth:p},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],onClick:l,onDoubleClick:s,onContextMenu:c,...b?.(a,r),children:d?d(a,r):S?S(a,r,T):Rt(a,T)}):null}var Be=require("@mantine/core");var ie=require("react/jsx-runtime");function Zo({open:e,colSpan:t,content:n,collapseProps:o}){let{expanded:a,visible:r}=ro(e,o?.transitionDuration);return r?(0,ie.jsxs)(ie.Fragment,{children:[(0,ie.jsx)(Be.TableTr,{}),(0,ie.jsx)(Be.TableTr,{children:(0,ie.jsx)(Be.TableTd,{className:"mantine-datatable-row-expansion-cell",colSpan:t,children:(0,ie.jsx)(Be.Collapse,{expanded:a,...o,children:(0,ie.jsx)("div",{className:"mantine-datatable-row-expansion-cell-content",children:n()})})})})]}):null}var _t=require("@mantine/core"),Yo=q(require("clsx"));var gn=require("react/jsx-runtime");function ea({className:e,style:t,record:n,index:o,trigger:a,onChange:r,withRightShadow:l,checkboxProps:s,getCheckboxProps:c,...m}){let i={...s,...c(n,o)},u=!m.disabled&&!i.disabled,p=T=>{T.stopPropagation(),a==="cell"&&u&&r?.(T)};return(0,gn.jsx)(_t.TableTd,{className:(0,Yo.default)("mantine-datatable-row-selector-cell",{[re]:a==="cell"&&u},e),style:t,"data-shadow-visible":l||void 0,onClick:p,children:(0,gn.jsx)(_t.Checkbox,{classNames:u?{input:re}:void 0,onChange:r,...m,...i})})}var ae=require("react/jsx-runtime");function oa({record:e,index:t,columns:n,defaultColumnProps:o,pinnedMap:a,defaultColumnRender:r,selectionTrigger:l,selectionVisible:s,selectionChecked:c,onSelectionChange:m,isRecordSelectable:i,selectionCheckboxProps:u,getSelectionCheckboxProps:p,onClick:T,onDoubleClick:d,onContextMenu:S,onCellClick:b,onCellDoubleClick:C,onCellContextMenu:E,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w,selectorCellShadowVisible:M,selectionColumnClassName:g,selectionColumnStyle:R,rowFactory:y}){let v=(0,ae.jsxs)(ae.Fragment,{children:[s&&(0,ae.jsx)(ea,{className:g,style:R,record:e,index:t,trigger:l,withRightShadow:M,checked:c,disabled:!m||(i?!i(e,t):!1),onChange:m,checkboxProps:u,getCheckboxProps:p}),n.map(({hidden:x,hiddenContent:P,...A},B)=>{if(x||P)return null;let{accessor:z,visibleMediaQuery:se,textAlign:we,noWrap:Se,ellipsis:Re,width:xe,render:ve,cellsClassName:O,cellsStyle:te,customCellAttributes:ce}={...o,...A};return(0,ae.jsx)(Jo,{pinnedInfo:a.get(String(z)),className:typeof O=="function"?O(e,t):O,style:te?.(e,t),visibleMediaQuery:se,record:e,index:t,onClick:b?ue=>b({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,onDoubleClick:C?ue=>C({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,onContextMenu:E?ue=>E({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,accessor:z,textAlign:we,noWrap:Se,ellipsis:Re,width:xe,render:ve,defaultRender:r,customCellAttributes:ce},z)})]}),H=D&&(0,ae.jsx)(Zo,{colSpan:n.filter(({hidden:x})=>!x).length+(s?1:0),open:D.isRowExpanded(e),content:D.content({record:e,index:t}),collapseProps:D.collapseProps}),k=Rr({record:e,index:t,selectionChecked:c,onClick:T,onDoubleClick:d,onContextMenu:S,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w});return y?y({record:e,index:t,rowProps:k,children:v,expandedElement:H}):(0,ae.jsxs)(ae.Fragment,{children:[(0,ae.jsx)(ta.TableTr,{...k,children:v}),H]})}function Rr({record:e,index:t,selectionChecked:n,onClick:o,onDoubleClick:a,onContextMenu:r,expansion:l,customAttributes:s,color:c,backgroundColor:m,className:i,style:u}){return{className:(0,na.default)("mantine-datatable-row",{[re]:o||a||l?.isExpandable({record:e,index:t})&&l?.expandOnClick},{[Lt]:r},typeof i=="function"?i(e,t):i),"data-selected":n||void 0,onClick:p=>{if(l){let{isExpandable:T,isRowExpanded:d,expandOnClick:S,expandRow:b,collapseRow:C}=l;T({record:e,index:t})&&S&&(d(e)?C(e):b(e))}o?.({event:p,record:e,index:t})},onDoubleClick:a?p=>a({event:p,record:e,index:t}):void 0,onContextMenu:r?p=>r({event:p,record:e,index:t}):void 0,style:[c||m?p=>{let T=c?.(e,t),d=m?.(e,t);return Mn({theme:p,color:T,backgroundColor:d})}:void 0,u?.(e,t)],...s?.(e,t)??{}}}var Tt=require("@mantine/core"),gt=q(require("clsx")),Ve=require("react/jsx-runtime");function aa({leftShadowBehind:e,rightShadowBehind:t,onScrollPositionChange:n,children:o,viewportRef:a,scrollAreaProps:r}){return(0,Ve.jsxs)(Tt.ScrollArea,{...r,viewportRef:a,classNames:{root:"mantine-datatable-scroll-area",scrollbar:"mantine-datatable-scroll-area-scrollbar",thumb:"mantine-datatable-scroll-area-thumb",corner:"mantine-datatable-scroll-area-corner"},onScrollPositionChange:n,children:[o,(0,Ve.jsx)(Tt.Box,{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-top-shadow")}),(0,Ve.jsx)("div",{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-left-shadow",{"mantine-datatable-scroll-area-shadow-behind":e})}),(0,Ve.jsx)("div",{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-right-shadow",{"mantine-datatable-scroll-area-shadow-behind":t})}),(0,Ve.jsx)(Tt.Box,{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-bottom-shadow")})]})}var Q=require("react/jsx-runtime");function ra({withTableBorder:e,borderRadius:t,textSelectionDisabled:n,height:o="100%",minHeight:a,maxHeight:r,shadow:l,verticalAlign:s="center",fetching:c,columns:m,storeColumnsKey:i,groups:u,pinFirstColumn:p,pinLastColumn:T,defaultColumnProps:d,defaultColumnRender:S,idAccessor:b="id",records:C,selectionTrigger:E="checkbox",selectedRecords:D,onSelectedRecordsChange:L,selectionColumnClassName:N,selectionColumnStyle:f,isRecordSelectable:h,selectionCheckboxProps:w,allRecordsSelectionCheckboxProps:M={"aria-label":"Select all records"},getRecordSelectionCheckboxProps:g=(Me,X)=>({"aria-label":`Select record ${X+1}`}),sortStatus:R,sortIcons:y,onSortStatusChange:v,horizontalSpacing:H,page:k,onPageChange:x,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:B,recordsPerPageOptions:z,recordsPerPageLabel:se="Records per page",paginationWithEdges:we,paginationWithControls:Se,paginationActiveTextColor:Re,paginationActiveBackgroundColor:xe,paginationSize:ve="sm",paginationText:O=({from:Me,to:X,totalRecords:Kt})=>`${Me} - ${X} / ${Kt}`,paginationWrapBreakpoint:te="sm",getPaginationControlProps:ce=Me=>Me==="previous"?{"aria-label":"Previous page"}:Me==="next"?{"aria-label":"Next page"}:{},getPaginationItemProps:ue,renderPagination:Gt,loaderBackgroundBlur:Ft,customLoader:$t,loaderSize:Xt,loaderType:ua,loaderColor:da,loadingText:ma="...",emptyState:pa,noRecordsText:hn="No records",noRecordsIcon:fa,highlightOnHover:ba,striped:ga,noHeader:Ta,onRowClick:ha,onRowDoubleClick:Ca,onRowContextMenu:ya,onCellClick:Pa,onCellDoubleClick:Da,onCellContextMenu:wa,onScroll:Sa,onScrollToTop:Ra,onScrollToBottom:xa,onScrollToLeft:va,onScrollToRight:Ma,c:ka,backgroundColor:Ea,borderColor:La,rowBorderColor:Ia,stripedColor:Na,highlightOnHoverColor:Ha,rowColor:Aa,rowBackgroundColor:Oa,rowExpansion:za,rowClassName:_a,rowStyle:Wa,customRowAttributes:Ba,scrollViewportRef:Va,scrollAreaProps:Ga,tableRef:Fa,bodyRef:$a,m:Xa,my:Ua,mx:Qa,mt:Ka,mb:ja,ml:Ja,mr:qa,className:Za,classNames:Ye,style:Ya,styles:et,rowFactory:er,tableWrapper:Ut,...Qt}){let Me=(0,J.useMemo)(()=>u?Xe(u):m,[m,u]),{refs:X,onScroll:Kt}=jn({scrollCallbacks:{onScroll:Sa,onScrollToTop:Ra,onScrollToBottom:xa,onScrollToLeft:va,onScrollToRight:Ma},withRowBorders:Qt.withRowBorders}),de=ln({key:i,columns:Me,headerRef:X.header,scrollViewportRef:X.scrollViewport}),tt=de.effectiveColumns,tr=(0,Wt.useMergedRef)(X.table,Fa),nr=(0,Wt.useMergedRef)(X.scrollViewport,Va),Cn=(0,J.useRef)(null),or=(0,Wt.useMergedRef)(Cn,$a),ar=ao({rowExpansion:za,records:C,idAccessor:b}),{pinnedMap:nt,hasLeftPinned:yn,hasRightPinned:rr}=Jn({columns:tt,theadRef:X.header,tbodyRef:Cn,selectionColumnHeaderRef:X.selectionColumnHeader,selectionVisible:!!D,pinFirstColumn:p,pinLastColumn:T}),ht=(0,J.useRef)(!1),Pn=(0,J.useRef)(k),jt=(0,J.useRef)(C),lr=(0,J.useCallback)(I=>{ht.current=!0,jt.current=C,x(I)},[x,C]);(0,J.useEffect)(()=>{Pn.current!==k&&(ht.current=!0,jt.current=C,Pn.current=k)},[k,C]);let ot=C?.length;(0,J.useLayoutEffect)(()=>{if(!ht.current||c||C===jt.current)return;let I=X.scrollViewport.current;if(!I)return;let ke=requestAnimationFrame(()=>{I.scrollTo({top:0,left:0}),ht.current=!1});return()=>cancelAnimationFrame(ke)},[c,C,X.scrollViewport]);let Dn=C?.map(I=>F(I,b)),Ge=!!D,at=D?.map(I=>F(I,b)),wn=Dn!==void 0&&at!==void 0&&at.length>0,Jt=h?C?.filter(h):C,Ct=Jt?.map(I=>F(I,b)),yt=wn&&Ct.every(I=>at.includes(I)),ir=wn&&Ct.some(I=>at.includes(I)),sr=(0,J.useCallback)(()=>{D&&L&&L(yt?D.filter(I=>!Ct.includes(F(I,b))):lt([...D,...Jt],I=>F(I,b)))},[yt,b,L,Ct,Jt,D]),{lastSelectionChangeIndex:Pt,setLastSelectionChangeIndex:cr}=qn(Dn),qt=Ge&&!yn,ur={m:Xa,my:Ua,mx:Qa,mt:Ka,mb:ja,ml:Ja,mr:qa},dr=(0,J.useCallback)(({children:I})=>Ut?Ut({children:I}):I,[Ut]);return(0,Q.jsx)(tn,{...de,pinnedMap:nt,children:(0,Q.jsxs)(Bt.Box,{ref:X.root,...ur,className:(0,Tn.default)("mantine-datatable",{"mantine-datatable-with-border":e},Za,Ye?.root),style:[I=>({...vn({theme:I,c:ka,backgroundColor:Ea,borderColor:La,rowBorderColor:Ia,stripedColor:Na,highlightOnHoverColor:Ha}),borderRadius:I.radius[t]||t,boxShadow:I.shadows[l]||l,height:o,minHeight:a,maxHeight:r}),Ya,et?.root,{position:"relative"}],children:[(0,Q.jsx)(aa,{viewportRef:nr,leftShadowBehind:Ge||yn,rightShadowBehind:rr,onScrollPositionChange:Kt,scrollAreaProps:Ga,children:(0,Q.jsx)(dr,{children:(0,Q.jsxs)(Bt.Table,{ref:tr,horizontalSpacing:H,className:(0,Tn.default)("mantine-datatable-table",{[lo]:n,"mantine-datatable-vertical-align-top":s==="top","mantine-datatable-vertical-align-bottom":s==="bottom","mantine-datatable-selection-column-visible":Ge,"mantine-datatable-resizable-columns":de.hasResizableColumns,"mantine-datatable-resize-locked":de.isLocked,"mantine-datatable-resizing":de.isResizing},Ye?.table),style:{...et?.table,...de.isLocked?{tableLayout:"fixed"}:null,...de.tableWidth!=null?{width:`${de.tableWidth}px`}:null},"data-striped":ot&&ga||void 0,"data-highlight-on-hover":ba||void 0,...Qt,children:[Ta?null:(0,Q.jsx)(tn,{...de,pinnedMap:nt,children:(0,Q.jsx)(Bo,{ref:X.header,selectionColumnHeaderRef:X.selectionColumnHeader,className:Ye?.header,style:et?.header,columns:tt,defaultColumnProps:d,groups:u,pinnedMap:nt,sortStatus:R,sortIcons:y,onSortStatusChange:v,selectionTrigger:E,selectionVisible:Ge,selectionChecked:yt,selectionIndeterminate:ir&&!yt,onSelectionChange:sr,selectionCheckboxProps:{...w,...M},selectorCellShadowVisible:qt,selectionColumnClassName:N,selectionColumnStyle:f,withColumnBorders:Qt.withColumnBorders})}),(0,Q.jsx)("tbody",{ref:or,children:ot?C.map((I,ke)=>{let Zt=F(I,b),Yt=at?.includes(Zt)||!1,Sn;return L&&D&&(Sn=mr=>{if(mr.nativeEvent.shiftKey&&Pt!==null){let Fe=C.filter(ke>Pt?(Ee,Le)=>Le>=Pt&&Le<=ke&&(h?h(Ee,Le):!0):(Ee,Le)=>Le>=ke&&Le<=Pt&&(h?h(Ee,Le):!0));L(Yt?an(D,Fe,Ee=>F(Ee,b)):lt([...D,...Fe],Ee=>F(Ee,b)))}else L(Yt?D.filter(Fe=>F(Fe,b)!==Zt):lt([...D,I],Fe=>F(Fe,b)));cr(ke)}),(0,Q.jsx)(oa,{record:I,index:ke,columns:tt,defaultColumnProps:d,pinnedMap:nt,defaultColumnRender:S,selectionTrigger:E,selectionVisible:Ge,selectionChecked:Yt,onSelectionChange:Sn,isRecordSelectable:h,selectionCheckboxProps:w,getSelectionCheckboxProps:g,onClick:ha,onDoubleClick:Ca,onCellClick:Pa,onCellDoubleClick:Da,onContextMenu:ya,onCellContextMenu:wa,expansion:ar,color:Aa,backgroundColor:Oa,className:_a,style:Wa,customAttributes:Ba,selectorCellShadowVisible:qt,selectionColumnClassName:N,selectionColumnStyle:f,idAccessor:b,rowFactory:er},Zt)}):(0,Q.jsx)(In,{})}),tt.some(({footer:I})=>I)&&(0,Q.jsx)(go,{ref:X.footer,className:Ye?.footer,style:et?.footer,columns:tt,defaultColumnProps:d,pinnedMap:nt,selectionVisible:Ge,selectorCellShadowVisible:qt})]})})}),!!(k&&ot)&&(0,Q.jsx)(Qo,{className:Ye?.pagination,style:et?.pagination,horizontalSpacing:H,fetching:c,page:k,onPageChange:lr,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:B,recordsPerPageOptions:z,recordsPerPageLabel:se,paginationWithEdges:we,paginationWithControls:Se,paginationActiveTextColor:Re,paginationActiveBackgroundColor:xe,paginationSize:ve,paginationText:O,paginationWrapBreakpoint:te,getPaginationControlProps:ce,getPaginationItemProps:ue,noRecordsText:hn,loadingText:ma,recordsLength:ot,renderPagination:Gt}),(0,Q.jsx)(Go,{fetching:c,backgroundBlur:Ft,customContent:$t,size:Xt,type:ua,color:da}),(0,Q.jsx)(Hn,{icon:fa,text:hn,active:!c&&!ot,children:pa})]})})}var la=require("@mantine/core"),ia=require("@mantine/hooks"),Vt=require("react"),ca=require("react/jsx-runtime");function sa({className:e,children:t,isDragging:n,ref:o,...a}){let r=(0,Vt.useRef)(null),l=(0,ia.useMergedRef)(r,o);return(0,Vt.useEffect)(()=>{if(!r.current||!n)return;let i=r.current.parentElement.parentElement.children[0].children[0];for(let u=0;u{for(var n in t)Dt(e,n,{get:t[n],enumerable:!0})},Rn=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of br(t))!Tr.call(e,a)&&a!==n&&Dt(e,a,{get:()=>t[a],enumerable:!(o=fr(t,a))||o.enumerable});return e};var q=(e,t,n)=>(n=e!=null?pr(gr(e)):{},Rn(t||!e||!e.__esModule?Dt(n,"default",{value:e,enumerable:!0}):n,e)),Cr=e=>Rn(Dt({},"__esModule",{value:!0}),e);var xr={};hr(xr,{DataTable:()=>ra,DataTableDraggableRow:()=>sa,calculateColSpan:()=>it,differenceBy:()=>an,flattenColumns:()=>Xe,getGroupsAtDepth:()=>vt,getMaxGroupDepth:()=>xt,getRecordId:()=>F,getValueAtPath:()=>Rt,hasStringAccessor:()=>rt,humanize:()=>Ie,needsRightBorder:()=>rn,reorderRecords:()=>yr,sanitizeStoredArray:()=>pe,swapRecords:()=>Pr,uniqBy:()=>lt,useDataTableColumns:()=>ln});module.exports=Cr(xr);var Bt=require("@mantine/core"),Wt=require("@mantine/hooks"),Tn=q(require("clsx")),J=require("react");var xn=require("@mantine/core");function V(e,t,n){return e?(0,xn.parseThemeColor)({color:typeof e=="object"?e[n]:e,theme:t}).value:void 0}function vn({theme:e,c:t,backgroundColor:n,borderColor:o,rowBorderColor:a,stripedColor:r,highlightOnHoverColor:l}){return{"--mantine-datatable-color-light":V(t,e,"light"),"--mantine-datatable-color-dark":V(t,e,"dark"),"--mantine-datatable-background-color-light":V(n,e,"light"),"--mantine-datatable-background-color-dark":V(n,e,"dark"),"--mantine-datatable-border-color-light":V(o,e,"light"),"--mantine-datatable-border-color-dark":V(o,e,"dark"),"--mantine-datatable-row-border-color-light":V(a,e,"light"),"--mantine-datatable-row-border-color-dark":V(a,e,"dark"),"--mantine-datatable-striped-color-light":V(r,e,"light"),"--mantine-datatable-striped-color-dark":V(r,e,"dark"),"--mantine-datatable-highlight-on-hover-color-light":V(l,e,"light"),"--mantine-datatable-highlight-on-hover-color-dark":V(l,e,"dark")}}function wt({theme:e,paginationActiveTextColor:t,paginationActiveBackgroundColor:n}){return{"--mantine-datatable-pagination-active-text-color-light":V(t,e,"light"),"--mantine-datatable-pagination-active-text-color-dark":V(t,e,"dark"),"--mantine-datatable-pagination-active-background-color-light":V(n,e,"light"),"--mantine-datatable-pagination-active-background-color-dark":V(n,e,"dark")}}function Mn({theme:e,color:t,backgroundColor:n}){return{"--mantine-datatable-row-color-light":V(t,e,"light"),"--mantine-datatable-row-color-dark":V(t,e,"dark"),"--mantine-datatable-row-background-color-light":V(n,e,"light"),"--mantine-datatable-row-background-color-dark":V(n,e,"dark")}}var en=require("react");var kn=require("@mantine/core"),[En,$e]=(0,kn.createSafeContext)("useDataTableColumnsContext must be used within DataTableColumnProvider");var Ln=require("react/jsx-runtime"),tn=e=>{let{children:t,columnsOrder:n,setColumnsOrder:o,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S,pinnedMap:b}=e,[C,E]=(0,en.useState)(""),[D,L]=(0,en.useState)("");return(0,Ln.jsx)(En,{value:{sourceColumn:C,setSourceColumn:E,targetColumn:D,setTargetColumn:L,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,swapColumns:()=>{if(!n||!o||!C||!D)return;let f=b.get(C),h=b.get(D);if(f?.logicalSide!==h?.logicalSide)return;let w=n.indexOf(C),M=n.indexOf(D);if(w!==-1&&M!==-1){let g=[...n],[R]=g.splice(w,1);g.splice(M,0,R),o(g)}},resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S},children:t})};var nn=require("react/jsx-runtime");function In(){return(0,nn.jsx)("tr",{className:"mantine-datatable-empty-row",children:(0,nn.jsx)("td",{})})}var St=require("@mantine/core");var be=require("react/jsx-runtime");function Nn(){return(0,be.jsxs)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,be.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,be.jsx)("path",{d:"M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74"}),(0,be.jsx)("path",{d:"M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6"}),(0,be.jsx)("path",{d:"M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4"}),(0,be.jsx)("path",{d:"M3 3l18 18"})]})}var me=require("react/jsx-runtime");function Hn({icon:e,text:t,active:n,children:o}){return(0,me.jsx)(St.Center,{className:"mantine-datatable-empty-state","data-active":n||void 0,children:o||(0,me.jsxs)(me.Fragment,{children:[e||(0,me.jsx)("div",{className:"mantine-datatable-empty-state-icon",children:(0,me.jsx)(Nn,{})}),(0,me.jsx)(St.Text,{component:"div",size:"sm",c:"dimmed",children:t})]})})}var It=require("@mantine/core"),bo=q(require("clsx"));var io=require("@mantine/core"),so=q(require("clsx"));var on=require("react");var An=require("@mantine/hooks"),st=require("react");function Ie(e){let t=e.replace(/([a-z\d])([A-Z]+)/g,"$1 $2").replace(/\W|_/g," ").trim().toLowerCase();return`${t.charAt(0).toUpperCase()}${t.slice(1)}`}function pe(e,t,n){return!Array.isArray(e)||n&&!e.every(n)?t:e}function rt(e){return typeof e=="object"&&e!==null&&typeof e.accessor=="string"}function an(e,t,n){return e.filter(o=>!t.map(n).includes(n(o)))}function lt(e,t){return e.filter((n,o,a)=>o===a.findIndex(r=>t(n)===t(r)))}function Rt(e,t){return t?t.match(/([^[.\]])+/g).reduce((o,a)=>o&&o[a],e):void 0}function F(e,t){return typeof t=="string"?Rt(e,t):t(e)}function yr(e,t){let n=structuredClone(t),o=n[e.source.index];return e.destination&&(n.splice(e.source.index,1),n.splice(e.destination.index,0,o)),n}function Pr(e,t){let n=structuredClone(t),o=e.destination;if(!o)return n;let a=n[e.source.index],r=n[o.index];return n.splice(o.index,1,a),n.splice(e.source.index,1,r),n}function xt(e){return!e||e.length===0?0:Math.max(...e.map(t=>t.groups&&t.groups.length>0?1+xt(t.groups):1))}function Xe(e){let t=[];for(let n of e)n.columns&&n.columns.length>0?t.push(...n.columns.filter(o=>o!=null)):n.groups&&n.groups.length>0&&t.push(...Xe(n.groups));return t.filter(n=>n!=null)}function it(e,t){return e.columns&&e.columns.length>0?e.columns.filter((n,o)=>n.hidden?!1:t?t[o]!==!1:!0).length:e.groups&&e.groups.length>0?e.groups.reduce((n,o)=>n+it(o,t),0):0}function vt(e,t,n=0){if(n===t)return[...e];let o=[];for(let a of e)a.groups&&a.groups.length>0&&o.push(...vt(a.groups,t,n+1));return o}function rn(e,t,n){return n?!e||t:!1}function On({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,st.useMemo)(()=>t.map(i=>({accessor:i.accessor,defaultPinned:i.pinned,pinnable:!!i.pinnable,pinned:i.pinned})),[t]),[a,r]=(0,An.useLocalStorage)({key:e?`${e}-columns-pinning`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,rt);function s(i){e&&r(typeof i=="function"?u=>i(u??o):i)}let c=(0,st.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultPinned:u.pinned,pinnable:!!u.pinnable,pinned:u.pinned})}),i},[l,t,o]);(0,st.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsPinning:c,setColumnsPinning:s,resetColumnsPinning:m}:{columnsPinning:o,setColumnsPinning:s,resetColumnsPinning:m}}var zn=require("@mantine/hooks"),ct=require("react");function _n({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,ct.useMemo)(()=>t?t.map(i=>i.accessor):[],[t]),[a,r]=(0,zn.useLocalStorage)({key:e?`${e}-columns-order`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,i=>typeof i=="string");function s(i){e&&r(i)}let c=(0,ct.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u)&&i.push(u)}),t.forEach(u=>{i.includes(u.accessor)||i.push(u.accessor)}),i},[l,t,o]);(0,ct.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsOrder:c,setColumnsOrder:s,resetColumnsOrder:m}:{columnsOrder:l,setColumnsOrder:s,resetColumnsOrder:m}}var Wn=require("@mantine/hooks"),_=require("react");function Bn({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o}){let a=(0,_.useMemo)(()=>t.some(f=>f.resizable&&!f.hidden&&f.accessor!=="__selection__"),[t]),r=(0,_.useCallback)(()=>t.filter(f=>f.accessor!=="__selection__").map(f=>({[f.accessor]:f.width??"auto"})),[t]),[l,s]=(0,Wn.useLocalStorage)({key:e?`${e}-columns-width`:"",defaultValue:e?r():void 0,getInitialValueInEffect:n}),c=pe(l,[],f=>typeof f=="object"&&f!==null),[m,i]=(0,_.useState)(()=>e&&c.length>0?c:r()),[u,p]=(0,_.useState)(!1),T=(0,_.useRef)(!1);(0,_.useEffect)(()=>{e&&(!c||c.length===0||i(c))},[e,c]),(0,_.useEffect)(()=>{i(f=>{let h=new Set(t.filter(y=>y.accessor!=="__selection__").map(y=>String(y.accessor))),w=f.filter(y=>h.has(Object.keys(y)[0])),M=new Set(w.map(y=>Object.keys(y)[0])),g=!1;for(let y of t){if(y.accessor==="__selection__")continue;let v=String(y.accessor);M.has(v)||(w.push({[v]:y.width??"auto"}),g=!0)}return w.length!==f.length||g?w:f})},[t]);let d=(0,_.useCallback)(f=>{let h=f.filter(w=>w.accessor!=="__selection__");h.length!==0&&(T.current=!0,i(w=>w.map(M=>{let g=Object.keys(M)[0],R=h.find(y=>y.accessor===g);return R?{[g]:R.width}:M})))},[]);(0,_.useEffect)(()=>{!e||!T.current||u||(T.current=!1,s(m))},[e,m,u,s]);let S=(0,_.useCallback)((f,h)=>{d([{accessor:f,width:h}])},[d]),b=(0,_.useCallback)(f=>{d(f)},[d]),C=(0,_.useCallback)(()=>{p(!0);let f=o?.current;if(!f)return;let h=Array.from(f.querySelectorAll("th[data-accessor]"));if(h.length===0)return;let w=[];for(let M of h){let g=M.getAttribute("data-accessor");if(!g||g==="__selection__")continue;let R=M.offsetWidth;w.push({[g]:`${R}px`})}T.current=!0,i(w)},[o]),E=(0,_.useCallback)(()=>{p(!1)},[]),D=(0,_.useCallback)(()=>{let f=r();T.current=!0,i(f),p(!1)},[r]),L=(0,_.useMemo)(()=>{if(!a)return!1;let f=t.filter(h=>h.resizable&&!h.hidden&&h.accessor!=="__selection__");return m.filter(h=>{let w=Object.keys(h)[0];return f.some(M=>M.accessor===w)}).every(h=>{let w=Object.values(h)[0];return w==="auto"||w==="initial"})},[a,t,m]),N=(0,_.useCallback)(()=>{},[]);return{columnsWidth:m,setColumnsWidth:d,setColumnWidth:S,setMultipleColumnWidths:b,resetColumnsWidth:D,hasResizableColumns:a,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:u,beginResize:C,endResize:E}}var Mt=require("react");var Vn=require("@mantine/hooks"),ut=require("react");function Gn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=(0,ut.useMemo)(()=>t?.map(i=>({accessor:i.accessor,defaultToggle:i.defaultToggle||!0,toggleable:i.toggleable,toggled:i.defaultToggle===void 0?!0:i.defaultToggle})),[t]),[a,r]=(0,Vn.useLocalStorage)({key:e?`${e}-columns-toggle`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=pe(a,o,rt);function s(i){e&&r(i)}let c=(0,ut.useMemo)(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultToggle:u.defaultToggle||!0,toggleable:u.toggleable,toggled:u.defaultToggle===void 0?!0:u.defaultToggle})}),i},[l,t,o]);(0,ut.useEffect)(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsToggle:c,setColumnsToggle:s,resetColumnsToggle:m}:{columnsToggle:l,setColumnsToggle:s,resetColumnsToggle:m}}var ln=({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o,scrollViewportRef:a})=>{let{columnsOrder:r,setColumnsOrder:l,resetColumnsOrder:s}=_n({key:e,columns:t,getInitialValueInEffect:n}),{columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i}=Gn({key:e,columns:t,getInitialValueInEffect:n}),{columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T}=On({key:e,columns:t,getInitialValueInEffect:n}),{columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,beginResize:h,endResize:w}=Bn({key:e,columns:t,getInitialValueInEffect:n,headerRef:o,scrollViewportRef:a}),M=(0,Mt.useMemo)(()=>{let y;r?y=r.map(P=>t.find(A=>A.accessor===P)).map(P=>{let A=u.find(z=>z.accessor===P?.accessor),B=P?.pinnable?A?.pinned:P?.pinned;return{...P,hidden:P?.hidden||!c.find(z=>z.accessor===P?.accessor)?.toggled,pinned:B}}):y=t;let v=y.filter(P=>P.pinned==="left"),H=y.filter(P=>!P.pinned),k=y.filter(P=>P.pinned==="right");return[...v,...H,...k].map(P=>{if(P?.accessor==="__selection__")return P;let A=P?.accessor,B=d.find(se=>A in se);if(!B)return P;let z=B[A];return z===void 0||z==="auto"||z==="initial"?P:{...P,width:z}})},[t,r,c,u,d]),g=(0,Mt.useMemo)(()=>{let y=M.filter(v=>!v.hidden&&v.accessor!=="__selection__");return y.length===0?!1:y.every(v=>typeof v.width=="string"&&/px$/.test(v.width))},[M]),R=(0,Mt.useMemo)(()=>{if(!g)return null;let y=0;for(let v of M){if(v.hidden||v.accessor==="__selection__")continue;let H=v.width;if(typeof H!="string")continue;let k=parseInt(H,10);Number.isNaN(k)||(y+=k)}return y>0?y:null},[g,M]);return{effectiveColumns:M,setColumnsOrder:l,columnsOrder:r,resetColumnsOrder:s,columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i,columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T,columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,isLocked:g,tableWidth:R,beginResize:h,endResize:w}};var Kn=require("@mantine/core"),K=require("react");var kt=require("react"),Ne=typeof window<"u"?kt.useLayoutEffect:kt.useEffect;var Fn=require("react");function sn(e){let t=(0,Fn.useRef)(e);return t.current=e,t}var $n="--mantine-datatable-header-height",Xn="--mantine-datatable-footer-height",Un="--mantine-datatable-selection-column-width",Qn="--mantine-datatable-last-row-border-bottom";function ne(e,t,n){e?.style.setProperty(t,n)}function un(e){let t=e.borderBoxSize?.[0]||e.contentBoxSize?.[0];return t?{width:t.inlineSize,height:t.blockSize}:{width:e.contentRect.width,height:e.contentRect.height}}function cn(e,t,n){if(e){let o=new ResizeObserver(a=>{let r=a[0];r&&t(un(r))});return o.observe(e),()=>{o.disconnect(),n()}}}function jn({scrollCallbacks:e,withRowBorders:t}){let n={root:(0,K.useRef)(null),table:(0,K.useRef)(null),scrollViewport:(0,K.useRef)(null),header:(0,K.useRef)(null),footer:(0,K.useRef)(null),selectionColumnHeader:(0,K.useRef)(null)},{root:o,table:a,scrollViewport:r,header:l,footer:s,selectionColumnHeader:c}=n,m=sn({withRowBorders:t}),i=sn(e),u=(0,K.useRef)(()=>{}),p=(0,K.useRef)(()=>{}),T=(0,K.useCallback)(d=>{i.current.onScroll?.(d),u.current()},[]);return(0,K.useEffect)(()=>cn(l.current,d=>{ne(o.current,$n,`${d.height}px`)},()=>ne(o.current,$n,"0")),[l.current]),(0,K.useEffect)(()=>cn(s.current,d=>{ne(o.current,Xn,`${d.height}px`)},()=>ne(o.current,Xn,"0")),[s.current]),(0,K.useEffect)(()=>cn(c.current,d=>{ne(o.current,Un,`${d.width}px`)},()=>ne(o.current,Un,"0")),[c.current]),Ne(()=>{if(typeof window>"u")return;let d={top:!0,bottom:!0,left:!0,right:!0},S={width:0,height:0},b={width:0,height:0};function C(f,h){let w=d[f];return d[f]=h,ne(o.current,`--mantine-datatable-${f}-shadow-opacity`,h?"0":"1"),w}function E(){let f=S.height-b.height,h=f<0;ne(o.current,"--mantine-datatable-footer-position",h?"relative":"sticky"),ne(o.current,"--mantine-datatable-footer-bottom",h?`${f}px`:"0")}function D(){m.current.withRowBorders&&S.height{for(let h of f)switch(h.target.tagName){case"TABLE":{S=un(h);break}case"DIV":{b=un(h);break}}L(),E(),D()});return N.observe(a.current),N.observe(r.current),()=>{N.disconnect()}},[]),Ne(()=>{p.current()},[t]),{refs:n,onScroll:T}}var Ue=require("react");function Jn({columns:e,theadRef:t,tbodyRef:n,selectionColumnHeaderRef:o,selectionVisible:a,pinFirstColumn:r,pinLastColumn:l}){let[s,c]=(0,Ue.useState)(new Map),m=(0,Ue.useCallback)(()=>{if(!r&&!l&&!e.some(g=>g.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=getComputedStyle(p).direction==="rtl",b=new Map;if(T)d.querySelectorAll("th[data-accessor]").forEach(g=>{let R=g.dataset.accessor;if(R){let y=g.getBoundingClientRect().width;y>0&&b.set(R,y)}});else{let g=e.filter(y=>!y.hidden);d.querySelectorAll("td:not(.mantine-datatable-row-selector-cell):not(.mantine-datatable-row-expansion-cell)").forEach((y,v)=>{if(g[v]){let H=y.getBoundingClientRect().width;H>0&&b.set(String(g[v].accessor),H)}})}let C=0;if(a){let g=o.current??p.querySelector(T?".mantine-datatable-header-selector-cell":".mantine-datatable-row-selector-cell");g&&(C=g.getBoundingClientRect().width)}let E=e.filter(g=>!g.hidden&&b.has(String(g.accessor))).map(g=>String(g.accessor)),D=new Map;for(let g of e){let R=String(g.accessor);E.includes(R)&&(g.pinned?D.set(R,g.pinned):r&&R===E[0]&&!D.has(R)?D.set(R,"left"):l&&R===E[E.length-1]&&!D.has(R)&&D.set(R,"right"))}let L=E.filter(g=>D.get(g)==="left"),N=E.filter(g=>D.get(g)==="right"),f=new Map,h=C;L.forEach((g,R)=>{f.set(g,{position:S?"right":"left",logicalSide:"left",offset:h,isBoundary:R===L.length-1}),h+=b.get(g)||0});let w=-.4,M=[...N].reverse();M.forEach((g,R)=>{f.set(g,{position:S?"left":"right",logicalSide:"right",offset:w,isBoundary:R===M.length-1}),w+=b.get(g)||0}),c(g=>{if(g.size!==f.size)return f;for(let[R,y]of f){let v=g.get(R);if(!v||v.position!==y.position||v.logicalSide!==y.logicalSide||v.offset!==y.offset||v.isBoundary!==y.isBoundary)return f}return g})},[e,a,t,n,o,r,l]);(0,Ue.useEffect)(()=>{if(!r&&!l&&!e.some(b=>b.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=new ResizeObserver(m);return d.querySelectorAll(T?"th":"td").forEach(b=>{S.observe(b)}),()=>S.disconnect()},[t,n,m]),Ne(m,[m]);let i=[...s.values()].some(p=>p.logicalSide==="left"),u=[...s.values()].some(p=>p.logicalSide==="right");return{pinnedMap:s,hasLeftPinned:i,hasRightPinned:u}}var Et=require("react");function qn(e){let[t,n]=(0,Et.useState)(null),o=e?.join(":")||"";return(0,Et.useEffect)(()=>{n(null)},[o]),{lastSelectionChangeIndex:t,setLastSelectionChangeIndex:n}}var Qe=require("react");function Dr(e,t){let n=()=>{t(e.map(a=>a.matches))},o=e.map(a=>{try{return a.addEventListener("change",n),()=>a.removeEventListener("change",n)}catch{return a.addListener(n),()=>a.removeListener(n)}});return()=>{o.forEach(a=>{a()})}}function wr(e,t){return t||(typeof window<"u"&&"matchMedia"in window?e.map(n=>window.matchMedia(n).matches):e.map(()=>!1))}function Zn(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[o,a]=(0,Qe.useState)(n?t:wr(e,t)),r=(0,Qe.useRef)(null);return(0,Qe.useEffect)(()=>{if("matchMedia"in window)return r.current=e.map(l=>window.matchMedia(l)),a(r.current.map(l=>l.matches)),Dr(r.current,l=>{a(l)})},[e]),o}var Yn=require("@mantine/core"),dn=require("react");function eo(e){let t=(0,Yn.useMantineTheme)(),n=(0,dn.useMemo)(()=>e.map(a=>(typeof a=="function"?a(t):a)??""),[e,t]),o=(0,dn.useMemo)(()=>e.map(()=>!0),[e]);return Zn(n,o)}var to=require("@mantine/core"),no=require("@mantine/hooks");function ge(e){let t=(0,to.useMantineTheme)(),n=typeof e=="function"?e(t):e;return(0,no.useMediaQuery)(n||"",!0)}var oo=require("react");function ao({rowExpansion:e,records:t,idAccessor:n}){let o=[];if(e&&t){let{trigger:s,allowMultiple:c,initiallyExpanded:m}=e;t&&s==="always"?o=t.map(i=>F(i,n)):m&&(o=t.filter((i,u)=>m({record:i,index:u})).map(i=>F(i,n)),c||(o=[o[0]]))}let a,r,l=(0,oo.useState)(o);if(e){let{expandable:s,trigger:c,allowMultiple:m,collapseProps:i,content:u}=e;e.expanded?{recordIds:a,onRecordIdsChange:r}=e.expanded:[a,r]=l;let p=T=>r?.(a.filter(d=>d!==F(T,n)));return{expandOnClick:c!=="always"&&c!=="never",isRowExpanded:T=>c==="always"?!0:a.includes(F(T,n)),isExpandable:({record:T,index:d})=>s?s({record:T,index:d}):!0,expandRow:T=>{let d=F(T,n);r?.(m?[...a,d]:[d])},collapseRow:p,collapseProps:i,content:({record:T,index:d})=>()=>u({record:T,index:d,collapse:()=>p(T)})}}}var mn=require("@mantine/hooks"),dt=require("react");function ro(e,t){let[n,o]=(0,dt.useState)(e),[a,r]=(0,dt.useState)(e),l=(0,mn.useTimeout)(()=>o(!0),0),s=(0,mn.useTimeout)(()=>r(!1),t||200);return(0,dt.useEffect)(()=>{e?(s.clear(),r(!0),l.start()):(l.clear(),o(!1),s.start())},[l,s,e]),{expanded:n,visible:a}}var Ke="mantine-datatable-nowrap",je="mantine-datatable-ellipsis",re="mantine-datatable-pointer-cursor",Lt="mantine-datatable-context-menu-cursor",lo="mantine-datatable-text-selection-disabled",Te="mantine-datatable-text-align-left",he="mantine-datatable-text-align-center",Ce="mantine-datatable-text-align-right";var uo=require("react/jsx-runtime");function co({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,title:a,noWrap:r,ellipsis:l,textAlign:s,width:c}){return ge(o)?(0,uo.jsx)(io.TableTh,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,so.default)({[Ke]:r||l,[je]:l,[Te]:s==="left",[he]:s==="center",[Ce]:s==="right"},e),style:[{width:c,minWidth:c,maxWidth:c},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],children:a}):null}var mo=require("@mantine/core"),fo=require("react/jsx-runtime");function po({shadowVisible:e}){return(0,fo.jsx)(mo.TableTh,{className:"mantine-datatable-footer-selector-placeholder-cell","data-shadow-visible":e||void 0})}var Je=require("react/jsx-runtime");function go({className:e,style:t,columns:n,defaultColumnProps:o,pinnedMap:a,selectionVisible:r,selectorCellShadowVisible:l,ref:s}){return(0,Je.jsx)(It.TableTfoot,{ref:s,className:(0,bo.default)("mantine-datatable-footer",e),style:t,children:(0,Je.jsxs)(It.TableTr,{children:[r&&(0,Je.jsx)(po,{shadowVisible:l}),n.map(({hidden:c,...m})=>{if(c)return null;let{accessor:i,visibleMediaQuery:u,textAlign:p,width:T,footer:d,footerClassName:S,footerStyle:b,noWrap:C,ellipsis:E}={...o,...m};return(0,Je.jsx)(co,{pinnedInfo:a.get(String(i)),className:S,style:b,visibleMediaQuery:u,textAlign:p,width:T,title:d,noWrap:C,ellipsis:E},i)})]})})}var $=require("@mantine/core"),_o=q(require("clsx")),Wo=require("react");var To=require("@mantine/core"),ho=q(require("clsx")),mt=require("react");var yo=require("react/jsx-runtime");function Co({group:{id:e,columns:t,groups:n,title:o,textAlign:a,className:r,style:l},pinnedInfo:s,maxDepth:c,currentDepth:m,previousGroups:i,isLastGroup:u,withColumnBorders:p=!1,totalTableColumns:T}){let d=(0,mt.useMemo)(()=>t&&t.length>0?t:n&&n.length>0?Xe([{id:e,columns:t,groups:n}]):[],[t,n,e]),S=(0,mt.useMemo)(()=>d.map(({visibleMediaQuery:h})=>h),[d]),b=eo(S),C=(0,mt.useMemo)(()=>it({id:e,columns:t,groups:n},b),[e,t,n,b]),E=(0,mt.useMemo)(()=>i.reduce((h,w)=>h+it(w,b),0),[i,b]),L=n&&n.length>0?1:c-m,N=E+C0?(0,yo.jsx)(To.TableTh,{colSpan:C,rowSpan:L>1?L:void 0,"data-pinned":s?.position,"data-pinned-shadow":s?.isBoundary?s.position:void 0,className:(0,ho.default)("mantine-datatable-column-group-header-cell",{[Te]:a==="left",[he]:a==="center",[Ce]:a==="right","mantine-datatable-column-group-header-cell--needs-border":f},r),style:[l,s&&{position:"sticky",[s.position]:s.offset,overflow:"visible"}],children:o??Ie(e)}):null}var U=require("@mantine/core"),bt=q(require("clsx")),At=require("react");var ye=require("@mantine/core"),Nt=require("@mantine/hooks");var pt=require("react/jsx-runtime");function Po(){return(0,pt.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,pt.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,pt.jsx)("path",{d:"M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227z"})]})}var ft=require("react/jsx-runtime");function Do(){return(0,ft.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,ft.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,ft.jsx)("path",{d:"M20 3h-16a1 1 0 0 0 -1 1v2.227l.008 .223a3 3 0 0 0 .772 1.795l4.22 4.641v8.114a1 1 0 0 0 1.316 .949l6 -2l.108 -.043a1 1 0 0 0 .576 -.906v-6.586l4.121 -4.12a3 3 0 0 0 .879 -2.123v-2.171a1 1 0 0 0 -1 -1z",strokeWidth:"0",fill:"currentColor"})]})}var He=require("react/jsx-runtime");function wo({children:e,isActive:t,filterPopoverProps:n,filterPopoverDisableClickOutside:o}){let[a,{close:r,toggle:l}]=(0,Nt.useDisclosure)(!1),s=t?Do:Po,c=(0,Nt.useClickOutside)(r);return o&&(c=void 0),(0,He.jsxs)(ye.Popover,{withArrow:!0,shadow:"md",trapFocus:!0,...n,opened:a,onClose:r,onDismiss:r,children:[(0,He.jsx)(ye.PopoverTarget,{children:(0,He.jsx)(ye.ActionIcon,{className:"mantine-datatable-header-cell-filter-action-icon","data-active":t||void 0,size:"sm",variant:"default",onClick:m=>{m.preventDefault(),l()},onKeyDown:m=>m.stopPropagation(),children:(0,He.jsx)(s,{})})}),(0,He.jsx)(ye.PopoverDropdown,{ref:c,onClick:m=>m.stopPropagation(),onKeyDown:m=>m.stopPropagation(),children:typeof e=="function"?e({close:r}):e})]})}var Y=require("@mantine/core"),fe=require("react");var Ae=require("react/jsx-runtime");function So(){return(0,Ae.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Ae.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Ae.jsx)("path",{d:"M5 12l14 0"}),(0,Ae.jsx)("path",{d:"M5 12l6 6"}),(0,Ae.jsx)("path",{d:"M5 12l6 -6"})]})}var Oe=require("react/jsx-runtime");function Ro(){return(0,Oe.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Oe.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Oe.jsx)("path",{d:"M5 12l14 0"}),(0,Oe.jsx)("path",{d:"M19 12l-6 6"}),(0,Oe.jsx)("path",{d:"M19 12l-6 -6"})]})}var ze=require("react/jsx-runtime");function xo(){return(0,ze.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,ze.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,ze.jsx)("path",{d:"M9 4v6l-2 4v2h10v-2l-2-4v-6"}),(0,ze.jsx)("path",{d:"M12 16v5"}),(0,ze.jsx)("path",{d:"M8 4h8"})]})}var qe=require("react/jsx-runtime");function Ht(){return(0,qe.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,qe.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,qe.jsx)("path",{d:"M18 6l-12 12"}),(0,qe.jsx)("path",{d:"M6 6l12 12"})]})}var j=require("react/jsx-runtime");function vo({currentPinned:e,onPinChange:t}){let[n,o]=(0,fe.useState)(!1),a=(0,fe.useRef)(null),[r,l]=(0,fe.useState)(!1),s=(0,fe.useCallback)(()=>{if(a.current){let p=getComputedStyle(a.current).direction;l(p==="rtl")}},[]);Ne(()=>{s()},[s]),(0,fe.useEffect)(()=>{n&&s()},[n,s]);let c=r?"right":"left",m=r?"left":"right",i=e===c,u=e===m;return(0,j.jsxs)(Y.Popover,{opened:n,onChange:o,position:"bottom",withArrow:!0,shadow:"sm",children:[(0,j.jsx)(Y.Popover.Target,{children:(0,j.jsx)(Y.Center,{ref:a,className:"mantine-datatable-header-cell-pinnable-icon",role:"button","aria-label":"Pin column",onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),children:(0,j.jsx)(Y.ActionIcon,{size:"xs",variant:"light",onClick:p=>p.stopPropagation(),children:(0,j.jsx)(xo,{})})})}),(0,j.jsx)(Y.Popover.Dropdown,{onClick:p=>p.stopPropagation(),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),className:"mantine-datatable-pinnable-dropdown",children:(0,j.jsxs)(Y.Group,{gap:4,children:[(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:i?"filled":"light",onClick:()=>t(i?void 0:c),"aria-label":"Pin left",children:(0,j.jsx)(So,{})}),(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:u?"filled":"light",onClick:()=>t(u?void 0:m),"aria-label":"Pin right",children:(0,j.jsx)(Ro,{})}),e&&(0,j.jsx)(Y.ActionIcon,{size:"sm",variant:"light",color:"red",onClick:()=>t(void 0),"aria-label":"Unpin",children:(0,j.jsx)(Ht,{})})]})})]})}var ko=require("@mantine/core"),Pe=require("react");var Lo=require("react/jsx-runtime"),Mo=50,Eo=e=>{let{accessor:t,columnRef:n}=e,[o,a]=(0,Pe.useState)(!1),r=(0,Pe.useRef)(0),l=(0,Pe.useRef)({current:0,next:0}),{dir:s}=(0,ko.useDirection)(),c=s==="rtl",{setMultipleColumnWidths:m,beginResize:i,endResize:u}=$e(),p=(0,Pe.useCallback)(d=>{if(d.preventDefault(),d.stopPropagation(),!n.current)return;let S=n.current,b=S.nextElementSibling;for(;b;){let f=b.getAttribute("data-accessor");if(f&&f!=="__selection__")break;b=b.nextElementSibling}if(!b)return;let C=b.getAttribute("data-accessor");if(!C)return;let E=S.offsetWidth,D=b.offsetWidth,P=S.getBoundingClientRect().width/E;Number.isFinite(P)&&P>0||(P=1),i(),a(!0),r.current=d.clientX,l.current={current:E,next:D};let L=f=>{let h=(f.clientX-r.current)/P;c&&(h=-h);let w=l.current.current-Mo,M=l.current.next-Mo,g=Math.max(-w,Math.min(h,M)),R=l.current.current+g,y=l.current.next-g;m([{accessor:t,width:`${R}px`},{accessor:C,width:`${y}px`}])},N=()=>{a(!1),document.body.style.cursor="initial",document.body.style.userSelect="initial",u(),document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",N)};document.body.style.cursor="col-resize",document.body.style.userSelect="none",document.addEventListener("mousemove",L),document.addEventListener("mouseup",N)},[t,n,c,m,i,u]),T=(0,Pe.useCallback)(()=>{if(!n.current)return;let S=n.current.nextElementSibling,b=[{accessor:t,width:"auto"}];if(S){let C=S.getAttribute("data-accessor");C&&C!=="__selection__"&&b.push({accessor:C,width:"auto"})}m(b)},[t,n,m]);return(0,Lo.jsx)("div",{className:"mantine-datatable-header-resizable-handle",style:{cursor:o?"col-resize":void 0},onMouseDown:p,onDoubleClick:T,onClick:d=>d.stopPropagation(),title:"Drag to resize column, double-click to reset"})};var _e=require("react/jsx-runtime");function Io(){return(0,_e.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,_e.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,_e.jsx)("path",{d:"M8 7l4 -4l4 4"}),(0,_e.jsx)("path",{d:"M8 17l4 4l4 -4"}),(0,_e.jsx)("path",{d:"M12 3l0 18"})]})}var We=require("react/jsx-runtime");function No(){return(0,We.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,We.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,We.jsx)("path",{d:"M12 5l0 14"}),(0,We.jsx)("path",{d:"M16 9l-4 -4"}),(0,We.jsx)("path",{d:"M8 9l4 -4"})]})}var le=require("react/jsx-runtime");function Ho(){return(0,le.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,le.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,le.jsx)("path",{d:"M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),(0,le.jsx)("path",{d:"M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"})]})}var G=require("react/jsx-runtime");function Ao({className:e,style:t,pinnedInfo:n,accessor:o,visibleMediaQuery:a,title:r,sortable:l,draggable:s,toggleable:c,pinnable:m,resizable:i,sortIcons:u,textAlign:p,width:T,sortStatus:d,onSortStatusChange:S,filter:b,filterPopoverProps:C,filterPopoverDisableClickOutside:E,filtering:D,sortKey:L}){let{setSourceColumn:N,setTargetColumn:f,swapColumns:h,columnsToggle:w,setColumnsToggle:M,columnsPinning:g,setColumnsPinning:R}=$e(),[y,v]=(0,At.useState)(!1),H=(0,At.useRef)(null),x=g?.find(O=>O.accessor===o)?.pinned;if(!ge(a))return null;let P=r??Ie(o),A=typeof P=="string"?P:void 0,B=l&&S?O=>{O?.defaultPrevented||S({sortKey:L,columnAccessor:o,direction:d?.columnAccessor===o?d.direction==="asc"?"desc":"asc":d?.direction??"asc"})}:void 0,z=O=>{O.stopPropagation(),N(o),v(!1)},se=O=>{O.preventDefault(),f(o),v(!0)},we=()=>{f(o),v(!1),h()},Se=()=>{v(!0)},Re=()=>{v(!1)},xe=O=>{O.stopPropagation(),M(w.map(te=>te.accessor===o?{...te,toggled:!1}:te))},ve=O=>{R(te=>te.map(ce=>ce.accessor===o?{...ce,pinned:O}:ce))};return(0,G.jsxs)(U.TableTh,{"data-accessor":o,"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,bt.default)({"mantine-datatable-header-cell-sortable":l,"mantine-datatable-header-cell-toggleable":c,"mantine-datatable-header-cell-pinnable":m,"mantine-datatable-header-cell-resizable":i},e),style:[{width:T,...i?{}:{minWidth:T,maxWidth:T}},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],role:l?"button":void 0,tabIndex:l?0:void 0,onClick:B,onKeyDown:O=>O.key==="Enter"&&B?.(),ref:H,children:[(0,G.jsxs)(U.Group,{className:"mantine-datatable-header-cell-sortable-group",justify:"space-between",wrap:"nowrap",children:[(0,G.jsxs)(U.Flex,{align:"center",w:"100%",className:(0,bt.default)({"mantine-datatable-header-cell-draggable":s,"mantine-datatable-header-cell-drag-over":y}),draggable:s,onDragStart:s?z:void 0,onDragEnter:s?Se:void 0,onDragOver:s?se:void 0,onDrop:s?we:void 0,onDragLeave:s?Re:void 0,children:[s?(0,G.jsx)(U.Center,{role:"img","aria-label":"Drag column",children:(0,G.jsx)(U.ActionIcon,{className:"mantine-datatable-header-cell-draggable-action-icon",variant:"subtle",size:"xs",onClick:O=>{O.stopPropagation()},children:(0,G.jsx)(Ho,{})})}):null,(0,G.jsx)(U.Box,{className:(0,bt.default)("mantine-datatable-header-cell-sortable-text",{[Te]:p==="left",[he]:p==="center",[Ce]:p==="right"},Ke,je),title:A,children:P})]}),m?(0,G.jsx)(vo,{currentPinned:x,onPinChange:ve}):null,c?(0,G.jsx)(U.Center,{className:"mantine-datatable-header-cell-toggleable-icon",role:"img","aria-label":"Toggle column",children:(0,G.jsx)(U.ActionIcon,{size:"xs",variant:"light",onClick:xe,children:(0,G.jsx)(Ht,{})})}):null,l||d?.columnAccessor===o?d?.columnAccessor===o?(0,G.jsx)(U.Center,{className:(0,bt.default)("mantine-datatable-header-cell-sortable-icon",{"mantine-datatable-header-cell-sortable-icon-reversed":d.direction==="desc"}),role:"img","aria-label":`Sorted ${d.direction==="desc"?"descending":"ascending"}`,children:u?.sorted||(0,G.jsx)(No,{})}):(0,G.jsx)(U.Center,{className:"mantine-datatable-header-cell-sortable-unsorted-icon",role:"img","aria-label":"Not sorted",children:u?.unsorted||(0,G.jsx)(Io,{})}):null,b?(0,G.jsx)(wo,{filterPopoverProps:C,isActive:!!D,filterPopoverDisableClickOutside:E,children:b}):null]}),i&&o!=="__selection__"?(0,G.jsx)(Eo,{accessor:o,columnRef:H}):null]})}var Ot=require("@mantine/core"),Oo=q(require("clsx"));var pn=require("react/jsx-runtime");function zo({className:e,style:t,trigger:n,shadowVisible:o,checked:a,indeterminate:r,checkboxProps:l,onChange:s,rowSpan:c,ref:m}){let i=!l.disabled;return(0,pn.jsx)(Ot.TableTh,{ref:m,className:(0,Oo.default)("mantine-datatable-header-selector-cell",{[re]:n==="cell"&&i},e),style:t,rowSpan:c,"data-shadow-visible":o||void 0,"data-accessor":"__selection__",onClick:n==="cell"&&i?s:void 0,children:(0,pn.jsx)(Ot.Checkbox,{classNames:i?{input:re}:void 0,checked:a,indeterminate:r,onChange:s,...l,disabled:!(s||l.onChange)||l.disabled})})}var Z=require("react/jsx-runtime");function Sr(e,t){let o=(e.columns??[]).filter(u=>!u.hidden).map(u=>String(u.accessor)),a=o.map(u=>t.get(u)).filter(Boolean);if(a.length===0||a.length!==o.length)return;let r=new Set(a.map(u=>u.logicalSide));if(r.size>1)return;let l=[...r][0],s=l==="left"?o[0]:o[o.length-1],c=t.get(s);if(!c)return;let m=l==="left"?o[o.length-1]:o[0],i=t.get(m);return{...c,isBoundary:i?.isBoundary??!1}}function Bo({selectionColumnHeaderRef:e,className:t,style:n,sortStatus:o,sortIcons:a,onSortStatusChange:r,columns:l,defaultColumnProps:s,groups:c,pinnedMap:m,selectionTrigger:i,selectionVisible:u,selectionChecked:p,selectionIndeterminate:T,onSelectionChange:d,selectionCheckboxProps:S,selectorCellShadowVisible:b,selectionColumnClassName:C,selectionColumnStyle:E,withColumnBorders:D=!1,ref:L}){let N=c?xt(c):0,f=N>0?N+1:1,h=u?(0,Z.jsx)(zo,{ref:e,className:C,style:E,trigger:i,shadowVisible:b,checked:p,indeterminate:T,checkboxProps:S,onChange:d,rowSpan:c?f:void 0}):null,{columnsToggle:w,setColumnsToggle:M}=$e(),[g,R]=(0,Wo.useState)(!1),y=l.some(k=>k.toggleable),v=y?Object.fromEntries(l.map(({accessor:k,title:x})=>[k,x??Ie(String(k))])):void 0,H=(0,Z.jsxs)($.TableThead,{className:(0,_o.default)("mantine-datatable-header",t),style:n,ref:L,onContextMenu:y?k=>{k.preventDefault(),R(x=>!x)}:void 0,children:[c&&Array.from({length:N},(k,x)=>{let P=vt(c,x);return(0,Z.jsxs)($.TableTr,{children:[x===0&&h,P.map((A,B)=>(0,Z.jsx)(Co,{group:A,pinnedInfo:Sr(A,m),maxDepth:N,currentDepth:x,previousGroups:P.slice(0,B),isLastGroup:B===P.length-1,withColumnBorders:D,totalTableColumns:l.length},A.id))]},`group-depth-${x}`)}),(0,Z.jsxs)($.TableTr,{children:[!c&&h,l.map(({hidden:k,...x},P)=>{if(k)return null;let{accessor:A,visibleMediaQuery:B,textAlign:z,width:se,title:we,sortable:Se,draggable:Re,toggleable:xe,pinnable:ve,resizable:O,titleClassName:te,titleStyle:ce,filter:ue,filterPopoverProps:Gt,filterPopoverDisableClickOutside:Ft,filtering:$t,sortKey:Xt}={...s,...x};return(0,Z.jsx)(Ao,{accessor:A,pinnedInfo:m.get(String(A)),className:te,style:ce,visibleMediaQuery:B,textAlign:z,width:se,title:we,sortable:Se,draggable:Re,toggleable:xe,pinnable:ve,resizable:O&&Pk.toggleable).map(k=>(0,Z.jsx)($.Group,{children:(0,Z.jsx)($.Checkbox,{classNames:{label:"mantine-datatable-header-column-toggle-checkbox-label"},size:"xs",label:v[k.accessor],checked:k.toggled,onChange:x=>{M(w.map(P=>P.accessor===k.accessor?{...P,toggled:x.currentTarget.checked}:P))}})},k.accessor))})})]}):H}var zt=require("@mantine/core"),Vo=q(require("clsx")),fn=require("react/jsx-runtime");function Go({fetching:e,customContent:t,backgroundBlur:n,size:o,type:a,color:r}){return(0,fn.jsx)(zt.Center,{className:(0,Vo.default)("mantine-datatable-loader",{"mantine-datatable-loader-fetching":e}),style:[{backdropFilter:n?`blur(${n}px)`:void 0}],children:e&&(t||(0,fn.jsx)(zt.Loader,{size:o,type:a,color:r}))})}var De=require("@mantine/core"),bn=q(require("clsx"));var W=require("@mantine/core"),Xo=q(require("clsx"));var Ze=require("react/jsx-runtime");function Fo(){return(0,Ze.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,Ze.jsx)("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),(0,Ze.jsx)("path",{d:"M8 9l4 -4l4 4"}),(0,Ze.jsx)("path",{d:"M16 15l-4 4l-4 -4"})]})}var oe=require("react/jsx-runtime"),$o={xs:(0,W.rem)(22),sm:(0,W.rem)(26),md:(0,W.rem)(32),lg:(0,W.rem)(38),xl:(0,W.rem)(44)};function Uo({size:e,label:t,values:n,value:o,activeTextColor:a,activeBackgroundColor:r,onChange:l}){return(0,oe.jsxs)(W.Group,{gap:"xs",children:[(0,oe.jsx)(W.Text,{component:"div",size:e,children:t}),(0,oe.jsxs)(W.Menu,{withinPortal:!0,withArrow:!0,offset:2,classNames:{arrow:"mantine-datatable-page-size-selector-menu-arrow"},children:[(0,oe.jsx)(W.MenuTarget,{children:(0,oe.jsx)(W.Button,{size:e,variant:"default",classNames:{section:"mantine-datatable-page-size-selector-button-icon"},rightSection:(0,oe.jsx)(Fo,{}),style:[{fontWeight:"normal"},s=>({height:$o[e],paddingLeft:s.spacing[e],paddingRight:s.spacing[e]})],children:o})}),(0,oe.jsx)(W.MenuDropdown,{children:n.map(s=>{let c=s===o;return(0,oe.jsx)(W.MenuItem,{className:(0,Xo.default)({"mantine-datatable-page-size-selector-active":c}),style:[{height:$o[e]},c&&(a||r)?m=>wt({theme:m,paginationActiveTextColor:a,paginationActiveBackgroundColor:r}):void 0],disabled:c,onClick:()=>l(s),children:(0,oe.jsx)(W.Text,{component:"div",size:e,children:s})},s)})})]})]})}var ee=require("react/jsx-runtime");function Qo({className:e,style:t,fetching:n,page:o,onPageChange:a,paginationWithEdges:r,paginationWithControls:l,paginationActiveTextColor:s,paginationActiveBackgroundColor:c,paginationSize:m,loadingText:i,noRecordsText:u,paginationText:p,totalRecords:T,recordsPerPage:d,onRecordsPerPageChange:S,recordsPerPageLabel:b,recordsPerPageOptions:C,recordsLength:E,horizontalSpacing:D,paginationWrapBreakpoint:L,getPaginationControlProps:N,getPaginationItemProps:f,renderPagination:h}){let w;if(T){let x=(o-1)*d+1,P=x+(E||0)-1;w=p({from:x,to:P,totalRecords:T})}else w=n?i:u;let M=T&&d?Math.max(1,Math.ceil(T/d)):1,g=T?(o-1)*(d??0)+1:void 0,R=T?(g??1)+(E??0)-1:void 0,v=!ge(({breakpoints:x})=>`(min-width: ${typeof L=="number"?`${(0,De.rem)(L)}rem`:x[L]||L})`),H={Text:x=>(0,ee.jsx)(De.Text,{component:"div",className:"mantine-datatable-pagination-text",size:m,...x,children:w}),PageSizeSelector:x=>C?(0,ee.jsx)(Uo,{activeTextColor:s,activeBackgroundColor:c,size:m,label:b,values:C,value:d,onChange:S,...x}):null,Pagination:x=>(0,ee.jsx)(De.Pagination,{classNames:{root:(0,bn.default)("mantine-datatable-pagination-pages",{"mantine-datatable-pagination-pages-fetching":n||!E}),control:"mantine-datatable-pagination-pages-control"},style:s||c?P=>wt({theme:P,paginationActiveTextColor:s,paginationActiveBackgroundColor:c}):void 0,withEdges:r,withControls:l,value:o,onChange:a,size:m,total:M,getControlProps:N,getItemProps:f,...x})},k={state:{paginationSize:m,page:o,totalPages:M,totalRecords:T,recordsPerPage:d,recordsLength:E,fetching:n,from:g,to:R,isWrapped:v},actions:{setPage:x=>a?.(x),setRecordsPerPage:S?x=>S(x):void 0},Controls:H};return(0,ee.jsx)(De.Box,{px:D??"xs",py:"xs",className:(0,bn.default)("mantine-datatable-pagination",e),style:[{flexDirection:v?"column":"row"},t],children:typeof h=="function"?h(k):(0,ee.jsxs)(ee.Fragment,{children:[(0,ee.jsx)(H.Text,{}),(0,ee.jsx)(H.PageSizeSelector,{}),(0,ee.jsx)(H.Pagination,{})]})})}var ta=require("@mantine/core"),na=q(require("clsx"));var Ko=require("@mantine/core"),jo=q(require("clsx"));var qo=require("react/jsx-runtime");function Jo({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,record:a,index:r,onClick:l,onDoubleClick:s,onContextMenu:c,noWrap:m,ellipsis:i,textAlign:u,width:p,accessor:T,render:d,defaultRender:S,customCellAttributes:b}){return ge(o)?(0,qo.jsx)(Ko.TableTd,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:(0,jo.default)({[Ke]:m||i,[je]:i,[re]:l||s,[Lt]:c,[Te]:u==="left",[he]:u==="center",[Ce]:u==="right"},e),style:[{width:p,minWidth:p,maxWidth:p},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],onClick:l,onDoubleClick:s,onContextMenu:c,...b?.(a,r),children:d?d(a,r):S?S(a,r,T):Rt(a,T)}):null}var Be=require("@mantine/core");var ie=require("react/jsx-runtime");function Zo({open:e,colSpan:t,content:n,collapseProps:o}){let{expanded:a,visible:r}=ro(e,o?.transitionDuration);return r?(0,ie.jsxs)(ie.Fragment,{children:[(0,ie.jsx)(Be.TableTr,{}),(0,ie.jsx)(Be.TableTr,{children:(0,ie.jsx)(Be.TableTd,{className:"mantine-datatable-row-expansion-cell",colSpan:t,children:(0,ie.jsx)(Be.Collapse,{expanded:a,...o,children:(0,ie.jsx)("div",{className:"mantine-datatable-row-expansion-cell-content",children:n()})})})})]}):null}var _t=require("@mantine/core"),Yo=q(require("clsx"));var gn=require("react/jsx-runtime");function ea({className:e,style:t,record:n,index:o,trigger:a,onChange:r,withRightShadow:l,checkboxProps:s,getCheckboxProps:c,...m}){let i={...s,...c(n,o)},u=!m.disabled&&!i.disabled,p=T=>{T.stopPropagation(),a==="cell"&&u&&r?.(T)};return(0,gn.jsx)(_t.TableTd,{className:(0,Yo.default)("mantine-datatable-row-selector-cell",{[re]:a==="cell"&&u},e),style:t,"data-shadow-visible":l||void 0,onClick:p,children:(0,gn.jsx)(_t.Checkbox,{classNames:u?{input:re}:void 0,onChange:r,...m,...i})})}var ae=require("react/jsx-runtime");function oa({record:e,index:t,columns:n,defaultColumnProps:o,pinnedMap:a,defaultColumnRender:r,selectionTrigger:l,selectionVisible:s,selectionChecked:c,onSelectionChange:m,isRecordSelectable:i,selectionCheckboxProps:u,getSelectionCheckboxProps:p,onClick:T,onDoubleClick:d,onContextMenu:S,onCellClick:b,onCellDoubleClick:C,onCellContextMenu:E,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w,selectorCellShadowVisible:M,selectionColumnClassName:g,selectionColumnStyle:R,rowFactory:y}){let v=(0,ae.jsxs)(ae.Fragment,{children:[s&&(0,ae.jsx)(ea,{className:g,style:R,record:e,index:t,trigger:l,withRightShadow:M,checked:c,disabled:!m||(i?!i(e,t):!1),onChange:m,checkboxProps:u,getCheckboxProps:p}),n.map(({hidden:x,hiddenContent:P,...A},B)=>{if(x||P)return null;let{accessor:z,visibleMediaQuery:se,textAlign:we,noWrap:Se,ellipsis:Re,width:xe,render:ve,cellsClassName:O,cellsStyle:te,customCellAttributes:ce}={...o,...A};return(0,ae.jsx)(Jo,{pinnedInfo:a.get(String(z)),className:typeof O=="function"?O(e,t):O,style:te?.(e,t),visibleMediaQuery:se,record:e,index:t,onClick:b?ue=>b({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,onDoubleClick:C?ue=>C({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,onContextMenu:E?ue=>E({event:ue,record:e,index:t,column:A,columnIndex:B}):void 0,accessor:z,textAlign:we,noWrap:Se,ellipsis:Re,width:xe,render:ve,defaultRender:r,customCellAttributes:ce},z)})]}),H=D&&(0,ae.jsx)(Zo,{colSpan:n.filter(({hidden:x})=>!x).length+(s?1:0),open:D.isRowExpanded(e),content:D.content({record:e,index:t}),collapseProps:D.collapseProps}),k=Rr({record:e,index:t,selectionChecked:c,onClick:T,onDoubleClick:d,onContextMenu:S,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w});return y?y({record:e,index:t,rowProps:k,children:v,expandedElement:H}):(0,ae.jsxs)(ae.Fragment,{children:[(0,ae.jsx)(ta.TableTr,{...k,children:v}),H]})}function Rr({record:e,index:t,selectionChecked:n,onClick:o,onDoubleClick:a,onContextMenu:r,expansion:l,customAttributes:s,color:c,backgroundColor:m,className:i,style:u}){return{className:(0,na.default)("mantine-datatable-row",{[re]:o||a||l?.isExpandable({record:e,index:t})&&l?.expandOnClick},{[Lt]:r},typeof i=="function"?i(e,t):i),"data-selected":n||void 0,onClick:p=>{if(l){let{isExpandable:T,isRowExpanded:d,expandOnClick:S,expandRow:b,collapseRow:C}=l;T({record:e,index:t})&&S&&(d(e)?C(e):b(e))}o?.({event:p,record:e,index:t})},onDoubleClick:a?p=>a({event:p,record:e,index:t}):void 0,onContextMenu:r?p=>r({event:p,record:e,index:t}):void 0,style:[c||m?p=>{let T=c?.(e,t),d=m?.(e,t);return Mn({theme:p,color:T,backgroundColor:d})}:void 0,u?.(e,t)],...s?.(e,t)??{}}}var Tt=require("@mantine/core"),gt=q(require("clsx")),Ve=require("react/jsx-runtime");function aa({leftShadowBehind:e,rightShadowBehind:t,onScrollPositionChange:n,children:o,viewportRef:a,scrollAreaProps:r}){return(0,Ve.jsxs)(Tt.ScrollArea,{...r,viewportRef:a,classNames:{root:"mantine-datatable-scroll-area",scrollbar:"mantine-datatable-scroll-area-scrollbar",thumb:"mantine-datatable-scroll-area-thumb",corner:"mantine-datatable-scroll-area-corner"},onScrollPositionChange:n,children:[o,(0,Ve.jsx)(Tt.Box,{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-top-shadow")}),(0,Ve.jsx)("div",{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-left-shadow",{"mantine-datatable-scroll-area-shadow-behind":e})}),(0,Ve.jsx)("div",{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-right-shadow",{"mantine-datatable-scroll-area-shadow-behind":t})}),(0,Ve.jsx)(Tt.Box,{className:(0,gt.default)("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-bottom-shadow")})]})}var Q=require("react/jsx-runtime");function ra({withTableBorder:e,borderRadius:t,textSelectionDisabled:n,height:o="100%",minHeight:a,maxHeight:r,shadow:l,verticalAlign:s="center",fetching:c,columns:m,storeColumnsKey:i,groups:u,pinFirstColumn:p,pinLastColumn:T,defaultColumnProps:d,defaultColumnRender:S,idAccessor:b="id",records:C,selectionTrigger:E="checkbox",selectedRecords:D,onSelectedRecordsChange:L,selectionColumnClassName:N,selectionColumnStyle:f,isRecordSelectable:h,selectionCheckboxProps:w,allRecordsSelectionCheckboxProps:M={"aria-label":"Select all records"},getRecordSelectionCheckboxProps:g=(Me,X)=>({"aria-label":`Select record ${X+1}`}),sortStatus:R,sortIcons:y,onSortStatusChange:v,horizontalSpacing:H,page:k,onPageChange:x,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:B,recordsPerPageOptions:z,recordsPerPageLabel:se="Records per page",paginationWithEdges:we,paginationWithControls:Se,paginationActiveTextColor:Re,paginationActiveBackgroundColor:xe,paginationSize:ve="sm",paginationText:O=({from:Me,to:X,totalRecords:Kt})=>`${Me} - ${X} / ${Kt}`,paginationWrapBreakpoint:te="sm",getPaginationControlProps:ce=Me=>Me==="previous"?{"aria-label":"Previous page"}:Me==="next"?{"aria-label":"Next page"}:{},getPaginationItemProps:ue,renderPagination:Gt,loaderBackgroundBlur:Ft,customLoader:$t,loaderSize:Xt,loaderType:ua,loaderColor:da,loadingText:ma="...",emptyState:pa,noRecordsText:hn="No records",noRecordsIcon:fa,highlightOnHover:ba,striped:ga,noHeader:Ta,onRowClick:ha,onRowDoubleClick:Ca,onRowContextMenu:ya,onCellClick:Pa,onCellDoubleClick:Da,onCellContextMenu:wa,onScroll:Sa,onScrollToTop:Ra,onScrollToBottom:xa,onScrollToLeft:va,onScrollToRight:Ma,c:ka,backgroundColor:Ea,borderColor:La,rowBorderColor:Ia,stripedColor:Na,highlightOnHoverColor:Ha,rowColor:Aa,rowBackgroundColor:Oa,rowExpansion:za,rowClassName:_a,rowStyle:Wa,customRowAttributes:Ba,scrollViewportRef:Va,scrollAreaProps:Ga,tableRef:Fa,bodyRef:$a,m:Xa,my:Ua,mx:Qa,mt:Ka,mb:ja,ml:Ja,mr:qa,className:Za,classNames:Ye,style:Ya,styles:et,rowFactory:er,tableWrapper:Ut,...Qt}){let Me=(0,J.useMemo)(()=>u?Xe(u):m,[m,u]),{refs:X,onScroll:Kt}=jn({scrollCallbacks:{onScroll:Sa,onScrollToTop:Ra,onScrollToBottom:xa,onScrollToLeft:va,onScrollToRight:Ma},withRowBorders:Qt.withRowBorders}),de=ln({key:i,columns:Me,headerRef:X.header,scrollViewportRef:X.scrollViewport}),tt=de.effectiveColumns,tr=(0,Wt.useMergedRef)(X.table,Fa),nr=(0,Wt.useMergedRef)(X.scrollViewport,Va),Cn=(0,J.useRef)(null),or=(0,Wt.useMergedRef)(Cn,$a),ar=ao({rowExpansion:za,records:C,idAccessor:b}),{pinnedMap:nt,hasLeftPinned:yn,hasRightPinned:rr}=Jn({columns:tt,theadRef:X.header,tbodyRef:Cn,selectionColumnHeaderRef:X.selectionColumnHeader,selectionVisible:!!D,pinFirstColumn:p,pinLastColumn:T}),ht=(0,J.useRef)(!1),Pn=(0,J.useRef)(k),jt=(0,J.useRef)(C),lr=(0,J.useCallback)(I=>{ht.current=!0,jt.current=C,x(I)},[x,C]);(0,J.useEffect)(()=>{Pn.current!==k&&(ht.current=!0,jt.current=C,Pn.current=k)},[k,C]);let ot=C?.length;(0,J.useLayoutEffect)(()=>{if(!ht.current||c||C===jt.current)return;let I=X.scrollViewport.current;if(!I)return;let ke=requestAnimationFrame(()=>{I.scrollTo({top:0,left:0}),ht.current=!1});return()=>cancelAnimationFrame(ke)},[c,C,X.scrollViewport]);let Dn=C?.map(I=>F(I,b)),Ge=!!D,at=D?.map(I=>F(I,b)),wn=Dn!==void 0&&at!==void 0&&at.length>0,Jt=h?C?.filter(h):C,Ct=Jt?.map(I=>F(I,b)),yt=wn&&Ct.every(I=>at.includes(I)),ir=wn&&Ct.some(I=>at.includes(I)),sr=(0,J.useCallback)(()=>{D&&L&&L(yt?D.filter(I=>!Ct.includes(F(I,b))):lt([...D,...Jt],I=>F(I,b)))},[yt,b,L,Ct,Jt,D]),{lastSelectionChangeIndex:Pt,setLastSelectionChangeIndex:cr}=qn(Dn),qt=Ge&&!yn,ur={m:Xa,my:Ua,mx:Qa,mt:Ka,mb:ja,ml:Ja,mr:qa},dr=(0,J.useCallback)(({children:I})=>Ut?Ut({children:I}):I,[Ut]);return(0,Q.jsx)(tn,{...de,pinnedMap:nt,children:(0,Q.jsxs)(Bt.Box,{ref:X.root,...ur,className:(0,Tn.default)("mantine-datatable",{"mantine-datatable-with-border":e},Za,Ye?.root),style:[I=>({...vn({theme:I,c:ka,backgroundColor:Ea,borderColor:La,rowBorderColor:Ia,stripedColor:Na,highlightOnHoverColor:Ha}),borderRadius:I.radius[t]||t,boxShadow:I.shadows[l]||l,height:o,minHeight:a,maxHeight:r}),Ya,et?.root,{position:"relative"}],children:[(0,Q.jsx)(aa,{viewportRef:nr,leftShadowBehind:Ge||yn,rightShadowBehind:rr,onScrollPositionChange:Kt,scrollAreaProps:Ga,children:(0,Q.jsx)(dr,{children:(0,Q.jsxs)(Bt.Table,{ref:tr,horizontalSpacing:H,className:(0,Tn.default)("mantine-datatable-table",{[lo]:n,"mantine-datatable-vertical-align-top":s==="top","mantine-datatable-vertical-align-bottom":s==="bottom","mantine-datatable-selection-column-visible":Ge,"mantine-datatable-resizable-columns":de.hasResizableColumns,"mantine-datatable-resize-locked":de.isLocked,"mantine-datatable-resizing":de.isResizing},Ye?.table),style:{...et?.table,...de.isLocked?{tableLayout:"fixed"}:null,...de.tableWidth!=null?{width:`${de.tableWidth}px`}:null},"data-striped":ot&&ga||void 0,"data-highlight-on-hover":ba||void 0,...Qt,children:[Ta?null:(0,Q.jsx)(tn,{...de,pinnedMap:nt,children:(0,Q.jsx)(Bo,{ref:X.header,selectionColumnHeaderRef:X.selectionColumnHeader,className:Ye?.header,style:et?.header,columns:tt,defaultColumnProps:d,groups:u,pinnedMap:nt,sortStatus:R,sortIcons:y,onSortStatusChange:v,selectionTrigger:E,selectionVisible:Ge,selectionChecked:yt,selectionIndeterminate:ir&&!yt,onSelectionChange:sr,selectionCheckboxProps:{...w,...M},selectorCellShadowVisible:qt,selectionColumnClassName:N,selectionColumnStyle:f,withColumnBorders:Qt.withColumnBorders})}),(0,Q.jsx)("tbody",{ref:or,children:ot?C.map((I,ke)=>{let Zt=F(I,b),Yt=at?.includes(Zt)||!1,Sn;return L&&D&&(Sn=mr=>{if(mr.nativeEvent.shiftKey&&Pt!==null){let Fe=C.filter(ke>Pt?(Ee,Le)=>Le>=Pt&&Le<=ke&&(h?h(Ee,Le):!0):(Ee,Le)=>Le>=ke&&Le<=Pt&&(h?h(Ee,Le):!0));L(Yt?an(D,Fe,Ee=>F(Ee,b)):lt([...D,...Fe],Ee=>F(Ee,b)))}else L(Yt?D.filter(Fe=>F(Fe,b)!==Zt):lt([...D,I],Fe=>F(Fe,b)));cr(ke)}),(0,Q.jsx)(oa,{record:I,index:ke,columns:tt,defaultColumnProps:d,pinnedMap:nt,defaultColumnRender:S,selectionTrigger:E,selectionVisible:Ge,selectionChecked:Yt,onSelectionChange:Sn,isRecordSelectable:h,selectionCheckboxProps:w,getSelectionCheckboxProps:g,onClick:ha,onDoubleClick:Ca,onCellClick:Pa,onCellDoubleClick:Da,onContextMenu:ya,onCellContextMenu:wa,expansion:ar,color:Aa,backgroundColor:Oa,className:_a,style:Wa,customAttributes:Ba,selectorCellShadowVisible:qt,selectionColumnClassName:N,selectionColumnStyle:f,idAccessor:b,rowFactory:er},Zt)}):(0,Q.jsx)(In,{})}),tt.some(({footer:I})=>I)&&(0,Q.jsx)(go,{ref:X.footer,className:Ye?.footer,style:et?.footer,columns:tt,defaultColumnProps:d,pinnedMap:nt,selectionVisible:Ge,selectorCellShadowVisible:qt})]})})}),!!(k&&ot)&&(0,Q.jsx)(Qo,{className:Ye?.pagination,style:et?.pagination,horizontalSpacing:H,fetching:c,page:k,onPageChange:lr,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:B,recordsPerPageOptions:z,recordsPerPageLabel:se,paginationWithEdges:we,paginationWithControls:Se,paginationActiveTextColor:Re,paginationActiveBackgroundColor:xe,paginationSize:ve,paginationText:O,paginationWrapBreakpoint:te,getPaginationControlProps:ce,getPaginationItemProps:ue,noRecordsText:hn,loadingText:ma,recordsLength:ot,renderPagination:Gt}),(0,Q.jsx)(Go,{fetching:c,backgroundBlur:Ft,customContent:$t,size:Xt,type:ua,color:da}),(0,Q.jsx)(Hn,{icon:fa,text:hn,active:!c&&!ot,children:pa})]})})}var la=require("@mantine/core"),ia=require("@mantine/hooks"),Vt=require("react"),ca=require("react/jsx-runtime");function sa({className:e,children:t,isDragging:n,ref:o,...a}){let r=(0,Vt.useRef)(null),l=(0,ia.useMergedRef)(r,o);return(0,Vt.useEffect)(()=>{if(!r.current||!n)return;let i=r.current.parentElement.parentElement.children[0].children[0];for(let u=0;u{let{children:t,columnsOrder:n,setColumnsOrder:o,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S,pinnedMap:b}=e,[C,E]=Ut(""),[D,L]=Ut("");return xa(Xt,{value:{sourceColumn:C,setSourceColumn:E,targetColumn:D,setTargetColumn:L,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,swapColumns:()=>{if(!n||!o||!C||!D)return;let f=b.get(C),h=b.get(D);if(f?.logicalSide!==h?.logicalSide)return;let w=n.indexOf(C),M=n.indexOf(D);if(w!==-1&&M!==-1){let g=[...n],[R]=g.splice(w,1);g.splice(M,0,R),o(g)}},resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S},children:t})};import{jsx as Qt}from"react/jsx-runtime";function Kt(){return Qt("tr",{className:"mantine-datatable-empty-row",children:Qt("td",{})})}import{Center as Ma,Text as ka}from"@mantine/core";import{jsx as Le,jsxs as va}from"react/jsx-runtime";function jt(){return va("svg",{width:"24",height:"24",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Le("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Le("path",{d:"M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74"}),Le("path",{d:"M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6"}),Le("path",{d:"M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4"}),Le("path",{d:"M3 3l18 18"})]})}import{Fragment as Ea,jsx as Be,jsxs as La}from"react/jsx-runtime";function Jt({icon:e,text:t,active:n,children:o}){return Be(Ma,{className:"mantine-datatable-empty-state","data-active":n||void 0,children:o||La(Ea,{children:[e||Be("div",{className:"mantine-datatable-empty-state-icon",children:Be(jt,{})}),Be(ka,{component:"div",size:"sm",c:"dimmed",children:t})]})})}import{TableTfoot as dr,TableTr as mr}from"@mantine/core";import pr from"clsx";import{TableTh as lr}from"@mantine/core";import ir from"clsx";import{useCallback as fi,useRef as bi,useState as gi}from"react";import{useLocalStorage as Ia}from"@mantine/hooks";import{useEffect as Na,useMemo as Yt}from"react";function De(e){let t=e.replace(/([a-z\d])([A-Z]+)/g,"$1 $2").replace(/\W|_/g," ").trim().toLowerCase();return`${t.charAt(0).toUpperCase()}${t.slice(1)}`}function Z(e,t,n){return!Array.isArray(e)||n&&!e.every(n)?t:e}function Ve(e){return typeof e=="object"&&e!==null&&typeof e.accessor=="string"}function qt(e,t,n){return e.filter(o=>!t.map(n).includes(n(o)))}function Ge(e,t){return e.filter((n,o,a)=>o===a.findIndex(r=>t(n)===t(r)))}function yt(e,t){return t?t.match(/([^[.\]])+/g).reduce((o,a)=>o&&o[a],e):void 0}function B(e,t){return typeof t=="string"?yt(e,t):t(e)}function hi(e,t){let n=structuredClone(t),o=n[e.source.index];return e.destination&&(n.splice(e.source.index,1),n.splice(e.destination.index,0,o)),n}function Ci(e,t){let n=structuredClone(t),o=e.destination;if(!o)return n;let a=n[e.source.index],r=n[o.index];return n.splice(o.index,1,a),n.splice(e.source.index,1,r),n}function Pt(e){return!e||e.length===0?0:Math.max(...e.map(t=>t.groups&&t.groups.length>0?1+Pt(t.groups):1))}function Ie(e){let t=[];for(let n of e)n.columns&&n.columns.length>0?t.push(...n.columns.filter(o=>o!=null)):n.groups&&n.groups.length>0&&t.push(...Ie(n.groups));return t.filter(n=>n!=null)}function Fe(e,t){return e.columns&&e.columns.length>0?e.columns.filter((n,o)=>n.hidden?!1:t?t[o]!==!1:!0).length:e.groups&&e.groups.length>0?e.groups.reduce((n,o)=>n+Fe(o,t),0):0}function Dt(e,t,n=0){if(n===t)return[...e];let o=[];for(let a of e)a.groups&&a.groups.length>0&&o.push(...Dt(a.groups,t,n+1));return o}function Zt(e,t,n){return n?!e||t:!1}function en({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=Yt(()=>t.map(i=>({accessor:i.accessor,defaultPinned:i.pinned,pinnable:!!i.pinnable,pinned:i.pinned})),[t]),[a,r]=Ia({key:e?`${e}-columns-pinning`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,Ve);function s(i){e&&r(typeof i=="function"?u=>i(u??o):i)}let c=Yt(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultPinned:u.pinned,pinnable:!!u.pinnable,pinned:u.pinned})}),i},[l,t,o]);Na(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsPinning:c,setColumnsPinning:s,resetColumnsPinning:m}:{columnsPinning:o,setColumnsPinning:s,resetColumnsPinning:m}}import{useLocalStorage as Ha}from"@mantine/hooks";import{useEffect as Aa,useMemo as tn}from"react";function nn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=tn(()=>t?t.map(i=>i.accessor):[],[t]),[a,r]=Ha({key:e?`${e}-columns-order`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,i=>typeof i=="string");function s(i){e&&r(i)}let c=tn(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u)&&i.push(u)}),t.forEach(u=>{i.includes(u.accessor)||i.push(u.accessor)}),i},[l,t,o]);Aa(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsOrder:c,setColumnsOrder:s,resetColumnsOrder:m}:{columnsOrder:l,setColumnsOrder:s,resetColumnsOrder:m}}import{useLocalStorage as Oa}from"@mantine/hooks";import{useCallback as Y,useEffect as wt,useMemo as on,useRef as za,useState as an}from"react";function rn({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o}){let a=on(()=>t.some(f=>f.resizable&&!f.hidden&&f.accessor!=="__selection__"),[t]),r=Y(()=>t.filter(f=>f.accessor!=="__selection__").map(f=>({[f.accessor]:f.width??"auto"})),[t]),[l,s]=Oa({key:e?`${e}-columns-width`:"",defaultValue:e?r():void 0,getInitialValueInEffect:n}),c=Z(l,[],f=>typeof f=="object"&&f!==null),[m,i]=an(()=>e&&c.length>0?c:r()),[u,p]=an(!1),T=za(!1);wt(()=>{e&&(!c||c.length===0||i(c))},[e,c]),wt(()=>{i(f=>{let h=new Set(t.filter(y=>y.accessor!=="__selection__").map(y=>String(y.accessor))),w=f.filter(y=>h.has(Object.keys(y)[0])),M=new Set(w.map(y=>Object.keys(y)[0])),g=!1;for(let y of t){if(y.accessor==="__selection__")continue;let v=String(y.accessor);M.has(v)||(w.push({[v]:y.width??"auto"}),g=!0)}return w.length!==f.length||g?w:f})},[t]);let d=Y(f=>{let h=f.filter(w=>w.accessor!=="__selection__");h.length!==0&&(T.current=!0,i(w=>w.map(M=>{let g=Object.keys(M)[0],R=h.find(y=>y.accessor===g);return R?{[g]:R.width}:M})))},[]);wt(()=>{!e||!T.current||u||(T.current=!1,s(m))},[e,m,u,s]);let S=Y((f,h)=>{d([{accessor:f,width:h}])},[d]),b=Y(f=>{d(f)},[d]),C=Y(()=>{p(!0);let f=o?.current;if(!f)return;let h=Array.from(f.querySelectorAll("th[data-accessor]"));if(h.length===0)return;let w=[];for(let M of h){let g=M.getAttribute("data-accessor");if(!g||g==="__selection__")continue;let R=Math.round(M.getBoundingClientRect().width);w.push({[g]:`${R}px`})}T.current=!0,i(w)},[o]),E=Y(()=>{p(!1)},[]),D=Y(()=>{let f=r();T.current=!0,i(f),p(!1)},[r]),L=on(()=>{if(!a)return!1;let f=t.filter(h=>h.resizable&&!h.hidden&&h.accessor!=="__selection__");return m.filter(h=>{let w=Object.keys(h)[0];return f.some(M=>M.accessor===w)}).every(h=>{let w=Object.values(h)[0];return w==="auto"||w==="initial"})},[a,t,m]),N=Y(()=>{},[]);return{columnsWidth:m,setColumnsWidth:d,setColumnWidth:S,setMultipleColumnWidths:b,resetColumnsWidth:D,hasResizableColumns:a,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:u,beginResize:C,endResize:E}}import{useMemo as St}from"react";import{useLocalStorage as _a}from"@mantine/hooks";import{useEffect as Wa,useMemo as ln}from"react";function sn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=ln(()=>t?.map(i=>({accessor:i.accessor,defaultToggle:i.defaultToggle||!0,toggleable:i.toggleable,toggled:i.defaultToggle===void 0?!0:i.defaultToggle})),[t]),[a,r]=_a({key:e?`${e}-columns-toggle`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,Ve);function s(i){e&&r(i)}let c=ln(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultToggle:u.defaultToggle||!0,toggleable:u.toggleable,toggled:u.defaultToggle===void 0?!0:u.defaultToggle})}),i},[l,t,o]);Wa(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsToggle:c,setColumnsToggle:s,resetColumnsToggle:m}:{columnsToggle:l,setColumnsToggle:s,resetColumnsToggle:m}}var cn=({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o,scrollViewportRef:a})=>{let{columnsOrder:r,setColumnsOrder:l,resetColumnsOrder:s}=nn({key:e,columns:t,getInitialValueInEffect:n}),{columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i}=sn({key:e,columns:t,getInitialValueInEffect:n}),{columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T}=en({key:e,columns:t,getInitialValueInEffect:n}),{columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,beginResize:h,endResize:w}=rn({key:e,columns:t,getInitialValueInEffect:n,headerRef:o,scrollViewportRef:a}),M=St(()=>{let y;r?y=r.map(P=>t.find(A=>A.accessor===P)).map(P=>{let A=u.find(z=>z.accessor===P?.accessor),_=P?.pinnable?A?.pinned:P?.pinned;return{...P,hidden:P?.hidden||!c.find(z=>z.accessor===P?.accessor)?.toggled,pinned:_}}):y=t;let v=y.filter(P=>P.pinned==="left"),H=y.filter(P=>!P.pinned),k=y.filter(P=>P.pinned==="right");return[...v,...H,...k].map(P=>{if(P?.accessor==="__selection__")return P;let A=P?.accessor,_=d.find(K=>A in K);if(!_)return P;let z=_[A];return z===void 0||z==="auto"||z==="initial"?P:{...P,width:z}})},[t,r,c,u,d]),g=St(()=>{let y=M.filter(v=>!v.hidden&&v.accessor!=="__selection__");return y.length===0?!1:y.every(v=>typeof v.width=="string"&&/px$/.test(v.width))},[M]),R=St(()=>{if(!g)return null;let y=0;for(let v of M){if(v.hidden||v.accessor==="__selection__")continue;let H=v.width;if(typeof H!="string")continue;let k=parseInt(H,10);Number.isNaN(k)||(y+=k)}return y>0?y:null},[g,M]);return{effectiveColumns:M,setColumnsOrder:l,columnsOrder:r,resetColumnsOrder:s,columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i,columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T,columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,isLocked:g,tableWidth:R,beginResize:h,endResize:w}};import{rem as Fa}from"@mantine/core";import{useCallback as $a,useEffect as xt,useRef as ee}from"react";import{useEffect as Ba,useLayoutEffect as Va}from"react";var be=typeof window<"u"?Va:Ba;import{useRef as Ga}from"react";function Rt(e){let t=Ga(e);return t.current=e,t}var un="--mantine-datatable-header-height",dn="--mantine-datatable-footer-height",mn="--mantine-datatable-selection-column-width",pn="--mantine-datatable-last-row-border-bottom";function X(e,t,n){e?.style.setProperty(t,n)}function Mt(e){let t=e.borderBoxSize?.[0]||e.contentBoxSize?.[0];return t?{width:t.inlineSize,height:t.blockSize}:{width:e.contentRect.width,height:e.contentRect.height}}function vt(e,t,n){if(e){let o=new ResizeObserver(a=>{let r=a[0];r&&t(Mt(r))});return o.observe(e),()=>{o.disconnect(),n()}}}function fn({scrollCallbacks:e,withRowBorders:t}){let n={root:ee(null),table:ee(null),scrollViewport:ee(null),header:ee(null),footer:ee(null),selectionColumnHeader:ee(null)},{root:o,table:a,scrollViewport:r,header:l,footer:s,selectionColumnHeader:c}=n,m=Rt({withRowBorders:t}),i=Rt(e),u=ee(()=>{}),p=ee(()=>{}),T=$a(d=>{i.current.onScroll?.(d),u.current()},[]);return xt(()=>vt(l.current,d=>{X(o.current,un,`${d.height}px`)},()=>X(o.current,un,"0")),[l.current]),xt(()=>vt(s.current,d=>{X(o.current,dn,`${d.height}px`)},()=>X(o.current,dn,"0")),[s.current]),xt(()=>vt(c.current,d=>{X(o.current,mn,`${d.width}px`)},()=>X(o.current,mn,"0")),[c.current]),be(()=>{if(typeof window>"u")return;let d={top:!0,bottom:!0,left:!0,right:!0},S={width:0,height:0},b={width:0,height:0};function C(f,h){let w=d[f];return d[f]=h,X(o.current,`--mantine-datatable-${f}-shadow-opacity`,h?"0":"1"),w}function E(){let f=S.height-b.height,h=f<0;X(o.current,"--mantine-datatable-footer-position",h?"relative":"sticky"),X(o.current,"--mantine-datatable-footer-bottom",h?`${f}px`:"0")}function D(){m.current.withRowBorders&&S.height{for(let h of f)switch(h.target.tagName){case"TABLE":{S=Mt(h);break}case"DIV":{b=Mt(h);break}}L(),E(),D()});return N.observe(a.current),N.observe(r.current),()=>{N.disconnect()}},[]),be(()=>{p.current()},[t]),{refs:n,onScroll:T}}import{useCallback as Xa,useEffect as Ua,useState as Qa}from"react";function bn({columns:e,theadRef:t,tbodyRef:n,selectionColumnHeaderRef:o,selectionVisible:a,pinFirstColumn:r,pinLastColumn:l}){let[s,c]=Qa(new Map),m=Xa(()=>{if(!r&&!l&&!e.some(g=>g.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=getComputedStyle(p).direction==="rtl",b=new Map;if(T)d.querySelectorAll("th[data-accessor]").forEach(g=>{let R=g.dataset.accessor;if(R){let y=g.getBoundingClientRect().width;y>0&&b.set(R,y)}});else{let g=e.filter(y=>!y.hidden);d.querySelectorAll("td:not(.mantine-datatable-row-selector-cell):not(.mantine-datatable-row-expansion-cell)").forEach((y,v)=>{if(g[v]){let H=y.getBoundingClientRect().width;H>0&&b.set(String(g[v].accessor),H)}})}let C=0;if(a){let g=o.current??p.querySelector(T?".mantine-datatable-header-selector-cell":".mantine-datatable-row-selector-cell");g&&(C=g.getBoundingClientRect().width)}let E=e.filter(g=>!g.hidden&&b.has(String(g.accessor))).map(g=>String(g.accessor)),D=new Map;for(let g of e){let R=String(g.accessor);E.includes(R)&&(g.pinned?D.set(R,g.pinned):r&&R===E[0]&&!D.has(R)?D.set(R,"left"):l&&R===E[E.length-1]&&!D.has(R)&&D.set(R,"right"))}let L=E.filter(g=>D.get(g)==="left"),N=E.filter(g=>D.get(g)==="right"),f=new Map,h=C;L.forEach((g,R)=>{f.set(g,{position:S?"right":"left",logicalSide:"left",offset:h,isBoundary:R===L.length-1}),h+=b.get(g)||0});let w=-.4,M=[...N].reverse();M.forEach((g,R)=>{f.set(g,{position:S?"left":"right",logicalSide:"right",offset:w,isBoundary:R===M.length-1}),w+=b.get(g)||0}),c(g=>{if(g.size!==f.size)return f;for(let[R,y]of f){let v=g.get(R);if(!v||v.position!==y.position||v.logicalSide!==y.logicalSide||v.offset!==y.offset||v.isBoundary!==y.isBoundary)return f}return g})},[e,a,t,n,o,r,l]);Ua(()=>{if(!r&&!l&&!e.some(b=>b.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=new ResizeObserver(m);return d.querySelectorAll(T?"th":"td").forEach(b=>{S.observe(b)}),()=>S.disconnect()},[t,n,m]),be(m,[m]);let i=[...s.values()].some(p=>p.logicalSide==="left"),u=[...s.values()].some(p=>p.logicalSide==="right");return{pinnedMap:s,hasLeftPinned:i,hasRightPinned:u}}import{useEffect as Ka,useState as ja}from"react";function gn(e){let[t,n]=ja(null),o=e?.join(":")||"";return Ka(()=>{n(null)},[o]),{lastSelectionChangeIndex:t,setLastSelectionChangeIndex:n}}import{useEffect as Ja,useRef as qa,useState as Za}from"react";function Ya(e,t){let n=()=>{t(e.map(a=>a.matches))},o=e.map(a=>{try{return a.addEventListener("change",n),()=>a.removeEventListener("change",n)}catch{return a.addListener(n),()=>a.removeListener(n)}});return()=>{o.forEach(a=>{a()})}}function er(e,t){return t||(typeof window<"u"&&"matchMedia"in window?e.map(n=>window.matchMedia(n).matches):e.map(()=>!1))}function Tn(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[o,a]=Za(n?t:er(e,t)),r=qa(null);return Ja(()=>{if("matchMedia"in window)return r.current=e.map(l=>window.matchMedia(l)),a(r.current.map(l=>l.matches)),Ya(r.current,l=>{a(l)})},[e]),o}import{useMantineTheme as tr}from"@mantine/core";import{useMemo as hn}from"react";function Cn(e){let t=tr(),n=hn(()=>e.map(a=>(typeof a=="function"?a(t):a)??""),[e,t]),o=hn(()=>e.map(()=>!0),[e]);return Tn(n,o)}import{useMantineTheme as nr}from"@mantine/core";import{useMediaQuery as or}from"@mantine/hooks";function te(e){let t=nr(),n=typeof e=="function"?e(t):e;return or(n||"",!0)}import{useState as ar}from"react";function yn({rowExpansion:e,records:t,idAccessor:n}){let o=[];if(e&&t){let{trigger:s,allowMultiple:c,initiallyExpanded:m}=e;t&&s==="always"?o=t.map(i=>B(i,n)):m&&(o=t.filter((i,u)=>m({record:i,index:u})).map(i=>B(i,n)),c||(o=[o[0]]))}let a,r,l=ar(o);if(e){let{expandable:s,trigger:c,allowMultiple:m,collapseProps:i,content:u}=e;e.expanded?{recordIds:a,onRecordIdsChange:r}=e.expanded:[a,r]=l;let p=T=>r?.(a.filter(d=>d!==B(T,n)));return{expandOnClick:c!=="always"&&c!=="never",isRowExpanded:T=>c==="always"?!0:a.includes(B(T,n)),isExpandable:({record:T,index:d})=>s?s({record:T,index:d}):!0,expandRow:T=>{let d=B(T,n);r?.(m?[...a,d]:[d])},collapseRow:p,collapseProps:i,content:({record:T,index:d})=>()=>u({record:T,index:d,collapse:()=>p(T)})}}}import{useTimeout as Pn}from"@mantine/hooks";import{useEffect as rr,useState as Dn}from"react";function wn(e,t){let[n,o]=Dn(e),[a,r]=Dn(e),l=Pn(()=>o(!0),0),s=Pn(()=>r(!1),t||200);return rr(()=>{e?(s.clear(),r(!0),l.start()):(l.clear(),o(!1),s.start())},[l,s,e]),{expanded:n,visible:a}}var we="mantine-datatable-nowrap",Se="mantine-datatable-ellipsis",Q="mantine-datatable-pointer-cursor",$e="mantine-datatable-context-menu-cursor",Sn="mantine-datatable-text-selection-disabled",ne="mantine-datatable-text-align-left",oe="mantine-datatable-text-align-center",ae="mantine-datatable-text-align-right";import{jsx as sr}from"react/jsx-runtime";function Rn({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,title:a,noWrap:r,ellipsis:l,textAlign:s,width:c}){return te(o)?sr(lr,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:ir({[we]:r||l,[Se]:l,[ne]:s==="left",[oe]:s==="center",[ae]:s==="right"},e),style:[{width:c,minWidth:c,maxWidth:c},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],children:a}):null}import{TableTh as cr}from"@mantine/core";import{jsx as ur}from"react/jsx-runtime";function xn({shadowVisible:e}){return ur(cr,{className:"mantine-datatable-footer-selector-placeholder-cell","data-shadow-visible":e||void 0})}import{jsx as kt,jsxs as fr}from"react/jsx-runtime";function vn({className:e,style:t,columns:n,defaultColumnProps:o,pinnedMap:a,selectionVisible:r,selectorCellShadowVisible:l,ref:s}){return kt(dr,{ref:s,className:pr("mantine-datatable-footer",e),style:t,children:fr(mr,{children:[r&&kt(xn,{shadowVisible:l}),n.map(({hidden:c,...m})=>{if(c)return null;let{accessor:i,visibleMediaQuery:u,textAlign:p,width:T,footer:d,footerClassName:S,footerStyle:b,noWrap:C,ellipsis:E}={...o,...m};return kt(Rn,{pinnedInfo:a.get(String(i)),className:S,style:b,visibleMediaQuery:u,textAlign:p,width:T,title:d,noWrap:C,ellipsis:E},i)})]})})}import{Checkbox as qr,Group as Zr,Popover as Yr,PopoverDropdown as el,PopoverTarget as tl,Stack as nl,TableThead as ol,TableTr as qn}from"@mantine/core";import al from"clsx";import{useState as rl}from"react";import{TableTh as br}from"@mantine/core";import gr from"clsx";import{useMemo as Xe}from"react";import{jsx as Tr}from"react/jsx-runtime";function Mn({group:{id:e,columns:t,groups:n,title:o,textAlign:a,className:r,style:l},pinnedInfo:s,maxDepth:c,currentDepth:m,previousGroups:i,isLastGroup:u,withColumnBorders:p=!1,totalTableColumns:T}){let d=Xe(()=>t&&t.length>0?t:n&&n.length>0?Ie([{id:e,columns:t,groups:n}]):[],[t,n,e]),S=Xe(()=>d.map(({visibleMediaQuery:h})=>h),[d]),b=Cn(S),C=Xe(()=>Fe({id:e,columns:t,groups:n},b),[e,t,n,b]),E=Xe(()=>i.reduce((h,w)=>h+Fe(w,b),0),[i,b]),L=n&&n.length>0?1:c-m,N=E+C0?Tr(br,{colSpan:C,rowSpan:L>1?L:void 0,"data-pinned":s?.position,"data-pinned-shadow":s?.isBoundary?s.position:void 0,className:gr("mantine-datatable-column-group-header-cell",{[ne]:a==="left",[oe]:a==="center",[ae]:a==="right","mantine-datatable-column-group-header-cell--needs-border":f},r),style:[l,s&&{position:"sticky",[s.position]:s.offset,overflow:"visible"}],children:o??De(e)}):null}import{ActionIcon as Qn,Box as Gr,Center as et,Flex as Fr,Group as $r,TableTh as Xr}from"@mantine/core";import tt from"clsx";import{useRef as Ur,useState as Qr}from"react";import{ActionIcon as yr,Popover as Pr,PopoverDropdown as Dr,PopoverTarget as wr}from"@mantine/core";import{useClickOutside as Sr,useDisclosure as Rr}from"@mantine/hooks";import{jsx as kn,jsxs as hr}from"react/jsx-runtime";function En(){return hr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[kn("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),kn("path",{d:"M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227z"})]})}import{jsx as Ln,jsxs as Cr}from"react/jsx-runtime";function In(){return Cr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ln("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ln("path",{d:"M20 3h-16a1 1 0 0 0 -1 1v2.227l.008 .223a3 3 0 0 0 .772 1.795l4.22 4.641v8.114a1 1 0 0 0 1.316 .949l6 -2l.108 -.043a1 1 0 0 0 .576 -.906v-6.586l4.121 -4.12a3 3 0 0 0 .879 -2.123v-2.171a1 1 0 0 0 -1 -1z",strokeWidth:"0",fill:"currentColor"})]})}import{jsx as Ue,jsxs as xr}from"react/jsx-runtime";function Nn({children:e,isActive:t,filterPopoverProps:n,filterPopoverDisableClickOutside:o}){let[a,{close:r,toggle:l}]=Rr(!1),s=t?In:En,c=Sr(r);return o&&(c=void 0),xr(Pr,{withArrow:!0,shadow:"md",trapFocus:!0,...n,opened:a,onClose:r,onDismiss:r,children:[Ue(wr,{children:Ue(yr,{className:"mantine-datatable-header-cell-filter-action-icon","data-active":t||void 0,size:"sm",variant:"default",onClick:m=>{m.preventDefault(),l()},onKeyDown:m=>m.stopPropagation(),children:Ue(s,{})})}),Ue(Dr,{ref:c,onClick:m=>m.stopPropagation(),onKeyDown:m=>m.stopPropagation(),children:typeof e=="function"?e({close:r}):e})]})}import{ActionIcon as qe,Center as Lr,Group as Ir,Popover as Lt}from"@mantine/core";import{useCallback as Nr,useEffect as Hr,useRef as Ar,useState as zn}from"react";import{jsx as Qe,jsxs as vr}from"react/jsx-runtime";function Hn(){return vr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Qe("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Qe("path",{d:"M5 12l14 0"}),Qe("path",{d:"M5 12l6 6"}),Qe("path",{d:"M5 12l6 -6"})]})}import{jsx as Ke,jsxs as Mr}from"react/jsx-runtime";function An(){return Mr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ke("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ke("path",{d:"M5 12l14 0"}),Ke("path",{d:"M19 12l-6 6"}),Ke("path",{d:"M19 12l-6 -6"})]})}import{jsx as je,jsxs as kr}from"react/jsx-runtime";function On(){return kr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[je("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),je("path",{d:"M9 4v6l-2 4v2h10v-2l-2-4v-6"}),je("path",{d:"M12 16v5"}),je("path",{d:"M8 4h8"})]})}import{jsx as Et,jsxs as Er}from"react/jsx-runtime";function Je(){return Er("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Et("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Et("path",{d:"M18 6l-12 12"}),Et("path",{d:"M6 6l12 12"})]})}import{jsx as U,jsxs as _n}from"react/jsx-runtime";function Wn({currentPinned:e,onPinChange:t}){let[n,o]=zn(!1),a=Ar(null),[r,l]=zn(!1),s=Nr(()=>{if(a.current){let p=getComputedStyle(a.current).direction;l(p==="rtl")}},[]);be(()=>{s()},[s]),Hr(()=>{n&&s()},[n,s]);let c=r?"right":"left",m=r?"left":"right",i=e===c,u=e===m;return _n(Lt,{opened:n,onChange:o,position:"bottom",withArrow:!0,shadow:"sm",children:[U(Lt.Target,{children:U(Lr,{ref:a,className:"mantine-datatable-header-cell-pinnable-icon",role:"button","aria-label":"Pin column",onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),children:U(qe,{size:"xs",variant:"light",onClick:p=>p.stopPropagation(),children:U(On,{})})})}),U(Lt.Dropdown,{onClick:p=>p.stopPropagation(),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),className:"mantine-datatable-pinnable-dropdown",children:_n(Ir,{gap:4,children:[U(qe,{size:"sm",variant:i?"filled":"light",onClick:()=>t(i?void 0:c),"aria-label":"Pin left",children:U(Hn,{})}),U(qe,{size:"sm",variant:u?"filled":"light",onClick:()=>t(u?void 0:m),"aria-label":"Pin right",children:U(An,{})}),e&&U(qe,{size:"sm",variant:"light",color:"red",onClick:()=>t(void 0),"aria-label":"Unpin",children:U(Je,{})})]})})]})}import{useDirection as Or}from"@mantine/core";import{useCallback as Bn,useRef as Vn,useState as zr}from"react";import{jsx as _r}from"react/jsx-runtime";var Gn=50,Fn=e=>{let{accessor:t,columnRef:n}=e,[o,a]=zr(!1),r=Vn(0),l=Vn({current:0,next:0}),{dir:s}=Or(),c=s==="rtl",{setMultipleColumnWidths:m,beginResize:i,endResize:u}=Pe(),p=Bn(d=>{if(d.preventDefault(),d.stopPropagation(),!n.current)return;let S=n.current,b=S.nextElementSibling;for(;b;){let f=b.getAttribute("data-accessor");if(f&&f!=="__selection__")break;b=b.nextElementSibling}if(!b)return;let C=b.getAttribute("data-accessor");if(!C)return;let E=S.offsetWidth,D=b.offsetWidth;i(),a(!0),r.current=d.clientX,l.current={current:E,next:D};let L=f=>{let h=f.clientX-r.current;c&&(h=-h);let w=l.current.current-Gn,M=l.current.next-Gn,g=Math.max(-w,Math.min(h,M)),R=l.current.current+g,y=l.current.next-g;m([{accessor:t,width:`${R}px`},{accessor:C,width:`${y}px`}])},N=()=>{a(!1),document.body.style.cursor="initial",document.body.style.userSelect="initial",u(),document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",N)};document.body.style.cursor="col-resize",document.body.style.userSelect="none",document.addEventListener("mousemove",L),document.addEventListener("mouseup",N)},[t,n,c,m,i,u]),T=Bn(()=>{if(!n.current)return;let S=n.current.nextElementSibling,b=[{accessor:t,width:"auto"}];if(S){let C=S.getAttribute("data-accessor");C&&C!=="__selection__"&&b.push({accessor:C,width:"auto"})}m(b)},[t,n,m]);return _r("div",{className:"mantine-datatable-header-resizable-handle",style:{cursor:o?"col-resize":void 0},onMouseDown:p,onDoubleClick:T,onClick:d=>d.stopPropagation(),title:"Drag to resize column, double-click to reset"})};import{jsx as Ze,jsxs as Wr}from"react/jsx-runtime";function $n(){return Wr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ze("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ze("path",{d:"M8 7l4 -4l4 4"}),Ze("path",{d:"M8 17l4 4l4 -4"}),Ze("path",{d:"M12 3l0 18"})]})}import{jsx as Ye,jsxs as Br}from"react/jsx-runtime";function Xn(){return Br("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ye("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ye("path",{d:"M12 5l0 14"}),Ye("path",{d:"M16 9l-4 -4"}),Ye("path",{d:"M8 9l4 -4"})]})}import{jsx as ge,jsxs as Vr}from"react/jsx-runtime";function Un(){return Vr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[ge("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),ge("path",{d:"M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"})]})}import{jsx as G,jsxs as It}from"react/jsx-runtime";function Kn({className:e,style:t,pinnedInfo:n,accessor:o,visibleMediaQuery:a,title:r,sortable:l,draggable:s,toggleable:c,pinnable:m,resizable:i,sortIcons:u,textAlign:p,width:T,sortStatus:d,onSortStatusChange:S,filter:b,filterPopoverProps:C,filterPopoverDisableClickOutside:E,filtering:D,sortKey:L}){let{setSourceColumn:N,setTargetColumn:f,swapColumns:h,columnsToggle:w,setColumnsToggle:M,columnsPinning:g,setColumnsPinning:R}=Pe(),[y,v]=Qr(!1),H=Ur(null),x=g?.find(O=>O.accessor===o)?.pinned;if(!te(a))return null;let P=r??De(o),A=typeof P=="string"?P:void 0,_=l&&S?O=>{O?.defaultPrevented||S({sortKey:L,columnAccessor:o,direction:d?.columnAccessor===o?d.direction==="asc"?"desc":"asc":d?.direction??"asc"})}:void 0,z=O=>{O.stopPropagation(),N(o),v(!1)},K=O=>{O.preventDefault(),f(o),v(!0)},le=()=>{f(o),v(!1),h()},ie=()=>{v(!0)},se=()=>{v(!1)},ce=O=>{O.stopPropagation(),M(w.map($=>$.accessor===o?{...$,toggled:!1}:$))},ue=O=>{R($=>$.map(j=>j.accessor===o?{...j,pinned:O}:j))};return It(Xr,{"data-accessor":o,"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:tt({"mantine-datatable-header-cell-sortable":l,"mantine-datatable-header-cell-toggleable":c,"mantine-datatable-header-cell-pinnable":m,"mantine-datatable-header-cell-resizable":i},e),style:[{width:T,...i?{}:{minWidth:T,maxWidth:T}},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],role:l?"button":void 0,tabIndex:l?0:void 0,onClick:_,onKeyDown:O=>O.key==="Enter"&&_?.(),ref:H,children:[It($r,{className:"mantine-datatable-header-cell-sortable-group",justify:"space-between",wrap:"nowrap",children:[It(Fr,{align:"center",w:"100%",className:tt({"mantine-datatable-header-cell-draggable":s,"mantine-datatable-header-cell-drag-over":y}),draggable:s,onDragStart:s?z:void 0,onDragEnter:s?ie:void 0,onDragOver:s?K:void 0,onDrop:s?le:void 0,onDragLeave:s?se:void 0,children:[s?G(et,{role:"img","aria-label":"Drag column",children:G(Qn,{className:"mantine-datatable-header-cell-draggable-action-icon",variant:"subtle",size:"xs",onClick:O=>{O.stopPropagation()},children:G(Un,{})})}):null,G(Gr,{className:tt("mantine-datatable-header-cell-sortable-text",{[ne]:p==="left",[oe]:p==="center",[ae]:p==="right"},we,Se),title:A,children:P})]}),m?G(Wn,{currentPinned:x,onPinChange:ue}):null,c?G(et,{className:"mantine-datatable-header-cell-toggleable-icon",role:"img","aria-label":"Toggle column",children:G(Qn,{size:"xs",variant:"light",onClick:ce,children:G(Je,{})})}):null,l||d?.columnAccessor===o?d?.columnAccessor===o?G(et,{className:tt("mantine-datatable-header-cell-sortable-icon",{"mantine-datatable-header-cell-sortable-icon-reversed":d.direction==="desc"}),role:"img","aria-label":`Sorted ${d.direction==="desc"?"descending":"ascending"}`,children:u?.sorted||G(Xn,{})}):G(et,{className:"mantine-datatable-header-cell-sortable-unsorted-icon",role:"img","aria-label":"Not sorted",children:u?.unsorted||G($n,{})}):null,b?G(Nn,{filterPopoverProps:C,isActive:!!D,filterPopoverDisableClickOutside:E,children:b}):null]}),i&&o!=="__selection__"?G(Fn,{accessor:o,columnRef:H}):null]})}import{Checkbox as Kr,TableTh as jr}from"@mantine/core";import Jr from"clsx";import{jsx as jn}from"react/jsx-runtime";function Jn({className:e,style:t,trigger:n,shadowVisible:o,checked:a,indeterminate:r,checkboxProps:l,onChange:s,rowSpan:c,ref:m}){let i=!l.disabled;return jn(jr,{ref:m,className:Jr("mantine-datatable-header-selector-cell",{[Q]:n==="cell"&&i},e),style:t,rowSpan:c,"data-shadow-visible":o||void 0,"data-accessor":"__selection__",onClick:n==="cell"&&i?s:void 0,children:jn(Kr,{classNames:i?{input:Q}:void 0,checked:a,indeterminate:r,onChange:s,...l,disabled:!(s||l.onChange)||l.disabled})})}import{jsx as re,jsxs as nt}from"react/jsx-runtime";function ll(e,t){let o=(e.columns??[]).filter(u=>!u.hidden).map(u=>String(u.accessor)),a=o.map(u=>t.get(u)).filter(Boolean);if(a.length===0||a.length!==o.length)return;let r=new Set(a.map(u=>u.logicalSide));if(r.size>1)return;let l=[...r][0],s=l==="left"?o[0]:o[o.length-1],c=t.get(s);if(!c)return;let m=l==="left"?o[o.length-1]:o[0],i=t.get(m);return{...c,isBoundary:i?.isBoundary??!1}}function Zn({selectionColumnHeaderRef:e,className:t,style:n,sortStatus:o,sortIcons:a,onSortStatusChange:r,columns:l,defaultColumnProps:s,groups:c,pinnedMap:m,selectionTrigger:i,selectionVisible:u,selectionChecked:p,selectionIndeterminate:T,onSelectionChange:d,selectionCheckboxProps:S,selectorCellShadowVisible:b,selectionColumnClassName:C,selectionColumnStyle:E,withColumnBorders:D=!1,ref:L}){let N=c?Pt(c):0,f=N>0?N+1:1,h=u?re(Jn,{ref:e,className:C,style:E,trigger:i,shadowVisible:b,checked:p,indeterminate:T,checkboxProps:S,onChange:d,rowSpan:c?f:void 0}):null,{columnsToggle:w,setColumnsToggle:M}=Pe(),[g,R]=rl(!1),y=l.some(k=>k.toggleable),v=y?Object.fromEntries(l.map(({accessor:k,title:x})=>[k,x??De(String(k))])):void 0,H=nt(ol,{className:al("mantine-datatable-header",t),style:n,ref:L,onContextMenu:y?k=>{k.preventDefault(),R(x=>!x)}:void 0,children:[c&&Array.from({length:N},(k,x)=>{let P=Dt(c,x);return nt(qn,{children:[x===0&&h,P.map((A,_)=>re(Mn,{group:A,pinnedInfo:ll(A,m),maxDepth:N,currentDepth:x,previousGroups:P.slice(0,_),isLastGroup:_===P.length-1,withColumnBorders:D,totalTableColumns:l.length},A.id))]},`group-depth-${x}`)}),nt(qn,{children:[!c&&h,l.map(({hidden:k,...x},P)=>{if(k)return null;let{accessor:A,visibleMediaQuery:_,textAlign:z,width:K,title:le,sortable:ie,draggable:se,toggleable:ce,pinnable:ue,resizable:O,titleClassName:$,titleStyle:j,filter:J,filterPopoverProps:it,filterPopoverDisableClickOutside:st,filtering:ct,sortKey:ut}={...s,...x};return re(Kn,{accessor:A,pinnedInfo:m.get(String(A)),className:$,style:j,visibleMediaQuery:_,textAlign:z,width:K,title:le,sortable:ie,draggable:se,toggleable:ce,pinnable:ue,resizable:O&&Pk.toggleable).map(k=>re(Zr,{children:re(qr,{classNames:{label:"mantine-datatable-header-column-toggle-checkbox-label"},size:"xs",label:v[k.accessor],checked:k.toggled,onChange:x=>{M(w.map(P=>P.accessor===k.accessor?{...P,toggled:x.currentTarget.checked}:P))}})},k.accessor))})})]}):H}import{Center as il,Loader as sl}from"@mantine/core";import cl from"clsx";import{jsx as Yn}from"react/jsx-runtime";function eo({fetching:e,customContent:t,backgroundBlur:n,size:o,type:a,color:r}){return Yn(il,{className:cl("mantine-datatable-loader",{"mantine-datatable-loader-fetching":e}),style:[{backdropFilter:n?`blur(${n}px)`:void 0}],children:e&&(t||Yn(sl,{size:o,type:a,color:r}))})}import{Box as hl,Pagination as Cl,rem as yl,Text as Pl}from"@mantine/core";import lo from"clsx";import{Button as dl,Group as ml,Menu as pl,MenuDropdown as fl,MenuItem as bl,MenuTarget as gl,rem as Ne,Text as no}from"@mantine/core";import Tl from"clsx";import{jsx as Nt,jsxs as ul}from"react/jsx-runtime";function to(){return ul("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Nt("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Nt("path",{d:"M8 9l4 -4l4 4"}),Nt("path",{d:"M16 15l-4 4l-4 -4"})]})}import{jsx as Te,jsxs as ao}from"react/jsx-runtime";var oo={xs:Ne(22),sm:Ne(26),md:Ne(32),lg:Ne(38),xl:Ne(44)};function ro({size:e,label:t,values:n,value:o,activeTextColor:a,activeBackgroundColor:r,onChange:l}){return ao(ml,{gap:"xs",children:[Te(no,{component:"div",size:e,children:t}),ao(pl,{withinPortal:!0,withArrow:!0,offset:2,classNames:{arrow:"mantine-datatable-page-size-selector-menu-arrow"},children:[Te(gl,{children:Te(dl,{size:e,variant:"default",classNames:{section:"mantine-datatable-page-size-selector-button-icon"},rightSection:Te(to,{}),style:[{fontWeight:"normal"},s=>({height:oo[e],paddingLeft:s.spacing[e],paddingRight:s.spacing[e]})],children:o})}),Te(fl,{children:n.map(s=>{let c=s===o;return Te(bl,{className:Tl({"mantine-datatable-page-size-selector-active":c}),style:[{height:oo[e]},c&&(a||r)?m=>We({theme:m,paginationActiveTextColor:a,paginationActiveBackgroundColor:r}):void 0],disabled:c,onClick:()=>l(s),children:Te(no,{component:"div",size:e,children:s})},s)})})]})]})}import{Fragment as Dl,jsx as he,jsxs as wl}from"react/jsx-runtime";function io({className:e,style:t,fetching:n,page:o,onPageChange:a,paginationWithEdges:r,paginationWithControls:l,paginationActiveTextColor:s,paginationActiveBackgroundColor:c,paginationSize:m,loadingText:i,noRecordsText:u,paginationText:p,totalRecords:T,recordsPerPage:d,onRecordsPerPageChange:S,recordsPerPageLabel:b,recordsPerPageOptions:C,recordsLength:E,horizontalSpacing:D,paginationWrapBreakpoint:L,getPaginationControlProps:N,getPaginationItemProps:f,renderPagination:h}){let w;if(T){let x=(o-1)*d+1,P=x+(E||0)-1;w=p({from:x,to:P,totalRecords:T})}else w=n?i:u;let M=T&&d?Math.max(1,Math.ceil(T/d)):1,g=T?(o-1)*(d??0)+1:void 0,R=T?(g??1)+(E??0)-1:void 0,v=!te(({breakpoints:x})=>`(min-width: ${typeof L=="number"?`${yl(L)}rem`:x[L]||L})`),H={Text:x=>he(Pl,{component:"div",className:"mantine-datatable-pagination-text",size:m,...x,children:w}),PageSizeSelector:x=>C?he(ro,{activeTextColor:s,activeBackgroundColor:c,size:m,label:b,values:C,value:d,onChange:S,...x}):null,Pagination:x=>he(Cl,{classNames:{root:lo("mantine-datatable-pagination-pages",{"mantine-datatable-pagination-pages-fetching":n||!E}),control:"mantine-datatable-pagination-pages-control"},style:s||c?P=>We({theme:P,paginationActiveTextColor:s,paginationActiveBackgroundColor:c}):void 0,withEdges:r,withControls:l,value:o,onChange:a,size:m,total:M,getControlProps:N,getItemProps:f,...x})},k={state:{paginationSize:m,page:o,totalPages:M,totalRecords:T,recordsPerPage:d,recordsLength:E,fetching:n,from:g,to:R,isWrapped:v},actions:{setPage:x=>a?.(x),setRecordsPerPage:S?x=>S(x):void 0},Controls:H};return he(hl,{px:D??"xs",py:"xs",className:lo("mantine-datatable-pagination",e),style:[{flexDirection:v?"column":"row"},t],children:typeof h=="function"?h(k):wl(Dl,{children:[he(H.Text,{}),he(H.PageSizeSelector,{}),he(H.Pagination,{})]})})}import{TableTr as Hl}from"@mantine/core";import Al from"clsx";import{TableTd as Sl}from"@mantine/core";import Rl from"clsx";import{jsx as xl}from"react/jsx-runtime";function so({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,record:a,index:r,onClick:l,onDoubleClick:s,onContextMenu:c,noWrap:m,ellipsis:i,textAlign:u,width:p,accessor:T,render:d,defaultRender:S,customCellAttributes:b}){return te(o)?xl(Sl,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:Rl({[we]:m||i,[Se]:i,[Q]:l||s,[$e]:c,[ne]:u==="left",[oe]:u==="center",[ae]:u==="right"},e),style:[{width:p,minWidth:p,maxWidth:p},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],onClick:l,onDoubleClick:s,onContextMenu:c,...b?.(a,r),children:d?d(a,r):S?S(a,r,T):yt(a,T)}):null}import{Collapse as vl,TableTd as Ml,TableTr as co}from"@mantine/core";import{Fragment as kl,jsx as He,jsxs as El}from"react/jsx-runtime";function uo({open:e,colSpan:t,content:n,collapseProps:o}){let{expanded:a,visible:r}=wn(e,o?.transitionDuration);return r?El(kl,{children:[He(co,{}),He(co,{children:He(Ml,{className:"mantine-datatable-row-expansion-cell",colSpan:t,children:He(vl,{expanded:a,...o,children:He("div",{className:"mantine-datatable-row-expansion-cell-content",children:n()})})})})]}):null}import{Checkbox as Ll,TableTd as Il}from"@mantine/core";import Nl from"clsx";import{jsx as mo}from"react/jsx-runtime";function po({className:e,style:t,record:n,index:o,trigger:a,onChange:r,withRightShadow:l,checkboxProps:s,getCheckboxProps:c,...m}){let i={...s,...c(n,o)},u=!m.disabled&&!i.disabled,p=T=>{T.stopPropagation(),a==="cell"&&u&&r?.(T)};return mo(Il,{className:Nl("mantine-datatable-row-selector-cell",{[Q]:a==="cell"&&u},e),style:t,"data-shadow-visible":l||void 0,onClick:p,children:mo(Ll,{classNames:u?{input:Q}:void 0,onChange:r,...m,...i})})}import{Fragment as fo,jsx as ot,jsxs as bo}from"react/jsx-runtime";function go({record:e,index:t,columns:n,defaultColumnProps:o,pinnedMap:a,defaultColumnRender:r,selectionTrigger:l,selectionVisible:s,selectionChecked:c,onSelectionChange:m,isRecordSelectable:i,selectionCheckboxProps:u,getSelectionCheckboxProps:p,onClick:T,onDoubleClick:d,onContextMenu:S,onCellClick:b,onCellDoubleClick:C,onCellContextMenu:E,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w,selectorCellShadowVisible:M,selectionColumnClassName:g,selectionColumnStyle:R,rowFactory:y}){let v=bo(fo,{children:[s&&ot(po,{className:g,style:R,record:e,index:t,trigger:l,withRightShadow:M,checked:c,disabled:!m||(i?!i(e,t):!1),onChange:m,checkboxProps:u,getCheckboxProps:p}),n.map(({hidden:x,hiddenContent:P,...A},_)=>{if(x||P)return null;let{accessor:z,visibleMediaQuery:K,textAlign:le,noWrap:ie,ellipsis:se,width:ce,render:ue,cellsClassName:O,cellsStyle:$,customCellAttributes:j}={...o,...A};return ot(so,{pinnedInfo:a.get(String(z)),className:typeof O=="function"?O(e,t):O,style:$?.(e,t),visibleMediaQuery:K,record:e,index:t,onClick:b?J=>b({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,onDoubleClick:C?J=>C({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,onContextMenu:E?J=>E({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,accessor:z,textAlign:le,noWrap:ie,ellipsis:se,width:ce,render:ue,defaultRender:r,customCellAttributes:j},z)})]}),H=D&&ot(uo,{colSpan:n.filter(({hidden:x})=>!x).length+(s?1:0),open:D.isRowExpanded(e),content:D.content({record:e,index:t}),collapseProps:D.collapseProps}),k=Ol({record:e,index:t,selectionChecked:c,onClick:T,onDoubleClick:d,onContextMenu:S,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w});return y?y({record:e,index:t,rowProps:k,children:v,expandedElement:H}):bo(fo,{children:[ot(Hl,{...k,children:v}),H]})}function Ol({record:e,index:t,selectionChecked:n,onClick:o,onDoubleClick:a,onContextMenu:r,expansion:l,customAttributes:s,color:c,backgroundColor:m,className:i,style:u}){return{className:Al("mantine-datatable-row",{[Q]:o||a||l?.isExpandable({record:e,index:t})&&l?.expandOnClick},{[$e]:r},typeof i=="function"?i(e,t):i),"data-selected":n||void 0,onClick:p=>{if(l){let{isExpandable:T,isRowExpanded:d,expandOnClick:S,expandRow:b,collapseRow:C}=l;T({record:e,index:t})&&S&&(d(e)?C(e):b(e))}o?.({event:p,record:e,index:t})},onDoubleClick:a?p=>a({event:p,record:e,index:t}):void 0,onContextMenu:r?p=>r({event:p,record:e,index:t}):void 0,style:[c||m?p=>{let T=c?.(e,t),d=m?.(e,t);return $t({theme:p,color:T,backgroundColor:d})}:void 0,u?.(e,t)],...s?.(e,t)??{}}}import{Box as To,ScrollArea as zl}from"@mantine/core";import at from"clsx";import{jsx as rt,jsxs as _l}from"react/jsx-runtime";function ho({leftShadowBehind:e,rightShadowBehind:t,onScrollPositionChange:n,children:o,viewportRef:a,scrollAreaProps:r}){return _l(zl,{...r,viewportRef:a,classNames:{root:"mantine-datatable-scroll-area",scrollbar:"mantine-datatable-scroll-area-scrollbar",thumb:"mantine-datatable-scroll-area-thumb",corner:"mantine-datatable-scroll-area-corner"},onScrollPositionChange:n,children:[o,rt(To,{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-top-shadow")}),rt("div",{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-left-shadow",{"mantine-datatable-scroll-area-shadow-behind":e})}),rt("div",{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-right-shadow",{"mantine-datatable-scroll-area-shadow-behind":t})}),rt(To,{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-bottom-shadow")})]})}import{jsx as F,jsxs as yo}from"react/jsx-runtime";function $l({withTableBorder:e,borderRadius:t,textSelectionDisabled:n,height:o="100%",minHeight:a,maxHeight:r,shadow:l,verticalAlign:s="center",fetching:c,columns:m,storeColumnsKey:i,groups:u,pinFirstColumn:p,pinLastColumn:T,defaultColumnProps:d,defaultColumnRender:S,idAccessor:b="id",records:C,selectionTrigger:E="checkbox",selectedRecords:D,onSelectedRecordsChange:L,selectionColumnClassName:N,selectionColumnStyle:f,isRecordSelectable:h,selectionCheckboxProps:w,allRecordsSelectionCheckboxProps:M={"aria-label":"Select all records"},getRecordSelectionCheckboxProps:g=(de,V)=>({"aria-label":`Select record ${V+1}`}),sortStatus:R,sortIcons:y,onSortStatusChange:v,horizontalSpacing:H,page:k,onPageChange:x,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:_,recordsPerPageOptions:z,recordsPerPageLabel:K="Records per page",paginationWithEdges:le,paginationWithControls:ie,paginationActiveTextColor:se,paginationActiveBackgroundColor:ce,paginationSize:ue="sm",paginationText:O=({from:de,to:V,totalRecords:pt})=>`${de} - ${V} / ${pt}`,paginationWrapBreakpoint:$="sm",getPaginationControlProps:j=de=>de==="previous"?{"aria-label":"Previous page"}:de==="next"?{"aria-label":"Next page"}:{},getPaginationItemProps:J,renderPagination:it,loaderBackgroundBlur:st,customLoader:ct,loaderSize:ut,loaderType:Po,loaderColor:Do,loadingText:wo="...",emptyState:So,noRecordsText:Ot="No records",noRecordsIcon:Ro,highlightOnHover:xo,striped:vo,noHeader:Mo,onRowClick:ko,onRowDoubleClick:Eo,onRowContextMenu:Lo,onCellClick:Io,onCellDoubleClick:No,onCellContextMenu:Ho,onScroll:Ao,onScrollToTop:Oo,onScrollToBottom:zo,onScrollToLeft:_o,onScrollToRight:Wo,c:Bo,backgroundColor:Vo,borderColor:Go,rowBorderColor:Fo,stripedColor:$o,highlightOnHoverColor:Xo,rowColor:Uo,rowBackgroundColor:Qo,rowExpansion:Ko,rowClassName:jo,rowStyle:Jo,customRowAttributes:qo,scrollViewportRef:Zo,scrollAreaProps:Yo,tableRef:ea,bodyRef:ta,m:na,my:oa,mx:aa,mt:ra,mb:la,ml:ia,mr:sa,className:ca,classNames:Re,style:ua,styles:xe,rowFactory:da,tableWrapper:dt,...mt}){let de=Fl(()=>u?Ie(u):m,[m,u]),{refs:V,onScroll:pt}=fn({scrollCallbacks:{onScroll:Ao,onScrollToTop:Oo,onScrollToBottom:zo,onScrollToLeft:_o,onScrollToRight:Wo},withRowBorders:mt.withRowBorders}),q=cn({key:i,columns:de,headerRef:V.header,scrollViewportRef:V.scrollViewport}),ve=q.effectiveColumns,ma=Ht(V.table,ea),pa=Ht(V.scrollViewport,Zo),zt=lt(null),fa=Ht(zt,ta),ba=yn({rowExpansion:Ko,records:C,idAccessor:b}),{pinnedMap:Me,hasLeftPinned:_t,hasRightPinned:ga}=bn({columns:ve,theadRef:V.header,tbodyRef:zt,selectionColumnHeaderRef:V.selectionColumnHeader,selectionVisible:!!D,pinFirstColumn:p,pinLastColumn:T}),Ae=lt(!1),Wt=lt(k),ft=lt(C),Ta=At(I=>{Ae.current=!0,ft.current=C,x(I)},[x,C]);Vl(()=>{Wt.current!==k&&(Ae.current=!0,ft.current=C,Wt.current=k)},[k,C]);let ke=C?.length;Gl(()=>{if(!Ae.current||c||C===ft.current)return;let I=V.scrollViewport.current;if(!I)return;let me=requestAnimationFrame(()=>{I.scrollTo({top:0,left:0}),Ae.current=!1});return()=>cancelAnimationFrame(me)},[c,C,V.scrollViewport]);let Bt=C?.map(I=>B(I,b)),Ce=!!D,Ee=D?.map(I=>B(I,b)),Vt=Bt!==void 0&&Ee!==void 0&&Ee.length>0,bt=h?C?.filter(h):C,Oe=bt?.map(I=>B(I,b)),ze=Vt&&Oe.every(I=>Ee.includes(I)),ha=Vt&&Oe.some(I=>Ee.includes(I)),Ca=At(()=>{D&&L&&L(ze?D.filter(I=>!Oe.includes(B(I,b))):Ge([...D,...bt],I=>B(I,b)))},[ze,b,L,Oe,bt,D]),{lastSelectionChangeIndex:_e,setLastSelectionChangeIndex:ya}=gn(Bt),gt=Ce&&!_t,Pa={m:na,my:oa,mx:aa,mt:ra,mb:la,ml:ia,mr:sa},Da=At(({children:I})=>dt?dt({children:I}):I,[dt]);return F(Ct,{...q,pinnedMap:Me,children:yo(Wl,{ref:V.root,...Pa,className:Co("mantine-datatable",{"mantine-datatable-with-border":e},ca,Re?.root),style:[I=>({...Ft({theme:I,c:Bo,backgroundColor:Vo,borderColor:Go,rowBorderColor:Fo,stripedColor:$o,highlightOnHoverColor:Xo}),borderRadius:I.radius[t]||t,boxShadow:I.shadows[l]||l,height:o,minHeight:a,maxHeight:r}),ua,xe?.root,{position:"relative"}],children:[F(ho,{viewportRef:pa,leftShadowBehind:Ce||_t,rightShadowBehind:ga,onScrollPositionChange:pt,scrollAreaProps:Yo,children:F(Da,{children:yo(Bl,{ref:ma,horizontalSpacing:H,className:Co("mantine-datatable-table",{[Sn]:n,"mantine-datatable-vertical-align-top":s==="top","mantine-datatable-vertical-align-bottom":s==="bottom","mantine-datatable-selection-column-visible":Ce,"mantine-datatable-resizable-columns":q.hasResizableColumns,"mantine-datatable-resize-locked":q.isLocked,"mantine-datatable-resizing":q.isResizing},Re?.table),style:{...xe?.table,...q.isLocked?{tableLayout:"fixed"}:null,...q.tableWidth!=null?{width:`${q.tableWidth}px`}:null},"data-striped":ke&&vo||void 0,"data-highlight-on-hover":xo||void 0,...mt,children:[Mo?null:F(Ct,{...q,pinnedMap:Me,children:F(Zn,{ref:V.header,selectionColumnHeaderRef:V.selectionColumnHeader,className:Re?.header,style:xe?.header,columns:ve,defaultColumnProps:d,groups:u,pinnedMap:Me,sortStatus:R,sortIcons:y,onSortStatusChange:v,selectionTrigger:E,selectionVisible:Ce,selectionChecked:ze,selectionIndeterminate:ha&&!ze,onSelectionChange:Ca,selectionCheckboxProps:{...w,...M},selectorCellShadowVisible:gt,selectionColumnClassName:N,selectionColumnStyle:f,withColumnBorders:mt.withColumnBorders})}),F("tbody",{ref:fa,children:ke?C.map((I,me)=>{let Tt=B(I,b),ht=Ee?.includes(Tt)||!1,Gt;return L&&D&&(Gt=wa=>{if(wa.nativeEvent.shiftKey&&_e!==null){let ye=C.filter(me>_e?(pe,fe)=>fe>=_e&&fe<=me&&(h?h(pe,fe):!0):(pe,fe)=>fe>=me&&fe<=_e&&(h?h(pe,fe):!0));L(ht?qt(D,ye,pe=>B(pe,b)):Ge([...D,...ye],pe=>B(pe,b)))}else L(ht?D.filter(ye=>B(ye,b)!==Tt):Ge([...D,I],ye=>B(ye,b)));ya(me)}),F(go,{record:I,index:me,columns:ve,defaultColumnProps:d,pinnedMap:Me,defaultColumnRender:S,selectionTrigger:E,selectionVisible:Ce,selectionChecked:ht,onSelectionChange:Gt,isRecordSelectable:h,selectionCheckboxProps:w,getSelectionCheckboxProps:g,onClick:ko,onDoubleClick:Eo,onCellClick:Io,onCellDoubleClick:No,onContextMenu:Lo,onCellContextMenu:Ho,expansion:ba,color:Uo,backgroundColor:Qo,className:jo,style:Jo,customAttributes:qo,selectorCellShadowVisible:gt,selectionColumnClassName:N,selectionColumnStyle:f,idAccessor:b,rowFactory:da},Tt)}):F(Kt,{})}),ve.some(({footer:I})=>I)&&F(vn,{ref:V.footer,className:Re?.footer,style:xe?.footer,columns:ve,defaultColumnProps:d,pinnedMap:Me,selectionVisible:Ce,selectorCellShadowVisible:gt})]})})}),!!(k&&ke)&&F(io,{className:Re?.pagination,style:xe?.pagination,horizontalSpacing:H,fetching:c,page:k,onPageChange:Ta,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:_,recordsPerPageOptions:z,recordsPerPageLabel:K,paginationWithEdges:le,paginationWithControls:ie,paginationActiveTextColor:se,paginationActiveBackgroundColor:ce,paginationSize:ue,paginationText:O,paginationWrapBreakpoint:$,getPaginationControlProps:j,getPaginationItemProps:J,noRecordsText:Ot,loadingText:wo,recordsLength:ke,renderPagination:it}),F(eo,{fetching:c,backgroundBlur:st,customContent:ct,size:ut,type:Po,color:Do}),F(Jt,{icon:Ro,text:Ot,active:!c&&!ke,children:So})]})})}import{TableTr as Xl}from"@mantine/core";import{useMergedRef as Ul}from"@mantine/hooks";import{useEffect as Ql,useRef as Kl}from"react";import{jsx as Jl}from"react/jsx-runtime";function jl({className:e,children:t,isDragging:n,ref:o,...a}){let r=Kl(null),l=Ul(r,o);return Ql(()=>{if(!r.current||!n)return;let i=r.current.parentElement.parentElement.children[0].children[0];for(let u=0;u{let{children:t,columnsOrder:n,setColumnsOrder:o,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S,pinnedMap:b}=e,[C,E]=Ut(""),[D,L]=Ut("");return xa(Xt,{value:{sourceColumn:C,setSourceColumn:E,targetColumn:D,setTargetColumn:L,columnsToggle:a,setColumnsToggle:r,columnsPinning:l,setColumnsPinning:s,swapColumns:()=>{if(!n||!o||!C||!D)return;let f=b.get(C),h=b.get(D);if(f?.logicalSide!==h?.logicalSide)return;let w=n.indexOf(C),M=n.indexOf(D);if(w!==-1&&M!==-1){let g=[...n],[R]=g.splice(w,1);g.splice(M,0,R),o(g)}},resetColumnsOrder:c,resetColumnsToggle:m,resetColumnsPinning:i,setColumnWidth:u,setMultipleColumnWidths:p,resetColumnsWidth:T,beginResize:d,endResize:S},children:t})};import{jsx as Qt}from"react/jsx-runtime";function Kt(){return Qt("tr",{className:"mantine-datatable-empty-row",children:Qt("td",{})})}import{Center as Ma,Text as ka}from"@mantine/core";import{jsx as Le,jsxs as va}from"react/jsx-runtime";function jt(){return va("svg",{width:"24",height:"24",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Le("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Le("path",{d:"M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74"}),Le("path",{d:"M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6"}),Le("path",{d:"M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4"}),Le("path",{d:"M3 3l18 18"})]})}import{Fragment as Ea,jsx as Be,jsxs as La}from"react/jsx-runtime";function Jt({icon:e,text:t,active:n,children:o}){return Be(Ma,{className:"mantine-datatable-empty-state","data-active":n||void 0,children:o||La(Ea,{children:[e||Be("div",{className:"mantine-datatable-empty-state-icon",children:Be(jt,{})}),Be(ka,{component:"div",size:"sm",c:"dimmed",children:t})]})})}import{TableTfoot as dr,TableTr as mr}from"@mantine/core";import pr from"clsx";import{TableTh as lr}from"@mantine/core";import ir from"clsx";import{useCallback as fi,useRef as bi,useState as gi}from"react";import{useLocalStorage as Ia}from"@mantine/hooks";import{useEffect as Na,useMemo as Yt}from"react";function De(e){let t=e.replace(/([a-z\d])([A-Z]+)/g,"$1 $2").replace(/\W|_/g," ").trim().toLowerCase();return`${t.charAt(0).toUpperCase()}${t.slice(1)}`}function Z(e,t,n){return!Array.isArray(e)||n&&!e.every(n)?t:e}function Ve(e){return typeof e=="object"&&e!==null&&typeof e.accessor=="string"}function qt(e,t,n){return e.filter(o=>!t.map(n).includes(n(o)))}function Ge(e,t){return e.filter((n,o,a)=>o===a.findIndex(r=>t(n)===t(r)))}function yt(e,t){return t?t.match(/([^[.\]])+/g).reduce((o,a)=>o&&o[a],e):void 0}function B(e,t){return typeof t=="string"?yt(e,t):t(e)}function hi(e,t){let n=structuredClone(t),o=n[e.source.index];return e.destination&&(n.splice(e.source.index,1),n.splice(e.destination.index,0,o)),n}function Ci(e,t){let n=structuredClone(t),o=e.destination;if(!o)return n;let a=n[e.source.index],r=n[o.index];return n.splice(o.index,1,a),n.splice(e.source.index,1,r),n}function Pt(e){return!e||e.length===0?0:Math.max(...e.map(t=>t.groups&&t.groups.length>0?1+Pt(t.groups):1))}function Ie(e){let t=[];for(let n of e)n.columns&&n.columns.length>0?t.push(...n.columns.filter(o=>o!=null)):n.groups&&n.groups.length>0&&t.push(...Ie(n.groups));return t.filter(n=>n!=null)}function Fe(e,t){return e.columns&&e.columns.length>0?e.columns.filter((n,o)=>n.hidden?!1:t?t[o]!==!1:!0).length:e.groups&&e.groups.length>0?e.groups.reduce((n,o)=>n+Fe(o,t),0):0}function Dt(e,t,n=0){if(n===t)return[...e];let o=[];for(let a of e)a.groups&&a.groups.length>0&&o.push(...Dt(a.groups,t,n+1));return o}function Zt(e,t,n){return n?!e||t:!1}function en({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=Yt(()=>t.map(i=>({accessor:i.accessor,defaultPinned:i.pinned,pinnable:!!i.pinnable,pinned:i.pinned})),[t]),[a,r]=Ia({key:e?`${e}-columns-pinning`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,Ve);function s(i){e&&r(typeof i=="function"?u=>i(u??o):i)}let c=Yt(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultPinned:u.pinned,pinnable:!!u.pinnable,pinned:u.pinned})}),i},[l,t,o]);Na(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsPinning:c,setColumnsPinning:s,resetColumnsPinning:m}:{columnsPinning:o,setColumnsPinning:s,resetColumnsPinning:m}}import{useLocalStorage as Ha}from"@mantine/hooks";import{useEffect as Aa,useMemo as tn}from"react";function nn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=tn(()=>t?t.map(i=>i.accessor):[],[t]),[a,r]=Ha({key:e?`${e}-columns-order`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,i=>typeof i=="string");function s(i){e&&r(i)}let c=tn(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u)&&i.push(u)}),t.forEach(u=>{i.includes(u.accessor)||i.push(u.accessor)}),i},[l,t,o]);Aa(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsOrder:c,setColumnsOrder:s,resetColumnsOrder:m}:{columnsOrder:l,setColumnsOrder:s,resetColumnsOrder:m}}import{useLocalStorage as Oa}from"@mantine/hooks";import{useCallback as Y,useEffect as wt,useMemo as on,useRef as za,useState as an}from"react";function rn({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o}){let a=on(()=>t.some(f=>f.resizable&&!f.hidden&&f.accessor!=="__selection__"),[t]),r=Y(()=>t.filter(f=>f.accessor!=="__selection__").map(f=>({[f.accessor]:f.width??"auto"})),[t]),[l,s]=Oa({key:e?`${e}-columns-width`:"",defaultValue:e?r():void 0,getInitialValueInEffect:n}),c=Z(l,[],f=>typeof f=="object"&&f!==null),[m,i]=an(()=>e&&c.length>0?c:r()),[u,p]=an(!1),T=za(!1);wt(()=>{e&&(!c||c.length===0||i(c))},[e,c]),wt(()=>{i(f=>{let h=new Set(t.filter(y=>y.accessor!=="__selection__").map(y=>String(y.accessor))),w=f.filter(y=>h.has(Object.keys(y)[0])),M=new Set(w.map(y=>Object.keys(y)[0])),g=!1;for(let y of t){if(y.accessor==="__selection__")continue;let v=String(y.accessor);M.has(v)||(w.push({[v]:y.width??"auto"}),g=!0)}return w.length!==f.length||g?w:f})},[t]);let d=Y(f=>{let h=f.filter(w=>w.accessor!=="__selection__");h.length!==0&&(T.current=!0,i(w=>w.map(M=>{let g=Object.keys(M)[0],R=h.find(y=>y.accessor===g);return R?{[g]:R.width}:M})))},[]);wt(()=>{!e||!T.current||u||(T.current=!1,s(m))},[e,m,u,s]);let S=Y((f,h)=>{d([{accessor:f,width:h}])},[d]),b=Y(f=>{d(f)},[d]),C=Y(()=>{p(!0);let f=o?.current;if(!f)return;let h=Array.from(f.querySelectorAll("th[data-accessor]"));if(h.length===0)return;let w=[];for(let M of h){let g=M.getAttribute("data-accessor");if(!g||g==="__selection__")continue;let R=M.offsetWidth;w.push({[g]:`${R}px`})}T.current=!0,i(w)},[o]),E=Y(()=>{p(!1)},[]),D=Y(()=>{let f=r();T.current=!0,i(f),p(!1)},[r]),L=on(()=>{if(!a)return!1;let f=t.filter(h=>h.resizable&&!h.hidden&&h.accessor!=="__selection__");return m.filter(h=>{let w=Object.keys(h)[0];return f.some(M=>M.accessor===w)}).every(h=>{let w=Object.values(h)[0];return w==="auto"||w==="initial"})},[a,t,m]),N=Y(()=>{},[]);return{columnsWidth:m,setColumnsWidth:d,setColumnWidth:S,setMultipleColumnWidths:b,resetColumnsWidth:D,hasResizableColumns:a,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:u,beginResize:C,endResize:E}}import{useMemo as St}from"react";import{useLocalStorage as _a}from"@mantine/hooks";import{useEffect as Wa,useMemo as ln}from"react";function sn({key:e,columns:t=[],getInitialValueInEffect:n=!0}){let o=ln(()=>t?.map(i=>({accessor:i.accessor,defaultToggle:i.defaultToggle||!0,toggleable:i.toggleable,toggled:i.defaultToggle===void 0?!0:i.defaultToggle})),[t]),[a,r]=_a({key:e?`${e}-columns-toggle`:"",defaultValue:e?o:void 0,getInitialValueInEffect:n}),l=Z(a,o,Ve);function s(i){e&&r(i)}let c=ln(()=>{if(!l)return o;let i=[];return l.forEach(u=>{t.find(p=>p.accessor===u.accessor)&&i.push(u)}),t.forEach(u=>{i.find(p=>p.accessor===u.accessor)||i.push({accessor:u.accessor,defaultToggle:u.defaultToggle||!0,toggleable:u.toggleable,toggled:u.defaultToggle===void 0?!0:u.defaultToggle})}),i},[l,t,o]);Wa(()=>{e&&l&&JSON.stringify(c)!==JSON.stringify(l)&&r(c)},[e,c,l,r]);let m=()=>{s(o)};return e?{columnsToggle:c,setColumnsToggle:s,resetColumnsToggle:m}:{columnsToggle:l,setColumnsToggle:s,resetColumnsToggle:m}}var cn=({key:e,columns:t=[],getInitialValueInEffect:n=!0,headerRef:o,scrollViewportRef:a})=>{let{columnsOrder:r,setColumnsOrder:l,resetColumnsOrder:s}=nn({key:e,columns:t,getInitialValueInEffect:n}),{columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i}=sn({key:e,columns:t,getInitialValueInEffect:n}),{columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T}=en({key:e,columns:t,getInitialValueInEffect:n}),{columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,beginResize:h,endResize:w}=rn({key:e,columns:t,getInitialValueInEffect:n,headerRef:o,scrollViewportRef:a}),M=St(()=>{let y;r?y=r.map(P=>t.find(A=>A.accessor===P)).map(P=>{let A=u.find(z=>z.accessor===P?.accessor),_=P?.pinnable?A?.pinned:P?.pinned;return{...P,hidden:P?.hidden||!c.find(z=>z.accessor===P?.accessor)?.toggled,pinned:_}}):y=t;let v=y.filter(P=>P.pinned==="left"),H=y.filter(P=>!P.pinned),k=y.filter(P=>P.pinned==="right");return[...v,...H,...k].map(P=>{if(P?.accessor==="__selection__")return P;let A=P?.accessor,_=d.find(K=>A in K);if(!_)return P;let z=_[A];return z===void 0||z==="auto"||z==="initial"?P:{...P,width:z}})},[t,r,c,u,d]),g=St(()=>{let y=M.filter(v=>!v.hidden&&v.accessor!=="__selection__");return y.length===0?!1:y.every(v=>typeof v.width=="string"&&/px$/.test(v.width))},[M]),R=St(()=>{if(!g)return null;let y=0;for(let v of M){if(v.hidden||v.accessor==="__selection__")continue;let H=v.width;if(typeof H!="string")continue;let k=parseInt(H,10);Number.isNaN(k)||(y+=k)}return y>0?y:null},[g,M]);return{effectiveColumns:M,setColumnsOrder:l,columnsOrder:r,resetColumnsOrder:s,columnsToggle:c,setColumnsToggle:m,resetColumnsToggle:i,columnsPinning:u,setColumnsPinning:p,resetColumnsPinning:T,columnsWidth:d,setColumnsWidth:S,setColumnWidth:b,setMultipleColumnWidths:C,resetColumnsWidth:E,hasResizableColumns:D,allResizableWidthsInitial:L,measureAndSetColumnWidths:N,isResizing:f,isLocked:g,tableWidth:R,beginResize:h,endResize:w}};import{rem as Fa}from"@mantine/core";import{useCallback as $a,useEffect as xt,useRef as ee}from"react";import{useEffect as Ba,useLayoutEffect as Va}from"react";var be=typeof window<"u"?Va:Ba;import{useRef as Ga}from"react";function Rt(e){let t=Ga(e);return t.current=e,t}var un="--mantine-datatable-header-height",dn="--mantine-datatable-footer-height",mn="--mantine-datatable-selection-column-width",pn="--mantine-datatable-last-row-border-bottom";function X(e,t,n){e?.style.setProperty(t,n)}function Mt(e){let t=e.borderBoxSize?.[0]||e.contentBoxSize?.[0];return t?{width:t.inlineSize,height:t.blockSize}:{width:e.contentRect.width,height:e.contentRect.height}}function vt(e,t,n){if(e){let o=new ResizeObserver(a=>{let r=a[0];r&&t(Mt(r))});return o.observe(e),()=>{o.disconnect(),n()}}}function fn({scrollCallbacks:e,withRowBorders:t}){let n={root:ee(null),table:ee(null),scrollViewport:ee(null),header:ee(null),footer:ee(null),selectionColumnHeader:ee(null)},{root:o,table:a,scrollViewport:r,header:l,footer:s,selectionColumnHeader:c}=n,m=Rt({withRowBorders:t}),i=Rt(e),u=ee(()=>{}),p=ee(()=>{}),T=$a(d=>{i.current.onScroll?.(d),u.current()},[]);return xt(()=>vt(l.current,d=>{X(o.current,un,`${d.height}px`)},()=>X(o.current,un,"0")),[l.current]),xt(()=>vt(s.current,d=>{X(o.current,dn,`${d.height}px`)},()=>X(o.current,dn,"0")),[s.current]),xt(()=>vt(c.current,d=>{X(o.current,mn,`${d.width}px`)},()=>X(o.current,mn,"0")),[c.current]),be(()=>{if(typeof window>"u")return;let d={top:!0,bottom:!0,left:!0,right:!0},S={width:0,height:0},b={width:0,height:0};function C(f,h){let w=d[f];return d[f]=h,X(o.current,`--mantine-datatable-${f}-shadow-opacity`,h?"0":"1"),w}function E(){let f=S.height-b.height,h=f<0;X(o.current,"--mantine-datatable-footer-position",h?"relative":"sticky"),X(o.current,"--mantine-datatable-footer-bottom",h?`${f}px`:"0")}function D(){m.current.withRowBorders&&S.height{for(let h of f)switch(h.target.tagName){case"TABLE":{S=Mt(h);break}case"DIV":{b=Mt(h);break}}L(),E(),D()});return N.observe(a.current),N.observe(r.current),()=>{N.disconnect()}},[]),be(()=>{p.current()},[t]),{refs:n,onScroll:T}}import{useCallback as Xa,useEffect as Ua,useState as Qa}from"react";function bn({columns:e,theadRef:t,tbodyRef:n,selectionColumnHeaderRef:o,selectionVisible:a,pinFirstColumn:r,pinLastColumn:l}){let[s,c]=Qa(new Map),m=Xa(()=>{if(!r&&!l&&!e.some(g=>g.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=getComputedStyle(p).direction==="rtl",b=new Map;if(T)d.querySelectorAll("th[data-accessor]").forEach(g=>{let R=g.dataset.accessor;if(R){let y=g.getBoundingClientRect().width;y>0&&b.set(R,y)}});else{let g=e.filter(y=>!y.hidden);d.querySelectorAll("td:not(.mantine-datatable-row-selector-cell):not(.mantine-datatable-row-expansion-cell)").forEach((y,v)=>{if(g[v]){let H=y.getBoundingClientRect().width;H>0&&b.set(String(g[v].accessor),H)}})}let C=0;if(a){let g=o.current??p.querySelector(T?".mantine-datatable-header-selector-cell":".mantine-datatable-row-selector-cell");g&&(C=g.getBoundingClientRect().width)}let E=e.filter(g=>!g.hidden&&b.has(String(g.accessor))).map(g=>String(g.accessor)),D=new Map;for(let g of e){let R=String(g.accessor);E.includes(R)&&(g.pinned?D.set(R,g.pinned):r&&R===E[0]&&!D.has(R)?D.set(R,"left"):l&&R===E[E.length-1]&&!D.has(R)&&D.set(R,"right"))}let L=E.filter(g=>D.get(g)==="left"),N=E.filter(g=>D.get(g)==="right"),f=new Map,h=C;L.forEach((g,R)=>{f.set(g,{position:S?"right":"left",logicalSide:"left",offset:h,isBoundary:R===L.length-1}),h+=b.get(g)||0});let w=-.4,M=[...N].reverse();M.forEach((g,R)=>{f.set(g,{position:S?"left":"right",logicalSide:"right",offset:w,isBoundary:R===M.length-1}),w+=b.get(g)||0}),c(g=>{if(g.size!==f.size)return f;for(let[R,y]of f){let v=g.get(R);if(!v||v.position!==y.position||v.logicalSide!==y.logicalSide||v.offset!==y.offset||v.isBoundary!==y.isBoundary)return f}return g})},[e,a,t,n,o,r,l]);Ua(()=>{if(!r&&!l&&!e.some(b=>b.pinned))return;let p=t.current??n.current;if(!p)return;let T=!!t.current,d=T?p.querySelector("tr:last-of-type"):p.querySelector("tr:first-of-type");if(!d)return;let S=new ResizeObserver(m);return d.querySelectorAll(T?"th":"td").forEach(b=>{S.observe(b)}),()=>S.disconnect()},[t,n,m]),be(m,[m]);let i=[...s.values()].some(p=>p.logicalSide==="left"),u=[...s.values()].some(p=>p.logicalSide==="right");return{pinnedMap:s,hasLeftPinned:i,hasRightPinned:u}}import{useEffect as Ka,useState as ja}from"react";function gn(e){let[t,n]=ja(null),o=e?.join(":")||"";return Ka(()=>{n(null)},[o]),{lastSelectionChangeIndex:t,setLastSelectionChangeIndex:n}}import{useEffect as Ja,useRef as qa,useState as Za}from"react";function Ya(e,t){let n=()=>{t(e.map(a=>a.matches))},o=e.map(a=>{try{return a.addEventListener("change",n),()=>a.removeEventListener("change",n)}catch{return a.addListener(n),()=>a.removeListener(n)}});return()=>{o.forEach(a=>{a()})}}function er(e,t){return t||(typeof window<"u"&&"matchMedia"in window?e.map(n=>window.matchMedia(n).matches):e.map(()=>!1))}function Tn(e,t,{getInitialValueInEffect:n}={getInitialValueInEffect:!0}){let[o,a]=Za(n?t:er(e,t)),r=qa(null);return Ja(()=>{if("matchMedia"in window)return r.current=e.map(l=>window.matchMedia(l)),a(r.current.map(l=>l.matches)),Ya(r.current,l=>{a(l)})},[e]),o}import{useMantineTheme as tr}from"@mantine/core";import{useMemo as hn}from"react";function Cn(e){let t=tr(),n=hn(()=>e.map(a=>(typeof a=="function"?a(t):a)??""),[e,t]),o=hn(()=>e.map(()=>!0),[e]);return Tn(n,o)}import{useMantineTheme as nr}from"@mantine/core";import{useMediaQuery as or}from"@mantine/hooks";function te(e){let t=nr(),n=typeof e=="function"?e(t):e;return or(n||"",!0)}import{useState as ar}from"react";function yn({rowExpansion:e,records:t,idAccessor:n}){let o=[];if(e&&t){let{trigger:s,allowMultiple:c,initiallyExpanded:m}=e;t&&s==="always"?o=t.map(i=>B(i,n)):m&&(o=t.filter((i,u)=>m({record:i,index:u})).map(i=>B(i,n)),c||(o=[o[0]]))}let a,r,l=ar(o);if(e){let{expandable:s,trigger:c,allowMultiple:m,collapseProps:i,content:u}=e;e.expanded?{recordIds:a,onRecordIdsChange:r}=e.expanded:[a,r]=l;let p=T=>r?.(a.filter(d=>d!==B(T,n)));return{expandOnClick:c!=="always"&&c!=="never",isRowExpanded:T=>c==="always"?!0:a.includes(B(T,n)),isExpandable:({record:T,index:d})=>s?s({record:T,index:d}):!0,expandRow:T=>{let d=B(T,n);r?.(m?[...a,d]:[d])},collapseRow:p,collapseProps:i,content:({record:T,index:d})=>()=>u({record:T,index:d,collapse:()=>p(T)})}}}import{useTimeout as Pn}from"@mantine/hooks";import{useEffect as rr,useState as Dn}from"react";function wn(e,t){let[n,o]=Dn(e),[a,r]=Dn(e),l=Pn(()=>o(!0),0),s=Pn(()=>r(!1),t||200);return rr(()=>{e?(s.clear(),r(!0),l.start()):(l.clear(),o(!1),s.start())},[l,s,e]),{expanded:n,visible:a}}var we="mantine-datatable-nowrap",Se="mantine-datatable-ellipsis",Q="mantine-datatable-pointer-cursor",$e="mantine-datatable-context-menu-cursor",Sn="mantine-datatable-text-selection-disabled",ne="mantine-datatable-text-align-left",oe="mantine-datatable-text-align-center",ae="mantine-datatable-text-align-right";import{jsx as sr}from"react/jsx-runtime";function Rn({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,title:a,noWrap:r,ellipsis:l,textAlign:s,width:c}){return te(o)?sr(lr,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:ir({[we]:r||l,[Se]:l,[ne]:s==="left",[oe]:s==="center",[ae]:s==="right"},e),style:[{width:c,minWidth:c,maxWidth:c},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],children:a}):null}import{TableTh as cr}from"@mantine/core";import{jsx as ur}from"react/jsx-runtime";function xn({shadowVisible:e}){return ur(cr,{className:"mantine-datatable-footer-selector-placeholder-cell","data-shadow-visible":e||void 0})}import{jsx as kt,jsxs as fr}from"react/jsx-runtime";function vn({className:e,style:t,columns:n,defaultColumnProps:o,pinnedMap:a,selectionVisible:r,selectorCellShadowVisible:l,ref:s}){return kt(dr,{ref:s,className:pr("mantine-datatable-footer",e),style:t,children:fr(mr,{children:[r&&kt(xn,{shadowVisible:l}),n.map(({hidden:c,...m})=>{if(c)return null;let{accessor:i,visibleMediaQuery:u,textAlign:p,width:T,footer:d,footerClassName:S,footerStyle:b,noWrap:C,ellipsis:E}={...o,...m};return kt(Rn,{pinnedInfo:a.get(String(i)),className:S,style:b,visibleMediaQuery:u,textAlign:p,width:T,title:d,noWrap:C,ellipsis:E},i)})]})})}import{Checkbox as qr,Group as Zr,Popover as Yr,PopoverDropdown as el,PopoverTarget as tl,Stack as nl,TableThead as ol,TableTr as qn}from"@mantine/core";import al from"clsx";import{useState as rl}from"react";import{TableTh as br}from"@mantine/core";import gr from"clsx";import{useMemo as Xe}from"react";import{jsx as Tr}from"react/jsx-runtime";function Mn({group:{id:e,columns:t,groups:n,title:o,textAlign:a,className:r,style:l},pinnedInfo:s,maxDepth:c,currentDepth:m,previousGroups:i,isLastGroup:u,withColumnBorders:p=!1,totalTableColumns:T}){let d=Xe(()=>t&&t.length>0?t:n&&n.length>0?Ie([{id:e,columns:t,groups:n}]):[],[t,n,e]),S=Xe(()=>d.map(({visibleMediaQuery:h})=>h),[d]),b=Cn(S),C=Xe(()=>Fe({id:e,columns:t,groups:n},b),[e,t,n,b]),E=Xe(()=>i.reduce((h,w)=>h+Fe(w,b),0),[i,b]),L=n&&n.length>0?1:c-m,N=E+C0?Tr(br,{colSpan:C,rowSpan:L>1?L:void 0,"data-pinned":s?.position,"data-pinned-shadow":s?.isBoundary?s.position:void 0,className:gr("mantine-datatable-column-group-header-cell",{[ne]:a==="left",[oe]:a==="center",[ae]:a==="right","mantine-datatable-column-group-header-cell--needs-border":f},r),style:[l,s&&{position:"sticky",[s.position]:s.offset,overflow:"visible"}],children:o??De(e)}):null}import{ActionIcon as Qn,Box as Gr,Center as et,Flex as Fr,Group as $r,TableTh as Xr}from"@mantine/core";import tt from"clsx";import{useRef as Ur,useState as Qr}from"react";import{ActionIcon as yr,Popover as Pr,PopoverDropdown as Dr,PopoverTarget as wr}from"@mantine/core";import{useClickOutside as Sr,useDisclosure as Rr}from"@mantine/hooks";import{jsx as kn,jsxs as hr}from"react/jsx-runtime";function En(){return hr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[kn("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),kn("path",{d:"M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227z"})]})}import{jsx as Ln,jsxs as Cr}from"react/jsx-runtime";function In(){return Cr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ln("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ln("path",{d:"M20 3h-16a1 1 0 0 0 -1 1v2.227l.008 .223a3 3 0 0 0 .772 1.795l4.22 4.641v8.114a1 1 0 0 0 1.316 .949l6 -2l.108 -.043a1 1 0 0 0 .576 -.906v-6.586l4.121 -4.12a3 3 0 0 0 .879 -2.123v-2.171a1 1 0 0 0 -1 -1z",strokeWidth:"0",fill:"currentColor"})]})}import{jsx as Ue,jsxs as xr}from"react/jsx-runtime";function Nn({children:e,isActive:t,filterPopoverProps:n,filterPopoverDisableClickOutside:o}){let[a,{close:r,toggle:l}]=Rr(!1),s=t?In:En,c=Sr(r);return o&&(c=void 0),xr(Pr,{withArrow:!0,shadow:"md",trapFocus:!0,...n,opened:a,onClose:r,onDismiss:r,children:[Ue(wr,{children:Ue(yr,{className:"mantine-datatable-header-cell-filter-action-icon","data-active":t||void 0,size:"sm",variant:"default",onClick:m=>{m.preventDefault(),l()},onKeyDown:m=>m.stopPropagation(),children:Ue(s,{})})}),Ue(Dr,{ref:c,onClick:m=>m.stopPropagation(),onKeyDown:m=>m.stopPropagation(),children:typeof e=="function"?e({close:r}):e})]})}import{ActionIcon as qe,Center as Lr,Group as Ir,Popover as Lt}from"@mantine/core";import{useCallback as Nr,useEffect as Hr,useRef as Ar,useState as zn}from"react";import{jsx as Qe,jsxs as vr}from"react/jsx-runtime";function Hn(){return vr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Qe("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Qe("path",{d:"M5 12l14 0"}),Qe("path",{d:"M5 12l6 6"}),Qe("path",{d:"M5 12l6 -6"})]})}import{jsx as Ke,jsxs as Mr}from"react/jsx-runtime";function An(){return Mr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ke("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ke("path",{d:"M5 12l14 0"}),Ke("path",{d:"M19 12l-6 6"}),Ke("path",{d:"M19 12l-6 -6"})]})}import{jsx as je,jsxs as kr}from"react/jsx-runtime";function On(){return kr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[je("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),je("path",{d:"M9 4v6l-2 4v2h10v-2l-2-4v-6"}),je("path",{d:"M12 16v5"}),je("path",{d:"M8 4h8"})]})}import{jsx as Et,jsxs as Er}from"react/jsx-runtime";function Je(){return Er("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Et("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Et("path",{d:"M18 6l-12 12"}),Et("path",{d:"M6 6l12 12"})]})}import{jsx as U,jsxs as _n}from"react/jsx-runtime";function Wn({currentPinned:e,onPinChange:t}){let[n,o]=zn(!1),a=Ar(null),[r,l]=zn(!1),s=Nr(()=>{if(a.current){let p=getComputedStyle(a.current).direction;l(p==="rtl")}},[]);be(()=>{s()},[s]),Hr(()=>{n&&s()},[n,s]);let c=r?"right":"left",m=r?"left":"right",i=e===c,u=e===m;return _n(Lt,{opened:n,onChange:o,position:"bottom",withArrow:!0,shadow:"sm",children:[U(Lt.Target,{children:U(Lr,{ref:a,className:"mantine-datatable-header-cell-pinnable-icon",role:"button","aria-label":"Pin column",onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),children:U(qe,{size:"xs",variant:"light",onClick:p=>p.stopPropagation(),children:U(On,{})})})}),U(Lt.Dropdown,{onClick:p=>p.stopPropagation(),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),className:"mantine-datatable-pinnable-dropdown",children:_n(Ir,{gap:4,children:[U(qe,{size:"sm",variant:i?"filled":"light",onClick:()=>t(i?void 0:c),"aria-label":"Pin left",children:U(Hn,{})}),U(qe,{size:"sm",variant:u?"filled":"light",onClick:()=>t(u?void 0:m),"aria-label":"Pin right",children:U(An,{})}),e&&U(qe,{size:"sm",variant:"light",color:"red",onClick:()=>t(void 0),"aria-label":"Unpin",children:U(Je,{})})]})})]})}import{useDirection as Or}from"@mantine/core";import{useCallback as Bn,useRef as Vn,useState as zr}from"react";import{jsx as _r}from"react/jsx-runtime";var Gn=50,Fn=e=>{let{accessor:t,columnRef:n}=e,[o,a]=zr(!1),r=Vn(0),l=Vn({current:0,next:0}),{dir:s}=Or(),c=s==="rtl",{setMultipleColumnWidths:m,beginResize:i,endResize:u}=Pe(),p=Bn(d=>{if(d.preventDefault(),d.stopPropagation(),!n.current)return;let S=n.current,b=S.nextElementSibling;for(;b;){let f=b.getAttribute("data-accessor");if(f&&f!=="__selection__")break;b=b.nextElementSibling}if(!b)return;let C=b.getAttribute("data-accessor");if(!C)return;let E=S.offsetWidth,D=b.offsetWidth,P=S.getBoundingClientRect().width/E;Number.isFinite(P)&&P>0||(P=1),i(),a(!0),r.current=d.clientX,l.current={current:E,next:D};let L=f=>{let h=(f.clientX-r.current)/P;c&&(h=-h);let w=l.current.current-Gn,M=l.current.next-Gn,g=Math.max(-w,Math.min(h,M)),R=l.current.current+g,y=l.current.next-g;m([{accessor:t,width:`${R}px`},{accessor:C,width:`${y}px`}])},N=()=>{a(!1),document.body.style.cursor="initial",document.body.style.userSelect="initial",u(),document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",N)};document.body.style.cursor="col-resize",document.body.style.userSelect="none",document.addEventListener("mousemove",L),document.addEventListener("mouseup",N)},[t,n,c,m,i,u]),T=Bn(()=>{if(!n.current)return;let S=n.current.nextElementSibling,b=[{accessor:t,width:"auto"}];if(S){let C=S.getAttribute("data-accessor");C&&C!=="__selection__"&&b.push({accessor:C,width:"auto"})}m(b)},[t,n,m]);return _r("div",{className:"mantine-datatable-header-resizable-handle",style:{cursor:o?"col-resize":void 0},onMouseDown:p,onDoubleClick:T,onClick:d=>d.stopPropagation(),title:"Drag to resize column, double-click to reset"})};import{jsx as Ze,jsxs as Wr}from"react/jsx-runtime";function $n(){return Wr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ze("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ze("path",{d:"M8 7l4 -4l4 4"}),Ze("path",{d:"M8 17l4 4l4 -4"}),Ze("path",{d:"M12 3l0 18"})]})}import{jsx as Ye,jsxs as Br}from"react/jsx-runtime";function Xn(){return Br("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Ye("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Ye("path",{d:"M12 5l0 14"}),Ye("path",{d:"M16 9l-4 -4"}),Ye("path",{d:"M8 9l4 -4"})]})}import{jsx as ge,jsxs as Vr}from"react/jsx-runtime";function Un(){return Vr("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[ge("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),ge("path",{d:"M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"}),ge("path",{d:"M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"})]})}import{jsx as G,jsxs as It}from"react/jsx-runtime";function Kn({className:e,style:t,pinnedInfo:n,accessor:o,visibleMediaQuery:a,title:r,sortable:l,draggable:s,toggleable:c,pinnable:m,resizable:i,sortIcons:u,textAlign:p,width:T,sortStatus:d,onSortStatusChange:S,filter:b,filterPopoverProps:C,filterPopoverDisableClickOutside:E,filtering:D,sortKey:L}){let{setSourceColumn:N,setTargetColumn:f,swapColumns:h,columnsToggle:w,setColumnsToggle:M,columnsPinning:g,setColumnsPinning:R}=Pe(),[y,v]=Qr(!1),H=Ur(null),x=g?.find(O=>O.accessor===o)?.pinned;if(!te(a))return null;let P=r??De(o),A=typeof P=="string"?P:void 0,_=l&&S?O=>{O?.defaultPrevented||S({sortKey:L,columnAccessor:o,direction:d?.columnAccessor===o?d.direction==="asc"?"desc":"asc":d?.direction??"asc"})}:void 0,z=O=>{O.stopPropagation(),N(o),v(!1)},K=O=>{O.preventDefault(),f(o),v(!0)},le=()=>{f(o),v(!1),h()},ie=()=>{v(!0)},se=()=>{v(!1)},ce=O=>{O.stopPropagation(),M(w.map($=>$.accessor===o?{...$,toggled:!1}:$))},ue=O=>{R($=>$.map(j=>j.accessor===o?{...j,pinned:O}:j))};return It(Xr,{"data-accessor":o,"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:tt({"mantine-datatable-header-cell-sortable":l,"mantine-datatable-header-cell-toggleable":c,"mantine-datatable-header-cell-pinnable":m,"mantine-datatable-header-cell-resizable":i},e),style:[{width:T,...i?{}:{minWidth:T,maxWidth:T}},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],role:l?"button":void 0,tabIndex:l?0:void 0,onClick:_,onKeyDown:O=>O.key==="Enter"&&_?.(),ref:H,children:[It($r,{className:"mantine-datatable-header-cell-sortable-group",justify:"space-between",wrap:"nowrap",children:[It(Fr,{align:"center",w:"100%",className:tt({"mantine-datatable-header-cell-draggable":s,"mantine-datatable-header-cell-drag-over":y}),draggable:s,onDragStart:s?z:void 0,onDragEnter:s?ie:void 0,onDragOver:s?K:void 0,onDrop:s?le:void 0,onDragLeave:s?se:void 0,children:[s?G(et,{role:"img","aria-label":"Drag column",children:G(Qn,{className:"mantine-datatable-header-cell-draggable-action-icon",variant:"subtle",size:"xs",onClick:O=>{O.stopPropagation()},children:G(Un,{})})}):null,G(Gr,{className:tt("mantine-datatable-header-cell-sortable-text",{[ne]:p==="left",[oe]:p==="center",[ae]:p==="right"},we,Se),title:A,children:P})]}),m?G(Wn,{currentPinned:x,onPinChange:ue}):null,c?G(et,{className:"mantine-datatable-header-cell-toggleable-icon",role:"img","aria-label":"Toggle column",children:G(Qn,{size:"xs",variant:"light",onClick:ce,children:G(Je,{})})}):null,l||d?.columnAccessor===o?d?.columnAccessor===o?G(et,{className:tt("mantine-datatable-header-cell-sortable-icon",{"mantine-datatable-header-cell-sortable-icon-reversed":d.direction==="desc"}),role:"img","aria-label":`Sorted ${d.direction==="desc"?"descending":"ascending"}`,children:u?.sorted||G(Xn,{})}):G(et,{className:"mantine-datatable-header-cell-sortable-unsorted-icon",role:"img","aria-label":"Not sorted",children:u?.unsorted||G($n,{})}):null,b?G(Nn,{filterPopoverProps:C,isActive:!!D,filterPopoverDisableClickOutside:E,children:b}):null]}),i&&o!=="__selection__"?G(Fn,{accessor:o,columnRef:H}):null]})}import{Checkbox as Kr,TableTh as jr}from"@mantine/core";import Jr from"clsx";import{jsx as jn}from"react/jsx-runtime";function Jn({className:e,style:t,trigger:n,shadowVisible:o,checked:a,indeterminate:r,checkboxProps:l,onChange:s,rowSpan:c,ref:m}){let i=!l.disabled;return jn(jr,{ref:m,className:Jr("mantine-datatable-header-selector-cell",{[Q]:n==="cell"&&i},e),style:t,rowSpan:c,"data-shadow-visible":o||void 0,"data-accessor":"__selection__",onClick:n==="cell"&&i?s:void 0,children:jn(Kr,{classNames:i?{input:Q}:void 0,checked:a,indeterminate:r,onChange:s,...l,disabled:!(s||l.onChange)||l.disabled})})}import{jsx as re,jsxs as nt}from"react/jsx-runtime";function ll(e,t){let o=(e.columns??[]).filter(u=>!u.hidden).map(u=>String(u.accessor)),a=o.map(u=>t.get(u)).filter(Boolean);if(a.length===0||a.length!==o.length)return;let r=new Set(a.map(u=>u.logicalSide));if(r.size>1)return;let l=[...r][0],s=l==="left"?o[0]:o[o.length-1],c=t.get(s);if(!c)return;let m=l==="left"?o[o.length-1]:o[0],i=t.get(m);return{...c,isBoundary:i?.isBoundary??!1}}function Zn({selectionColumnHeaderRef:e,className:t,style:n,sortStatus:o,sortIcons:a,onSortStatusChange:r,columns:l,defaultColumnProps:s,groups:c,pinnedMap:m,selectionTrigger:i,selectionVisible:u,selectionChecked:p,selectionIndeterminate:T,onSelectionChange:d,selectionCheckboxProps:S,selectorCellShadowVisible:b,selectionColumnClassName:C,selectionColumnStyle:E,withColumnBorders:D=!1,ref:L}){let N=c?Pt(c):0,f=N>0?N+1:1,h=u?re(Jn,{ref:e,className:C,style:E,trigger:i,shadowVisible:b,checked:p,indeterminate:T,checkboxProps:S,onChange:d,rowSpan:c?f:void 0}):null,{columnsToggle:w,setColumnsToggle:M}=Pe(),[g,R]=rl(!1),y=l.some(k=>k.toggleable),v=y?Object.fromEntries(l.map(({accessor:k,title:x})=>[k,x??De(String(k))])):void 0,H=nt(ol,{className:al("mantine-datatable-header",t),style:n,ref:L,onContextMenu:y?k=>{k.preventDefault(),R(x=>!x)}:void 0,children:[c&&Array.from({length:N},(k,x)=>{let P=Dt(c,x);return nt(qn,{children:[x===0&&h,P.map((A,_)=>re(Mn,{group:A,pinnedInfo:ll(A,m),maxDepth:N,currentDepth:x,previousGroups:P.slice(0,_),isLastGroup:_===P.length-1,withColumnBorders:D,totalTableColumns:l.length},A.id))]},`group-depth-${x}`)}),nt(qn,{children:[!c&&h,l.map(({hidden:k,...x},P)=>{if(k)return null;let{accessor:A,visibleMediaQuery:_,textAlign:z,width:K,title:le,sortable:ie,draggable:se,toggleable:ce,pinnable:ue,resizable:O,titleClassName:$,titleStyle:j,filter:J,filterPopoverProps:it,filterPopoverDisableClickOutside:st,filtering:ct,sortKey:ut}={...s,...x};return re(Kn,{accessor:A,pinnedInfo:m.get(String(A)),className:$,style:j,visibleMediaQuery:_,textAlign:z,width:K,title:le,sortable:ie,draggable:se,toggleable:ce,pinnable:ue,resizable:O&&Pk.toggleable).map(k=>re(Zr,{children:re(qr,{classNames:{label:"mantine-datatable-header-column-toggle-checkbox-label"},size:"xs",label:v[k.accessor],checked:k.toggled,onChange:x=>{M(w.map(P=>P.accessor===k.accessor?{...P,toggled:x.currentTarget.checked}:P))}})},k.accessor))})})]}):H}import{Center as il,Loader as sl}from"@mantine/core";import cl from"clsx";import{jsx as Yn}from"react/jsx-runtime";function eo({fetching:e,customContent:t,backgroundBlur:n,size:o,type:a,color:r}){return Yn(il,{className:cl("mantine-datatable-loader",{"mantine-datatable-loader-fetching":e}),style:[{backdropFilter:n?`blur(${n}px)`:void 0}],children:e&&(t||Yn(sl,{size:o,type:a,color:r}))})}import{Box as hl,Pagination as Cl,rem as yl,Text as Pl}from"@mantine/core";import lo from"clsx";import{Button as dl,Group as ml,Menu as pl,MenuDropdown as fl,MenuItem as bl,MenuTarget as gl,rem as Ne,Text as no}from"@mantine/core";import Tl from"clsx";import{jsx as Nt,jsxs as ul}from"react/jsx-runtime";function to(){return ul("svg",{width:"14",height:"14",viewBox:"0 0 24 24",strokeWidth:"2",stroke:"currentColor",fill:"none",strokeLinecap:"round",strokeLinejoin:"round",children:[Nt("path",{stroke:"none",d:"M0 0h24v24H0z",fill:"none"}),Nt("path",{d:"M8 9l4 -4l4 4"}),Nt("path",{d:"M16 15l-4 4l-4 -4"})]})}import{jsx as Te,jsxs as ao}from"react/jsx-runtime";var oo={xs:Ne(22),sm:Ne(26),md:Ne(32),lg:Ne(38),xl:Ne(44)};function ro({size:e,label:t,values:n,value:o,activeTextColor:a,activeBackgroundColor:r,onChange:l}){return ao(ml,{gap:"xs",children:[Te(no,{component:"div",size:e,children:t}),ao(pl,{withinPortal:!0,withArrow:!0,offset:2,classNames:{arrow:"mantine-datatable-page-size-selector-menu-arrow"},children:[Te(gl,{children:Te(dl,{size:e,variant:"default",classNames:{section:"mantine-datatable-page-size-selector-button-icon"},rightSection:Te(to,{}),style:[{fontWeight:"normal"},s=>({height:oo[e],paddingLeft:s.spacing[e],paddingRight:s.spacing[e]})],children:o})}),Te(fl,{children:n.map(s=>{let c=s===o;return Te(bl,{className:Tl({"mantine-datatable-page-size-selector-active":c}),style:[{height:oo[e]},c&&(a||r)?m=>We({theme:m,paginationActiveTextColor:a,paginationActiveBackgroundColor:r}):void 0],disabled:c,onClick:()=>l(s),children:Te(no,{component:"div",size:e,children:s})},s)})})]})]})}import{Fragment as Dl,jsx as he,jsxs as wl}from"react/jsx-runtime";function io({className:e,style:t,fetching:n,page:o,onPageChange:a,paginationWithEdges:r,paginationWithControls:l,paginationActiveTextColor:s,paginationActiveBackgroundColor:c,paginationSize:m,loadingText:i,noRecordsText:u,paginationText:p,totalRecords:T,recordsPerPage:d,onRecordsPerPageChange:S,recordsPerPageLabel:b,recordsPerPageOptions:C,recordsLength:E,horizontalSpacing:D,paginationWrapBreakpoint:L,getPaginationControlProps:N,getPaginationItemProps:f,renderPagination:h}){let w;if(T){let x=(o-1)*d+1,P=x+(E||0)-1;w=p({from:x,to:P,totalRecords:T})}else w=n?i:u;let M=T&&d?Math.max(1,Math.ceil(T/d)):1,g=T?(o-1)*(d??0)+1:void 0,R=T?(g??1)+(E??0)-1:void 0,v=!te(({breakpoints:x})=>`(min-width: ${typeof L=="number"?`${yl(L)}rem`:x[L]||L})`),H={Text:x=>he(Pl,{component:"div",className:"mantine-datatable-pagination-text",size:m,...x,children:w}),PageSizeSelector:x=>C?he(ro,{activeTextColor:s,activeBackgroundColor:c,size:m,label:b,values:C,value:d,onChange:S,...x}):null,Pagination:x=>he(Cl,{classNames:{root:lo("mantine-datatable-pagination-pages",{"mantine-datatable-pagination-pages-fetching":n||!E}),control:"mantine-datatable-pagination-pages-control"},style:s||c?P=>We({theme:P,paginationActiveTextColor:s,paginationActiveBackgroundColor:c}):void 0,withEdges:r,withControls:l,value:o,onChange:a,size:m,total:M,getControlProps:N,getItemProps:f,...x})},k={state:{paginationSize:m,page:o,totalPages:M,totalRecords:T,recordsPerPage:d,recordsLength:E,fetching:n,from:g,to:R,isWrapped:v},actions:{setPage:x=>a?.(x),setRecordsPerPage:S?x=>S(x):void 0},Controls:H};return he(hl,{px:D??"xs",py:"xs",className:lo("mantine-datatable-pagination",e),style:[{flexDirection:v?"column":"row"},t],children:typeof h=="function"?h(k):wl(Dl,{children:[he(H.Text,{}),he(H.PageSizeSelector,{}),he(H.Pagination,{})]})})}import{TableTr as Hl}from"@mantine/core";import Al from"clsx";import{TableTd as Sl}from"@mantine/core";import Rl from"clsx";import{jsx as xl}from"react/jsx-runtime";function so({className:e,style:t,pinnedInfo:n,visibleMediaQuery:o,record:a,index:r,onClick:l,onDoubleClick:s,onContextMenu:c,noWrap:m,ellipsis:i,textAlign:u,width:p,accessor:T,render:d,defaultRender:S,customCellAttributes:b}){return te(o)?xl(Sl,{"data-pinned":n?.position,"data-pinned-shadow":n?.isBoundary?n.position:void 0,className:Rl({[we]:m||i,[Se]:i,[Q]:l||s,[$e]:c,[ne]:u==="left",[oe]:u==="center",[ae]:u==="right"},e),style:[{width:p,minWidth:p,maxWidth:p},t,n&&{position:"sticky",[n.position]:n.offset,overflow:"visible"}],onClick:l,onDoubleClick:s,onContextMenu:c,...b?.(a,r),children:d?d(a,r):S?S(a,r,T):yt(a,T)}):null}import{Collapse as vl,TableTd as Ml,TableTr as co}from"@mantine/core";import{Fragment as kl,jsx as He,jsxs as El}from"react/jsx-runtime";function uo({open:e,colSpan:t,content:n,collapseProps:o}){let{expanded:a,visible:r}=wn(e,o?.transitionDuration);return r?El(kl,{children:[He(co,{}),He(co,{children:He(Ml,{className:"mantine-datatable-row-expansion-cell",colSpan:t,children:He(vl,{expanded:a,...o,children:He("div",{className:"mantine-datatable-row-expansion-cell-content",children:n()})})})})]}):null}import{Checkbox as Ll,TableTd as Il}from"@mantine/core";import Nl from"clsx";import{jsx as mo}from"react/jsx-runtime";function po({className:e,style:t,record:n,index:o,trigger:a,onChange:r,withRightShadow:l,checkboxProps:s,getCheckboxProps:c,...m}){let i={...s,...c(n,o)},u=!m.disabled&&!i.disabled,p=T=>{T.stopPropagation(),a==="cell"&&u&&r?.(T)};return mo(Il,{className:Nl("mantine-datatable-row-selector-cell",{[Q]:a==="cell"&&u},e),style:t,"data-shadow-visible":l||void 0,onClick:p,children:mo(Ll,{classNames:u?{input:Q}:void 0,onChange:r,...m,...i})})}import{Fragment as fo,jsx as ot,jsxs as bo}from"react/jsx-runtime";function go({record:e,index:t,columns:n,defaultColumnProps:o,pinnedMap:a,defaultColumnRender:r,selectionTrigger:l,selectionVisible:s,selectionChecked:c,onSelectionChange:m,isRecordSelectable:i,selectionCheckboxProps:u,getSelectionCheckboxProps:p,onClick:T,onDoubleClick:d,onContextMenu:S,onCellClick:b,onCellDoubleClick:C,onCellContextMenu:E,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w,selectorCellShadowVisible:M,selectionColumnClassName:g,selectionColumnStyle:R,rowFactory:y}){let v=bo(fo,{children:[s&&ot(po,{className:g,style:R,record:e,index:t,trigger:l,withRightShadow:M,checked:c,disabled:!m||(i?!i(e,t):!1),onChange:m,checkboxProps:u,getCheckboxProps:p}),n.map(({hidden:x,hiddenContent:P,...A},_)=>{if(x||P)return null;let{accessor:z,visibleMediaQuery:K,textAlign:le,noWrap:ie,ellipsis:se,width:ce,render:ue,cellsClassName:O,cellsStyle:$,customCellAttributes:j}={...o,...A};return ot(so,{pinnedInfo:a.get(String(z)),className:typeof O=="function"?O(e,t):O,style:$?.(e,t),visibleMediaQuery:K,record:e,index:t,onClick:b?J=>b({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,onDoubleClick:C?J=>C({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,onContextMenu:E?J=>E({event:J,record:e,index:t,column:A,columnIndex:_}):void 0,accessor:z,textAlign:le,noWrap:ie,ellipsis:se,width:ce,render:ue,defaultRender:r,customCellAttributes:j},z)})]}),H=D&&ot(uo,{colSpan:n.filter(({hidden:x})=>!x).length+(s?1:0),open:D.isRowExpanded(e),content:D.content({record:e,index:t}),collapseProps:D.collapseProps}),k=Ol({record:e,index:t,selectionChecked:c,onClick:T,onDoubleClick:d,onContextMenu:S,expansion:D,customAttributes:L,color:N,backgroundColor:f,className:h,style:w});return y?y({record:e,index:t,rowProps:k,children:v,expandedElement:H}):bo(fo,{children:[ot(Hl,{...k,children:v}),H]})}function Ol({record:e,index:t,selectionChecked:n,onClick:o,onDoubleClick:a,onContextMenu:r,expansion:l,customAttributes:s,color:c,backgroundColor:m,className:i,style:u}){return{className:Al("mantine-datatable-row",{[Q]:o||a||l?.isExpandable({record:e,index:t})&&l?.expandOnClick},{[$e]:r},typeof i=="function"?i(e,t):i),"data-selected":n||void 0,onClick:p=>{if(l){let{isExpandable:T,isRowExpanded:d,expandOnClick:S,expandRow:b,collapseRow:C}=l;T({record:e,index:t})&&S&&(d(e)?C(e):b(e))}o?.({event:p,record:e,index:t})},onDoubleClick:a?p=>a({event:p,record:e,index:t}):void 0,onContextMenu:r?p=>r({event:p,record:e,index:t}):void 0,style:[c||m?p=>{let T=c?.(e,t),d=m?.(e,t);return $t({theme:p,color:T,backgroundColor:d})}:void 0,u?.(e,t)],...s?.(e,t)??{}}}import{Box as To,ScrollArea as zl}from"@mantine/core";import at from"clsx";import{jsx as rt,jsxs as _l}from"react/jsx-runtime";function ho({leftShadowBehind:e,rightShadowBehind:t,onScrollPositionChange:n,children:o,viewportRef:a,scrollAreaProps:r}){return _l(zl,{...r,viewportRef:a,classNames:{root:"mantine-datatable-scroll-area",scrollbar:"mantine-datatable-scroll-area-scrollbar",thumb:"mantine-datatable-scroll-area-thumb",corner:"mantine-datatable-scroll-area-corner"},onScrollPositionChange:n,children:[o,rt(To,{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-top-shadow")}),rt("div",{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-left-shadow",{"mantine-datatable-scroll-area-shadow-behind":e})}),rt("div",{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-right-shadow",{"mantine-datatable-scroll-area-shadow-behind":t})}),rt(To,{className:at("mantine-datatable-scroll-area-shadow","mantine-datatable-scroll-area-bottom-shadow")})]})}import{jsx as F,jsxs as yo}from"react/jsx-runtime";function $l({withTableBorder:e,borderRadius:t,textSelectionDisabled:n,height:o="100%",minHeight:a,maxHeight:r,shadow:l,verticalAlign:s="center",fetching:c,columns:m,storeColumnsKey:i,groups:u,pinFirstColumn:p,pinLastColumn:T,defaultColumnProps:d,defaultColumnRender:S,idAccessor:b="id",records:C,selectionTrigger:E="checkbox",selectedRecords:D,onSelectedRecordsChange:L,selectionColumnClassName:N,selectionColumnStyle:f,isRecordSelectable:h,selectionCheckboxProps:w,allRecordsSelectionCheckboxProps:M={"aria-label":"Select all records"},getRecordSelectionCheckboxProps:g=(de,V)=>({"aria-label":`Select record ${V+1}`}),sortStatus:R,sortIcons:y,onSortStatusChange:v,horizontalSpacing:H,page:k,onPageChange:x,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:_,recordsPerPageOptions:z,recordsPerPageLabel:K="Records per page",paginationWithEdges:le,paginationWithControls:ie,paginationActiveTextColor:se,paginationActiveBackgroundColor:ce,paginationSize:ue="sm",paginationText:O=({from:de,to:V,totalRecords:pt})=>`${de} - ${V} / ${pt}`,paginationWrapBreakpoint:$="sm",getPaginationControlProps:j=de=>de==="previous"?{"aria-label":"Previous page"}:de==="next"?{"aria-label":"Next page"}:{},getPaginationItemProps:J,renderPagination:it,loaderBackgroundBlur:st,customLoader:ct,loaderSize:ut,loaderType:Po,loaderColor:Do,loadingText:wo="...",emptyState:So,noRecordsText:Ot="No records",noRecordsIcon:Ro,highlightOnHover:xo,striped:vo,noHeader:Mo,onRowClick:ko,onRowDoubleClick:Eo,onRowContextMenu:Lo,onCellClick:Io,onCellDoubleClick:No,onCellContextMenu:Ho,onScroll:Ao,onScrollToTop:Oo,onScrollToBottom:zo,onScrollToLeft:_o,onScrollToRight:Wo,c:Bo,backgroundColor:Vo,borderColor:Go,rowBorderColor:Fo,stripedColor:$o,highlightOnHoverColor:Xo,rowColor:Uo,rowBackgroundColor:Qo,rowExpansion:Ko,rowClassName:jo,rowStyle:Jo,customRowAttributes:qo,scrollViewportRef:Zo,scrollAreaProps:Yo,tableRef:ea,bodyRef:ta,m:na,my:oa,mx:aa,mt:ra,mb:la,ml:ia,mr:sa,className:ca,classNames:Re,style:ua,styles:xe,rowFactory:da,tableWrapper:dt,...mt}){let de=Fl(()=>u?Ie(u):m,[m,u]),{refs:V,onScroll:pt}=fn({scrollCallbacks:{onScroll:Ao,onScrollToTop:Oo,onScrollToBottom:zo,onScrollToLeft:_o,onScrollToRight:Wo},withRowBorders:mt.withRowBorders}),q=cn({key:i,columns:de,headerRef:V.header,scrollViewportRef:V.scrollViewport}),ve=q.effectiveColumns,ma=Ht(V.table,ea),pa=Ht(V.scrollViewport,Zo),zt=lt(null),fa=Ht(zt,ta),ba=yn({rowExpansion:Ko,records:C,idAccessor:b}),{pinnedMap:Me,hasLeftPinned:_t,hasRightPinned:ga}=bn({columns:ve,theadRef:V.header,tbodyRef:zt,selectionColumnHeaderRef:V.selectionColumnHeader,selectionVisible:!!D,pinFirstColumn:p,pinLastColumn:T}),Ae=lt(!1),Wt=lt(k),ft=lt(C),Ta=At(I=>{Ae.current=!0,ft.current=C,x(I)},[x,C]);Vl(()=>{Wt.current!==k&&(Ae.current=!0,ft.current=C,Wt.current=k)},[k,C]);let ke=C?.length;Gl(()=>{if(!Ae.current||c||C===ft.current)return;let I=V.scrollViewport.current;if(!I)return;let me=requestAnimationFrame(()=>{I.scrollTo({top:0,left:0}),Ae.current=!1});return()=>cancelAnimationFrame(me)},[c,C,V.scrollViewport]);let Bt=C?.map(I=>B(I,b)),Ce=!!D,Ee=D?.map(I=>B(I,b)),Vt=Bt!==void 0&&Ee!==void 0&&Ee.length>0,bt=h?C?.filter(h):C,Oe=bt?.map(I=>B(I,b)),ze=Vt&&Oe.every(I=>Ee.includes(I)),ha=Vt&&Oe.some(I=>Ee.includes(I)),Ca=At(()=>{D&&L&&L(ze?D.filter(I=>!Oe.includes(B(I,b))):Ge([...D,...bt],I=>B(I,b)))},[ze,b,L,Oe,bt,D]),{lastSelectionChangeIndex:_e,setLastSelectionChangeIndex:ya}=gn(Bt),gt=Ce&&!_t,Pa={m:na,my:oa,mx:aa,mt:ra,mb:la,ml:ia,mr:sa},Da=At(({children:I})=>dt?dt({children:I}):I,[dt]);return F(Ct,{...q,pinnedMap:Me,children:yo(Wl,{ref:V.root,...Pa,className:Co("mantine-datatable",{"mantine-datatable-with-border":e},ca,Re?.root),style:[I=>({...Ft({theme:I,c:Bo,backgroundColor:Vo,borderColor:Go,rowBorderColor:Fo,stripedColor:$o,highlightOnHoverColor:Xo}),borderRadius:I.radius[t]||t,boxShadow:I.shadows[l]||l,height:o,minHeight:a,maxHeight:r}),ua,xe?.root,{position:"relative"}],children:[F(ho,{viewportRef:pa,leftShadowBehind:Ce||_t,rightShadowBehind:ga,onScrollPositionChange:pt,scrollAreaProps:Yo,children:F(Da,{children:yo(Bl,{ref:ma,horizontalSpacing:H,className:Co("mantine-datatable-table",{[Sn]:n,"mantine-datatable-vertical-align-top":s==="top","mantine-datatable-vertical-align-bottom":s==="bottom","mantine-datatable-selection-column-visible":Ce,"mantine-datatable-resizable-columns":q.hasResizableColumns,"mantine-datatable-resize-locked":q.isLocked,"mantine-datatable-resizing":q.isResizing},Re?.table),style:{...xe?.table,...q.isLocked?{tableLayout:"fixed"}:null,...q.tableWidth!=null?{width:`${q.tableWidth}px`}:null},"data-striped":ke&&vo||void 0,"data-highlight-on-hover":xo||void 0,...mt,children:[Mo?null:F(Ct,{...q,pinnedMap:Me,children:F(Zn,{ref:V.header,selectionColumnHeaderRef:V.selectionColumnHeader,className:Re?.header,style:xe?.header,columns:ve,defaultColumnProps:d,groups:u,pinnedMap:Me,sortStatus:R,sortIcons:y,onSortStatusChange:v,selectionTrigger:E,selectionVisible:Ce,selectionChecked:ze,selectionIndeterminate:ha&&!ze,onSelectionChange:Ca,selectionCheckboxProps:{...w,...M},selectorCellShadowVisible:gt,selectionColumnClassName:N,selectionColumnStyle:f,withColumnBorders:mt.withColumnBorders})}),F("tbody",{ref:fa,children:ke?C.map((I,me)=>{let Tt=B(I,b),ht=Ee?.includes(Tt)||!1,Gt;return L&&D&&(Gt=wa=>{if(wa.nativeEvent.shiftKey&&_e!==null){let ye=C.filter(me>_e?(pe,fe)=>fe>=_e&&fe<=me&&(h?h(pe,fe):!0):(pe,fe)=>fe>=me&&fe<=_e&&(h?h(pe,fe):!0));L(ht?qt(D,ye,pe=>B(pe,b)):Ge([...D,...ye],pe=>B(pe,b)))}else L(ht?D.filter(ye=>B(ye,b)!==Tt):Ge([...D,I],ye=>B(ye,b)));ya(me)}),F(go,{record:I,index:me,columns:ve,defaultColumnProps:d,pinnedMap:Me,defaultColumnRender:S,selectionTrigger:E,selectionVisible:Ce,selectionChecked:ht,onSelectionChange:Gt,isRecordSelectable:h,selectionCheckboxProps:w,getSelectionCheckboxProps:g,onClick:ko,onDoubleClick:Eo,onCellClick:Io,onCellDoubleClick:No,onContextMenu:Lo,onCellContextMenu:Ho,expansion:ba,color:Uo,backgroundColor:Qo,className:jo,style:Jo,customAttributes:qo,selectorCellShadowVisible:gt,selectionColumnClassName:N,selectionColumnStyle:f,idAccessor:b,rowFactory:da},Tt)}):F(Kt,{})}),ve.some(({footer:I})=>I)&&F(vn,{ref:V.footer,className:Re?.footer,style:xe?.footer,columns:ve,defaultColumnProps:d,pinnedMap:Me,selectionVisible:Ce,selectorCellShadowVisible:gt})]})})}),!!(k&&ke)&&F(io,{className:Re?.pagination,style:xe?.pagination,horizontalSpacing:H,fetching:c,page:k,onPageChange:Ta,totalRecords:P,recordsPerPage:A,onRecordsPerPageChange:_,recordsPerPageOptions:z,recordsPerPageLabel:K,paginationWithEdges:le,paginationWithControls:ie,paginationActiveTextColor:se,paginationActiveBackgroundColor:ce,paginationSize:ue,paginationText:O,paginationWrapBreakpoint:$,getPaginationControlProps:j,getPaginationItemProps:J,noRecordsText:Ot,loadingText:wo,recordsLength:ke,renderPagination:it}),F(eo,{fetching:c,backgroundBlur:st,customContent:ct,size:ut,type:Po,color:Do}),F(Jt,{icon:Ro,text:Ot,active:!c&&!ke,children:So})]})})}import{TableTr as Xl}from"@mantine/core";import{useMergedRef as Ul}from"@mantine/hooks";import{useEffect as Ql,useRef as Kl}from"react";import{jsx as Jl}from"react/jsx-runtime";function jl({className:e,children:t,isDragging:n,ref:o,...a}){let r=Kl(null),l=Ul(r,o);return Ql(()=>{if(!r.current||!n)return;let i=r.current.parentElement.parentElement.children[0].children[0];for(let u=0;u=5.0.0 <=5.4.18": ">=8.1.4" patchedDependencies: "@types/node-unifi": "patches/@types__node-unifi.patch" + mantine-datatable@9.3.1: patches/mantine-datatable@9.3.1.patch "trpc-to-openapi": "patches/trpc-to-openapi.patch" allowUnusedPatches: true From 64e3d045179f59d51af5487d5c2e323ac320cf9b Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Aug 2026 15:55:38 +0200 Subject: [PATCH 07/11] fix(calendar): improve event indicators (#6652) --- apps/docs/docs/widgets/calendar/index.mdx | 1 + .../widgets/src/calendar/calender-day.tsx | 39 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/apps/docs/docs/widgets/calendar/index.mdx b/apps/docs/docs/widgets/calendar/index.mdx index 57ca565fe..82d8a1cbd 100644 --- a/apps/docs/docs/widgets/calendar/index.mdx +++ b/apps/docs/docs/widgets/calendar/index.mdx @@ -69,6 +69,7 @@ import advancedView from "./img/advanced-view.png"; ### Interactions +- Colored dots directly below a date mark its events. They scale with the widget and are hidden when the calendar is too short. - Hover over a marked date to preview its events in a popover. - The advanced view replaces the month grid with a grouped event list, keeps every event attributed to its source integration, and expands titles, locations, and descriptions. Use its previous and next controls to change months. - If one selected integration is unavailable, its warning indicator remains visible while events from healthy integrations continue to load. diff --git a/packages/widgets/src/calendar/calender-day.tsx b/packages/widgets/src/calendar/calender-day.tsx index 75fb09570..679d4e9f6 100644 --- a/packages/widgets/src/calendar/calender-day.tsx +++ b/packages/widgets/src/calendar/calender-day.tsx @@ -20,27 +20,29 @@ export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: C const minAxisSize = Math.min(rootWidth, rootHeight); const shouldScaleDown = minAxisSize < 350; - const isSmall = rootHeight < 256; + const shouldShowIndicators = rootHeight >= 256; + const indicatorSize = shouldScaleDown ? 3 : 4; const cell = ( {date.getDate()} - + ); @@ -67,25 +69,34 @@ export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: C interface NotificationIndicatorProps { events: CalendarEvent[]; - isSmall: boolean; + size: number; + visible: boolean; } -const NotificationIndicator = ({ events, isSmall }: NotificationIndicatorProps) => { - const notificationEvents = [...new Set(events.map((event) => event.indicatorColor))].filter(String); +const NotificationIndicator = ({ events, size, visible }: NotificationIndicatorProps) => { + const notificationEvents = [...new Set(events.map((event) => event.indicatorColor))].filter( + (color): color is string => Boolean(color), + ); + + if (!visible) return null; + return ( {notificationEvents.map((notificationEvent) => { - return ; + return ( + + ); })} ); From 3c2f95d5295ce9b540982f61a1827f8b1b73a1ae Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 21 Aug 2026 16:17:42 +0200 Subject: [PATCH 08/11] perf(board): stabilize edit mode layout (#6650) --- .../app/[locale]/boards/(content)/_client.tsx | 3 +- .../components/board/items/item-content.tsx | 15 +- .../board/sections/container-section.tsx | 6 +- .../grid/board-grid-editor-boundary.tsx | 55 +-- .../board/sections/grid/fixed-grid-item.tsx | 53 +-- .../board/sections/grid/grid-editor-loader.ts | 16 +- .../sections/grid/grid-editor-registry.tsx | 117 +++++++ .../board/sections/grid/grid-editor.css | 24 +- .../board/sections/grid/grid-editor.tsx | 123 +++---- .../board/sections/grid/grid-entry-memo.ts | 4 +- .../board/sections/grid/grid-portal-host.tsx | 221 +------------ .../sections/grid/section-grid.module.css | 39 +-- .../board/sections/grid/section-grid.tsx | 313 +++++++++++------- .../grid/test/fixed-grid-item.spec.ts | 12 + .../grid/test/grid-entry-memo.spec.tsx | 13 +- .../grid/test/grid-import-boundary.spec.ts | 18 +- .../grid/test/grid-portal-host.spec.ts | 104 ------ .../board/sections/section-collapse.tsx | 67 +++- .../board/sections/section-context.ts | 5 + .../board/sections/use-section-items.ts | 88 ++--- 20 files changed, 616 insertions(+), 680 deletions(-) create mode 100644 apps/nextjs/src/components/board/sections/grid/grid-editor-registry.tsx delete mode 100644 apps/nextjs/src/components/board/sections/grid/test/grid-portal-host.spec.ts diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx index 9fa4b7c5d..efd9be8fb 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_client.tsx @@ -20,7 +20,7 @@ import { import { ScaledBoardCanvas } from "~/components/board/layout/scaled-board-canvas"; import { BoardEmptySection } from "~/components/board/sections/empty-section"; import { BoardGridEditorBoundary } from "~/components/board/sections/grid/board-grid-editor-boundary"; -import { BoardGridPortalHost, BoardGridPortalRenderer } from "~/components/board/sections/grid/grid-portal-host"; +import { BoardGridPortalHost } from "~/components/board/sections/grid/grid-portal-host"; import { BoardSectionCollapseProvider } from "~/components/board/sections/section-collapse"; import { BoardBackgroundVideo } from "~/components/layout/background"; import { BoardSelectionProvider } from "~/components/board/selection/board-selection-context"; @@ -80,7 +80,6 @@ export const ClientBoard = () => { label={board.name} > -
{leftColumnCount > 0 && leftSection && (