@withpotter/components 0.13.0-next.124 → 0.13.0-next.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/billing/overview-tab.js +3 -4
  2. package/dist/billing/overview-tab.js.map +1 -1
  3. package/dist/billing/plans-tab.js +12 -6
  4. package/dist/billing/plans-tab.js.map +1 -1
  5. package/dist/charts/index.d.ts +1 -1
  6. package/dist/charts/index.js +1 -3
  7. package/dist/charts/index.js.map +1 -1
  8. package/dist/dashboard/activity-feed.d.ts +0 -2
  9. package/dist/dashboard/activity-feed.js +1 -4
  10. package/dist/dashboard/activity-feed.js.map +1 -1
  11. package/dist/dashboard/home/ask-controls.js +0 -1
  12. package/dist/dashboard/home/ask-controls.js.map +1 -1
  13. package/dist/dashboard/home/ask-mode-chip.js +4 -4
  14. package/dist/dashboard/home/ask-mode-chip.js.map +1 -1
  15. package/dist/dashboard/home/home-ask.js +3 -3
  16. package/dist/dashboard/home/home-ask.js.map +1 -1
  17. package/dist/dashboard/home/home-quick-actions.js +1 -9
  18. package/dist/dashboard/home/home-quick-actions.js.map +1 -1
  19. package/dist/dashboard/home/widgets/activity-widget.js +43 -56
  20. package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
  21. package/dist/dashboard/home/widgets/home-widgets.js +2 -3
  22. package/dist/dashboard/home/widgets/home-widgets.js.map +1 -1
  23. package/dist/dashboard/home/widgets/money-widgets.js +4 -9
  24. package/dist/dashboard/home/widgets/money-widgets.js.map +1 -1
  25. package/dist/dashboard/home/widgets/registry.d.ts +3 -6
  26. package/dist/dashboard/home/widgets/registry.js +2 -12
  27. package/dist/dashboard/home/widgets/registry.js.map +1 -1
  28. package/dist/dashboard/home/widgets/team-widget.js +3 -15
  29. package/dist/dashboard/home/widgets/team-widget.js.map +1 -1
  30. package/dist/dashboard/home/widgets/types.d.ts +1 -13
  31. package/dist/dashboard/home/widgets/types.js.map +1 -1
  32. package/dist/dashboard/home/widgets/use-home-layout.js +4 -9
  33. package/dist/dashboard/home/widgets/use-home-layout.js.map +1 -1
  34. package/dist/dashboard/home/widgets/website-widget.js +1 -9
  35. package/dist/dashboard/home/widgets/website-widget.js.map +1 -1
  36. package/dist/dashboard/page-transition.d.ts +0 -11
  37. package/dist/dashboard/page-transition.js +2 -9
  38. package/dist/dashboard/page-transition.js.map +1 -1
  39. package/dist/features/gift-card-artwork.js +1 -1
  40. package/dist/features/gift-card-artwork.js.map +1 -1
  41. package/dist/features/gift-cards/gift-cards-feature.js +518 -656
  42. package/dist/features/gift-cards/gift-cards-feature.js.map +1 -1
  43. package/dist/features/product-form.js +1 -1
  44. package/dist/features/product-form.js.map +1 -1
  45. package/dist/mobile/screens/billing-flows.js +6 -7
  46. package/dist/mobile/screens/billing-flows.js.map +1 -1
  47. package/dist/mobile/screens/billing.js +2 -2
  48. package/dist/mobile/screens/billing.js.map +1 -1
  49. package/dist/notifications-dropdown.js +1 -11
  50. package/dist/notifications-dropdown.js.map +1 -1
  51. package/dist/shell/notification-kinds.d.ts +2 -11
  52. package/dist/shell/notification-kinds.js +1 -16
  53. package/dist/shell/notification-kinds.js.map +1 -1
  54. package/dist/shell/notification-row.d.ts +2 -6
  55. package/dist/shell/notification-row.js +33 -59
  56. package/dist/shell/notification-row.js.map +1 -1
  57. package/dist/shell/product-switcher.js +0 -3
  58. package/dist/shell/product-switcher.js.map +1 -1
  59. package/dist/shell/settings-modal-context.d.ts +0 -2
  60. package/dist/shell/settings-modal-context.js +2 -4
  61. package/dist/shell/settings-modal-context.js.map +1 -1
  62. package/dist/shell/settings-panels.js +9 -29
  63. package/dist/shell/settings-panels.js.map +1 -1
  64. package/dist/shell/settings-rows.d.ts +2 -7
  65. package/dist/shell/settings-rows.js +36 -93
  66. package/dist/shell/settings-rows.js.map +1 -1
  67. package/dist/shell/shell.css +0 -192
  68. package/dist/shell/sidebar-footer.js +2 -6
  69. package/dist/shell/sidebar-footer.js.map +1 -1
  70. package/dist/shell/unified-settings-modal.d.ts +2 -3
  71. package/dist/shell/unified-settings-modal.js +74 -151
  72. package/dist/shell/unified-settings-modal.js.map +1 -1
  73. package/dist/sidebar/config.js +3 -19
  74. package/dist/sidebar/config.js.map +1 -1
  75. package/dist/sidebar/more-group.js +1 -1
  76. package/dist/sidebar/more-group.js.map +1 -1
  77. package/dist/sidebar/nav-renderers.js +0 -2
  78. package/dist/sidebar/nav-renderers.js.map +1 -1
  79. package/dist/sidebar/nav-section.js +3 -5
  80. package/dist/sidebar/nav-section.js.map +1 -1
  81. package/dist/sidebar/setup-progress.d.ts +1 -3
  82. package/dist/sidebar/setup-progress.js +2 -2
  83. package/dist/sidebar/setup-progress.js.map +1 -1
  84. package/dist/sidebar/spine-groups.js +1 -7
  85. package/dist/sidebar/spine-groups.js.map +1 -1
  86. package/dist/sidebar.js +5 -5
  87. package/dist/sidebar.js.map +1 -1
  88. package/dist/templates/template-live-thumb.js +5 -16
  89. package/dist/templates/template-live-thumb.js.map +1 -1
  90. package/package.json +5 -5
  91. package/dist/dashboard/activity-paid.d.ts +0 -10
  92. package/dist/dashboard/activity-paid.js +0 -7
  93. package/dist/dashboard/activity-paid.js.map +0 -1
  94. package/dist/dashboard/home/host-quick-actions.d.ts +0 -19
  95. package/dist/dashboard/home/host-quick-actions.js +0 -12
  96. package/dist/dashboard/home/host-quick-actions.js.map +0 -1
  97. package/dist/dashboard/home/widgets/host-widgets.d.ts +0 -6
  98. package/dist/dashboard/home/widgets/host-widgets.js +0 -16
  99. package/dist/dashboard/home/widgets/host-widgets.js.map +0 -1
  100. package/dist/dashboard/home/widgets/member-label.d.ts +0 -11
  101. package/dist/dashboard/home/widgets/member-label.js +0 -10
  102. package/dist/dashboard/home/widgets/member-label.js.map +0 -1
  103. package/dist/dashboard/home/widgets/middle-ellipsis.d.ts +0 -5
  104. package/dist/dashboard/home/widgets/middle-ellipsis.js +0 -11
  105. package/dist/dashboard/home/widgets/middle-ellipsis.js.map +0 -1
  106. package/dist/dashboard/home/widgets/revenue-series.d.ts +0 -12
  107. package/dist/dashboard/home/widgets/revenue-series.js +0 -26
  108. package/dist/dashboard/home/widgets/revenue-series.js.map +0 -1
  109. package/dist/dashboard/page-transition-key.d.ts +0 -7
  110. package/dist/dashboard/page-transition-key.js +0 -11
  111. package/dist/dashboard/page-transition-key.js.map +0 -1
  112. package/dist/features/gift-cards/gift-card-logic.d.ts +0 -41
  113. package/dist/features/gift-cards/gift-card-logic.js +0 -65
  114. package/dist/features/gift-cards/gift-card-logic.js.map +0 -1
  115. package/dist/features/gift-cards/hint.d.ts +0 -9
  116. package/dist/features/gift-cards/hint.js +0 -25
  117. package/dist/features/gift-cards/hint.js.map +0 -1
  118. package/dist/shell/notification-actions.d.ts +0 -12
  119. package/dist/shell/notification-actions.js +0 -18
  120. package/dist/shell/notification-actions.js.map +0 -1
  121. package/dist/shell/settings-groups.d.ts +0 -23
  122. package/dist/shell/settings-groups.js +0 -22
  123. package/dist/shell/settings-groups.js.map +0 -1
  124. package/dist/shell/settings-host.d.ts +0 -39
  125. package/dist/shell/settings-host.js +0 -37
  126. package/dist/shell/settings-host.js.map +0 -1
  127. package/dist/shell/sidebar-cards.d.ts +0 -32
  128. package/dist/shell/sidebar-cards.js +0 -37
  129. package/dist/shell/sidebar-cards.js.map +0 -1
  130. package/dist/templates/template-tile-source.d.ts +0 -24
  131. package/dist/templates/template-tile-source.js +0 -18
  132. package/dist/templates/template-tile-source.js.map +0 -1
