@saasicat/ui-vue 1.0.0-rc.2 → 1.0.0-rc.4

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 (124) hide show
  1. package/README.md +50 -14
  2. package/dist/.build-stamp +1 -1
  3. package/dist/{batch-column-fetcher-VwjOxerL.d.ts → batch-column-fetcher-BsX1cJiU.d.ts} +1 -1
  4. package/dist/{batch-column-fetcher-Dk9X92db.d.cts → batch-column-fetcher-DLq3zI7m.d.cts} +1 -1
  5. package/dist/{catalog-C5VKGbxk.d.cts → catalog-D257Y044.d.cts} +3 -1
  6. package/dist/{catalog-C5VKGbxk.d.ts → catalog-D257Y044.d.ts} +3 -1
  7. package/dist/{chunk-6X6AGH66.js → chunk-6B5WYQFC.js} +1 -1
  8. package/dist/{chunk-L7HPDVHV.js → chunk-JXHRJ2ZD.js} +34 -1
  9. package/dist/{chunk-P4EJF43F.js → chunk-RJDW4RJT.js} +6 -2
  10. package/dist/{chunk-A7JICGQ5.js → chunk-U634B27W.js} +1 -1
  11. package/dist/{chunk-4CJBABLZ.js → chunk-UHNJ2W3B.js} +57 -8
  12. package/dist/client/index.cjs +40 -2
  13. package/dist/client/index.d.cts +23 -5
  14. package/dist/client/index.d.ts +23 -5
  15. package/dist/client/index.js +5 -3
  16. package/dist/index.cjs +97 -9
  17. package/dist/index.d.cts +5 -5
  18. package/dist/index.d.ts +5 -5
  19. package/dist/index.js +9 -5
  20. package/dist/quasar/index.cjs +6 -2
  21. package/dist/quasar/index.d.cts +2 -2
  22. package/dist/quasar/index.d.ts +2 -2
  23. package/dist/quasar/index.js +2 -2
  24. package/dist/{resource-registry-CrHUvVx6.d.cts → resource-registry-DvSYLWb-.d.cts} +1 -1
  25. package/dist/{resource-registry-Cw_c0F11.d.ts → resource-registry-rFrbzc_M.d.ts} +1 -1
  26. package/dist/testing/admin-pages-suite.cjs +4 -1
  27. package/dist/testing/admin-pages-suite.d.cts +10 -1
  28. package/dist/testing/admin-pages-suite.d.ts +10 -1
  29. package/dist/testing/admin-pages-suite.js +3 -1
  30. package/dist/testing/mount-with-quasar.cjs +65 -0
  31. package/dist/testing/mount-with-quasar.d.cts +7 -0
  32. package/dist/testing/mount-with-quasar.d.ts +7 -0
  33. package/dist/testing/mount-with-quasar.js +63 -0
  34. package/dist/vue/index.cjs +61 -7
  35. package/dist/vue/index.d.cts +39 -5
  36. package/dist/vue/index.d.ts +39 -5
  37. package/dist/vue/index.js +6 -4
  38. package/package.json +3 -3
  39. package/src/auth/SuperAdminLoginPage.vue +16 -16
  40. package/src/auth/SuperAdminSetupWizard.vue +33 -28
  41. package/src/client/http/axios-http-client.ts +4 -3
  42. package/src/client/i18n/messages/marketing.ts +6 -2
  43. package/src/client/index.ts +1 -0
  44. package/src/client/reorder-priorities.ts +76 -0
  45. package/src/features/bundle/BundleVersionInlineEditor.vue +92 -155
  46. package/src/features/bundle/BundleVersionPublishDialog.vue +27 -15
  47. package/src/features/bundle/BundleVersionStrip.vue +25 -31
  48. package/src/features/bundle/VersionDiffPreview.vue +9 -9
  49. package/src/features/bundle/internal/BundleCreatePanel.vue +63 -97
  50. package/src/features/bundle/internal/BundleFeaturesEditor.vue +12 -9
  51. package/src/features/bundle/internal/BundlePlanCompatPicker.vue +22 -19
  52. package/src/features/bundle/internal/BundleQuotasEditor.vue +18 -19
  53. package/src/features/bundle/internal/BundleStatusBanner.vue +12 -23
  54. package/src/features/marketing/MarketingPromotionsTab.vue +155 -120
  55. package/src/features/plan/PlanCreateDialog.vue +48 -128
  56. package/src/features/plan/PlanDetail.vue +51 -70
  57. package/src/features/plan/PlanList.vue +72 -64
  58. package/src/features/plan/PlanMatrix.vue +83 -96
  59. package/src/features/plan/PlanReview.vue +58 -54
  60. package/src/features/plan/PlanVersionEditor.vue +124 -216
  61. package/src/features/plan/internal/PlanCatalogPreview.vue +17 -13
  62. package/src/features/plan/internal/PlanComponentPool.vue +11 -5
  63. package/src/features/plan/internal/PlanTitleEdit.vue +12 -9
  64. package/src/features/plan/internal/PlanVersionBasket.vue +61 -47
  65. package/src/features/plan/internal/PlanVersionEditorHeader.vue +16 -20
  66. package/src/features/plan/internal/PlanVersionsPanel.vue +26 -43
  67. package/src/features/tenant/TenantActionConfirmDialog.vue +4 -4
  68. package/src/index.ts +1 -0
  69. package/src/internal/bundles-page/BundleAccordionList.vue +2 -2
  70. package/src/internal/bundles-page/BundlesToolbar.vue +9 -5
  71. package/src/internal/dialogs/PilotCreateDialog.vue +65 -104
  72. package/src/internal/dialogs/PilotEditDialog.vue +22 -23
  73. package/src/internal/dialogs/PromoCodeCreateDialog.vue +27 -23
  74. package/src/internal/dialogs/PromoCodeDialogFields.vue +203 -252
  75. package/src/internal/dialogs/PromoCodeEditDialog.vue +52 -48
  76. package/src/internal/dialogs/pilot-dialog.css +16 -111
  77. package/src/internal/discovery-page/CatalogEntryTransPanel.vue +18 -18
  78. package/src/internal/discovery-page/DiscoveryCapList.vue +15 -15
  79. package/src/internal/discovery-page/DiscoveryFeatureCard.vue +20 -20
  80. package/src/internal/discovery-page/DiscoveryQuotaCard.vue +10 -10
  81. package/src/internal/discovery-page/DiscoveryStatusControl.vue +1 -1
  82. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogAdmin.vue +333 -217
  83. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogHeader.vue +21 -16
  84. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogPreview.vue +1 -1
  85. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogToolbar.vue +14 -10
  86. package/src/internal/plan-versions/PlanDiffCard.vue +15 -15
  87. package/src/internal/plan-versions/PlanVersionsDiff.vue +11 -11
  88. package/src/internal/plan-versions/PlanVersionsTimeline.vue +35 -31
  89. package/src/internal/plans-page/PlanBundleOverview.vue +8 -8
  90. package/src/internal/plans-page/PlanPublishDialog.vue +2 -2
  91. package/src/internal/plans-page/PlansPageToast.vue +4 -4
  92. package/src/layouts/AdminLayout.vue +21 -21
  93. package/src/pages/AdminManifestErrorPage.vue +7 -7
  94. package/src/pages/AuditPage.vue +2 -2
  95. package/src/pages/BundlesPage.vue +24 -67
  96. package/src/pages/DashboardPage.vue +106 -97
  97. package/src/pages/DiscoveryPage.vue +22 -28
  98. package/src/pages/EmailHistoryPage.vue +19 -16
  99. package/src/pages/MarketingCatalogPage.vue +261 -263
  100. package/src/pages/PilotsPage.vue +14 -13
  101. package/src/pages/PlanVersionEditorPage.vue +1 -1
  102. package/src/pages/PlansPage.vue +73 -60
  103. package/src/pages/PlatformEmailPage.vue +27 -23
  104. package/src/pages/PromoCodeDetailPage.vue +12 -18
  105. package/src/pages/PromoCodesPage.vue +14 -13
  106. package/src/pages/TenantDetailPage.vue +15 -40
  107. package/src/pages/TenantsPage.vue +7 -7
  108. package/src/testing/admin-pages-suite.ts +11 -1
  109. package/src/testing/mount-with-quasar.ts +73 -0
  110. package/src/ui/data/AdminPaginator.vue +30 -20
  111. package/src/ui/data/KvBlock.vue +4 -4
  112. package/src/ui/entitlement/FeatureGate.vue +2 -2
  113. package/src/ui/feedback/AdminRefreshBtn.vue +10 -7
  114. package/src/ui/overlay/MfaPromptDialog.vue +3 -3
  115. package/src/ui/page/AdminAccordion.vue +27 -10
  116. package/src/ui/page/WizardStepper.vue +6 -6
  117. package/src/ui/theme/base.css +31 -0
  118. package/src/ui/theme/components/accordion.css +9 -9
  119. package/src/ui/theme/components/button.css +10 -68
  120. package/src/ui/theme/components/card.css +0 -8
  121. package/src/ui/theme/components/paginator.css +14 -28
  122. package/src/vue/index.ts +1 -0
  123. package/src/vue/use-row-reorder.ts +109 -0
  124. /package/src/internal/{marketing-catalog → marketing-catalog-page}/types.ts +0 -0
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @saasicat/ui-vue
2
2
 
