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

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 (309) hide show
  1. package/README.md +84 -19
  2. package/dist/.build-stamp +1 -1
  3. package/dist/assets/material-icons/material-icons.css +31 -0
  4. package/dist/assets/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff +0 -0
  5. package/dist/assets/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2 +0 -0
  6. package/dist/assets/quasar.css +11696 -0
  7. package/dist/assets/ui-vue.css +6923 -0
  8. package/dist/auth/SuperAdminLoginPage.js +213 -0
  9. package/dist/auth/SuperAdminSetupWizard.js +5 -0
  10. package/dist/{batch-column-fetcher-VwjOxerL.d.ts → batch-column-fetcher-DQ89o95K.d.cts} +5 -5
  11. package/dist/{batch-column-fetcher-Dk9X92db.d.cts → batch-column-fetcher-Sr48UWEX.d.ts} +5 -5
  12. package/dist/{catalog-C5VKGbxk.d.cts → catalog-0eZwciPW.d.cts} +125 -86
  13. package/dist/{catalog-C5VKGbxk.d.ts → catalog-0eZwciPW.d.ts} +125 -86
  14. package/dist/{chunk-L7HPDVHV.js → chunk-47HDYN35.js} +86 -7
  15. package/dist/{chunk-A7JICGQ5.js → chunk-7CHT2ATN.js} +16 -3
  16. package/dist/{chunk-P4EJF43F.js → chunk-CZIVA536.js} +358 -300
  17. package/dist/{chunk-6X6AGH66.js → chunk-RFJ4UGOE.js} +7 -4
  18. package/dist/chunk-U7LNAX6T.js +35 -0
  19. package/dist/{chunk-4CJBABLZ.js → chunk-VLGEOPZH.js} +685 -196
  20. package/dist/chunks/AdminAccordion.vue_vue_type_script_setup_true_lang-DOwlTV5_.js +175 -0
  21. package/dist/chunks/AdminBanner.vue_vue_type_script_setup_true_lang-D27JEaak.js +83 -0
  22. package/dist/chunks/AdminBody.vue_vue_type_script_setup_true_lang-DxcVRzXE.js +41 -0
  23. package/dist/chunks/AdminDialog.vue_vue_type_script_setup_true_lang-CpxfSrx-.js +138 -0
  24. package/dist/chunks/AdminEmptyState.vue_vue_type_script_setup_true_lang-jaYkgNbE.js +48 -0
  25. package/dist/chunks/AdminErrorBanner.vue_vue_type_script_setup_true_lang-t-5uqt82.js +50 -0
  26. package/dist/chunks/AdminField.vue_vue_type_script_setup_true_lang-CtPByt2w.js +63 -0
  27. package/dist/chunks/AdminFieldGrid.vue_vue_type_script_setup_true_lang-BxuyHtBU.js +21 -0
  28. package/dist/chunks/AdminFilters.vue_vue_type_script_setup_true_lang-DSnzAlwj.js +18 -0
  29. package/dist/chunks/AdminFormDialog.vue_vue_type_script_setup_true_lang-BpWshaWv.js +84 -0
  30. package/dist/chunks/AdminHero.vue_vue_type_script_setup_true_lang-CoaRxXLF.js +46 -0
  31. package/dist/chunks/AdminKpi.vue_vue_type_script_setup_true_lang-CgCcGGmY.js +79 -0
  32. package/dist/chunks/AdminPage.vue_vue_type_script_setup_true_lang-L7St_zOY.js +15 -0
  33. package/dist/chunks/AdminPaginator.vue_vue_type_script_setup_true_lang-Cv8EEBGP.js +157 -0
  34. package/dist/chunks/AdminRefreshBtn.vue_vue_type_script_setup_true_lang-Bxk5GWOB.js +41 -0
  35. package/dist/chunks/AdminSection.vue_vue_type_script_setup_true_lang-DpjApOU-.js +62 -0
  36. package/dist/chunks/AdminStatistics.vue_vue_type_script_setup_true_lang-DwHeeYCX.js +32 -0
  37. package/dist/chunks/AdminStatusPill.vue_vue_type_script_setup_true_lang-09G3rCxN.js +32 -0
  38. package/dist/chunks/AdminTable.vue_vue_type_script_setup_true_lang-BzAusc-r.js +2007 -0
  39. package/dist/chunks/ClosePopup-iyFHWknU.js +48 -0
  40. package/dist/chunks/PromoCodeEditDialog.vue_vue_type_script_setup_true_lang-NiW-Ky7H.js +639 -0
  41. package/dist/chunks/QBadge-B8LYhEVv.js +43 -0
  42. package/dist/chunks/QBtn-CSVcAwrE.js +770 -0
  43. package/dist/chunks/QCard-g0Hwqx6i.js +30 -0
  44. package/dist/chunks/QCardSection-FvPMZdQ4.js +22 -0
  45. package/dist/chunks/QCheckbox-BwxH7EyE.js +63 -0
  46. package/dist/chunks/QDialog-CcwVEueo.js +331 -0
  47. package/dist/chunks/QIcon-DaxYRLz0.js +195 -0
  48. package/dist/chunks/QInput-SRRCebo8.js +1663 -0
  49. package/dist/chunks/QItem-DunhGoPO.js +127 -0
  50. package/dist/chunks/QList-D3jMUITp.js +37 -0
  51. package/dist/chunks/QMenu-BFP-qtEm.js +831 -0
  52. package/dist/chunks/QSelect-jOvICNCy.js +1885 -0
  53. package/dist/chunks/QSeparator-CrLemO5z.js +60 -0
  54. package/dist/chunks/QSpinner-DblEBRSQ.js +57 -0
  55. package/dist/chunks/QToggle-BBogOutP.js +40 -0
  56. package/dist/chunks/QTooltip-Ch_r2hMZ.js +320 -0
  57. package/dist/chunks/SuperAdminSetupWizard-UO4j4Eyu.js +465 -0
  58. package/dist/chunks/TenantActionConfirmDialog-DU11RlmB.js +201 -0
  59. package/dist/chunks/_plugin-vue_export-helper-1tPrXgE0.js +10 -0
  60. package/dist/chunks/catalog-i18n-tKlkm6-2.js +32 -0
  61. package/dist/chunks/create-Ck-e8kVF.js +42 -0
  62. package/dist/chunks/detached-fullscreen-DuM-R9uv.js +488 -0
  63. package/dist/chunks/focus-manager-Cc4uTjsQ.js +301 -0
  64. package/dist/chunks/format-BIQKj_qk.js +23 -0
  65. package/dist/chunks/is-DNOHJAOK.js +269 -0
  66. package/dist/chunks/plan-area-context-RiY4rHni.js +20 -0
  67. package/dist/chunks/private.use-form-BdJSI6of.js +30 -0
  68. package/dist/chunks/render-DSsuXum_.js +32 -0
  69. package/dist/chunks/symbols-Cy0ehVpr.js +67 -0
  70. package/dist/chunks/touch-B8tpYG34.js +162 -0
  71. package/dist/chunks/use-checkbox-CBtmpkdc.js +221 -0
  72. package/dist/chunks/use-dark-VmDF1SYZ.js +14 -0
  73. package/dist/chunks/use-hydration-PO-eh3w4.js +14 -0
  74. package/dist/chunks/use-prevent-scroll-B9A_pIzH.js +156 -0
  75. package/dist/chunks/use-render-cache-Bj5SbY5v.js +22 -0
  76. package/dist/chunks/use-size-C7KmPm4L.js +23 -0
  77. package/dist/chunks/vm-Dl-TBfKC.js +182 -0
  78. package/dist/client/index.cjs +488 -306
  79. package/dist/client/index.d.cts +107 -7
  80. package/dist/client/index.d.ts +107 -7
  81. package/dist/client/index.js +25 -3
  82. package/dist/index.cjs +1226 -536
  83. package/dist/index.d.cts +5 -5
  84. package/dist/index.d.ts +5 -5
  85. package/dist/index.js +53 -7
  86. package/dist/layouts/AdminLayout.js +1711 -0
  87. package/dist/pages/AdminManifestErrorPage.js +99 -0
  88. package/dist/pages/AuditPage.js +244 -0
  89. package/dist/pages/BundlesPage.js +2883 -0
  90. package/dist/pages/DashboardPage.js +299 -0
  91. package/dist/pages/DiscoveryPage.js +2563 -0
  92. package/dist/pages/EmailHistoryPage.js +509 -0
  93. package/dist/pages/MarketingCatalogPage.js +2095 -0
  94. package/dist/pages/PilotsPage.js +1197 -0
  95. package/dist/pages/PlanReviewPage.js +563 -0
  96. package/dist/pages/PlanVersionEditorPage.js +1626 -0
  97. package/dist/pages/PlansPage.js +6164 -0
  98. package/dist/pages/PlatformEmailPage.js +455 -0
  99. package/dist/pages/PromoCodeDetailPage.js +240 -0
  100. package/dist/pages/PromoCodesPage.js +567 -0
  101. package/dist/pages/SettingsPage.js +216 -0
  102. package/dist/pages/SubscriptionsPage.js +113 -0
  103. package/dist/pages/TenantDetailPage.js +393 -0
  104. package/dist/pages/TenantsPage.js +430 -0
  105. package/dist/pages/UsersPage.js +386 -0
  106. package/dist/pages/index.js +75 -0
  107. package/dist/quasar/index.cjs +388 -297
  108. package/dist/quasar/index.d.cts +13 -3
  109. package/dist/quasar/index.d.ts +13 -3
  110. package/dist/quasar/index.js +10 -6
  111. package/dist/{resource-registry-CrHUvVx6.d.cts → resource-registry-GAxu2kn6.d.ts} +32 -18
  112. package/dist/{resource-registry-Cw_c0F11.d.ts → resource-registry-zfCLvEgx.d.cts} +32 -18
  113. package/dist/testing/admin-pages-suite.cjs +4 -1
  114. package/dist/testing/admin-pages-suite.d.cts +10 -1
  115. package/dist/testing/admin-pages-suite.d.ts +10 -1
  116. package/dist/testing/admin-pages-suite.js +3 -1
  117. package/dist/testing/mount-with-quasar.cjs +65 -0
  118. package/dist/testing/mount-with-quasar.d.cts +7 -0
  119. package/dist/testing/mount-with-quasar.d.ts +7 -0
  120. package/dist/testing/mount-with-quasar.js +63 -0
  121. package/dist/ui/data/AdminKpi.js +4 -0
  122. package/dist/ui/data/AdminPaginator.js +4 -0
  123. package/dist/ui/data/AdminRowActions.js +37 -0
  124. package/dist/ui/data/AdminStatistics.js +4 -0
  125. package/dist/ui/data/AdminStatusPill.js +4 -0
  126. package/dist/ui/data/AdminTable.js +4 -0
  127. package/dist/ui/data/KvBlock.js +24 -0
  128. package/dist/ui/entitlement/FeatureGate.js +25 -0
  129. package/dist/ui/feedback/AdminBanner.js +4 -0
  130. package/dist/ui/feedback/AdminEmptyState.js +4 -0
  131. package/dist/ui/feedback/AdminErrorBanner.js +4 -0
  132. package/dist/ui/feedback/AdminRefreshBtn.js +4 -0
  133. package/dist/ui/overlay/AdminConfirmDialog.js +112 -0
  134. package/dist/ui/overlay/AdminDialog.js +4 -0
  135. package/dist/ui/overlay/AdminFormDialog.js +4 -0
  136. package/dist/ui/overlay/MfaPromptDialog.js +131 -0
  137. package/dist/ui/page/AdminAccordion.js +4 -0
  138. package/dist/ui/page/AdminBody.js +4 -0
  139. package/dist/ui/page/AdminField.js +4 -0
  140. package/dist/ui/page/AdminFieldGrid.js +4 -0
  141. package/dist/ui/page/AdminFilters.js +4 -0
  142. package/dist/ui/page/AdminHero.js +4 -0
  143. package/dist/ui/page/AdminPage.js +4 -0
  144. package/dist/ui/page/AdminSection.js +4 -0
  145. package/dist/ui/page/AdminToolbar.js +35 -0
  146. package/dist/ui/page/LocaleSwitcher.js +90 -0
  147. package/dist/ui/page/ThemeSwitcher.js +95 -0
  148. package/dist/ui/page/WizardStepper.js +55 -0
  149. package/dist/vue/index.cjs +1135 -524
  150. package/dist/vue/index.d.cts +520 -44
  151. package/dist/vue/index.d.ts +520 -44
  152. package/dist/vue/index.js +30 -6
  153. package/package.json +30 -15
  154. package/src/auth/SuperAdminLoginPage.vue +16 -16
  155. package/src/auth/SuperAdminSetupWizard.vue +33 -28
  156. package/src/client/admin-resource-client.ts +23 -8
  157. package/src/client/billing-area.ts +14 -0
  158. package/src/client/brand-bridge.ts +98 -0
  159. package/src/client/http/axios-http-client.ts +4 -3
  160. package/src/client/http-json.ts +16 -3
  161. package/src/client/i18n/messages/audit.ts +1 -0
  162. package/src/client/i18n/messages/bundles.ts +17 -4
  163. package/src/client/i18n/messages/common.ts +1 -0
  164. package/src/client/i18n/messages/dashboard.ts +1 -0
  165. package/src/client/i18n/messages/discovery.ts +10 -19
  166. package/src/client/i18n/messages/email.ts +1 -0
  167. package/src/client/i18n/messages/errors.ts +1 -0
  168. package/src/client/i18n/messages/marketing.ts +7 -2
  169. package/src/client/i18n/messages/nav.ts +3 -0
  170. package/src/client/i18n/messages/pilots.ts +1 -0
  171. package/src/client/i18n/messages/plan-detail.ts +1 -0
  172. package/src/client/i18n/messages/plan-editor.ts +1 -0
  173. package/src/client/i18n/messages/plan-versions.ts +3 -4
  174. package/src/client/i18n/messages/plans.ts +9 -4
  175. package/src/client/i18n/messages/promos.ts +3 -0
  176. package/src/client/i18n/messages/settings.ts +75 -0
  177. package/src/client/i18n/messages/shell.ts +1 -0
  178. package/src/client/i18n/messages/tenants.ts +1 -0
  179. package/src/client/i18n/messages/users.ts +1 -0
  180. package/src/client/i18n/messages.ts +3 -0
  181. package/src/client/index.ts +10 -0
  182. package/src/client/mfa-header.ts +14 -0
  183. package/src/client/nav-builder.ts +7 -4
  184. package/src/client/reorder-priorities.ts +76 -0
  185. package/src/client/resources/bundles.resource.ts +24 -38
  186. package/src/client/resources/catalog.resource.ts +91 -113
  187. package/src/client/resources/define-resource.ts +2 -75
  188. package/src/client/resources/index.ts +3 -0
  189. package/src/client/resources/marketing.resource.ts +77 -80
  190. package/src/client/resources/pilots.resource.ts +1 -11
  191. package/src/client/resources/plans.resource.ts +47 -64
  192. package/src/client/resources/platform-email.resource.ts +1 -11
  193. package/src/client/resources/promotions.resource.ts +18 -25
  194. package/src/client/resources/settings.resource.ts +68 -0
  195. package/src/client/resources/tenants.resource.ts +10 -4
  196. package/src/client/resources/users.resource.ts +3 -2
  197. package/src/client/settings-view.ts +50 -0
  198. package/src/client/version-change-fields.ts +17 -0
  199. package/src/features/bundle/BundleVersionInlineEditor.vue +112 -155
  200. package/src/features/bundle/BundleVersionPublishDialog.vue +34 -16
  201. package/src/features/bundle/BundleVersionStrip.vue +25 -31
  202. package/src/features/bundle/VersionDiffPreview.vue +21 -15
  203. package/src/features/bundle/internal/BundleCreatePanel.vue +65 -119
  204. package/src/features/bundle/internal/BundleFeaturesEditor.vue +18 -31
  205. package/src/features/bundle/internal/BundlePlanCompatPicker.vue +23 -29
  206. package/src/features/bundle/internal/BundleQuotasEditor.vue +20 -41
  207. package/src/features/bundle/internal/BundleStatusBanner.vue +16 -70
  208. package/src/features/marketing/MarketingPromotionsTab.vue +155 -122
  209. package/src/features/plan/PlanCreateDialog.vue +49 -129
  210. package/src/features/plan/PlanDetail.vue +57 -76
  211. package/src/features/plan/PlanList.vue +81 -156
  212. package/src/features/plan/PlanMatrix.vue +89 -158
  213. package/src/features/plan/PlanReview.vue +62 -80
  214. package/src/features/plan/PlanVersionEditor.vue +134 -238
  215. package/src/features/plan/internal/PlanCatalogPreview.vue +22 -68
  216. package/src/features/plan/internal/PlanComponentPool.vue +18 -88
  217. package/src/features/plan/internal/PlanTitleEdit.vue +12 -9
  218. package/src/features/plan/internal/PlanVersionBasket.vue +65 -89
  219. package/src/features/plan/internal/PlanVersionEditorHeader.vue +16 -20
  220. package/src/features/plan/internal/PlanVersionsPanel.vue +29 -75
  221. package/src/features/tenant/TenantActionConfirmDialog.vue +4 -4
  222. package/src/index.ts +9 -0
  223. package/src/internal/bundles-page/BundleAccordionList.vue +32 -2
  224. package/src/internal/bundles-page/BundleDetailPanel.vue +37 -21
  225. package/src/internal/bundles-page/BundlesToolbar.vue +9 -5
  226. package/src/internal/dialogs/PilotCreateDialog.vue +65 -104
  227. package/src/internal/dialogs/PilotEditDialog.vue +22 -23
  228. package/src/internal/dialogs/PromoCodeCreateDialog.vue +27 -23
  229. package/src/internal/dialogs/PromoCodeDialogFields.vue +203 -252
  230. package/src/internal/dialogs/PromoCodeEditDialog.vue +52 -48
  231. package/src/internal/dialogs/pilot-dialog.css +16 -111
  232. package/src/internal/discovery-page/CatalogEntryTransPanel.vue +18 -18
  233. package/src/internal/discovery-page/DiscoveryCapList.vue +20 -20
  234. package/src/internal/discovery-page/DiscoveryFeatureCard.vue +20 -20
  235. package/src/internal/discovery-page/DiscoveryQuotaCard.vue +10 -10
  236. package/src/internal/discovery-page/DiscoveryStatusControl.vue +1 -1
  237. package/src/internal/marketing-catalog-page/MarketingCatalogAdmin.vue +660 -0
  238. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogHeader.vue +21 -16
  239. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogPreview.vue +26 -13
  240. package/src/internal/{marketing-catalog → marketing-catalog-page}/MarketingCatalogToolbar.vue +14 -10
  241. package/src/internal/plan-versions/PlanDiffCard.vue +15 -15
  242. package/src/internal/plan-versions/PlanVersionsDiff.vue +11 -11
  243. package/src/internal/plan-versions/PlanVersionsTimeline.vue +35 -31
  244. package/src/internal/plans-page/PlanBundleOverview.vue +8 -8
  245. package/src/internal/plans-page/PlanPublishDialog.vue +2 -2
  246. package/src/internal/tenant-detail/TenantUsage.vue +3 -3
  247. package/src/internal/tenant-detail/types.ts +2 -2
  248. package/src/layouts/AdminLayout.vue +22 -22
  249. package/src/pages/AdminManifestErrorPage.vue +7 -7
  250. package/src/pages/AuditPage.vue +2 -2
  251. package/src/pages/BundlesPage.vue +81 -89
  252. package/src/pages/DashboardPage.vue +106 -97
  253. package/src/pages/DiscoveryPage.vue +22 -28
  254. package/src/pages/EmailHistoryPage.vue +26 -34
  255. package/src/pages/MarketingCatalogPage.vue +289 -281
  256. package/src/pages/PilotsPage.vue +22 -35
  257. package/src/pages/PlanVersionEditorPage.vue +1 -1
  258. package/src/pages/PlansPage.vue +181 -107
  259. package/src/pages/PlatformEmailPage.vue +33 -41
  260. package/src/pages/PromoCodeDetailPage.vue +12 -18
  261. package/src/pages/PromoCodesPage.vue +36 -14
  262. package/src/pages/SettingsPage.vue +226 -0
  263. package/src/pages/TenantDetailPage.vue +28 -53
  264. package/src/pages/TenantsPage.vue +7 -7
  265. package/src/pages/UsersPage.vue +7 -20
  266. package/src/pages/index.ts +3 -1
  267. package/src/quasar/create-super-admin-app.ts +13 -2
  268. package/src/quasar/dark-bridge.ts +8 -4
  269. package/src/quasar/index.ts +11 -0
  270. package/src/testing/admin-pages-suite.ts +11 -1
  271. package/src/testing/mount-with-quasar.ts +73 -0
  272. package/src/ui/data/AdminPaginator.vue +30 -20
  273. package/src/ui/data/KvBlock.vue +4 -4
  274. package/src/ui/entitlement/FeatureGate.vue +2 -2
  275. package/src/ui/feedback/AdminRefreshBtn.vue +10 -7
  276. package/src/ui/overlay/MfaPromptDialog.vue +5 -6
  277. package/src/ui/page/AdminAccordion.vue +27 -10
  278. package/src/ui/page/WizardStepper.vue +7 -18
  279. package/src/ui/theme/base.css +31 -0
  280. package/src/ui/theme/components/accordion.css +9 -9
  281. package/src/ui/theme/components/button.css +10 -68
  282. package/src/ui/theme/components/card.css +0 -8
  283. package/src/ui/theme/components/paginator.css +14 -28
  284. package/src/ui/theme/tokens.semantic.dark.css +13 -0
  285. package/src/ui/theme/tokens.semantic.light.css +39 -0
  286. package/src/vue/index.ts +9 -0
  287. package/src/vue/platform-loaders.ts +1 -3
  288. package/src/vue/resource-registry.ts +0 -8
  289. package/src/vue/super-admin-context.ts +14 -15
  290. package/src/vue/use-applied-settings.ts +79 -0
  291. package/src/vue/use-bulk-publish.ts +2 -5
  292. package/src/vue/use-bundles.ts +6 -10
  293. package/src/vue/use-catalog-entries.ts +9 -16
  294. package/src/vue/use-dialog.ts +221 -0
  295. package/src/vue/use-marketing-projections.ts +5 -6
  296. package/src/vue/use-mfa-prompt.ts +61 -11
  297. package/src/vue/use-plans.ts +28 -21
  298. package/src/vue/use-promotions.ts +1 -7
  299. package/src/vue/use-row-reorder.ts +109 -0
  300. package/src/vue/use-sa-theme.ts +31 -0
  301. package/src/vue/use-steps.ts +109 -0
  302. package/src/vue/use-subscription-draft.ts +63 -60
  303. package/src/vue/use-tenant-billing-details.ts +119 -0
  304. package/src/vue/use-tenant-billing.ts +250 -33
  305. package/src/vue/use-tenant-payment-method.ts +112 -0
  306. package/src/vue/use-tenant-subscription-bundles.ts +8 -0
  307. package/src/internal/marketing-catalog/MarketingCatalogAdmin.vue +0 -553
  308. package/src/internal/plans-page/PlansPageToast.vue +0 -69
  309. /package/src/internal/{marketing-catalog → marketing-catalog-page}/types.ts +0 -0