@@ -20,8 +20,6 @@ const isSettingsPath = (path) => !!path && (path === SETTINGS_PREFIX || path.sta
20
20
  const SettingsModalContext = createContext({
21
21
  openSettings: () => {
22
22
  },
23
- closeSettings: () => {
24
- },
25
23
  prewarmSettings: () => {
26
24
  }
27
25
  });
@@ -117,8 +115,8 @@ function SettingsModalProvider({ children }) {
117
115
  router.replace(lastOutside.current);
118
116
  }, [pathname, openSettings, router]);
119
117
  const api = useMemo(
120
- () => ({ openSettings, closeSettings: close, prewarmSettings }),
121
- [openSettings, close, prewarmSettings]
118
+ () => ({ openSettings, prewarmSettings }),
119
+ [openSettings, prewarmSettings]
122
120
  );
123
121
  return /* @__PURE__ */ jsxs(SettingsModalContext.Provider, { value: api, children: [
124
122
  children,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/settings-modal-context.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport { isEmbeddedFrame } from '@withpotter/lib/embedded-frame';\nimport { isMobileViewport } from '@withpotter/hooks/use-mobile';\nimport { UnifiedSettingsModal } from './unified-settings-modal';\nimport { movedSettingsTarget } from './moved-settings';\n\nconst SETTINGS_PREFIX = '/dashboard/settings';\n\n/** `/dashboard/settings` and anything under it, but not `/dashboard/settingsX`. */\nconst isSettingsPath = (path: string | null | undefined) =>\n !!path &&\n (path === SETTINGS_PREFIX || path.startsWith(`${SETTINGS_PREFIX}/`));\n\ninterface SettingsModalApi {\n /** Open the settings window, on a section when given a settings address. */\n openSettings: (href?: string) => void;\n /** Close it, for a screen inside it that leaves for another page. */\n closeSettings: () => void;\n /**\n * Warm the window before it is opened: a hidden copy of the settings page\n * loads once so the real one boots with its code fetched and its route\n * compiled. Cheap to call repeatedly; only the first call in a page load does\n * anything. Call it on the signals that precede opening (a menu opening, a\n * pointer arriving on a settings link).\n */\n prewarmSettings: () => void;\n}\n\nconst SettingsModalContext = createContext<SettingsModalApi>({\n openSettings: () => {},\n closeSettings: () => {},\n prewarmSettings: () => {},\n});\n\nexport const useSettingsModal = () => useContext(SettingsModalContext);\n\n/**\n * Settings is a window, never a page.\n *\n * The workspace menu always opened it as one; everything else that pointed at a\n * settings address (a setup step, the Customize sidebar row, a link in an email,\n * a typed URL) still navigated to the page. This owns the one modal and makes\n * every road into settings arrive in it:\n *\n * - a CLICK on a link to a settings address is caught before it navigates and\n * opens the window on that section;\n * - anything else that lands on a settings route (`router.push`, a pasted\n * address, back/forward) is sent back to where you were and the window opens\n * on it, with the page itself never drawn (see the layout).\n *\n * The framed copy of a page is the one place a settings route is allowed to\n * render, so nothing here runs inside it.\n */\nexport function SettingsModalProvider({ children }: { children: ReactNode }) {\n const router = useRouter();\n const pathname = usePathname();\n const [state, setState] = useState<{ open: boolean; href: string | null }>({\n open: false,\n href: null,\n });\n\n // The window is for a desktop screen. On a phone settings stays what it was:\n // pages you go to, with the mobile shell around them.\n const openSettings = useCallback(\n (href?: string) => {\n // An address that moved out of settings goes to its page, not the window.\n const moved = href\n ? movedSettingsTarget(\n href.split(/[?#]/)[0],\n href.includes('?') ? `?${href.split('?')[1].split('#')[0]}` : '',\n )\n : null;\n if (moved) {\n router.push(moved);\n return;\n }\n if (isMobileViewport()) {\n router.push(href ?? '/dashboard/settings');\n return;\n }\n setState({ open: true, href: href ?? null });\n },\n [router],\n );\n // Kept so callers that warm the window on hover still compile: the window no\n // longer boots a frame, so there is nothing to warm.\n const prewarmSettings = useCallback(() => {}, []);\n\n const close = useCallback(() => setState((s) => ({ ...s, open: false })), []);\n\n // Where you were before settings, so closing leaves you there.\n const lastOutside = useRef('/dashboard');\n useEffect(() => {\n if (!isSettingsPath(pathname) && pathname) lastOutside.current = pathname;\n }, [pathname]);\n\n // A click on a link to settings: open the window instead of going there.\n useEffect(() => {\n if (isEmbeddedFrame()) return;\n const onClick = (e: MouseEvent) => {\n if (isMobileViewport()) return;\n if (e.defaultPrevented || e.button !== 0) return;\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n const a = (e.target as Element | null)?.closest?.('a[href]');\n if (!a || a.getAttribute('target') === '_blank') return;\n let url: URL;\n try {\n url = new URL(a.getAttribute('href') ?? '', window.location.href);\n } catch {\n return;\n }\n if (url.origin !== window.location.origin) return;\n if (!isSettingsPath(url.pathname)) return;\n const moved = movedSettingsTarget(url.pathname, url.search);\n if (moved) {\n e.preventDefault();\n router.push(moved);\n return;\n }\n // Not stopped: the link's own handlers still run (a dialog closing as you\n // leave it, say). Next skips its navigation for a prevented click.\n e.preventDefault();\n openSettings(url.pathname + url.search + url.hash);\n };\n // Capture, so this runs before the framework's own link handling.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [openSettings, router]);\n\n // A pointer or keyboard focus arriving on a link to settings is the last\n // moment before the click; start warming then.\n useEffect(() => {\n if (isEmbeddedFrame()) return;\n const onNear = (e: Event) => {\n const a = (e.target as Element | null)?.closest?.('a[href]');\n const href = a?.getAttribute('href');\n const path = href?.split(/[?#]/)[0];\n if (path && isSettingsPath(path) && !movedSettingsTarget(path)) {\n prewarmSettings();\n }\n };\n document.addEventListener('pointerover', onNear, true);\n document.addEventListener('focusin', onNear, true);\n return () => {\n document.removeEventListener('pointerover', onNear, true);\n document.removeEventListener('focusin', onNear, true);\n };\n }, [prewarmSettings]);\n\n // Anything else that lands on a settings route: back to where you were, with\n // the window open on it. Before paint, so the page is never seen.\n useLayoutEffect(() => {\n if (isEmbeddedFrame() || isMobileViewport()) return;\n if (!isSettingsPath(pathname)) return;\n const moved = movedSettingsTarget(pathname, window.location.search);\n if (moved) {\n router.replace(moved);\n return;\n }\n // eslint-disable-next-line react-hooks/set-state-in-effect -- before paint, so the page is never drawn\n openSettings(\n pathname +\n (typeof window !== 'undefined'\n ? window.location.search + window.location.hash\n : ''),\n );\n router.replace(lastOutside.current);\n }, [pathname, openSettings, router]);\n\n const api = useMemo(\n () => ({ openSettings, closeSettings: close, prewarmSettings }),\n [openSettings, close, prewarmSettings],\n );\n\n return (\n <SettingsModalContext.Provider value={api}>\n {children}\n <UnifiedSettingsModal\n open={state.open}\n href={state.href}\n onClose={close}\n />\n </SettingsModalContext.Provider>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/shell/settings-modal-context.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport { isEmbeddedFrame } from '@withpotter/lib/embedded-frame';\nimport { isMobileViewport } from '@withpotter/hooks/use-mobile';\nimport { UnifiedSettingsModal } from './unified-settings-modal';\nimport { movedSettingsTarget } from './moved-settings';\n\nconst SETTINGS_PREFIX = '/dashboard/settings';\n\n/** `/dashboard/settings` and anything under it, but not `/dashboard/settingsX`. */\nconst isSettingsPath = (path: string | null | undefined) =>\n !!path &&\n (path === SETTINGS_PREFIX || path.startsWith(`${SETTINGS_PREFIX}/`));\n\ninterface SettingsModalApi {\n /** Open the settings window, on a section when given a settings address. */\n openSettings: (href?: string) => void;\n /**\n * Warm the window before it is opened: a hidden copy of the settings page\n * loads once so the real one boots with its code fetched and its route\n * compiled. Cheap to call repeatedly; only the first call in a page load does\n * anything. Call it on the signals that precede opening (a menu opening, a\n * pointer arriving on a settings link).\n */\n prewarmSettings: () => void;\n}\n\nconst SettingsModalContext = createContext<SettingsModalApi>({\n openSettings: () => {},\n prewarmSettings: () => {},\n});\n\nexport const useSettingsModal = () => useContext(SettingsModalContext);\n\n/**\n * Settings is a window, never a page.\n *\n * The workspace menu always opened it as one; everything else that pointed at a\n * settings address (a setup step, the Customize sidebar row, a link in an email,\n * a typed URL) still navigated to the page. This owns the one modal and makes\n * every road into settings arrive in it:\n *\n * - a CLICK on a link to a settings address is caught before it navigates and\n * opens the window on that section;\n * - anything else that lands on a settings route (`router.push`, a pasted\n * address, back/forward) is sent back to where you were and the window opens\n * on it, with the page itself never drawn (see the layout).\n *\n * The framed copy of a page is the one place a settings route is allowed to\n * render, so nothing here runs inside it.\n */\nexport function SettingsModalProvider({ children }: { children: ReactNode }) {\n const router = useRouter();\n const pathname = usePathname();\n const [state, setState] = useState<{ open: boolean; href: string | null }>({\n open: false,\n href: null,\n });\n\n // The window is for a desktop screen. On a phone settings stays what it was:\n // pages you go to, with the mobile shell around them.\n const openSettings = useCallback(\n (href?: string) => {\n // An address that moved out of settings goes to its page, not the window.\n const moved = href\n ? movedSettingsTarget(\n href.split(/[?#]/)[0],\n href.includes('?') ? `?${href.split('?')[1].split('#')[0]}` : '',\n )\n : null;\n if (moved) {\n router.push(moved);\n return;\n }\n if (isMobileViewport()) {\n router.push(href ?? '/dashboard/settings');\n return;\n }\n setState({ open: true, href: href ?? null });\n },\n [router],\n );\n // Kept so callers that warm the window on hover still compile: the window no\n // longer boots a frame, so there is nothing to warm.\n const prewarmSettings = useCallback(() => {}, []);\n\n const close = useCallback(() => setState((s) => ({ ...s, open: false })), []);\n\n // Where you were before settings, so closing leaves you there.\n const lastOutside = useRef('/dashboard');\n useEffect(() => {\n if (!isSettingsPath(pathname) && pathname) lastOutside.current = pathname;\n }, [pathname]);\n\n // A click on a link to settings: open the window instead of going there.\n useEffect(() => {\n if (isEmbeddedFrame()) return;\n const onClick = (e: MouseEvent) => {\n if (isMobileViewport()) return;\n if (e.defaultPrevented || e.button !== 0) return;\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n const a = (e.target as Element | null)?.closest?.('a[href]');\n if (!a || a.getAttribute('target') === '_blank') return;\n let url: URL;\n try {\n url = new URL(a.getAttribute('href') ?? '', window.location.href);\n } catch {\n return;\n }\n if (url.origin !== window.location.origin) return;\n if (!isSettingsPath(url.pathname)) return;\n const moved = movedSettingsTarget(url.pathname, url.search);\n if (moved) {\n e.preventDefault();\n router.push(moved);\n return;\n }\n // Not stopped: the link's own handlers still run (a dialog closing as you\n // leave it, say). Next skips its navigation for a prevented click.\n e.preventDefault();\n openSettings(url.pathname + url.search + url.hash);\n };\n // Capture, so this runs before the framework's own link handling.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [openSettings, router]);\n\n // A pointer or keyboard focus arriving on a link to settings is the last\n // moment before the click; start warming then.\n useEffect(() => {\n if (isEmbeddedFrame()) return;\n const onNear = (e: Event) => {\n const a = (e.target as Element | null)?.closest?.('a[href]');\n const href = a?.getAttribute('href');\n const path = href?.split(/[?#]/)[0];\n if (path && isSettingsPath(path) && !movedSettingsTarget(path)) {\n prewarmSettings();\n }\n };\n document.addEventListener('pointerover', onNear, true);\n document.addEventListener('focusin', onNear, true);\n return () => {\n document.removeEventListener('pointerover', onNear, true);\n document.removeEventListener('focusin', onNear, true);\n };\n }, [prewarmSettings]);\n\n // Anything else that lands on a settings route: back to where you were, with\n // the window open on it. Before paint, so the page is never seen.\n useLayoutEffect(() => {\n if (isEmbeddedFrame() || isMobileViewport()) return;\n if (!isSettingsPath(pathname)) return;\n const moved = movedSettingsTarget(pathname, window.location.search);\n if (moved) {\n router.replace(moved);\n return;\n }\n // eslint-disable-next-line react-hooks/set-state-in-effect -- before paint, so the page is never drawn\n openSettings(\n pathname +\n (typeof window !== 'undefined'\n ? window.location.search + window.location.hash\n : ''),\n );\n router.replace(lastOutside.current);\n }, [pathname, openSettings, router]);\n\n const api = useMemo(\n () => ({ openSettings, prewarmSettings }),\n [openSettings, prewarmSettings],\n );\n\n return (\n <SettingsModalContext.Provider value={api}>\n {children}\n <UnifiedSettingsModal\n open={state.open}\n href={state.href}\n onClose={close}\n />\n </SettingsModalContext.Provider>\n );\n}\n"],"mappings":"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","names":[]}
@@ -8,10 +8,8 @@ import { SignInContent } from "@withpotter/pages-core/dashboard/settings/devices
8
8
  import { DataPrivacyContent } from "@withpotter/pages-core/dashboard/settings/data-privacy/_components/data-privacy-content";
9
9
  import { PreferencesContent } from "@withpotter/pages-core/dashboard/settings/language/_components/preferences-content";
10
10
  import { useAccessibility } from "@withpotter/lib/accessibility-context";
11
- import { usePermissions } from "@withpotter/lib/use-permissions";
12
11
  import { RotateCcw } from "lucide-react";
13
12
  import { AccessibilityCard } from "@withpotter/pages-core/dashboard/settings/accessibility/_components/accessibility-card";
14
- import { TaxesSettingsContent } from "@withpotter/pages-core/dashboard/settings/taxes/_components/taxes-content";
15
13
  import { PoliciesContent } from "@withpotter/pages-core/dashboard/settings/policies/_components/policies-content";
16
14
  import { UsageContent } from "@withpotter/pages-core/dashboard/settings/usage/_components/usage-content";
17
15
  import { GeneralContent } from "@withpotter/pages-core/dashboard/settings/_components/general-content";
@@ -20,8 +18,6 @@ import { TeamContent } from "@withpotter/pages-core/dashboard/settings/team/_com
20
18
  import { BillingContent } from "@withpotter/pages-core/dashboard/settings/billing/_components/billing-content";
21
19
  import { LegalContent } from "@withpotter/pages-core/dashboard/settings/legal/_components/legal-content";
22
20
  import { PaymentApprovalsContent } from "@withpotter/pages-core/dashboard/settings/payment-approvals/_components/payment-approvals-content";
23
- import { DemoContentCard } from "@withpotter/pages-core/dashboard/settings/demo-content/_components/demo-content-card";
24
- import { FeaturesContent } from "@withpotter/pages-core/dashboard/settings/features/_components/features-content";
25
21
  const loading = () => /* @__PURE__ */ jsx(StateView, { kind: "loading", title: "Loading" });
26
22
  function Section({
27
23
  title,
@@ -36,23 +32,12 @@ function Section({
36
32
  children
37
33
  ] });
38
34
  }
39
- function ProfilePanel() {
40
- return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
41
- /* @__PURE__ */ jsx(ProfileContent, {}),
42
- /* @__PURE__ */ jsx(SecurityContent, {})
43
- ] });
44
- }
45
- function SecurityPanel() {
35
+ function AccountPanel() {
46
36
  return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
37
+ /* @__PURE__ */ jsx(Section, { title: "Profile", children: /* @__PURE__ */ jsx(ProfileContent, {}) }),
47
38
  /* @__PURE__ */ jsx(Section, { title: "Sign-in and security", children: /* @__PURE__ */ jsx("div", { className: "px-4", children: /* @__PURE__ */ jsx(SignInContent, {}) }) }),
48
- /* @__PURE__ */ jsx(Section, { title: "Data & privacy", children: /* @__PURE__ */ jsx(DataPrivacyContent, {}) })
49
- ] });
50
- }
51
- function ExtrasPanel() {
52
- const { can } = usePermissions();
53
- return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
54
- can("canManageSettings") && /* @__PURE__ */ jsx(FeaturesContent, {}),
55
- /* @__PURE__ */ jsx(Section, { title: "Sample content", children: /* @__PURE__ */ jsx(DemoContentCard, {}) })
39
+ /* @__PURE__ */ jsx(Section, { title: "Data & privacy", children: /* @__PURE__ */ jsx(DataPrivacyContent, {}) }),
40
+ /* @__PURE__ */ jsx(SecurityContent, {})
56
41
  ] });