3
+ ## What this is
4
+
3
5
  Vue 3 components + composables for the SuperAdmin UI shell. Provides the
4
6
  boot loader, manifest loader (ETag cache), nav builder, action registry,
5
7
  standard pages (Dashboard, Tenants, Plans, Discovery, …) and the tenant
@@ -9,18 +11,45 @@ self-service building blocks (`FeatureGate`, `useTenantManifest`,
9
11
  Peer dependencies: `vue` (required); `vue-router`, `pinia`, `quasar`
10
12
  (optional — see the layers below for what actually needs them).
11
13
 
12
- ## Layers
13
-
14
- The package is layered so that each entry only loads what it names.
15
- Lower layers never import upward; ESLint (`no-restricted-imports`, repo
16
- root config) enforces the boundaries in CI.
17
-
18
- | Entry | Source | May import | Contents |
19
- | ----------------------------------------------------------------------------------------------- | -------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
- | `@saasicat/ui-vue/client` | `src/client/` | `@saasicat/core` only | Framework-free core: `BootLoader`, `ManifestLoader` (ETag cache), nav builder, action registry, batch column fetcher, the `HttpClient`/`KvStore` contract with its `fetch` and axios adapters, `AdminError`. Usable from any framework or plain Node/TypeScript. |
21
- | `@saasicat/ui-vue` (main) | `src/vue/` (+ client re-exports) | Vue, `vue-router`, Pinia — **no Quasar** | Composables (`useTenants`, `usePlanEditor`, …), router guards, injection keys + shell contract, notify-port type, optional Pinia store factory. |
22
- | `@saasicat/ui-vue/quasar` | `src/quasar/` | everything above + Quasar | `createSuperAdminApp()` bootstrap and the Quasar notify-port implementation. |
23
- | `@saasicat/ui-vue/pages/*.vue`, `/layouts/*.vue`, `/auth/*.vue`, `/ui/*.vue`, `/pages-tenant/*` | SFC directories | everything | The Quasar reference UI, shipped as raw `.vue` from `src/` (compiled by the consumer's Vite). |
14
+ ## What this is not
15
+
16
+ Not a component library to pick from. It is the SuperAdmin application's
17
+ inside: complete pages, the shell around them, the theme they read and the
18
+ data layer beneath them. Taking one primitive out of context is possible and
19
+ rarely what you want.
20
+
21
+ Not a design system for your product, either. The `Admin*` roster exists for
22
+ admin surfaces; everything else is Quasar, styled through the theme.
23
+
24
+ Not compiled for the subpaths that ship `.vue` and `.ts` from `src/` — your
25
+ build compiles those, which is what makes Quasar and Sass theming work and why
26
+ that source stays within ES2021.
27
+
28
+ ## Entry points
29
+
30
+ The package is layered so that each entry only loads what it names. Lower
31
+ layers never import upward, and ESLint enforces it — the reasoning is in
32
+ [ADR 0004](../../docs/explanation/adr/0004-ui-vue-layer-boundaries.md).
33
+
34
+ | Entry | Source | May import | When you take it |
35
+ | --------------------- | --------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
36
+ | `./client` | `src/client/` | `@saasicat/core` only | Framework-free: loaders, nav builder, action registry, the resource descriptors, `HttpClient`/`KvStore` with their `fetch` and axios adapters, `AdminError`. Usable from plain Node. |
37
+ | `.` | `src/vue/` | Vue, `vue-router`, Pinia | Composables, router guards, injection keys, `createAdminRoutes`, the store factory. No Quasar. |
38
+ | `./quasar` | `src/quasar/` | everything above, plus Quasar | `createSuperAdminApp()` and the Quasar implementations of the notify and confirm ports. |
39
+ | `./pages` | `src/pages/` | everything | `standardAdminChildren()` — the platform's own screens as route children. |
40
+ | `./pages/*.vue` | `src/pages/` | everything | One standard page, when you mount it yourself. |
41
+ | `./layouts/*.vue` | `src/layouts/` | everything | `AdminLayout` — the shell around the pages. |
42
+ | `./auth/*.vue` | `src/auth/` | everything | The login screen and the first-run setup wizard. |
43
+ | `./ui/*.vue` | `src/ui/` | everything | The `Admin*` primitives, when you build a page of your own. |
44
+ | `./theme.css` | `src/ui/theme/` | — | The stylesheet. Import it once; without it the pages render unstyled. |
45
+ | `./theme/*` | `src/ui/theme/` | — | The individual token layers, for a consumer that overrides roles. |
46
+ | `./theme/breakpoints` | `src/ui/theme/` | — | The five breakpoint values, for a build that needs them in JavaScript. |
47
+ | `./vue` | `src/vue/` | Vue, `vue-router`, Pinia | The Vue layer alone, without the client re-exports. |
48
+ | `./testing/*` | `src/testing/` | everything | Fixtures for component and end-to-end tests against the pages. |
49
+
50
+ The four subpaths that hand out `.vue` and `.ts` from `src/` are compiled by
51
+ **your** build, which is what makes Quasar and Sass theming work —
52
+ [ADR 0005](../../docs/explanation/adr/0005-ship-sfc-source-not-dist.md).
24
53
 
25
54
  ### The shipped source has a language floor
26
55
 
@@ -125,7 +154,8 @@ pnpm create saasicat-admin admin --project-key=myapp
125
154
  ```
126
155
 
127
156
  See the [quickstart](https://github.com/uelker70/saasicat/blob/main/docs/quickstart.md) (step 9) for the full setup
128
- and the [handbook](https://github.com/uelker70/saasicat/blob/main/docs/handbook.md) for the architecture.
157
+ and the [architecture](https://github.com/uelker70/saasicat/blob/main/docs/explanation/architecture.md)
158
+ for how the pieces fit.
129
159
 
130
160
  ## Language
131
161
 
@@ -145,7 +175,7 @@ own call: `i18n.locales` narrows the set, `i18n.additionalLocales` adds
145
175
  languages the platform does not ship, `i18n.overrides` replaces individual
146
176
  strings, and `i18n.switcher: false` drops the control entirely. Components read
147
177
  the catalog via `useSaMessages('<namespace>')` / `useSuperAdminI18n()`.
148
- See [handbook §8.7](https://github.com/uelker70/saasicat/blob/main/docs/handbook.md#87-ui-language-i18n).
178
+ See [UI language](https://github.com/uelker70/saasicat/blob/main/docs/guides/build-the-admin-frontend.md#ui-language-i18n).
149
179
 
150
180
  ## Build
151
181
 
@@ -155,3 +185,9 @@ pnpm --filter @saasicat/ui-vue build
155
185
 
156
186
  Produces `dist/{index,client/index,quasar/index}.{js,cjs,d.ts}` via tsup
157
187
  (`tsup.config.ts`); `vue`, `vue-router`, `pinia` and `quasar` stay external.
188
+
189
+ ## Next
190
+
191
+ - [Build the admin frontend](../../docs/guides/build-the-admin-frontend.md) — wiring it into an app
192
+ - [Design guide](../../docs/explanation/design-guide.md) — the page recipe and the colour roles
193
+ - [Pages take resources, not callbacks](../../docs/explanation/adr/0008-resource-ports-over-props.md)
package/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 14b1904cc8763759de50625a5589d989cdf7e6be7f84abe3f6c54aa3ca8ac9ae
1
+ 024a49d3b5411560f32b7690e677e4a35c170ed8457314fd33c2493b868a2cc9
@@ -1,4 +1,4 @@
1
- import { a5 as SaMessages, H as HttpClient, l as KvStore, a2 as SaLocale, aA as SaNavMessages } from './catalog-C5VKGbxk.js';
1
+ import { a5 as SaMessages, H as HttpClient, l as KvStore, a2 as SaLocale, aA as SaNavMessages } from './catalog-D257Y044.js';
2
2
  import { PublicBootResponse, AdminManifest, StandardPageKey, TenantColumnDef } from '@saasicat/core';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { a5 as SaMessages, H as HttpClient, l as KvStore, a2 as SaLocale, aA as SaNavMessages } from './catalog-C5VKGbxk.cjs';
1
+ import { a5 as SaMessages, H as HttpClient, l as KvStore, a2 as SaLocale, aA as SaNavMessages } from './catalog-D257Y044.cjs';
2
2
  import { PublicBootResponse, AdminManifest, StandardPageKey, TenantColumnDef } from '@saasicat/core';
3
3
 
4
4
  /**
@@ -1320,8 +1320,10 @@ declare const marketingMessages: Record<SaBuiltinLocale, {
1320
1320
  colPlan: string;
1321
1321
  colVisible: string;
1322
1322
  colBadge: string;
1323
- colPriority: string;
1323
+ colOrder: string;
1324
1324
  colHighlight: string;
1325
+ reorderLabel: string;
1326
+ reorderTitle: string;
1325
1327
  versionTabsLabel: string;
1326
1328
  versionTitle: string;
1327
1329
  versionValidFromUnknown: string;
@@ -1320,8 +1320,10 @@ declare const marketingMessages: Record<SaBuiltinLocale, {
1320
1320
  colPlan: string;
1321
1321
  colVisible: string;
1322
1322
  colBadge: string;
1323
- colPriority: string;
1323
+ colOrder: string;
1324
1324
  colHighlight: string;
1325
+ reorderLabel: string;
1326
+ reorderTitle: string;
1325
1327
  versionTabsLabel: string;
1326
1328
  versionTitle: string;
1327
1329
  versionValidFromUnknown: string;
@@ -3,7 +3,7 @@ import {
3
3
  defaultHttpClient,
4
4
  isSaBuiltinLocale,
5
5
  navMessages
6
- } from "./chunk-P4EJF43F.js";
6
+ } from "./chunk-RJDW4RJT.js";
7
7
 
8
8
  // src/client/nav-builder.ts
9
9
  var DEFAULT_STANDARD_PAGE_ROUTES = {
@@ -3,7 +3,7 @@ import {
3
3
  isAxiosNoResponseError,
4
4
  markTransportFailure,
5
5
  trimTrailingSlashes
6
- } from "./chunk-P4EJF43F.js";
6
+ } from "./chunk-RJDW4RJT.js";
7
7
 
8
8
  // src/client/version.ts
9
9
  var ADMIN_UI_VERSION = "1.2.0";
@@ -173,6 +173,38 @@ function isProductionBoot(boot) {
173
173
  return boot?.project?.environment === "production";
174
174
  }
175
175
 
176
+ // src/client/reorder-priorities.ts
177
+ import { MARKETING_PRIORITY_MAX, MARKETING_PRIORITY_MIN } from "@saasicat/core";
178
+ function reorderedPriorities(priorities, from, to) {
179
+ const count = priorities.length;
180
+ if (from === to || from < 0 || to < 0 || from >= count || to >= count) {
181
+ return priorities.map(() => null);
182
+ }
183
+ const order = priorities.map((_, index) => index);
184
+ order.splice(to, 0, ...order.splice(from, 1));
185
+ const values = descendingDistinct(priorities);
186
+ const next = priorities.map(() => null);
187
+ order.forEach((originalIndex, position) => {
188
+ const value = values[position];
189
+ if (value !== priorities[originalIndex]) next[originalIndex] = value;
190
+ });
191
+ return next;
192
+ }
193
+ function descendingDistinct(priorities) {
194
+ const values = [...priorities].sort((a, b) => b - a);
195
+ const count = values.length;
196
+ for (let i = 0; i < count; i++) {
197
+ values[i] = Math.min(values[i], MARKETING_PRIORITY_MAX - i);
198
+ }
199
+ for (let i = 1; i < count; i++) {
200
+ values[i] = Math.min(values[i], values[i - 1] - 1);
201
+ }
202
+ for (let i = count - 1; i >= 0; i--) {
203
+ values[i] = Math.max(values[i], MARKETING_PRIORITY_MIN + (count - 1 - i));
204
+ }
205
+ return values;
206
+ }
207
+
176
208
  // src/client/resolve-plans.ts
177
209
  function todayIsoDate() {
178
210
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -311,6 +343,7 @@ export {
311
343
  createAdminResourceClient,
312
344
  resolveLoginBranding,
313
345
  isProductionBoot,
346
+ reorderedPriorities,
314
347
  todayIsoDate,
315
348
  isCurrentlyValid,
316
349
  isFutureScheduled,
@@ -2420,8 +2420,10 @@ var marketingMessages = defineMessages(
2420
2420
  colPlan: "Plan",
2421
2421
  colVisible: "Sichtbar",
2422
2422
  colBadge: "Badge",
2423
- colPriority: "Priorit\xE4t",
2423
+ colOrder: "Reihenfolge",
2424
2424
  colHighlight: "Highlight",
2425
+ reorderLabel: "{plan} verschieben \xB7 Position {position} von {total}",
2426
+ reorderTitle: "Ziehen oder Pfeiltasten: Reihenfolge \xE4ndern",
2425
2427
  versionTabsLabel: "Plan-Versionen f\xFCr {plan}",
2426
2428
  versionTitle: "Version {version}: g\xFCltig {from} bis {until}{changeNote}",
2427
2429
  versionValidFromUnknown: "unbekannt",
@@ -2544,8 +2546,10 @@ var marketingMessages = defineMessages(
2544
2546
  colPlan: "Plan",
2545
2547
  colVisible: "Visible",
2546
2548
  colBadge: "Badge",
2547
- colPriority: "Priority",
2549
+ colOrder: "Order",
2548
2550
  colHighlight: "Highlight",
2551
+ reorderLabel: "Move {plan} \xB7 position {position} of {total}",
2552
+ reorderTitle: "Drag or use the arrow keys to reorder",
2549
2553
  versionTabsLabel: "Plan versions for {plan}",
2550
2554
  versionTitle: "Version {version}: valid {from} to {until}{changeNote}",
2551
2555
  versionValidFromUnknown: "unknown",
@@ -4,7 +4,7 @@ import {
4
4
  bindResource,
5
5
  createSaCatalog,
6
6
  defaultKvStore
7
- } from "./chunk-P4EJF43F.js";
7
+ } from "./chunk-RJDW4RJT.js";
8
8
 
9
9
  // src/vue/super-admin-context.ts
10
10
  var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
@@ -4,7 +4,7 @@ import {
4
4
  buildRoutes,
5
5
  buildSidebar,
6
6
  defaultSectionOrder
7
- } from "./chunk-6X6AGH66.js";
7
+ } from "./chunk-6B5WYQFC.js";
8
8
  import {
9
9
  SUPER_ADMIN_ACTIONS_KEY,
10
10
  SUPER_ADMIN_BRAND_KEY,
@@ -17,7 +17,7 @@ import {
17
17
  useResource,
18
18
  useSaMessages,
19
19
  useSuperAdminI18n
20
- } from "./chunk-A7JICGQ5.js";
20
+ } from "./chunk-U634B27W.js";
21
21
  import {
22
22
  AdminError,
23
23
  BootLoader,
@@ -38,7 +38,7 @@ import {
38
38
  requireServerAnswer,
39
39
  toAdminError,
40
40
  trimTrailingSlashes
41
- } from "./chunk-P4EJF43F.js";
41
+ } from "./chunk-RJDW4RJT.js";
42
42
 
43
43
  // src/vue/status.ts
44
44
  var PROMO_STATUS_TONE = {
@@ -2636,15 +2636,63 @@ function useBatchColumns(manifest, tenantIds, options = {}) {
2636
2636
  return { data, loading, error, reload: load };
2637
2637
  }
2638
2638
 
2639
+ // src/vue/use-row-reorder.ts
2640
+ import { onScopeDispose, ref as ref28 } from "vue";
2641
+ function useRowReorder(rowCount, handleOf, onDrop) {
2642
+ const draggingIndex = ref28(null);
2643
+ const targetIndex = ref28(null);
2644
+ function positionAt(clientY, from) {
2645
+ const count = rowCount();
2646
+ let insertAt = count;
2647
+ for (let index = 0; index < count; index++) {
2648
+ const rect = handleOf(index)?.getBoundingClientRect();
2649
+ if (rect && clientY < rect.top + rect.height / 2) {
2650
+ insertAt = index;
2651
+ break;
2652
+ }
2653
+ }
2654
+ return insertAt > from ? insertAt - 1 : insertAt;
2655
+ }
2656
+ function onMove(event) {
2657
+ if (draggingIndex.value === null) return;
2658
+ targetIndex.value = positionAt(event.clientY, draggingIndex.value);
2659
+ }
2660
+ function onUp() {
2661
+ const from = draggingIndex.value;
2662
+ const to = targetIndex.value;
2663
+ stop();
2664
+ if (from !== null && to !== null && from !== to) onDrop(from, to);
2665
+ }
2666
+ function stop() {
2667
+ draggingIndex.value = null;
2668
+ targetIndex.value = null;
2669
+ document.removeEventListener("pointermove", onMove);
2670
+ document.removeEventListener("pointerup", onUp);
2671
+ document.removeEventListener("pointercancel", stop);
2672
+ }
2673
+ function start(index, event) {
2674
+ if (event.button !== 0) return;
2675
+ event.preventDefault();
2676
+ draggingIndex.value = index;
2677
+ targetIndex.value = index;
2678
+ event.currentTarget?.setPointerCapture?.(event.pointerId);
2679
+ document.addEventListener("pointermove", onMove);
2680
+ document.addEventListener("pointerup", onUp);
2681
+ document.addEventListener("pointercancel", stop);
2682
+ }
2683
+ onScopeDispose(stop);
2684
+ return { draggingIndex, targetIndex, start };
2685
+ }
2686
+
2639
2687
  // src/vue/manifest-store-factory.ts
2640
2688
  import { defineStore } from "pinia";
2641
- import { ref as ref28 } from "vue";
2689
+ import { ref as ref29 } from "vue";
2642
2690
  function createManifestStore(options) {
2643
2691
  return defineStore(options.id ?? "admin-manifest", () => {
2644
- const manifest = ref28(null);
2645
- const loading = ref28(false);
2646
- const error = ref28(null);
2647
- const loaded = ref28(false);
2692
+ const manifest = ref29(null);
2693
+ const loading = ref29(false);
2694
+ const error = ref29(null);
2695
+ const loaded = ref29(false);
2648
2696
  let inflight = null;
2649
2697
  async function load() {
2650
2698
  loading.value = true;
@@ -2748,5 +2796,6 @@ export {
2748
2796
  defaultToneForActionKey,
2749
2797
  usePlatformTenantActions,
2750
2798
  useBatchColumns,
2799
+ useRowReorder,
2751
2800
  createManifestStore
2752
2801
  };
@@ -107,6 +107,7 @@ __export(client_exports, {
107
107
  readErrorCode: () => readErrorCode,
108
108
  readErrorDetail: () => readErrorDetail,
109
109
  readListPage: () => readListPage,
110
+ reorderedPriorities: () => reorderedPriorities,
110
111
  requestJson: () => requestJson,
111
112
  requestJsonBody: () => requestJsonBody,
112
113
  requireServerAnswer: () => requireServerAnswer,
@@ -3056,8 +3057,10 @@ var marketingMessages = defineMessages(
3056
3057
  colPlan: "Plan",
3057
3058
  colVisible: "Sichtbar",
3058
3059
  colBadge: "Badge",
3059
- colPriority: "Priorit\xE4t",
3060
+ colOrder: "Reihenfolge",
3060
3061
  colHighlight: "Highlight",
3062
+ reorderLabel: "{plan} verschieben \xB7 Position {position} von {total}",
3063
+ reorderTitle: "Ziehen oder Pfeiltasten: Reihenfolge \xE4ndern",
3061
3064
  versionTabsLabel: "Plan-Versionen f\xFCr {plan}",
3062
3065
  versionTitle: "Version {version}: g\xFCltig {from} bis {until}{changeNote}",
3063
3066
  versionValidFromUnknown: "unbekannt",
@@ -3180,8 +3183,10 @@ var marketingMessages = defineMessages(
3180
3183
  colPlan: "Plan",
3181
3184
  colVisible: "Visible",
3182
3185
  colBadge: "Badge",
3183
- colPriority: "Priority",
3186
+ colOrder: "Order",
3184
3187
  colHighlight: "Highlight",
3188
+ reorderLabel: "Move {plan} \xB7 position {position} of {total}",
3189
+ reorderTitle: "Drag or use the arrow keys to reorder",
3185
3190
  versionTabsLabel: "Plan versions for {plan}",
3186
3191
  versionTitle: "Version {version}: valid {from} to {until}{changeNote}",
3187
3192
  versionValidFromUnknown: "unknown",
@@ -5055,6 +5060,38 @@ function isProductionBoot(boot) {
5055
5060
  return boot?.project?.environment === "production";
5056
5061
  }
5057
5062
 
5063
+ // src/client/reorder-priorities.ts
5064
+ var import_core2 = require("@saasicat/core");
5065
+ function reorderedPriorities(priorities, from, to) {
5066
+ const count = priorities.length;
5067
+ if (from === to || from < 0 || to < 0 || from >= count || to >= count) {
5068
+ return priorities.map(() => null);
5069
+ }
5070
+ const order = priorities.map((_, index) => index);
5071
+ order.splice(to, 0, ...order.splice(from, 1));
5072
+ const values = descendingDistinct(priorities);
5073
+ const next = priorities.map(() => null);
5074
+ order.forEach((originalIndex, position) => {
5075
+ const value = values[position];
5076
+ if (value !== priorities[originalIndex]) next[originalIndex] = value;
5077
+ });
5078
+ return next;
5079
+ }
5080
+ function descendingDistinct(priorities) {
5081
+ const values = [...priorities].sort((a, b) => b - a);
5082
+ const count = values.length;
5083
+ for (let i = 0; i < count; i++) {
5084
+ values[i] = Math.min(values[i], import_core2.MARKETING_PRIORITY_MAX - i);
5085
+ }
5086
+ for (let i = 1; i < count; i++) {
5087
+ values[i] = Math.min(values[i], values[i - 1] - 1);
5088
+ }
5089
+ for (let i = count - 1; i >= 0; i--) {
5090
+ values[i] = Math.max(values[i], import_core2.MARKETING_PRIORITY_MIN + (count - 1 - i));
5091
+ }
5092
+ return values;
5093
+ }
5094
+
5058
5095
  // src/client/resolve-plans.ts
5059
5096
  function todayIsoDate() {
5060
5097
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -5275,6 +5312,7 @@ function trimChar(value, ch) {
5275
5312
  readErrorCode,
5276
5313
  readErrorDetail,
5277
5314
  readListPage,
5315
+ reorderedPriorities,
5278
5316
  requestJson,
5279
5317
  requestJsonBody,
5280
5318
  requireServerAnswer,
@@ -1,7 +1,7 @@
1
- import { H as HttpClient, T as ResourceDef, S as ResourceContext, y as PilotRow, u as PilotCreatePayload, v as PilotCreateResult, w as PilotEditPayload, x as PilotEditResult, f as EmailHistoryFilter, g as EmailHistoryListResult, E as EmailHistoryDetail, h as EmailHistoryResendResult, z as PlatformEmailProvider, G as PlatformEmailWriteInput, C as PlatformEmailTestInput, F as PlatformEmailTestResult, a2 as SaLocale } from '../catalog-C5VKGbxk.cjs';
2
- export { A as ActionDefNotInManifestError, a as ActionHandler, b as ActionRegistry, c as AuditListFilter, B as Bound, D as DEFAULT_SA_LOCALE, d as DefineResourceOptions, e as DiscoveryRead, i as EmailHistoryRow, k as HttpResponse, K as KpiReading, l as KvStore, L as LIST_FIRST_PAGE, m as LIST_PAGE_SIZE_DEFAULT, n as LIST_PAGE_SIZE_MAX, o as LIST_PAGINATION_PARAMS, p as ListFilterOf, q as ListOp, r as ListQuery, M as MarketingProjectionsFilter, s as MessageTree, t as MissingHandlerError, P as PartialMessages, I as PlatformResources, J as PromoCodesFilter, N as PromoDetail, O as PublishBundleVersionOptions, Q as PublishPlanVersionOptions, R as ResolvedAction, U as ResourceListPage, V as ResourceOp, W as ResourceOps, X as SA_INTL_LOCALES, Y as SA_LOCALES, Z as SA_LOCALE_LABELS, _ as SA_MESSAGES, $ as SaBuiltinLocale, a0 as SaCatalog, a1 as SaCatalogOptions, a3 as SaLocaleDefinition, a4 as SaLocaleOption, a5 as SaMessages, a6 as SaMessagesOverrides, a7 as TenantsListFilter, a8 as TranslationOf, a9 as auditResource, aa as bindResource, ab as bundleVersionsResource, ac as bundlesResource, ad as clampListPage, ae as clampListPageSize, af as createSaCatalog, ag as dashboardResource, ah as defaultHttpClient, ai as defaultKvStore, aj as defineListOp, ak as defineMessages, al as defineResource, am as discoveryResource, an as filterQueryString, ao as isSaBuiltinLocale, ap as isSentInQuery, aq as listUrl, ar as marketingResource, as as mergeMessages, at as planVersionsResource, au as plansResource, av as platformResources, aw as promoCodesResource, ax as readListPage, ay as resolveMessages, az as tenantsResource } from '../catalog-C5VKGbxk.cjs';
3
- import { A as AdminError } from '../batch-column-fetcher-Dk9X92db.cjs';
4
- export { a as AdminErrorInit, B as BatchColumnData, b as BatchColumnDriftError, c as BatchColumnFetcher, d as BatchColumnFetcherOptions, e as BatchColumnRow, f as BatchColumnValue, g as BootLoadError, h as BootLoader, i as BootLoaderOptions, j as BuildRouteEntry, C as CachedManifestEntry, D as DEFAULT_STANDARD_PAGE_ROUTES, M as ManifestLoadError, k as ManifestLoader, l as ManifestLoaderOptions, N as NavBuilderOptions, P as ParamStyle, S as SidebarItem, m as SidebarSection, n as adminErrorMessage, o as buildRoutes, p as buildSidebar, q as defaultSectionOrder, r as httpStatusOf, s as isAdminError, t as isAxiosNoResponseError, u as isEmptyResponse, v as isPlatformError, w as isTransportFailure, x as markEmptyResponse, y as markPlatformError, z as markTransportFailure, E as readErrorCode, F as readErrorDetail, G as resolveExtension, H as toAdminError } from '../batch-column-fetcher-Dk9X92db.cjs';
1
+ import { H as HttpClient, T as ResourceDef, S as ResourceContext, y as PilotRow, u as PilotCreatePayload, v as PilotCreateResult, w as PilotEditPayload, x as PilotEditResult, f as EmailHistoryFilter, g as EmailHistoryListResult, E as EmailHistoryDetail, h as EmailHistoryResendResult, z as PlatformEmailProvider, G as PlatformEmailWriteInput, C as PlatformEmailTestInput, F as PlatformEmailTestResult, a2 as SaLocale } from '../catalog-D257Y044.cjs';
2
+ export { A as ActionDefNotInManifestError, a as ActionHandler, b as ActionRegistry, c as AuditListFilter, B as Bound, D as DEFAULT_SA_LOCALE, d as DefineResourceOptions, e as DiscoveryRead, i as EmailHistoryRow, k as HttpResponse, K as KpiReading, l as KvStore, L as LIST_FIRST_PAGE, m as LIST_PAGE_SIZE_DEFAULT, n as LIST_PAGE_SIZE_MAX, o as LIST_PAGINATION_PARAMS, p as ListFilterOf, q as ListOp, r as ListQuery, M as MarketingProjectionsFilter, s as MessageTree, t as MissingHandlerError, P as PartialMessages, I as PlatformResources, J as PromoCodesFilter, N as PromoDetail, O as PublishBundleVersionOptions, Q as PublishPlanVersionOptions, R as ResolvedAction, U as ResourceListPage, V as ResourceOp, W as ResourceOps, X as SA_INTL_LOCALES, Y as SA_LOCALES, Z as SA_LOCALE_LABELS, _ as SA_MESSAGES, $ as SaBuiltinLocale, a0 as SaCatalog, a1 as SaCatalogOptions, a3 as SaLocaleDefinition, a4 as SaLocaleOption, a5 as SaMessages, a6 as SaMessagesOverrides, a7 as TenantsListFilter, a8 as TranslationOf, a9 as auditResource, aa as bindResource, ab as bundleVersionsResource, ac as bundlesResource, ad as clampListPage, ae as clampListPageSize, af as createSaCatalog, ag as dashboardResource, ah as defaultHttpClient, ai as defaultKvStore, aj as defineListOp, ak as defineMessages, al as defineResource, am as discoveryResource, an as filterQueryString, ao as isSaBuiltinLocale, ap as isSentInQuery, aq as listUrl, ar as marketingResource, as as mergeMessages, at as planVersionsResource, au as plansResource, av as platformResources, aw as promoCodesResource, ax as readListPage, ay as resolveMessages, az as tenantsResource } from '../catalog-D257Y044.cjs';
3
+ import { A as AdminError } from '../batch-column-fetcher-DLq3zI7m.cjs';
4
+ export { a as AdminErrorInit, B as BatchColumnData, b as BatchColumnDriftError, c as BatchColumnFetcher, d as BatchColumnFetcherOptions, e as BatchColumnRow, f as BatchColumnValue, g as BootLoadError, h as BootLoader, i as BootLoaderOptions, j as BuildRouteEntry, C as CachedManifestEntry, D as DEFAULT_STANDARD_PAGE_ROUTES, M as ManifestLoadError, k as ManifestLoader, l as ManifestLoaderOptions, N as NavBuilderOptions, P as ParamStyle, S as SidebarItem, m as SidebarSection, n as adminErrorMessage, o as buildRoutes, p as buildSidebar, q as defaultSectionOrder, r as httpStatusOf, s as isAdminError, t as isAxiosNoResponseError, u as isEmptyResponse, v as isPlatformError, w as isTransportFailure, x as markEmptyResponse, y as markPlatformError, z as markTransportFailure, E as readErrorCode, F as readErrorDetail, G as resolveExtension, H as toAdminError } from '../batch-column-fetcher-DLq3zI7m.cjs';
5
5
  import { CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, DiscoverySnapshot, SyncDiscoveryResult, PromotionRow, CreatePromotionData, UpdatePromotionData, AdminUserListFilter, AdminUserListRow, AdminSubscriptionListRow, PromoCodeRecord, AdminTenantDetail, AdminAuditListFilter, AuditEntry } from '@saasicat/core';
6
6
 
7
7
  declare const ADMIN_UI_VERSION = "1.2.0";
@@ -383,6 +383,24 @@ declare function isProductionBoot(boot: {
383
383
  project?: LoginBootProject | null;
384
384
  } | null | undefined): boolean;
385
385
 
386
+ /**
387
+ * The priority values that turn a drag into a new order.
388
+ *
389
+ * The marketing list sorts by priority descending, so "move this row here" has
390
+ * to be expressed as numbers before it can be persisted. Two things make that
391
+ * more than an assignment:
392
+ *
393
+ * - **Ties.** Every plan starts at `0`, and swapping two zeroes changes
394
+ * nothing. The values have to be pulled apart before they can carry an order.
395
+ * - **Existing values mean something.** An operator who set 100 / 50 / 10 chose
396
+ * those gaps. Renumbering to 3 / 2 / 1 would answer a question nobody asked,
397
+ * so the values are kept and only their assignment moves.
398
+ *
399
+ * The result is per ORIGINAL index, and `null` where a row keeps what it has —
400
+ * each change is one write, so not changing is worth reporting.
401
+ */
402
+ declare function reorderedPriorities(priorities: readonly number[], from: number, to: number): (number | null)[];
403
+
386
404
  interface PlanRowLike {
387
405
  id: string;
388
406
  planKey: string;
@@ -512,4 +530,4 @@ declare function looksLikeEmail(value: string): boolean;
512
530
  /** `value` without any run of `ch` at either end — `trim()` for one character. */
513
531
  declare function trimChar(value: string, ch: string): string;
514
532
 
515
- export { ADMIN_UI_VERSION, AdminError, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type AxiosHttpClientOptions, type AxiosLike, type AxiosLikeConfig, type AxiosLikeResponse, type AxiosLikeResponseConfig, type AxiosResponseBody, EmailHistoryDetail, EmailHistoryFilter, EmailHistoryListResult, EmailHistoryResendResult, type FetchHttpClientOptions, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, type LoginBootProject, type LoginBrandFallback, type LoginBranding, type MessageParams, PilotCreatePayload, PilotCreateResult, PilotEditPayload, PilotEditResult, PilotRow, type PlanCounts, type PlanRowLike, type PlanVersionLike, PlatformEmailProvider, PlatformEmailTestInput, PlatformEmailTestResult, PlatformEmailWriteInput, type ResolvePlansInput, type ResolvedPlan, ResourceContext, ResourceDef, type ResourceRequestInit, SaLocale, catalogResource, countPlans, createAdminResourceClient, createAxiosHttpClient, createFetchHttpClient, emailHistoryResource, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, looksLikeEmail, pilotsResource, platformEmailResource, postJson, promotionsResource, requestJson, requestJsonBody, requireServerAnswer, resolveLoginBranding, resolvePlans, subscriptionsResource, todayIsoDate, trimChar, trimTrailingSlashes, usersResource };
533
+ export { ADMIN_UI_VERSION, AdminError, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type AxiosHttpClientOptions, type AxiosLike, type AxiosLikeConfig, type AxiosLikeResponse, type AxiosLikeResponseConfig, type AxiosResponseBody, EmailHistoryDetail, EmailHistoryFilter, EmailHistoryListResult, EmailHistoryResendResult, type FetchHttpClientOptions, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, type LoginBootProject, type LoginBrandFallback, type LoginBranding, type MessageParams, PilotCreatePayload, PilotCreateResult, PilotEditPayload, PilotEditResult, PilotRow, type PlanCounts, type PlanRowLike, type PlanVersionLike, PlatformEmailProvider, PlatformEmailTestInput, PlatformEmailTestResult, PlatformEmailWriteInput, type ResolvePlansInput, type ResolvedPlan, ResourceContext, ResourceDef, type ResourceRequestInit, SaLocale, catalogResource, countPlans, createAdminResourceClient, createAxiosHttpClient, createFetchHttpClient, emailHistoryResource, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, looksLikeEmail, pilotsResource, platformEmailResource, postJson, promotionsResource, reorderedPriorities, requestJson, requestJsonBody, requireServerAnswer, resolveLoginBranding, resolvePlans, subscriptionsResource, todayIsoDate, trimChar, trimTrailingSlashes, usersResource };
@@ -1,7 +1,7 @@
1
- import { H as HttpClient, T as ResourceDef, S as ResourceContext, y as PilotRow, u as PilotCreatePayload, v as PilotCreateResult, w as PilotEditPayload, x as PilotEditResult, f as EmailHistoryFilter, g as EmailHistoryListResult, E as EmailHistoryDetail, h as EmailHistoryResendResult, z as PlatformEmailProvider, G as PlatformEmailWriteInput, C as PlatformEmailTestInput, F as PlatformEmailTestResult, a2 as SaLocale } from '../catalog-C5VKGbxk.js';
2
- export { A as ActionDefNotInManifestError, a as ActionHandler, b as ActionRegistry, c as AuditListFilter, B as Bound, D as DEFAULT_SA_LOCALE, d as DefineResourceOptions, e as DiscoveryRead, i as EmailHistoryRow, k as HttpResponse, K as KpiReading, l as KvStore, L as LIST_FIRST_PAGE, m as LIST_PAGE_SIZE_DEFAULT, n as LIST_PAGE_SIZE_MAX, o as LIST_PAGINATION_PARAMS, p as ListFilterOf, q as ListOp, r as ListQuery, M as MarketingProjectionsFilter, s as MessageTree, t as MissingHandlerError, P as PartialMessages, I as PlatformResources, J as PromoCodesFilter, N as PromoDetail, O as PublishBundleVersionOptions, Q as PublishPlanVersionOptions, R as ResolvedAction, U as ResourceListPage, V as ResourceOp, W as ResourceOps, X as SA_INTL_LOCALES, Y as SA_LOCALES, Z as SA_LOCALE_LABELS, _ as SA_MESSAGES, $ as SaBuiltinLocale, a0 as SaCatalog, a1 as SaCatalogOptions, a3 as SaLocaleDefinition, a4 as SaLocaleOption, a5 as SaMessages, a6 as SaMessagesOverrides, a7 as TenantsListFilter, a8 as TranslationOf, a9 as auditResource, aa as bindResource, ab as bundleVersionsResource, ac as bundlesResource, ad as clampListPage, ae as clampListPageSize, af as createSaCatalog, ag as dashboardResource, ah as defaultHttpClient, ai as defaultKvStore, aj as defineListOp, ak as defineMessages, al as defineResource, am as discoveryResource, an as filterQueryString, ao as isSaBuiltinLocale, ap as isSentInQuery, aq as listUrl, ar as marketingResource, as as mergeMessages, at as planVersionsResource, au as plansResource, av as platformResources, aw as promoCodesResource, ax as readListPage, ay as resolveMessages, az as tenantsResource } from '../catalog-C5VKGbxk.js';
3
- import { A as AdminError } from '../batch-column-fetcher-VwjOxerL.js';
4
- export { a as AdminErrorInit, B as BatchColumnData, b as BatchColumnDriftError, c as BatchColumnFetcher, d as BatchColumnFetcherOptions, e as BatchColumnRow, f as BatchColumnValue, g as BootLoadError, h as BootLoader, i as BootLoaderOptions, j as BuildRouteEntry, C as CachedManifestEntry, D as DEFAULT_STANDARD_PAGE_ROUTES, M as ManifestLoadError, k as ManifestLoader, l as ManifestLoaderOptions, N as NavBuilderOptions, P as ParamStyle, S as SidebarItem, m as SidebarSection, n as adminErrorMessage, o as buildRoutes, p as buildSidebar, q as defaultSectionOrder, r as httpStatusOf, s as isAdminError, t as isAxiosNoResponseError, u as isEmptyResponse, v as isPlatformError, w as isTransportFailure, x as markEmptyResponse, y as markPlatformError, z as markTransportFailure, E as readErrorCode, F as readErrorDetail, G as resolveExtension, H as toAdminError } from '../batch-column-fetcher-VwjOxerL.js';
1
+ import { H as HttpClient, T as ResourceDef, S as ResourceContext, y as PilotRow, u as PilotCreatePayload, v as PilotCreateResult, w as PilotEditPayload, x as PilotEditResult, f as EmailHistoryFilter, g as EmailHistoryListResult, E as EmailHistoryDetail, h as EmailHistoryResendResult, z as PlatformEmailProvider, G as PlatformEmailWriteInput, C as PlatformEmailTestInput, F as PlatformEmailTestResult, a2 as SaLocale } from '../catalog-D257Y044.js';
2
+ export { A as ActionDefNotInManifestError, a as ActionHandler, b as ActionRegistry, c as AuditListFilter, B as Bound, D as DEFAULT_SA_LOCALE, d as DefineResourceOptions, e as DiscoveryRead, i as EmailHistoryRow, k as HttpResponse, K as KpiReading, l as KvStore, L as LIST_FIRST_PAGE, m as LIST_PAGE_SIZE_DEFAULT, n as LIST_PAGE_SIZE_MAX, o as LIST_PAGINATION_PARAMS, p as ListFilterOf, q as ListOp, r as ListQuery, M as MarketingProjectionsFilter, s as MessageTree, t as MissingHandlerError, P as PartialMessages, I as PlatformResources, J as PromoCodesFilter, N as PromoDetail, O as PublishBundleVersionOptions, Q as PublishPlanVersionOptions, R as ResolvedAction, U as ResourceListPage, V as ResourceOp, W as ResourceOps, X as SA_INTL_LOCALES, Y as SA_LOCALES, Z as SA_LOCALE_LABELS, _ as SA_MESSAGES, $ as SaBuiltinLocale, a0 as SaCatalog, a1 as SaCatalogOptions, a3 as SaLocaleDefinition, a4 as SaLocaleOption, a5 as SaMessages, a6 as SaMessagesOverrides, a7 as TenantsListFilter, a8 as TranslationOf, a9 as auditResource, aa as bindResource, ab as bundleVersionsResource, ac as bundlesResource, ad as clampListPage, ae as clampListPageSize, af as createSaCatalog, ag as dashboardResource, ah as defaultHttpClient, ai as defaultKvStore, aj as defineListOp, ak as defineMessages, al as defineResource, am as discoveryResource, an as filterQueryString, ao as isSaBuiltinLocale, ap as isSentInQuery, aq as listUrl, ar as marketingResource, as as mergeMessages, at as planVersionsResource, au as plansResource, av as platformResources, aw as promoCodesResource, ax as readListPage, ay as resolveMessages, az as tenantsResource } from '../catalog-D257Y044.js';
3
+ import { A as AdminError } from '../batch-column-fetcher-BsX1cJiU.js';
4
+ export { a as AdminErrorInit, B as BatchColumnData, b as BatchColumnDriftError, c as BatchColumnFetcher, d as BatchColumnFetcherOptions, e as BatchColumnRow, f as BatchColumnValue, g as BootLoadError, h as BootLoader, i as BootLoaderOptions, j as BuildRouteEntry, C as CachedManifestEntry, D as DEFAULT_STANDARD_PAGE_ROUTES, M as ManifestLoadError, k as ManifestLoader, l as ManifestLoaderOptions, N as NavBuilderOptions, P as ParamStyle, S as SidebarItem, m as SidebarSection, n as adminErrorMessage, o as buildRoutes, p as buildSidebar, q as defaultSectionOrder, r as httpStatusOf, s as isAdminError, t as isAxiosNoResponseError, u as isEmptyResponse, v as isPlatformError, w as isTransportFailure, x as markEmptyResponse, y as markPlatformError, z as markTransportFailure, E as readErrorCode, F as readErrorDetail, G as resolveExtension, H as toAdminError } from '../batch-column-fetcher-BsX1cJiU.js';
5
5
  import { CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, DiscoverySnapshot, SyncDiscoveryResult, PromotionRow, CreatePromotionData, UpdatePromotionData, AdminUserListFilter, AdminUserListRow, AdminSubscriptionListRow, PromoCodeRecord, AdminTenantDetail, AdminAuditListFilter, AuditEntry } from '@saasicat/core';
6
6
 
7
7
  declare const ADMIN_UI_VERSION = "1.2.0";
@@ -383,6 +383,24 @@ declare function isProductionBoot(boot: {
383
383
  project?: LoginBootProject | null;
384
384
  } | null | undefined): boolean;
385
385
 
386
+ /**
387
+ * The priority values that turn a drag into a new order.
388
+ *
389
+ * The marketing list sorts by priority descending, so "move this row here" has
390
+ * to be expressed as numbers before it can be persisted. Two things make that
391
+ * more than an assignment:
392
+ *
393
+ * - **Ties.** Every plan starts at `0`, and swapping two zeroes changes
394
+ * nothing. The values have to be pulled apart before they can carry an order.
395
+ * - **Existing values mean something.** An operator who set 100 / 50 / 10 chose
396
+ * those gaps. Renumbering to 3 / 2 / 1 would answer a question nobody asked,
397
+ * so the values are kept and only their assignment moves.
398
+ *
399
+ * The result is per ORIGINAL index, and `null` where a row keeps what it has —
400
+ * each change is one write, so not changing is worth reporting.
401
+ */
402
+ declare function reorderedPriorities(priorities: readonly number[], from: number, to: number): (number | null)[];
403
+
386
404
  interface PlanRowLike {
387
405
  id: string;
388
406
  planKey: string;
@@ -512,4 +530,4 @@ declare function looksLikeEmail(value: string): boolean;
512
530
  /** `value` without any run of `ch` at either end — `trim()` for one character. */
513
531
  declare function trimChar(value: string, ch: string): string;
514
532
 
515
- export { ADMIN_UI_VERSION, AdminError, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type AxiosHttpClientOptions, type AxiosLike, type AxiosLikeConfig, type AxiosLikeResponse, type AxiosLikeResponseConfig, type AxiosResponseBody, EmailHistoryDetail, EmailHistoryFilter, EmailHistoryListResult, EmailHistoryResendResult, type FetchHttpClientOptions, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, type LoginBootProject, type LoginBrandFallback, type LoginBranding, type MessageParams, PilotCreatePayload, PilotCreateResult, PilotEditPayload, PilotEditResult, PilotRow, type PlanCounts, type PlanRowLike, type PlanVersionLike, PlatformEmailProvider, PlatformEmailTestInput, PlatformEmailTestResult, PlatformEmailWriteInput, type ResolvePlansInput, type ResolvedPlan, ResourceContext, ResourceDef, type ResourceRequestInit, SaLocale, catalogResource, countPlans, createAdminResourceClient, createAxiosHttpClient, createFetchHttpClient, emailHistoryResource, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, looksLikeEmail, pilotsResource, platformEmailResource, postJson, promotionsResource, requestJson, requestJsonBody, requireServerAnswer, resolveLoginBranding, resolvePlans, subscriptionsResource, todayIsoDate, trimChar, trimTrailingSlashes, usersResource };
533
+ export { ADMIN_UI_VERSION, AdminError, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type AxiosHttpClientOptions, type AxiosLike, type AxiosLikeConfig, type AxiosLikeResponse, type AxiosLikeResponseConfig, type AxiosResponseBody, EmailHistoryDetail, EmailHistoryFilter, EmailHistoryListResult, EmailHistoryResendResult, type FetchHttpClientOptions, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, type LoginBootProject, type LoginBrandFallback, type LoginBranding, type MessageParams, PilotCreatePayload, PilotCreateResult, PilotEditPayload, PilotEditResult, PilotRow, type PlanCounts, type PlanRowLike, type PlanVersionLike, PlatformEmailProvider, PlatformEmailTestInput, PlatformEmailTestResult, PlatformEmailWriteInput, type ResolvePlansInput, type ResolvedPlan, ResourceContext, ResourceDef, type ResourceRequestInit, SaLocale, catalogResource, countPlans, createAdminResourceClient, createAxiosHttpClient, createFetchHttpClient, emailHistoryResource, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, looksLikeEmail, pilotsResource, platformEmailResource, postJson, promotionsResource, reorderedPriorities, requestJson, requestJsonBody, requireServerAnswer, resolveLoginBranding, resolvePlans, subscriptionsResource, todayIsoDate, trimChar, trimTrailingSlashes, usersResource };
@@ -15,11 +15,12 @@ import {
15
15
  isFutureScheduled,
16
16
  isProductionBoot,
17
17
  looksLikeEmail,
18
+ reorderedPriorities,
18
19
  resolveLoginBranding,
19
20
  resolvePlans,
20
21
  todayIsoDate,
21
22
  trimChar
22
- } from "../chunk-L7HPDVHV.js";
23
+ } from "../chunk-JXHRJ2ZD.js";
23
24
  import {
24
25
  ActionDefNotInManifestError,
25
26
  ActionRegistry,
@@ -31,7 +32,7 @@ import {
31
32
  buildSidebar,
32
33
  defaultSectionOrder,
33
34
  resolveExtension
34
- } from "../chunk-6X6AGH66.js";
35
+ } from "../chunk-6B5WYQFC.js";
35
36
  import {
36
37
  AdminError,
37
38
  BootLoadError,
@@ -104,7 +105,7 @@ import {
104
105
  toAdminError,
105
106
  trimTrailingSlashes,
106
107
  usersResource
107
- } from "../chunk-P4EJF43F.js";
108
+ } from "../chunk-RJDW4RJT.js";
108
109
  export {
109
110
  ADMIN_UI_VERSION,
110
111
  ActionDefNotInManifestError,
@@ -193,6 +194,7 @@ export {
193
194
  readErrorCode,
194
195
  readErrorDetail,
195
196
  readListPage,
197
+ reorderedPriorities,
196
198
  requestJson,
197
199
  requestJsonBody,
198
200
  requireServerAnswer,