@@ -162,7 +162,7 @@ const props = withDefaults(
162
162
  standardPageRoutes?: Partial<Record<StandardPageKey, string>>;
163
163
  /**
164
164
  * Override for `navSection` per StandardPage. The platform default follows
165
- * the plan-simulation layout (Übersicht / Produktkatalog / Kunden /
165
+ * the plan-simulation layout (Overview / Product catalog / Customers /
166
166
  * System) and usually needs no adjustment.
167
167
  */
168
168
  standardPageNavSection?: Partial<Record<StandardPageKey, string>>;
@@ -309,10 +309,10 @@ const currentPageTitle = computed(() => {
309
309
  display: flex;
310
310
  align-items: center;
311
311
  justify-content: center;
312
- gap: 8px;
313
- padding: 6px 12px;
312
+ gap: var(--sa-space-3);
313
+ padding: var(--sa-space-2) var(--sa-space-4);
314
314
  font-size: var(--sa-text-sm);
315
- letter-spacing: 0.05em;
315
+ letter-spacing: var(--sa-tracking-wider);
316
316
  text-transform: uppercase;
317
317
  }
318
318
  .sa-admin-banner--prod {
@@ -363,15 +363,15 @@ const currentPageTitle = computed(() => {
363
363
  background: var(--sa-admin-badge-bg, var(--sa-color-inverse-accent));
364
364
  color: var(--sa-admin-badge-fg, var(--sa-color-inverse-accent-fg));
365
365
  font-weight: 800;
366
- letter-spacing: 0.08em;
366
+ letter-spacing: var(--sa-tracking-wider);
367
367
  font-size: var(--sa-text-2xs);
368
- padding: 4px 8px;
368
+ padding: var(--sa-space-2) var(--sa-space-3);
369
369
  }
370
370
 
371
371
  .sa-admin-user {
372
372
  display: flex;
373
373
  align-items: center;
374
- gap: 8px;
374
+ gap: var(--sa-space-3);
375
375
  /* The row's flexible member. Without this the identity block below sits at
376
376
  * its content width, and a long name pushes the sign-out button off the
377
377
  * end of a `nowrap` toolbar instead of being shortened. */
@@ -473,14 +473,14 @@ const currentPageTitle = computed(() => {
473
473
  .sa-admin-drawer__brand {
474
474
  display: flex;
475
475
  align-items: center;
476
- gap: 12px;
477
- padding: 18px 16px;
476
+ gap: var(--sa-space-4);
477
+ padding: var(--sa-space-5) var(--sa-space-5);
478
478
  border-bottom: 1px solid var(--sa-color-inverse-border);
479
479
  }
480
480
  .sa-admin-drawer__logo {
481
481
  width: 36px;
482
482
  height: 36px;
483
- border-radius: 10px;
483
+ border-radius: var(--sa-radius-tile);
484
484
  background: var(
485
485
  --sa-admin-drawer-logo-bg,
486
486
  linear-gradient(
@@ -503,33 +503,33 @@ const currentPageTitle = computed(() => {
503
503
  font-size: var(--sa-text-xs);
504
504
  color: var(--sa-admin-drawer-brand-tag, var(--sa-color-inverse-accent));
505
505
  text-transform: uppercase;
506
- letter-spacing: 0.08em;
506
+ letter-spacing: var(--sa-tracking-wider);
507
507
  }
508
508
 
509
509
  .sa-admin-drawer__list {
510
510
  flex: 1;
511
- padding: 6px 4px;
511
+ padding: var(--sa-space-2) var(--sa-space-2);
512
512
  }
513
513
  .sa-admin-drawer__section {
514
514
  font-size: var(--sa-text-2xs);
515
515
  text-transform: uppercase;
516
- letter-spacing: 0.12em;
516
+ letter-spacing: var(--sa-tracking-wider);
517
517
  color: var(--sa-admin-drawer-section-fg, var(--sa-color-inverse-fg-muted));
518
- padding: 14px 18px 6px;
518
+ padding: var(--sa-space-4) var(--sa-space-5) var(--sa-space-2);
519
519
  font-weight: 600;
520
520
  }
521
521
  .sa-admin-drawer__list :deep(.q-item) {
522
522
  color: var(--sa-admin-drawer-item-fg, var(--sa-color-inverse-fg-muted));
523
- border-radius: 7px;
524
- margin: 1px 6px;
523
+ border-radius: var(--sa-radius-control);
524
+ margin: var(--sa-space-0) var(--sa-space-2);
525
525
  min-height: 36px;
526
- padding: 6px 12px;
526
+ padding: var(--sa-space-2) var(--sa-space-4);
527
527
  font-size: var(--sa-text-md);
528
528
  font-weight: 500;
529
529
  }
530
530
  .sa-admin-drawer__list :deep(.q-item__section--avatar) {
531
531
  min-width: 28px;
532
- padding-right: 8px;
532
+ padding-right: var(--sa-space-3);
533
533
  }
534
534
  .sa-admin-drawer__list :deep(.q-item .q-icon) {
535
535
  color: var(--sa-color-inverse-fg-subtle);
@@ -554,7 +554,7 @@ const currentPageTitle = computed(() => {
554
554
  }
555
555
 
556
556
  .sa-admin-drawer__footer {
557
- padding: 12px 16px;
557
+ padding: var(--sa-space-4) var(--sa-space-5);
558
558
  border-top: 1px solid var(--sa-color-inverse-border);
559
559
  font-size: var(--sa-text-xs);
560
560
  color: var(--sa-color-inverse-fg-muted);
@@ -562,8 +562,8 @@ const currentPageTitle = computed(() => {
562
562
  .sa-admin-drawer__doc {
563
563
  display: inline-flex;
564
564
  align-items: center;
565
- gap: 6px;
566
- margin-top: 6px;
565
+ gap: var(--sa-space-2);
566
+ margin-top: var(--sa-space-2);
567
567
  color: var(--sa-color-inverse-fg-muted);
568
568
  text-decoration: none;
569
569
  }
@@ -574,7 +574,7 @@ const currentPageTitle = computed(() => {
574
574
  .sa-admin-content {
575
575
  max-width: 1600px;
576
576
  margin: 0 auto;
577
- padding: 16px 24px 32px;
577
+ padding: var(--sa-space-5) var(--sa-space-7) var(--sa-space-8);
578
578
  }
579
579
  .sa-admin-content--fullbleed {
580
580
  max-width: none;
@@ -125,7 +125,7 @@ function logout(): void | Promise<void> {
125
125
 
126
126
  <style scoped>
127
127
  .sa-manifest-error {
128
- padding: 32px;
128
+ padding: var(--sa-space-8);
129
129
  display: flex;
130
130
  justify-content: center;
131
131
  /* The page frame is a full viewport tall, and a stretched flex item would
@@ -139,7 +139,7 @@ function logout(): void | Promise<void> {
139
139
  .sa-manifest-error__head {
140
140
  display: flex;
141
141
  align-items: center;
142
- gap: 12px;
142
+ gap: var(--sa-space-4);
143
143
  }
144
144
  .sa-manifest-error__title {
145
145
  margin: 0;
@@ -147,22 +147,22 @@ function logout(): void | Promise<void> {
147
147
  }
148
148
  .sa-manifest-error__lead,
149
149
  .sa-manifest-error__detail {
150
- margin: 0 0 12px;
150
+ margin: 0 0 var(--sa-space-4);
151
151
  color: var(--sa-color-fg-secondary);
152
152
  line-height: 1.6;
153
153
  }
154
154
  .sa-manifest-error__detail {
155
155
  background: var(--sa-color-negative-surface);
156
156
  border-left: 3px solid var(--sa-color-negative);
157
- padding: 8px 12px;
158
- border-radius: 4px;
157
+ padding: var(--sa-space-3) var(--sa-space-4);
158
+ border-radius: var(--sa-radius-badge);
159
159
  font-family: ui-monospace, SFMono-Regular, monospace;
160
160
  font-size: var(--sa-text-md);
161
161
  color: var(--sa-color-negative-fg);
162
162
  }
163
163
  .sa-manifest-error__actions {
164
164
  display: flex;
165
- gap: 8px;
166
- margin-top: 16px;
165
+ gap: var(--sa-space-3);
166
+ margin-top: var(--sa-space-5);
167
167
  }
168
168
  </style>
@@ -215,8 +215,8 @@ function formatTs(iso: string | null | undefined): string {
215
215
  .sa-audit__kv {
216
216
  background: var(--sa-color-bg-sunken);
217
217
  border: 1px solid var(--sa-color-border);
218
- border-radius: 8px;
219
- padding: 12px;
218
+ border-radius: var(--sa-radius-field);
219
+ padding: var(--sa-space-4);
220
220
  font-size: var(--sa-text-sm);
221
221
  margin: 0;
222
222
  overflow-x: auto;
@@ -41,7 +41,6 @@
41
41
  <BundleCreatePanel
42
42
  v-if="createOpen"
43
43
  class="q-mb-sm"
44
- :project-key="projectKey"
45
44
  :available-features="snapshot?.features ?? []"
46
45
  :available-quotas="snapshot?.quotas ?? []"
47
46
  :plans="plans"
@@ -67,6 +66,7 @@
67
66
  :aggregate-status-of="aggregateStatusOf"
68
67
  :i18n-locale-count="i18nLocaleCount"
69
68
  @toggle="toggle"
69
+ @delete-bundle="confirmDelete"
70
70
  >
71
71
  <template #detail="{ bundle }">
72
72
  <BundleDetailPanel
@@ -93,7 +93,6 @@
93
93
  @inline-save="onInlineSave"
94
94
  @discard-version="onDiscardVersion"
95
95
  @publish-version="openPublish"
96
- @delete-bundle="confirmDelete"
97
96
  />
98
97
  </template>
99
98
  </BundleAccordionList>
@@ -129,6 +128,14 @@
129
128
  :submit="onPublishSubmit"
130
129
  @submitted="onPublishSubmitted"
131
130
  />
131
+
132
+ <MfaPromptDialog
133
+ :model-value="mfa.show.value"
134
+ :description="mfa.description.value"
135
+ :error="mfa.error.value"
136
+ @update:model-value="mfa.onVisibility"
137
+ @confirm="mfa.onConfirm"
138
+ />
132
139
  </AdminPage>
133
140
  </template>
134
141
 
@@ -137,7 +144,6 @@ import { computed, onMounted, ref, watch } from 'vue';
137
144
  import { useSuperAdminManifest } from '../vue/use-super-admin-context.js';
138
145
  import { classifyBundleVersionDiff } from '@saasicat/core';
139
146
  import { useResource } from '../vue/resource-registry.js';
140
- import { useSuperAdminEndpoints } from '../vue/use-super-admin-context.js';
141
147
  import type { ResourceOverride } from '../vue/resource-registry.js';
142
148
  import type {
143
149
  bundlesResource,
@@ -164,6 +170,8 @@ import type {
164
170
  } from '@saasicat/core';
165
171
 
166
172
  import BundleVersionPublishDialog from '../features/bundle/BundleVersionPublishDialog.vue';
173
+ import MfaPromptDialog from '../ui/overlay/MfaPromptDialog.vue';
174
+ import { useMfaPrompt } from '../vue/use-mfa-prompt.js';
167
175
  import BundleCreatePanel from '../features/bundle/internal/BundleCreatePanel.vue';
168
176
  import type { FeatureMeta } from '../features/bundle/internal/BundleFeaturesEditor.vue';
169
177
  import {
@@ -180,6 +188,7 @@ import {
180
188
  } from '../features/bundle/internal/bundle-version-status';
181
189
  import { formatMessage } from '../client/i18n/format.js';
182
190
  import { useSaMessages } from '../vue/use-super-admin-i18n.js';
191
+ import { useSuperAdminConfirm } from '../quasar/confirm.js';
183
192
  import BundleAccordionList from '../internal/bundles-page/BundleAccordionList.vue';
184
193
  import BundleDetailPanel from '../internal/bundles-page/BundleDetailPanel.vue';
185
194
  import BundlesFilterBar from '../internal/bundles-page/BundlesFilterBar.vue';
@@ -249,11 +258,6 @@ const props = defineProps<{
249
258
  // wired four composables by hand to produce them — `useBundles`,
250
259
  // `useBundleVersionsMap`, `useCatalogEntries`, `usePlanVersions` — plus a
251
260
  // snapshot loader. All five read endpoints these resources already declare.
252
- // The project the shell was configured for. It used to be a prop, which meant
253
- // an app could hand this page a different project than the one its resources
254
- // read from — two answers to one question.
255
- const { projectKey } = useSuperAdminEndpoints();
256
-
257
261
  const bundlesOps = useResource('bundles', props.resources?.bundles);
258
262
  const versionsOps = useResource('bundleVersions', props.resources?.bundleVersions);
259
263
  const catalogOps = useResource('catalog', props.resources?.catalog);
@@ -339,6 +343,9 @@ onMounted(() => void reload());
339
343
 
340
344
  const msg = useSaMessages('bundles');
341
345
  const common = useSaMessages('common');
346
+ const shell = useSaMessages('shell');
347
+ const mfa = useMfaPrompt();
348
+ const askConfirm = useSuperAdminConfirm();
342
349
 
343
350
  // The pool this project declares, unless the app narrows it. Read here rather
344
351
  // than taken as a required prop: the shell already holds the manifest, and a
@@ -546,9 +553,21 @@ async function submitEdit(): Promise<void> {
546
553
  }
547
554
 
548
555
  async function confirmDelete(bundle: BundleRow): Promise<void> {
549
- const ok = window.confirm(
550
- formatMessage(msg.value.page.confirmSoftDelete, { bundleKey: bundle.bundleKey }),
551
- );
556
+ // Through the platform's confirm port, like every other destructive action
557
+ // in this admin. `window.confirm` cannot be themed, cannot be reached by
558
+ // the same keyboard path, and looks like the browser rather than like the
559
+ // application asking — which is the wrong impression for the one dialog
560
+ // where a reader should slow down.
561
+ const { ok } = await askConfirm({
562
+ title: msg.value.page.confirmSoftDeleteTitle,
563
+ message: formatMessage(msg.value.page.confirmSoftDelete, {
564
+ label: bundle.label,
565
+ versions: versionsByBundle.value[bundle.id]?.length ?? 0,
566
+ }),
567
+ confirmLabel: msg.value.detail.softDelete,
568
+ cancelLabel: common.value.cancel,
569
+ tone: 'negative',
570
+ });
552
571
  if (!ok) return;
553
572
  await bundlesOps.softDelete(bundle.id);
554
573
  if (openKey.value === bundle.id) openKey.value = null;
@@ -675,7 +694,13 @@ async function onDiscardVersion(bundleId: string, versionId: string): Promise<vo
675
694
  'Discard is not wired up in the wrapper — add the `discardDraft` prop.';
676
695
  return;
677
696
  }
678
- const ok = window.confirm(msg.value.page.confirmDiscardVersion);
697
+ const { ok } = await askConfirm({
698
+ title: msg.value.page.confirmDiscardVersionTitle,
699
+ message: msg.value.page.confirmDiscardVersion,
700
+ confirmLabel: common.value.discard,
701
+ cancelLabel: common.value.cancel,
702
+ tone: 'negative',
703
+ });
679
704
  if (!ok) return;
680
705
  inlineEditorSaving.value = true;
681
706
  inlineEditorError.value = null;
@@ -719,11 +744,21 @@ async function onPublishSubmit(opts: {
719
744
  allowZeroPrice?: boolean;
720
745
  validFrom?: string | null;
721
746
  validUntil?: string | null;
722
- }): Promise<BundleVersionMutationResult> {
723
- if (!publishDraft.value) {
747
+ }): Promise<BundleVersionMutationResult | null> {
748
+ const draft = publishDraft.value;
749
+ if (!draft) {
724
750
  throw new Error('BundlesPage: publish submit without draft context');
725
751
  }
726
- return versionsOps.publish(publishDraft.value.id, opts);
752
+ // Publishing requires the second factor on the server.
753
+ const outcome = await mfa.run(
754
+ formatMessage(msg.value.publishDialog.mfaDescription, {
755
+ bundleKey: detailBundle.value?.bundleKey ?? '',
756
+ version: draft.version,
757
+ }),
758
+ shell.value.mfa.invalidCode,
759
+ (code) => versionsOps.publish(draft.id, opts, code),
760
+ );
761
+ return outcome.done ? outcome.value : null;
727
762
  }
728
763
 
729
764
  async function onPublishSubmitted(result: BundleVersionMutationResult): Promise<void> {
@@ -741,15 +776,10 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
741
776
  </script>
742
777
 
743
778
  <style>
744
- .sa-bundles {
745
- display: flex;
746
- flex-direction: column;
747
- gap: 14px;
748
- }
749
779
  .sa-bundles__filter-row {
750
780
  display: flex;
751
781
  align-items: center;
752
- gap: 10px;
782
+ gap: var(--sa-space-3);
753
783
  flex-wrap: wrap;
754
784
  }
755
785
  .sa-bundles__search {
@@ -762,7 +792,7 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
762
792
  .sa-bundles__list {
763
793
  display: flex;
764
794
  flex-direction: column;
765
- gap: 8px;
795
+ gap: var(--sa-space-3);
766
796
  }
767
797
  /* The card's surface, open border, head padding, chevron and body now come
768
798
  * from `AdminAccordion` — twelve declarations that were this page's fourth
@@ -779,7 +809,7 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
779
809
  }
780
810
  .sa-bd-card__titlerow {
781
811
  display: flex;
782
- gap: 8px;
812
+ gap: var(--sa-space-3);
783
813
  align-items: center;
784
814
  }
785
815
  .sa-bd-card__key {
@@ -790,7 +820,7 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
790
820
  .sa-bd-card__name {
791
821
  font-size: var(--sa-text-md);
792
822
  font-weight: 600;
793
- margin-top: 2px;
823
+ margin-top: var(--sa-space-1);
794
824
  }
795
825
  .sa-bd-card__desc {
796
826
  font-size: var(--sa-text-sm);
@@ -799,7 +829,7 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
799
829
  .sa-bd-grid {
800
830
  display: grid;
801
831
  grid-template-columns: minmax(280px, 360px) 1fr;
802
- gap: 18px;
832
+ gap: var(--sa-space-5);
803
833
  }
804
834
  @media (max-width: 1023.98px) {
805
835
  .sa-bd-grid {
@@ -809,28 +839,22 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
809
839
  .sa-bd-col {
810
840
  display: flex;
811
841
  flex-direction: column;
812
- gap: 8px;
842
+ gap: var(--sa-space-3);
813
843
  }
814
844
  .sa-bd-col--versions {
815
- gap: 14px;
816
- }
817
- .sa-bd-version-actions {
818
- display: flex;
819
- justify-content: space-between;
820
- gap: 12px;
821
- margin-top: 4px;
845
+ gap: var(--sa-space-4);
822
846
  }
823
847
  .sa-bd-section-label {
824
848
  font-size: var(--sa-text-xs);
825
849
  font-weight: 700;
826
850
  text-transform: uppercase;
827
- letter-spacing: 0.5px;
851
+ letter-spacing: var(--sa-tracking-wide);
828
852
  color: var(--sa-color-fg-secondary);
829
853
  display: flex;
830
854
  justify-content: space-between;
831
855
  }
832
856
  .sa-bd-section-label--mt {
833
- margin-top: 10px;
857
+ margin-top: var(--sa-space-3);
834
858
  }
835
859
  .sa-bd-section-count {
836
860
  color: var(--sa-color-fg-subtle);
@@ -839,7 +863,7 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
839
863
  .sa-bundles__form {
840
864
  display: flex;
841
865
  flex-direction: column;
842
- gap: 10px;
866
+ gap: var(--sa-space-3);
843
867
  }
844
868
  .sa-bd-i18n-hint {
845
869
  font-size: var(--sa-text-xs);
@@ -848,85 +872,53 @@ const classifyDiff = computed(() => classifyBundleVersionDiff);
848
872
  .sa-bd-i18n-block {
849
873
  display: flex;
850
874
  flex-direction: column;
851
- gap: 6px;
852
- padding: 8px;
875
+ gap: var(--sa-space-2);
876
+ padding: var(--sa-space-3);
853
877
  border: 1px solid var(--sa-color-border);
854
- border-radius: 8px;
878
+ border-radius: var(--sa-radius-field);
855
879
  background: var(--sa-color-bg-surface);
856
880
  }
857
881
  .sa-bd-i18n-head {
858
882
  display: flex;
859
- gap: 8px;
883
+ gap: var(--sa-space-3);
860
884
  align-items: center;
861
885
  }
862
886
  .sa-bd-i18n-code {
863
887
  font-size: var(--sa-text-2xs);
864
888
  font-weight: 700;
865
889
  background: var(--sa-color-border-soft);
866
- padding: 2px 7px;
867
- border-radius: 5px;
890
+ padding: var(--sa-space-1) var(--sa-space-3);
891
+ border-radius: var(--sa-radius-badge);
868
892
  }
869
893
  .sa-bd-i18n-fallback {
870
894
  font-size: var(--sa-text-2xs);
871
895
  color: var(--sa-color-warning-fg);
872
896
  }
873
- .sa-bd-save {
874
- margin-top: 8px;
875
- align-self: flex-start;
897
+ /* Reinforces the disabled state of the save button beside it. The button is
898
+ what a screen reader hears; this is what a sighted reader sees without
899
+ hunting for a greyed-out control. */
900
+ .sa-bd-dirty {
901
+ width: var(--sa-space-2);
902
+ height: var(--sa-space-2);
903
+ border-radius: 50%;
904
+ background: var(--sa-color-warning);
905
+ align-self: center;
876
906
  }
877
- .sa-bd-delete {
878
- margin-top: 8px;
907
+ .sa-bd-save {
908
+ margin-top: var(--sa-space-3);
879
909
  align-self: flex-start;
880
910
  }
881
- .sa-bd-version {
882
- display: flex;
883
- gap: 10px;
884
- align-items: flex-start;
885
- background: var(--sa-color-bg-surface);
886
- border: 1px solid var(--sa-color-border);
887
- border-radius: 8px;
888
- padding: 10px;
889
- }
890
- .sa-bd-version__main {
891
- flex: 1;
892
- min-width: 0;
893
- }
894
- .sa-bd-version__titlerow {
895
- display: flex;
896
- gap: 8px;
897
- align-items: center;
898
- }
899
- .sa-bd-version__sub {
900
- font-size: var(--sa-text-xs);
901
- color: var(--sa-color-fg-muted);
902
- margin: 2px 0;
903
- }
904
- .sa-bd-version__feats {
905
- display: flex;
906
- flex-wrap: wrap;
907
- gap: 4px;
908
- }
909
- .sa-bd-feat-chip {
910
- font-size: var(--sa-text-2xs);
911
- background: var(--sa-color-border-soft);
912
- padding: 2px 6px;
913
- border-radius: 5px;
914
- }
915
- .sa-bd-version__act {
916
- display: flex;
917
- gap: 2px;
918
- }
919
911
  .sa-bd-empty-row {
920
- padding: 20px;
912
+ padding: var(--sa-space-6);
921
913
  text-align: center;
922
914
  color: var(--sa-color-fg-subtle);
923
915
  font-size: var(--sa-text-sm);
924
916
  border: 1px dashed var(--sa-color-border-strong);
925
- border-radius: 8px;
917
+ border-radius: var(--sa-radius-field);
926
918
  }
927
919
  .sa-bundles__warnings-list {
928
- margin: 8px 0 0;
929
- padding-left: 20px;
920
+ margin: var(--sa-space-3) 0 0;
921
+ padding-left: var(--sa-space-6);
930
922
  font-size: var(--sa-text-md);
931
923
  }
932
924
  </style>