57
42
  }
58
43
  function ResetAccessibility() {
@@ -71,24 +56,19 @@ function ResetAccessibility() {
71
56
  );
72
57
  }
73
58
  function PreferencesPanel() {
74
- return /* @__PURE__ */ jsx(
75
- PreferencesContent,
76
- {
77
- accessibility: /* @__PURE__ */ jsx(AccessibilityCard, { action: /* @__PURE__ */ jsx(ResetAccessibility, {}) })
78
- }
79
- );
59
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
60
+ /* @__PURE__ */ jsx(Section, { title: "Appearance, language and region", children: /* @__PURE__ */ jsx(PreferencesContent, {}) }),
61
+ /* @__PURE__ */ jsx(Section, { title: "Accessibility", action: /* @__PURE__ */ jsx(ResetAccessibility, {}), children: /* @__PURE__ */ jsx("div", { className: "px-4", children: /* @__PURE__ */ jsx(AccessibilityCard, {}) }) })
62
+ ] });
80
63
  }
81
64
  const PANELS = {
82
65
  general: () => /* @__PURE__ */ jsx(GeneralContent, {}),
83
- extras: () => /* @__PURE__ */ jsx(ExtrasPanel, {}),
84
- profile: () => /* @__PURE__ */ jsx(ProfilePanel, {}),
85
- security: () => /* @__PURE__ */ jsx(SecurityPanel, {}),
66
+ account: () => /* @__PURE__ */ jsx(AccountPanel, {}),
86
67
  team: () => /* @__PURE__ */ jsx(TeamContent, {}),
87
68
  notifications: () => /* @__PURE__ */ jsx(NotificationsContent, {}),
88
69
  preferences: () => /* @__PURE__ */ jsx(PreferencesPanel, {}),
89
70
  "payment-approvals": () => /* @__PURE__ */ jsx(PaymentApprovalsContent, {}),
90
71
  billing: () => /* @__PURE__ */ jsx(BillingContent, {}),
91
- taxes: () => /* @__PURE__ */ jsx(TaxesSettingsContent, {}),
92
72
  usage: () => /* @__PURE__ */ jsx(UsageContent, {}),
93
73
  policies: () => /* @__PURE__ */ jsx(PoliciesContent, {}),
94
74
  legal: () => /* @__PURE__ */ jsx(LegalContent, {})
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/settings-panels.tsx"],"sourcesContent":["'use client';\n\nimport { StateView } from '@withpotter/ui-kit/state-view';\nimport { Suspense, type ReactNode } from 'react';\nimport { ProfileContent } from '@withpotter/pages-core/dashboard/settings/profile/_components/profile-content';\nimport { SecurityContent } from '@withpotter/pages-core/dashboard/settings/profile/_components/security-content';\nimport { SignInContent } from '@withpotter/pages-core/dashboard/settings/devices/_components/sign-in-content';\nimport { DataPrivacyContent } from '@withpotter/pages-core/dashboard/settings/data-privacy/_components/data-privacy-content';\nimport { PreferencesContent } from '@withpotter/pages-core/dashboard/settings/language/_components/preferences-content';\nimport { useAccessibility } from '@withpotter/lib/accessibility-context';\nimport { usePermissions } from '@withpotter/lib/use-permissions';\nimport { RotateCcw } from 'lucide-react';\nimport { AccessibilityCard } from '@withpotter/pages-core/dashboard/settings/accessibility/_components/accessibility-card';\nimport { TaxesSettingsContent } from '@withpotter/pages-core/dashboard/settings/taxes/_components/taxes-content';\nimport { PoliciesContent } from '@withpotter/pages-core/dashboard/settings/policies/_components/policies-content';\nimport { UsageContent } from '@withpotter/pages-core/dashboard/settings/usage/_components/usage-content';\nimport { GeneralContent } from '@withpotter/pages-core/dashboard/settings/_components/general-content';\nimport { NotificationsContent } from '@withpotter/pages-core/dashboard/settings/notifications/_components/notifications-content';\nimport { TeamContent } from '@withpotter/pages-core/dashboard/settings/team/_components/team-content';\nimport { BillingContent } from '@withpotter/pages-core/dashboard/settings/billing/_components/billing-content';\nimport { LegalContent } from '@withpotter/pages-core/dashboard/settings/legal/_components/legal-content';\nimport { PaymentApprovalsContent } from '@withpotter/pages-core/dashboard/settings/payment-approvals/_components/payment-approvals-content';\nimport { DemoContentCard } from '@withpotter/pages-core/dashboard/settings/demo-content/_components/demo-content-card';\nimport { FeaturesContent } from '@withpotter/pages-core/dashboard/settings/features/_components/features-content';\nimport type { SettingsRowId } from './settings-rows';\n\n/**\n * The page behind a row, drawn in the window.\n *\n * The business rows render the real settings page, so each keeps its own\n * loading, saving and sections. They are loaded on first open of a row rather\n * than with the window, and each is a client page that reads the same hooks it\n * always did.\n */\n/** What a row shows while its code loads, so a first open is never blank. */\nconst loading = () => <StateView kind=\"loading\" title=\"Loading\" />;\n\n/** A section heading inside a merged page. */\nfunction Section({\n title,\n action,\n children,\n}: {\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}) {\n return (\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3 px-4\">\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n {title}\n </h3>\n {action}\n </div>\n {children}\n </section>\n );\n}\n\n/**\n * Who the person is: their profile. Closing the account is last, as it is the\n * one thing here that cannot be undone.\n */\nfunction ProfilePanel() {\n return (\n <div className=\"space-y-8\">\n <ProfileContent />\n <SecurityContent />\n </div>\n );\n}\n\n/** How the person signs in and where, and what is kept about them. */\nfunction SecurityPanel() {\n return (\n <div className=\"space-y-8\">\n <Section title=\"Sign-in and security\">\n <div className=\"px-4\">\n <SignInContent />\n </div>\n </Section>\n <Section title=\"Data & privacy\">\n <DataPrivacyContent />\n </Section>\n </div>\n );\n}\n\n/**\n * The switches (Blog today) and the sample content, in one place. The switches\n * are shown only to someone who may change them; the demo card decides for\n * itself who sees its actions.\n */\nfunction ExtrasPanel() {\n const { can } = usePermissions();\n return (\n <div className=\"space-y-8\">\n {can('canManageSettings') && <FeaturesContent />}\n <Section title=\"Sample content\">\n <DemoContentCard />\n </Section>\n </div>\n );\n}\n\n/** Reset to defaults was a page header action; here it sits on its section. */\nfunction ResetAccessibility() {\n const a11y = useAccessibility();\n return (\n <button\n type=\"button\"\n onClick={a11y.reset}\n className=\"inline-flex items-center gap-2 text-xs font-medium text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n <RotateCcw className=\"size-4\" />\n Reset to defaults\n </button>\n );\n}\n\n/** How the app looks and behaves for the person using it. */\nfunction PreferencesPanel() {\n return (\n <PreferencesContent\n accessibility={<AccessibilityCard action={<ResetAccessibility />} />}\n />\n );\n}\n\nconst PANELS: Record<SettingsRowId, () => ReactNode> = {\n general: () => <GeneralContent />,\n extras: () => <ExtrasPanel />,\n profile: () => <ProfilePanel />,\n security: () => <SecurityPanel />,\n team: () => <TeamContent />,\n notifications: () => <NotificationsContent />,\n preferences: () => <PreferencesPanel />,\n 'payment-approvals': () => <PaymentApprovalsContent />,\n billing: () => <BillingContent />,\n taxes: () => <TaxesSettingsContent />,\n usage: () => <UsageContent />,\n policies: () => <PoliciesContent />,\n legal: () => <LegalContent />,\n};\n\nexport function SettingsPanel({ id }: { id: SettingsRowId }) {\n // Some pages read the query string, which needs a boundary above it.\n return <Suspense fallback={loading()}>{PANELS[id]()}</Suspense>;\n}\n"],"mappings":";AAmCsB,cAchB,YAdgB;AAjCtB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,+BAA+B;AACxC,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAYhC,MAAM,UAAU,MAAM,oBAAC,aAAU,MAAK,WAAU,OAAM,WAAU;AAGhE,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,aAAQ,WAAU,aACjB;AAAA,yBAAC,SAAI,WAAU,gDACb;AAAA,0BAAC,QAAG,WAAU,oDACX,iBACH;AAAA,MACC;AAAA,OACH;AAAA,IACC;AAAA,KACH;AAEJ;AAMA,SAAS,eAAe;AACtB,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,kBAAe;AAAA,IAChB,oBAAC,mBAAgB;AAAA,KACnB;AAEJ;AAGA,SAAS,gBAAgB;AACvB,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,WAAQ,OAAM,wBACb,8BAAC,SAAI,WAAU,QACb,8BAAC,iBAAc,GACjB,GACF;AAAA,IACA,oBAAC,WAAQ,OAAM,kBACb,8BAAC,sBAAmB,GACtB;AAAA,KACF;AAEJ;AAOA,SAAS,cAAc;AACrB,QAAM,EAAE,IAAI,IAAI,eAAe;AAC/B,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,QAAI,mBAAmB,KAAK,oBAAC,mBAAgB;AAAA,IAC9C,oBAAC,WAAQ,OAAM,kBACb,8BAAC,mBAAgB,GACnB;AAAA,KACF;AAEJ;AAGA,SAAS,qBAAqB;AAC5B,QAAM,OAAO,iBAAiB;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS,KAAK;AAAA,MACd,WAAU;AAAA,MAEV;AAAA,4BAAC,aAAU,WAAU,UAAS;AAAA,QAAE;AAAA;AAAA;AAAA,EAElC;AAEJ;AAGA,SAAS,mBAAmB;AAC1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAe,oBAAC,qBAAkB,QAAQ,oBAAC,sBAAmB,GAAI;AAAA;AAAA,EACpE;AAEJ;AAEA,MAAM,SAAiD;AAAA,EACrD,SAAS,MAAM,oBAAC,kBAAe;AAAA,EAC/B,QAAQ,MAAM,oBAAC,eAAY;AAAA,EAC3B,SAAS,MAAM,oBAAC,gBAAa;AAAA,EAC7B,UAAU,MAAM,oBAAC,iBAAc;AAAA,EAC/B,MAAM,MAAM,oBAAC,eAAY;AAAA,EACzB,eAAe,MAAM,oBAAC,wBAAqB;AAAA,EAC3C,aAAa,MAAM,oBAAC,oBAAiB;AAAA,EACrC,qBAAqB,MAAM,oBAAC,2BAAwB;AAAA,EACpD,SAAS,MAAM,oBAAC,kBAAe;AAAA,EAC/B,OAAO,MAAM,oBAAC,wBAAqB;AAAA,EACnC,OAAO,MAAM,oBAAC,gBAAa;AAAA,EAC3B,UAAU,MAAM,oBAAC,mBAAgB;AAAA,EACjC,OAAO,MAAM,oBAAC,gBAAa;AAC7B;AAEO,SAAS,cAAc,EAAE,GAAG,GAA0B;AAE3D,SAAO,oBAAC,YAAS,UAAU,QAAQ,GAAI,iBAAO,EAAE,EAAE,GAAE;AACtD;","names":[]}
1
+ {"version":3,"sources":["../../src/shell/settings-panels.tsx"],"sourcesContent":["'use client';\n\nimport { StateView } from '@withpotter/ui-kit/state-view';\nimport { Suspense, type ReactNode } from 'react';\nimport { ProfileContent } from '@withpotter/pages-core/dashboard/settings/profile/_components/profile-content';\nimport { SecurityContent } from '@withpotter/pages-core/dashboard/settings/profile/_components/security-content';\nimport { SignInContent } from '@withpotter/pages-core/dashboard/settings/devices/_components/sign-in-content';\nimport { DataPrivacyContent } from '@withpotter/pages-core/dashboard/settings/data-privacy/_components/data-privacy-content';\nimport { PreferencesContent } from '@withpotter/pages-core/dashboard/settings/language/_components/preferences-content';\nimport { useAccessibility } from '@withpotter/lib/accessibility-context';\nimport { RotateCcw } from 'lucide-react';\nimport { AccessibilityCard } from '@withpotter/pages-core/dashboard/settings/accessibility/_components/accessibility-card';\nimport { PoliciesContent } from '@withpotter/pages-core/dashboard/settings/policies/_components/policies-content';\nimport { UsageContent } from '@withpotter/pages-core/dashboard/settings/usage/_components/usage-content';\nimport { GeneralContent } from '@withpotter/pages-core/dashboard/settings/_components/general-content';\nimport { NotificationsContent } from '@withpotter/pages-core/dashboard/settings/notifications/_components/notifications-content';\nimport { TeamContent } from '@withpotter/pages-core/dashboard/settings/team/_components/team-content';\nimport { BillingContent } from '@withpotter/pages-core/dashboard/settings/billing/_components/billing-content';\nimport { LegalContent } from '@withpotter/pages-core/dashboard/settings/legal/_components/legal-content';\nimport { PaymentApprovalsContent } from '@withpotter/pages-core/dashboard/settings/payment-approvals/_components/payment-approvals-content';\nimport type { SettingsRowId } from './settings-rows';\n\n/**\n * The page behind a row, drawn in the window.\n *\n * The business rows render the real settings page, so each keeps its own\n * loading, saving and sections. They are loaded on first open of a row rather\n * than with the window, and each is a client page that reads the same hooks it\n * always did.\n */\n/** What a row shows while its code loads, so a first open is never blank. */\nconst loading = () => <StateView kind=\"loading\" title=\"Loading\" />;\n\n/** A section heading inside a merged page. */\nfunction Section({\n title,\n action,\n children,\n}: {\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}) {\n return (\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3 px-4\">\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n {title}\n </h3>\n {action}\n </div>\n {children}\n </section>\n );\n}\n\n/**\n * Everything about the person: profile, how they sign in, their data, and the\n * way out. Closing the account is last, as it is the one thing here that\n * cannot be undone.\n */\nfunction AccountPanel() {\n return (\n <div className=\"space-y-8\">\n <Section title=\"Profile\">\n <ProfileContent />\n </Section>\n <Section title=\"Sign-in and security\">\n <div className=\"px-4\">\n <SignInContent />\n </div>\n </Section>\n <Section title=\"Data & privacy\">\n <DataPrivacyContent />\n </Section>\n <SecurityContent />\n </div>\n );\n}\n\n/** Reset to defaults was a page header action; here it sits on its section. */\nfunction ResetAccessibility() {\n const a11y = useAccessibility();\n return (\n <button\n type=\"button\"\n onClick={a11y.reset}\n className=\"inline-flex items-center gap-2 text-xs font-medium text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\n >\n <RotateCcw className=\"size-4\" />\n Reset to defaults\n </button>\n );\n}\n\n/** How the app looks and behaves for the person using it. */\nfunction PreferencesPanel() {\n return (\n <div className=\"space-y-8\">\n <Section title=\"Appearance, language and region\">\n <PreferencesContent />\n </Section>\n <Section title=\"Accessibility\" action={<ResetAccessibility />}>\n <div className=\"px-4\">\n <AccessibilityCard />\n </div>\n </Section>\n </div>\n );\n}\n\nconst PANELS: Record<SettingsRowId, () => ReactNode> = {\n general: () => <GeneralContent />,\n account: () => <AccountPanel />,\n team: () => <TeamContent />,\n notifications: () => <NotificationsContent />,\n preferences: () => <PreferencesPanel />,\n 'payment-approvals': () => <PaymentApprovalsContent />,\n billing: () => <BillingContent />,\n usage: () => <UsageContent />,\n policies: () => <PoliciesContent />,\n legal: () => <LegalContent />,\n};\n\nexport function SettingsPanel({ id }: { id: SettingsRowId }) {\n // Some pages read the query string, which needs a boundary above it.\n return <Suspense fallback={loading()}>{PANELS[id]()}</Suspense>;\n}\n"],"mappings":";AA+BsB,cAchB,YAdgB;AA7BtB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,+BAA+B;AAYxC,MAAM,UAAU,MAAM,oBAAC,aAAU,MAAK,WAAU,OAAM,WAAU;AAGhE,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,aAAQ,WAAU,aACjB;AAAA,yBAAC,SAAI,WAAU,gDACb;AAAA,0BAAC,QAAG,WAAU,oDACX,iBACH;AAAA,MACC;AAAA,OACH;AAAA,IACC;AAAA,KACH;AAEJ;AAOA,SAAS,eAAe;AACtB,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,WAAQ,OAAM,WACb,8BAAC,kBAAe,GAClB;AAAA,IACA,oBAAC,WAAQ,OAAM,wBACb,8BAAC,SAAI,WAAU,QACb,8BAAC,iBAAc,GACjB,GACF;AAAA,IACA,oBAAC,WAAQ,OAAM,kBACb,8BAAC,sBAAmB,GACtB;AAAA,IACA,oBAAC,mBAAgB;AAAA,KACnB;AAEJ;AAGA,SAAS,qBAAqB;AAC5B,QAAM,OAAO,iBAAiB;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS,KAAK;AAAA,MACd,WAAU;AAAA,MAEV;AAAA,4BAAC,aAAU,WAAU,UAAS;AAAA,QAAE;AAAA;AAAA;AAAA,EAElC;AAEJ;AAGA,SAAS,mBAAmB;AAC1B,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,WAAQ,OAAM,mCACb,8BAAC,sBAAmB,GACtB;AAAA,IACA,oBAAC,WAAQ,OAAM,iBAAgB,QAAQ,oBAAC,sBAAmB,GACzD,8BAAC,SAAI,WAAU,QACb,8BAAC,qBAAkB,GACrB,GACF;AAAA,KACF;AAEJ;AAEA,MAAM,SAAiD;AAAA,EACrD,SAAS,MAAM,oBAAC,kBAAe;AAAA,EAC/B,SAAS,MAAM,oBAAC,gBAAa;AAAA,EAC7B,MAAM,MAAM,oBAAC,eAAY;AAAA,EACzB,eAAe,MAAM,oBAAC,wBAAqB;AAAA,EAC3C,aAAa,MAAM,oBAAC,oBAAiB;AAAA,EACrC,qBAAqB,MAAM,oBAAC,2BAAwB;AAAA,EACpD,SAAS,MAAM,oBAAC,kBAAe;AAAA,EAC/B,OAAO,MAAM,oBAAC,gBAAa;AAAA,EAC3B,UAAU,MAAM,oBAAC,mBAAgB;AAAA,EACjC,OAAO,MAAM,oBAAC,gBAAa;AAC7B;AAEO,SAAS,cAAc,EAAE,GAAG,GAA0B;AAE3D,SAAO,oBAAC,YAAS,UAAU,QAAQ,GAAI,iBAAO,EAAE,EAAE,GAAE;AACtD;","names":[]}
@@ -12,12 +12,10 @@ import type { PermissionKey } from '@withpotter/lib/use-permissions';
12
12
  * `keywords` are what search also matches: the names of the sections inside a
13
13
  * row, so "passkeys" or "time zone" finds the row that holds them.
14
14
  */
15
- export type SettingsRowId = 'general' | 'extras' | 'profile' | 'security' | 'team' | 'notifications' | 'preferences' | 'payment-approvals' | 'billing' | 'taxes' | 'usage' | 'policies' | 'legal';
15
+ export type SettingsRowId = 'general' | 'account' | 'team' | 'notifications' | 'preferences' | 'payment-approvals' | 'billing' | 'usage' | 'policies' | 'legal';
16
16
  export interface SettingsRow {
17
17
  id: SettingsRowId;
18
18
  name: string;
19
- /** The line under the title when the row is open. */
20
- subtitle?: string;
21
19
  icon: LucideIcon;
22
20
  paths: string[];
23
21
  keywords: string[];
@@ -40,7 +38,4 @@ export declare const HELP_ICON: import("react").ForwardRefExoticComponent<Omit<i
40
38
  */
41
39
  export declare function rowForHref(href: string | null | undefined): SettingsRowId;
42
40
  /** Rows whose name or inner sections match what was typed. */
43
- export declare function matchRows<T extends {
44
- name: string;
45
- keywords: string[];
46
- }>(rows: T[], query: string): T[];
41
+ export declare function matchRows(rows: SettingsRow[], query: string): SettingsRow[];
@@ -6,10 +6,7 @@ import {
6
6
  CircleHelp,
7
7
  CreditCard,
8
8
  FileText,
9
- Lock,
10
- ReceiptText,
11
9
  Scale,
12
- Sparkles,
13
10
  SlidersHorizontal,
14
11
  UserRound,
15
12
  Users
@@ -17,25 +14,14 @@ import {
17
14
  const BILLING_ROW = {
18
15
  id: "billing",
19
16
  name: "Billing & plan",
20
- subtitle: "Your plan, how you pay, and your payments.",
21
17
  icon: CreditCard,
22
18
  paths: ["/dashboard/settings/billing"],
23
19
  keywords: ["plan", "invoice", "credits", "top-up", "cancel"],
24
20
  permission: ["canManageSettings"]
25
21
  };
26
- const TAXES_ROW = {
27
- id: "taxes",
28
- name: "Taxes",
29
- subtitle: "What you charge, and the number shown on receipts.",
30
- icon: ReceiptText,
31
- paths: ["/dashboard/settings/taxes"],
32
- keywords: ["vat", "tax rate", "registration number", "receipts"],
33
- permission: ["canManageSettings"]
34
- };
35
22
  const PAYMENT_APPROVALS_ROW = {
36
23
  id: "payment-approvals",
37
24
  name: "Payment approvals",
38
- subtitle: "Who approves money going out.",
39
25
  icon: CircleCheck,
40
26
  paths: ["/dashboard/settings/payment-approvals"],
41
27
  keywords: ["approval", "accepted", "rejected", "decisions", "history"]
@@ -44,7 +30,6 @@ const SETTINGS_ROWS = [
44
30
  {
45
31
  id: "general",
46
32
  name: "General",
47
- subtitle: "Your business details and store address.",
48
33
  icon: Building2,
49
34
  paths: [
50
35
  "/dashboard/settings",
@@ -54,99 +39,49 @@ const SETTINGS_ROWS = [
54
39
  keywords: ["business name", "branding", "logo", "colours", "numbering"],
55
40
  permission: ["canManageBusinessProfile", "canManageSettings"]
56
41
  },
57
- // A licensed box that bills outside the console hides the row.
58
- ...process.env.NEXT_PUBLIC_BOX_HIDE_BILLING === "true" ? [] : [BILLING_ROW],
59
- // A box that keeps a tax (VAT) setting has it as its own row, after the plan.
60
- ...process.env.NEXT_PUBLIC_BOX_TAXES_IN_POLICIES === "true" ? [TAXES_ROW] : [],
61
- {
62
- id: "team",
63
- name: "Team & permissions",
64
- subtitle: "Who can do what in this business.",
65
- icon: Users,
66
- paths: [
67
- "/dashboard/settings/team",
68
- "/dashboard/settings/roles",
69
- "/dashboard/settings/activity"
70
- ],
71
- keywords: ["people", "invite", "roles", "activity", "seats"],
72
- permission: ["canManageTeam"],
73
- // A box with its own team screen (its roles and invitations come from
74
- // its engine, which does not serve this panel's routes) names it here.
75
- href: process.env.NEXT_PUBLIC_BOX_TEAM_PAGE || void 0
76
- },
77
- ...process.env.NEXT_PUBLIC_BOX_PAYMENT_APPROVALS === "true" ? [PAYMENT_APPROVALS_ROW] : [],
78
- {
79
- id: "policies",
80
- name: "Policies",
81
- subtitle: "What your customers agree to.",
82
- icon: Scale,
83
- paths: ["/dashboard/settings/policies"],
84
- keywords: ["returns", "shipping", "privacy", "terms"],
85
- permission: ["canManageSettings"]
86
- },
87
- {
88
- // The switches no business type implies (Blog today) and the sample
89
- // content to start with, clear, or add again. One row for both. No
90
- // permission gate on the row, like the Demo content entry in the sidebar:
91
- // everyone sees the state, the panel shows the switches only to those who
92
- // may change them, and the demo card shows its actions only to the owner
93
- // and admins.
94
- id: "extras",
95
- name: "Extras",
96
- subtitle: "Switches and sample content for your store.",
97
- icon: Sparkles,
98
- paths: [
99
- "/dashboard/settings/features",
100
- "/dashboard/settings/demo-content"
101
- ],
102
- keywords: [
103
- "blog",
104
- "posts",
105
- "write",
106
- "publish",
107
- "switch",
108
- "website",
109
- "features",
110
- "sample",
111
- "products",
112
- "starter",
113
- "clear",
114
- "example data",
115
- "demo content"
116
- ]
117
- },
118
42
  {
119
- id: "profile",
120
- name: "Profile",
121
- subtitle: "Your name and how we reach you.",
43
+ id: "account",
44
+ name: "Account",
122
45
  icon: UserRound,
123
- paths: ["/dashboard/settings/profile"],
124
- keywords: ["name", "email", "photo", "phone", "close account"]
125
- },
126
- {
127
- id: "security",
128
- name: "Security",
129
- subtitle: "How you sign in, and where you are signed in.",
130
- icon: Lock,
131
46
  paths: [
47
+ "/dashboard/settings/profile",
132
48
  "/dashboard/settings/devices",
133
49
  "/dashboard/settings/data-privacy"
134
50
  ],
135
51
  keywords: [
52
+ "profile",
53
+ "name",
54
+ "email",
55
+ "photo",
56
+ "security",
136
57
  "password",
137
58
  "passkeys",
138
59
  "two-factor",
139
60
  "sessions",
140
61
  "devices",
141
- "sign in",
142
62
  "privacy",
143
- "export"
63
+ "export",
64
+ "close account"
144
65
  ]
145
66
  },
67
+ {
68
+ id: "team",
69
+ name: "Team & permissions",
70
+ icon: Users,
71
+ paths: [
72
+ "/dashboard/settings/team",
73
+ "/dashboard/settings/roles",
74
+ "/dashboard/settings/activity"
75
+ ],
76
+ keywords: ["people", "invite", "roles", "activity", "seats"],
77
+ permission: ["canManageTeam"],
78
+ // A box with its own team screen (its roles and invitations come from
79
+ // its engine, which does not serve this panel's routes) names it here.
80
+ href: process.env.NEXT_PUBLIC_BOX_TEAM_PAGE || void 0
81
+ },
146
82
  {
147
83
  id: "notifications",
148
84
  name: "Notifications",
149
- subtitle: "What we tell you, and what your customers hear from you.",
150
85
  icon: Bell,
151
86
  paths: ["/dashboard/settings/notifications"],
152
87
  keywords: ["alerts", "low stock", "email sender", "digest", "reminders"]
@@ -154,7 +89,6 @@ const SETTINGS_ROWS = [
154
89
  {
155
90
  id: "preferences",
156
91
  name: "Preferences",
157
- subtitle: "How the dashboard looks and behaves for you.",
158
92
  icon: SlidersHorizontal,
159
93
  paths: [
160
94
  "/dashboard/settings/language",
@@ -174,22 +108,31 @@ const SETTINGS_ROWS = [
174
108
  "text size"
175
109
  ]
176
110
  },
111
+ ...process.env.NEXT_PUBLIC_BOX_PAYMENT_APPROVALS === "true" ? [PAYMENT_APPROVALS_ROW] : [],
112
+ // A licensed box that bills outside the console hides the row.
113
+ ...process.env.NEXT_PUBLIC_BOX_HIDE_BILLING === "true" ? [] : [BILLING_ROW],
177
114
  // A box that meters nothing in the console leaves Usage out.
178
115
  ...process.env.NEXT_PUBLIC_BOX_HIDE_USAGE === "true" ? [] : [
179
116
  {
180
117
  id: "usage",
181
118
  name: "Usage",
182
- subtitle: "What you have used this month.",
183
119
  icon: ChartColumn,
184
120
  paths: ["/dashboard/settings/usage"],
185
121
  keywords: ["credits", "delivery", "spend"],
186
122
  permission: ["canManageSettings"]
187
123
  }
188
124
  ],
125
+ {
126
+ id: "policies",
127
+ name: "Policies",
128
+ icon: Scale,
129
+ paths: ["/dashboard/settings/policies"],
130
+ keywords: ["returns", "shipping", "privacy", "terms"],
131
+ permission: ["canManageSettings"]
132
+ },
189
133
  {
190
134
  id: "legal",
191
135
  name: "Legal",
192
- subtitle: "Your terms and privacy pages.",
193
136
  icon: FileText,
194
137
  paths: ["/dashboard/settings/legal"],
195
138
  keywords: ["terms", "privacy policy", "licences"]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/settings-rows.ts"],"sourcesContent":["import {\n Bell,\n Building2,\n ChartColumn,\n CircleCheck,\n CircleHelp,\n CreditCard,\n FileText,\n Lock,\n ReceiptText,\n Scale,\n Sparkles,\n SlidersHorizontal,\n UserRound,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport type { PermissionKey } from '@withpotter/lib/use-permissions';\n\n/**\n * The rows of the settings window, in the order they are drawn.\n *\n * One list for the whole window: yours and the business's, mixed, because the\n * person looking for \"Notifications\" or \"Language\" should not have to know\n * whose setting it is. Rows that leave the window (`external`) carry an arrow.\n *\n * `paths` are the addresses a row owns. A link to any of them, or to anything\n * below them (a team member, a credits page), opens the window on that row.\n * `keywords` are what search also matches: the names of the sections inside a\n * row, so \"passkeys\" or \"time zone\" finds the row that holds them.\n */\nexport type SettingsRowId =\n | 'general'\n | 'extras'\n | 'profile'\n | 'security'\n | 'team'\n | 'notifications'\n | 'preferences'\n | 'payment-approvals'\n | 'billing'\n | 'taxes'\n | 'usage'\n | 'policies'\n | 'legal';\n\nexport interface SettingsRow {\n id: SettingsRowId;\n name: string;\n /** The line under the title when the row is open. */\n subtitle?: string;\n icon: LucideIcon;\n paths: string[];\n keywords: string[];\n /** Hidden from a member who lacks every one of these. Empty: everyone. */\n permission?: PermissionKey[];\n /**\n * A page this row leaves the window for, where a host has its own screen for\n * the subject. The row then is a link, and the panel is never drawn.\n */\n href?: string;\n}\n\nconst BILLING_ROW: SettingsRow = {\n id: 'billing',\n name: 'Billing & plan',\n subtitle: 'Your plan, how you pay, and your payments.',\n icon: CreditCard,\n paths: ['/dashboard/settings/billing'],\n keywords: ['plan', 'invoice', 'credits', 'top-up', 'cancel'],\n permission: ['canManageSettings'],\n};\n\nconst TAXES_ROW: SettingsRow = {\n id: 'taxes',\n name: 'Taxes',\n subtitle: 'What you charge, and the number shown on receipts.',\n icon: ReceiptText,\n paths: ['/dashboard/settings/taxes'],\n keywords: ['vat', 'tax rate', 'registration number', 'receipts'],\n permission: ['canManageSettings'],\n};\n\n// A box whose card payments wait for a second person lists the decisions.\nconst PAYMENT_APPROVALS_ROW: SettingsRow = {\n id: 'payment-approvals',\n name: 'Payment approvals',\n subtitle: 'Who approves money going out.',\n icon: CircleCheck,\n paths: ['/dashboard/settings/payment-approvals'],\n keywords: ['approval', 'accepted', 'rejected', 'decisions', 'history'],\n};\n\nexport const SETTINGS_ROWS: SettingsRow[] = [\n {\n id: 'general',\n name: 'General',\n subtitle: 'Your business details and store address.',\n icon: Building2,\n paths: [\n '/dashboard/settings',\n '/dashboard/settings/branding',\n '/dashboard/settings/regional',\n ],\n keywords: ['business name', 'branding', 'logo', 'colours', 'numbering'],\n permission: ['canManageBusinessProfile', 'canManageSettings'],\n },\n // A licensed box that bills outside the console hides the row.\n ...(process.env.NEXT_PUBLIC_BOX_HIDE_BILLING === 'true' ? [] : [BILLING_ROW]),\n // A box that keeps a tax (VAT) setting has it as its own row, after the plan.\n ...(process.env.NEXT_PUBLIC_BOX_TAXES_IN_POLICIES === 'true' ? [TAXES_ROW] : []),\n {\n id: 'team',\n name: 'Team & permissions',\n subtitle: 'Who can do what in this business.',\n icon: Users,\n paths: [\n '/dashboard/settings/team',\n '/dashboard/settings/roles',\n '/dashboard/settings/activity',\n ],\n keywords: ['people', 'invite', 'roles', 'activity', 'seats'],\n permission: ['canManageTeam'],\n // A box with its own team screen (its roles and invitations come from\n // its engine, which does not serve this panel's routes) names it here.\n href: process.env.NEXT_PUBLIC_BOX_TEAM_PAGE || undefined,\n },\n ...(process.env.NEXT_PUBLIC_BOX_PAYMENT_APPROVALS === 'true'\n ? [PAYMENT_APPROVALS_ROW]\n : []),\n {\n id: 'policies',\n name: 'Policies',\n subtitle: 'What your customers agree to.',\n icon: Scale,\n paths: ['/dashboard/settings/policies'],\n keywords: ['returns', 'shipping', 'privacy', 'terms'],\n permission: ['canManageSettings'],\n },\n {\n // The switches no business type implies (Blog today) and the sample\n // content to start with, clear, or add again. One row for both. No\n // permission gate on the row, like the Demo content entry in the sidebar:\n // everyone sees the state, the panel shows the switches only to those who\n // may change them, and the demo card shows its actions only to the owner\n // and admins.\n id: 'extras',\n name: 'Extras',\n subtitle: 'Switches and sample content for your store.',\n icon: Sparkles,\n paths: [\n '/dashboard/settings/features',\n '/dashboard/settings/demo-content',\n ],\n keywords: [\n 'blog',\n 'posts',\n 'write',\n 'publish',\n 'switch',\n 'website',\n 'features',\n 'sample',\n 'products',\n 'starter',\n 'clear',\n 'example data',\n 'demo content',\n ],\n },\n {\n id: 'profile',\n name: 'Profile',\n subtitle: 'Your name and how we reach you.',\n icon: UserRound,\n paths: ['/dashboard/settings/profile'],\n keywords: ['name', 'email', 'photo', 'phone', 'close account'],\n },\n {\n id: 'security',\n name: 'Security',\n subtitle: 'How you sign in, and where you are signed in.',\n icon: Lock,\n paths: [\n '/dashboard/settings/devices',\n '/dashboard/settings/data-privacy',\n ],\n keywords: [\n 'password',\n 'passkeys',\n 'two-factor',\n 'sessions',\n 'devices',\n 'sign in',\n 'privacy',\n 'export',\n ],\n },\n {\n id: 'notifications',\n name: 'Notifications',\n subtitle: 'What we tell you, and what your customers hear from you.',\n icon: Bell,\n paths: ['/dashboard/settings/notifications'],\n keywords: ['alerts', 'low stock', 'email sender', 'digest', 'reminders'],\n },\n {\n id: 'preferences',\n name: 'Preferences',\n subtitle: 'How the dashboard looks and behaves for you.',\n icon: SlidersHorizontal,\n paths: [\n '/dashboard/settings/language',\n '/dashboard/settings/accessibility',\n ],\n keywords: [\n 'appearance',\n 'theme',\n 'dark',\n 'light',\n 'language',\n 'time zone',\n 'date format',\n 'accessibility',\n 'high contrast',\n 'reduce motion',\n 'text size',\n ],\n },\n // A box that meters nothing in the console leaves Usage out.\n ...(process.env.NEXT_PUBLIC_BOX_HIDE_USAGE === 'true'\n ? []\n : [\n {\n id: 'usage' as const,\n name: 'Usage',\n subtitle: 'What you have used this month.',\n icon: ChartColumn,\n paths: ['/dashboard/settings/usage'],\n keywords: ['credits', 'delivery', 'spend'],\n permission: ['canManageSettings' as const],\n },\n ]),\n {\n id: 'legal',\n name: 'Legal',\n subtitle: 'Your terms and privacy pages.',\n icon: FileText,\n paths: ['/dashboard/settings/legal'],\n keywords: ['terms', 'privacy policy', 'licences'],\n },\n];\n\n/** Where \"Get help\" goes. It leaves the window, so it is not a row above. */\nexport const HELP_HREF = '/dashboard/help';\nexport const HELP_ICON = CircleHelp;\n\nconst pathOf = (full: string) => full.split(/[?#]/)[0];\n\n/**\n * The row that owns an address: the longest of its paths that equals the\n * address or is a parent of it. General's own address is a prefix of every\n * other settings address, so it only owns itself.\n */\nexport function rowForHref(href: string | null | undefined): SettingsRowId {\n const path = pathOf(href ?? '/dashboard/settings');\n let best: { id: SettingsRowId; len: number } | null = null;\n for (const row of SETTINGS_ROWS) {\n for (const p of row.paths) {\n const owns =\n path === p || (p !== '/dashboard/settings' && path.startsWith(`${p}/`));\n if (owns && (!best || p.length > best.len))\n best = { id: row.id, len: p.length };\n }\n }\n return best?.id ?? 'general';\n}\n\n/** Rows whose name or inner sections match what was typed. */\nexport function matchRows<T extends { name: string; keywords: string[] }>(\n rows: T[],\n query: string,\n): T[] {\n const q = query.trim().toLowerCase();\n if (!q) return rows;\n return rows.filter(\n (r) =>\n r.name.toLowerCase().includes(q) || r.keywords.some((k) => k.includes(q)),\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/shell/settings-rows.ts"],"sourcesContent":["import {\n Bell,\n Building2,\n ChartColumn,\n CircleCheck,\n CircleHelp,\n CreditCard,\n FileText,\n Scale,\n SlidersHorizontal,\n UserRound,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport type { PermissionKey } from '@withpotter/lib/use-permissions';\n\n/**\n * The rows of the settings window, in the order they are drawn.\n *\n * One list for the whole window: yours and the business's, mixed, because the\n * person looking for \"Notifications\" or \"Language\" should not have to know\n * whose setting it is. Rows that leave the window (`external`) carry an arrow.\n *\n * `paths` are the addresses a row owns. A link to any of them, or to anything\n * below them (a team member, a credits page), opens the window on that row.\n * `keywords` are what search also matches: the names of the sections inside a\n * row, so \"passkeys\" or \"time zone\" finds the row that holds them.\n */\nexport type SettingsRowId =\n | 'general'\n | 'account'\n | 'team'\n | 'notifications'\n | 'preferences'\n | 'payment-approvals'\n | 'billing'\n | 'usage'\n | 'policies'\n | 'legal';\n\nexport interface SettingsRow {\n id: SettingsRowId;\n name: string;\n icon: LucideIcon;\n paths: string[];\n keywords: string[];\n /** Hidden from a member who lacks every one of these. Empty: everyone. */\n permission?: PermissionKey[];\n /**\n * A page this row leaves the window for, where a host has its own screen for\n * the subject. The row then is a link, and the panel is never drawn.\n */\n href?: string;\n}\n\nconst BILLING_ROW: SettingsRow = {\n id: 'billing',\n name: 'Billing & plan',\n icon: CreditCard,\n paths: ['/dashboard/settings/billing'],\n keywords: ['plan', 'invoice', 'credits', 'top-up', 'cancel'],\n permission: ['canManageSettings'],\n};\n\n// A box whose card payments wait for a second person lists the decisions.\nconst PAYMENT_APPROVALS_ROW: SettingsRow = {\n id: 'payment-approvals',\n name: 'Payment approvals',\n icon: CircleCheck,\n paths: ['/dashboard/settings/payment-approvals'],\n keywords: ['approval', 'accepted', 'rejected', 'decisions', 'history'],\n};\n\nexport const SETTINGS_ROWS: SettingsRow[] = [\n {\n id: 'general',\n name: 'General',\n icon: Building2,\n paths: [\n '/dashboard/settings',\n '/dashboard/settings/branding',\n '/dashboard/settings/regional',\n ],\n keywords: ['business name', 'branding', 'logo', 'colours', 'numbering'],\n permission: ['canManageBusinessProfile', 'canManageSettings'],\n },\n {\n id: 'account',\n name: 'Account',\n icon: UserRound,\n paths: [\n '/dashboard/settings/profile',\n '/dashboard/settings/devices',\n '/dashboard/settings/data-privacy',\n ],\n keywords: [\n 'profile',\n 'name',\n 'email',\n 'photo',\n 'security',\n 'password',\n 'passkeys',\n 'two-factor',\n 'sessions',\n 'devices',\n 'privacy',\n 'export',\n 'close account',\n ],\n },\n {\n id: 'team',\n name: 'Team & permissions',\n icon: Users,\n paths: [\n '/dashboard/settings/team',\n '/dashboard/settings/roles',\n '/dashboard/settings/activity',\n ],\n keywords: ['people', 'invite', 'roles', 'activity', 'seats'],\n permission: ['canManageTeam'],\n // A box with its own team screen (its roles and invitations come from\n // its engine, which does not serve this panel's routes) names it here.\n href: process.env.NEXT_PUBLIC_BOX_TEAM_PAGE || undefined,\n },\n {\n id: 'notifications',\n name: 'Notifications',\n icon: Bell,\n paths: ['/dashboard/settings/notifications'],\n keywords: ['alerts', 'low stock', 'email sender', 'digest', 'reminders'],\n },\n {\n id: 'preferences',\n name: 'Preferences',\n icon: SlidersHorizontal,\n paths: [\n '/dashboard/settings/language',\n '/dashboard/settings/accessibility',\n ],\n keywords: [\n 'appearance',\n 'theme',\n 'dark',\n 'light',\n 'language',\n 'time zone',\n 'date format',\n 'accessibility',\n 'high contrast',\n 'reduce motion',\n 'text size',\n ],\n },\n ...(process.env.NEXT_PUBLIC_BOX_PAYMENT_APPROVALS === 'true'\n ? [PAYMENT_APPROVALS_ROW]\n : []),\n // A licensed box that bills outside the console hides the row.\n ...(process.env.NEXT_PUBLIC_BOX_HIDE_BILLING === 'true' ? [] : [BILLING_ROW]),\n // A box that meters nothing in the console leaves Usage out.\n ...(process.env.NEXT_PUBLIC_BOX_HIDE_USAGE === 'true'\n ? []\n : [\n {\n id: 'usage' as const,\n name: 'Usage',\n icon: ChartColumn,\n paths: ['/dashboard/settings/usage'],\n keywords: ['credits', 'delivery', 'spend'],\n permission: ['canManageSettings' as const],\n },\n ]),\n {\n id: 'policies',\n name: 'Policies',\n icon: Scale,\n paths: ['/dashboard/settings/policies'],\n keywords: ['returns', 'shipping', 'privacy', 'terms'],\n permission: ['canManageSettings'],\n },\n {\n id: 'legal',\n name: 'Legal',\n icon: FileText,\n paths: ['/dashboard/settings/legal'],\n keywords: ['terms', 'privacy policy', 'licences'],\n },\n];\n\n/** Where \"Get help\" goes. It leaves the window, so it is not a row above. */\nexport const HELP_HREF = '/dashboard/help';\nexport const HELP_ICON = CircleHelp;\n\nconst pathOf = (full: string) => full.split(/[?#]/)[0];\n\n/**\n * The row that owns an address: the longest of its paths that equals the\n * address or is a parent of it. General's own address is a prefix of every\n * other settings address, so it only owns itself.\n */\nexport function rowForHref(href: string | null | undefined): SettingsRowId {\n const path = pathOf(href ?? '/dashboard/settings');\n let best: { id: SettingsRowId; len: number } | null = null;\n for (const row of SETTINGS_ROWS) {\n for (const p of row.paths) {\n const owns =\n path === p || (p !== '/dashboard/settings' && path.startsWith(`${p}/`));\n if (owns && (!best || p.length > best.len))\n best = { id: row.id, len: p.length };\n }\n }\n return best?.id ?? 'general';\n}\n\n/** Rows whose name or inner sections match what was typed. */\nexport function matchRows(rows: SettingsRow[], query: string): SettingsRow[] {\n const q = query.trim().toLowerCase();\n if (!q) return rows;\n return rows.filter(\n (r) =>\n r.name.toLowerCase().includes(q) || r.keywords.some((k) => k.includes(q)),\n );\n}\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AA0CP,MAAM,cAA2B;AAAA,EAC/B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO,CAAC,6BAA6B;AAAA,EACrC,UAAU,CAAC,QAAQ,WAAW,WAAW,UAAU,QAAQ;AAAA,EAC3D,YAAY,CAAC,mBAAmB;AAClC;AAGA,MAAM,wBAAqC;AAAA,EACzC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO,CAAC,uCAAuC;AAAA,EAC/C,UAAU,CAAC,YAAY,YAAY,YAAY,aAAa,SAAS;AACvE;AAEO,MAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,iBAAiB,YAAY,QAAQ,WAAW,WAAW;AAAA,IACtE,YAAY,CAAC,4BAA4B,mBAAmB;AAAA,EAC9D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,UAAU,UAAU,SAAS,YAAY,OAAO;AAAA,IAC3D,YAAY,CAAC,eAAe;AAAA;AAAA;AAAA,IAG5B,MAAM,QAAQ,IAAI,6BAA6B;AAAA,EACjD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC,mCAAmC;AAAA,IAC3C,UAAU,CAAC,UAAU,aAAa,gBAAgB,UAAU,WAAW;AAAA,EACzE;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,GAAI,QAAQ,IAAI,sCAAsC,SAClD,CAAC,qBAAqB,IACtB,CAAC;AAAA;AAAA,EAEL,GAAI,QAAQ,IAAI,iCAAiC,SAAS,CAAC,IAAI,CAAC,WAAW;AAAA;AAAA,EAE3E,GAAI,QAAQ,IAAI,+BAA+B,SAC3C,CAAC,IACD;AAAA,IACE;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO,CAAC,2BAA2B;AAAA,MACnC,UAAU,CAAC,WAAW,YAAY,OAAO;AAAA,MACzC,YAAY,CAAC,mBAA4B;AAAA,IAC3C;AAAA,EACF;AAAA,EACJ;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC,8BAA8B;AAAA,IACtC,UAAU,CAAC,WAAW,YAAY,WAAW,OAAO;AAAA,IACpD,YAAY,CAAC,mBAAmB;AAAA,EAClC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC,2BAA2B;AAAA,IACnC,UAAU,CAAC,SAAS,kBAAkB,UAAU;AAAA,EAClD;AACF;AAGO,MAAM,YAAY;AAClB,MAAM,YAAY;AAEzB,MAAM,SAAS,CAAC,SAAiB,KAAK,MAAM,MAAM,EAAE,CAAC;AAO9C,SAAS,WAAW,MAAgD;AACzE,QAAM,OAAO,OAAO,QAAQ,qBAAqB;AACjD,MAAI,OAAkD;AACtD,aAAW,OAAO,eAAe;AAC/B,eAAW,KAAK,IAAI,OAAO;AACzB,YAAM,OACJ,SAAS,KAAM,MAAM,yBAAyB,KAAK,WAAW,GAAG,CAAC,GAAG;AACvE,UAAI,SAAS,CAAC,QAAQ,EAAE,SAAS,KAAK;AACpC,eAAO,EAAE,IAAI,IAAI,IAAI,KAAK,EAAE,OAAO;AAAA,IACvC;AAAA,EACF;AACA,SAAO,MAAM,MAAM;AACrB;AAGO,SAAS,UAAU,MAAqB,OAA8B;AAC3E,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,KAAK;AAAA,IACV,CAAC,MACC,EAAE,KAAK,YAAY,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAAA,EAC5E;AACF;","names":[]}