@saasicat/ui-vue 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +64 -4
  2. package/dist/chunk-A3W2N26K.js +714 -0
  3. package/dist/chunk-QZOSDCNP.js +98 -0
  4. package/dist/chunk-X5SQ5HMB.js +3231 -0
  5. package/dist/client/index.cjs +3988 -0
  6. package/dist/client/index.d.cts +321 -0
  7. package/dist/client/index.d.ts +321 -0
  8. package/dist/client/index.js +70 -0
  9. package/dist/index.cjs +4446 -1249
  10. package/dist/index.d.cts +297 -888
  11. package/dist/index.d.ts +297 -888
  12. package/dist/index.js +609 -1348
  13. package/dist/messages-7b0P8W75.d.cts +1652 -0
  14. package/dist/messages-7b0P8W75.d.ts +1652 -0
  15. package/dist/quasar/index.cjs +3340 -0
  16. package/dist/quasar/index.d.cts +95 -0
  17. package/dist/quasar/index.d.ts +95 -0
  18. package/dist/quasar/index.js +96 -0
  19. package/dist/testing-e2e/admin-pages-suite.cjs +14 -11
  20. package/dist/testing-e2e/admin-pages-suite.js +14 -11
  21. package/dist/use-super-admin-i18n-B3v3-SWZ.d.ts +210 -0
  22. package/dist/use-super-admin-i18n-DMktRVEt.d.cts +210 -0
  23. package/package.json +23 -3
  24. package/src/{action-registry.ts → client/action-registry.ts} +4 -4
  25. package/src/{batch-column-fetcher.ts → client/batch-column-fetcher.ts} +4 -4
  26. package/src/{boot-loader.ts → client/boot-loader.ts} +3 -3
  27. package/src/client/i18n/currency.ts +42 -0
  28. package/src/client/i18n/define.ts +59 -0
  29. package/src/client/i18n/format.ts +12 -0
  30. package/src/client/i18n/index.ts +14 -0
  31. package/src/client/i18n/locale.ts +15 -0
  32. package/src/client/i18n/messages/audit.ts +42 -0
  33. package/src/client/i18n/messages/bundles.ts +459 -0
  34. package/src/client/i18n/messages/common.ts +147 -0
  35. package/src/client/i18n/messages/dashboard.ts +16 -0
  36. package/src/client/i18n/messages/discovery.ts +229 -0
  37. package/src/client/i18n/messages/email.ts +125 -0
  38. package/src/client/i18n/messages/marketing.ts +270 -0
  39. package/src/client/i18n/messages/nav.ts +72 -0
  40. package/src/client/i18n/messages/pilots.ts +192 -0
  41. package/src/client/i18n/messages/plan-detail.ts +266 -0
  42. package/src/client/i18n/messages/plan-editor.ts +177 -0
  43. package/src/client/i18n/messages/plan-versions.ts +251 -0
  44. package/src/client/i18n/messages/plans.ts +503 -0
  45. package/src/client/i18n/messages/promos.ts +202 -0
  46. package/src/client/i18n/messages/shell.ts +145 -0
  47. package/src/client/i18n/messages/tenants.ts +104 -0
  48. package/src/client/i18n/messages/users.ts +78 -0
  49. package/src/client/i18n/messages.ts +84 -0
  50. package/src/client/index.ts +27 -0
  51. package/src/{manifest-loader.ts → client/manifest-loader.ts} +4 -4
  52. package/src/{nav-builder.ts → client/nav-builder.ts} +44 -51
  53. package/src/{plan-versions-catalog.ts → client/plan-versions-catalog.ts} +42 -30
  54. package/src/components/BundleVersionPublishDialog.vue +35 -24
  55. package/src/components/FeatureGate.vue +1 -1
  56. package/src/components/MarketingPromotionsTab.vue +112 -59
  57. package/src/components/MfaPromptDialog.vue +11 -11
  58. package/src/components/TenantActionConfirmDialog.vue +19 -17
  59. package/src/components/VersionDiffPreview.vue +72 -43
  60. package/src/components/bundle-editor/BundleCreatePanel.vue +76 -63
  61. package/src/components/bundle-editor/BundleFeaturesEditor.vue +13 -6
  62. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +19 -14
  63. package/src/components/bundle-editor/BundleQuotasEditor.vue +7 -4
  64. package/src/components/bundle-editor/BundleStatusBanner.vue +36 -19
  65. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +51 -35
  66. package/src/components/bundle-editor/BundleVersionStrip.vue +24 -12
  67. package/src/components/bundle-editor/bundle-version-status.ts +29 -29
  68. package/src/components/dialogs/PilotCreateDialog.vue +58 -63
  69. package/src/components/dialogs/PilotEditDialog.vue +40 -34
  70. package/src/components/dialogs/PromoCodeCreateDialog.vue +89 -63
  71. package/src/components/dialogs/PromoCodeEditDialog.vue +98 -66
  72. package/src/components/dialogs/types.ts +3 -13
  73. package/src/components/plan/PlanCycleToggle.vue +21 -10
  74. package/src/components/plan/PlanGrid.vue +2 -2
  75. package/src/components/plan/PriceSummary.vue +1 -1
  76. package/src/components/plan/PromoCodeInput.vue +1 -1
  77. package/src/components/plan/PublicBundleGrid.vue +1 -1
  78. package/src/components/plan-cockpit/PlanCockpit.vue +34 -15
  79. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +13 -6
  80. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +18 -9
  81. package/src/components/plan-cockpit/PlanCockpitHeader.vue +9 -6
  82. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +24 -11
  83. package/src/components/plan-cockpit/PlanCockpitKpis.vue +28 -17
  84. package/src/components/plan-cockpit/PlanCockpitVersions.vue +50 -25
  85. package/src/components/plan-create-dialog/PlanCreateDialog.vue +45 -35
  86. package/src/components/plan-detail/PlanAuditLog.vue +11 -6
  87. package/src/components/plan-detail/PlanDetail.vue +21 -23
  88. package/src/components/plan-detail/PlanDetailHeader.vue +20 -11
  89. package/src/components/plan-detail/PlanDetailKpis.vue +30 -17
  90. package/src/components/plan-detail/PlanTerminateDialog.vue +22 -9
  91. package/src/components/plan-detail/PlanVersionDiffPanel.vue +61 -27
  92. package/src/components/plan-detail/PlanVersionsPanel.vue +71 -36
  93. package/src/components/plan-list/PlanList.vue +99 -57
  94. package/src/components/plan-matrix/PlanMatrix.vue +85 -38
  95. package/src/components/plan-review/PlanReview.vue +105 -80
  96. package/src/components/plan-version-editor/PlanCatalogPreview.vue +43 -20
  97. package/src/components/plan-version-editor/PlanComponentPool.vue +15 -7
  98. package/src/components/plan-version-editor/PlanVersionBasket.vue +45 -24
  99. package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +8 -7
  100. package/src/components/plan-version-editor/PlanVersionEditor.vue +74 -40
  101. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +24 -12
  102. package/src/index.ts +82 -79
  103. package/src/pages-standard/AdminLayout.vue +43 -18
  104. package/src/pages-standard/AdminManifestErrorPage.vue +10 -10
  105. package/src/pages-standard/AuditPage.vue +58 -19
  106. package/src/pages-standard/BundlesPage.vue +29 -18
  107. package/src/pages-standard/DashboardPage.vue +31 -17
  108. package/src/pages-standard/DiscoveryPage.vue +40 -28
  109. package/src/pages-standard/EmailHistoryPage.vue +98 -69
  110. package/src/pages-standard/MarketingCatalogPage.vue +40 -21
  111. package/src/pages-standard/PilotsPage.vue +64 -52
  112. package/src/pages-standard/PlanVersionsPage.vue +9 -5
  113. package/src/pages-standard/PlansPage.vue +91 -59
  114. package/src/pages-standard/PlatformEmailPage.vue +120 -53
  115. package/src/pages-standard/PromoCodeDetailPage.vue +66 -63
  116. package/src/pages-standard/PromoCodesPage.vue +51 -54
  117. package/src/pages-standard/SubscriptionsPage.vue +14 -9
  118. package/src/pages-standard/SuperAdminLoginPage.vue +19 -16
  119. package/src/pages-standard/SuperAdminSetupWizard.vue +53 -49
  120. package/src/pages-standard/TenantDetailPage.vue +54 -40
  121. package/src/pages-standard/TenantsPage.vue +57 -44
  122. package/src/pages-standard/UsersPage.vue +70 -52
  123. package/src/pages-standard/bundles-page/BundleAccordionList.vue +20 -8
  124. package/src/pages-standard/bundles-page/BundleDetailPanel.vue +28 -18
  125. package/src/pages-standard/bundles-page/BundlesFilterBar.vue +4 -1
  126. package/src/pages-standard/bundles-page/BundlesHeader.vue +9 -8
  127. package/src/pages-standard/bundles-page/BundlesKpis.vue +29 -11
  128. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +20 -8
  129. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +5 -3
  130. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +43 -24
  131. package/src/pages-standard/discovery-page/DiscoveryHeader.vue +7 -4
  132. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +28 -15
  133. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +20 -10
  134. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +9 -6
  135. package/src/pages-standard/discovery-page/discovery-ui.ts +40 -52
  136. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +81 -49
  137. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +13 -10
  138. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +24 -20
  139. package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +6 -3
  140. package/src/pages-standard/plan-versions/PlanDiffCard.vue +16 -6
  141. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +42 -26
  142. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +46 -27
  143. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +8 -6
  144. package/src/pages-standard/plan-versions/PlanVersionsList.vue +40 -17
  145. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +1 -1
  146. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +25 -13
  147. package/src/pages-standard/plan-versions/format.ts +33 -17
  148. package/src/pages-standard/plans-page/PlanArchiveDialog.vue +12 -8
  149. package/src/pages-standard/plans-page/PlanBundleOverview.vue +15 -6
  150. package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +18 -7
  151. package/src/pages-standard/plans-page/PlanPublishDialog.vue +23 -12
  152. package/src/pages-standard/tenants/format.ts +3 -2
  153. package/src/pages-tenant/MySubscriptionBundlesPage.vue +73 -38
  154. package/src/pages-tenant/OnboardingConfigurator.vue +3 -3
  155. package/src/pages-tenant/PackageSnapshotPanel.vue +1 -1
  156. package/src/pages-tenant/PlanChangeWizard.vue +10 -6
  157. package/src/pages-tenant/TenantPlanSection.vue +20 -16
  158. package/src/pages-tenant/default-i18n.ts +217 -1
  159. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +9 -7
  160. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +11 -12
  161. package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +4 -1
  162. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +1 -1
  163. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +1 -1
  164. package/src/quasar/create-super-admin-app.ts +192 -0
  165. package/src/quasar/index.ts +11 -0
  166. package/src/quasar/notify.ts +30 -0
  167. package/src/testing-e2e/admin-pages-suite.ts +14 -11
  168. package/src/{manifest-store-factory.ts → vue/manifest-store-factory.ts} +1 -1
  169. package/src/{platform-loaders.ts → vue/platform-loaders.ts} +4 -4
  170. package/src/{project-page-host.ts → vue/project-page-host.ts} +6 -4
  171. package/src/{create-super-admin-app.ts → vue/super-admin-context.ts} +20 -139
  172. package/src/vue/ui-notify.ts +30 -0
  173. package/src/{use-actions.ts → vue/use-actions.ts} +1 -1
  174. package/src/{use-api-list.ts → vue/use-api-list.ts} +1 -1
  175. package/src/{use-audit-entries.ts → vue/use-audit-entries.ts} +3 -3
  176. package/src/{use-batch-columns.ts → vue/use-batch-columns.ts} +1 -1
  177. package/src/{use-bulk-publish.ts → vue/use-bulk-publish.ts} +7 -5
  178. package/src/{use-bundle-versions-map.ts → vue/use-bundle-versions-map.ts} +7 -5
  179. package/src/{use-bundles.ts → vue/use-bundles.ts} +14 -14
  180. package/src/{use-catalog-entries.ts → vue/use-catalog-entries.ts} +14 -11
  181. package/src/{use-discovery.ts → vue/use-discovery.ts} +9 -6
  182. package/src/{use-entitlement.ts → vue/use-entitlement.ts} +4 -4
  183. package/src/{use-live-plan-versions.ts → vue/use-live-plan-versions.ts} +5 -5
  184. package/src/{use-manifest.ts → vue/use-manifest.ts} +1 -1
  185. package/src/{use-marketing-projections.ts → vue/use-marketing-projections.ts} +10 -7
  186. package/src/{use-nav.ts → vue/use-nav.ts} +9 -3
  187. package/src/{use-plan-editor.ts → vue/use-plan-editor.ts} +2 -2
  188. package/src/{use-plan-versions.ts → vue/use-plan-versions.ts} +3 -3
  189. package/src/{use-plans.ts → vue/use-plans.ts} +13 -13
  190. package/src/{use-platform-tenant-actions.ts → vue/use-platform-tenant-actions.ts} +9 -6
  191. package/src/{use-promotions.ts → vue/use-promotions.ts} +10 -11
  192. package/src/{use-public-boot.ts → vue/use-public-boot.ts} +1 -1
  193. package/src/{use-subscription-draft.ts → vue/use-subscription-draft.ts} +2 -3
  194. package/src/{use-super-admin-context.ts → vue/use-super-admin-context.ts} +5 -5
  195. package/src/vue/use-super-admin-i18n.ts +82 -0
  196. package/src/{use-tenant-action-flow.ts → vue/use-tenant-action-flow.ts} +12 -7
  197. package/src/{use-tenant-billing-catalog.ts → vue/use-tenant-billing-catalog.ts} +2 -2
  198. package/src/{use-tenant-billing.ts → vue/use-tenant-billing.ts} +2 -2
  199. package/src/{use-tenant-manifest.ts → vue/use-tenant-manifest.ts} +2 -4
  200. package/src/{use-tenant-subscription-bundles.ts → vue/use-tenant-subscription-bundles.ts} +5 -5
  201. package/src/{use-tenants.ts → vue/use-tenants.ts} +3 -3
  202. package/dist/testing-e2e/admin-pages-suite.d.cts +0 -37
  203. package/dist/testing-e2e/admin-pages-suite.d.ts +0 -37
  204. package/src/components/BusinessTypeVersionEditorDialog.vue +0 -417
  205. package/src/components/BusinessTypeVersionPublishDialog.vue +0 -258
  206. package/src/pages-standard/BusinessTypesPage.vue +0 -662
  207. package/src/pages-standard/plan-versions/VersionDiffPreview.vue +0 -167
  208. package/src/use-business-types.ts +0 -269
  209. /package/src/{http-json.ts → client/http-json.ts} +0 -0
  210. /package/src/{types.ts → client/types.ts} +0 -0
  211. /package/src/{version.ts → client/version.ts} +0 -0
  212. /package/src/{create-admin-routes.ts → vue/create-admin-routes.ts} +0 -0
  213. /package/src/{entitlement-provider.ts → vue/entitlement-provider.ts} +0 -0
  214. /package/src/{feature-router-guard.ts → vue/feature-router-guard.ts} +0 -0
@@ -2,11 +2,8 @@
2
2
  <div class="msb">
3
3
  <header class="msb-head">
4
4
  <div>
5
- <h2 class="msb-title">Meine Bundles</h2>
6
- <p class="msb-sub">
7
- Eigenständig gebuchte Add-On-Pakete zu deinem Plan. Mindestlaufzeit +
8
- Kündigungs- Termin pro Bundle.
9
- </p>
5
+ <h2 class="msb-title">{{ effectiveI18n.myBundlesTitle }}</h2>
6
+ <p class="msb-sub">{{ effectiveI18n.myBundlesSubtitle }}</p>
10
7
  </div>
11
8
  <button class="msb-btn msb-btn--primary" type="button" @click="openAddDialog">
12
9
  <svg
@@ -19,22 +16,24 @@
19
16
  >
20
17
  <path d="M12 5v14M5 12h14" />
21
18
  </svg>
22
- <span>Bundle buchen</span>
19
+ <span>{{ effectiveI18n.bundleBookAction }}</span>
23
20
  </button>
24
21
  </header>
25
22
 
26
23
  <div v-if="error" class="msb-error" role="alert">
27
- <strong>Fehler:</strong> {{ error.message }}
24
+ <strong>{{ effectiveI18n.errorLabel }}:</strong> {{ error.message }}
28
25
  </div>
29
26
 
30
- <div v-if="loading && bundles.length === 0" class="msb-loading">Lade …</div>
27
+ <div v-if="loading && bundles.length === 0" class="msb-loading">
28
+ {{ effectiveI18n.loading }}
29
+ </div>
31
30
 
32
31
  <div v-else-if="bundles.length === 0" class="msb-empty">
33
- Du hast noch kein Bundle gebucht. Über
32
+ {{ effectiveI18n.myBundlesEmptyPrefix }}
34
33
  <button type="button" class="msb-empty-link" @click="openAddDialog">
35
- Bundle buchen
34
+ {{ effectiveI18n.bundleBookAction }}
36
35
  </button>
37
- kannst du dein Paket um zusätzliche Features &amp; Quotas erweitern.
36
+ {{ effectiveI18n.myBundlesEmptySuffix }}
38
37
  </div>
39
38
 
40
39
  <div v-else class="msb-list">
@@ -48,9 +47,10 @@
48
47
  </span>
49
48
  </div>
50
49
  <div class="msb-card-meta">
51
- Gebucht seit {{ formatDate(b.startedAt) }}
50
+ {{ effectiveI18n.myBundlesBookedSince }} {{ formatDate(b.startedAt) }}
52
51
  <template v-if="b.minimumTermEndsAt">
53
- · Mindestlaufzeit bis {{ formatDate(b.minimumTermEndsAt) }}
52
+ · {{ effectiveI18n.bundleMinimumTermUntil }}
53
+ {{ formatDate(b.minimumTermEndsAt) }}
54
54
  </template>
55
55
  </div>
56
56
  </div>
@@ -61,11 +61,16 @@
61
61
  :disabled="cancellingId === b.id"
62
62
  @click="onCancel(b)"
63
63
  >
64
- {{ cancellingId === b.id ? 'Kündige …' : 'Kündigen' }}
64
+ {{
65
+ cancellingId === b.id
66
+ ? effectiveI18n.myBundlesCancelInProgress
67
+ : effectiveI18n.bundleCancelAction
68
+ }}
65
69
  </button>
66
70
  </header>
67
71
  <div v-if="b.canceledAt !== null" class="msb-cancel-info">
68
- Gekündigt am {{ formatDate(b.canceledAt) }} · läuft bis
72
+ {{ effectiveI18n.myBundlesCanceledAt }} {{ formatDate(b.canceledAt) }} ·
73
+ {{ effectiveI18n.myBundlesRunsUntil }}
69
74
  <strong>{{ formatDate(b.canceledEffectiveAt) }}</strong>
70
75
  </div>
71
76
  </article>
@@ -75,11 +80,11 @@
75
80
  <div v-if="addOpen" class="msb-modal-bg" @click="closeAddDialog">
76
81
  <div class="msb-modal" @click.stop>
77
82
  <div class="msb-modal-head">
78
- <div class="msb-modal-title">Bundle buchen</div>
83
+ <div class="msb-modal-title">{{ effectiveI18n.bundleBookAction }}</div>
79
84
  <button
80
85
  class="msb-modal-x"
81
86
  type="button"
82
- aria-label="Schließen"
87
+ :aria-label="effectiveI18n.wizardClose"
83
88
  @click="closeAddDialog"
84
89
  >
85
90
  <svg
@@ -98,9 +103,13 @@
98
103
  <!-- Selection list via public catalog, otherwise fallback to direct UUID input. -->
99
104
  <template v-if="bookable.length > 0">
100
105
  <label class="msb-field">
101
- <span class="msb-field-label">Bundle</span>
106
+ <span class="msb-field-label">{{
107
+ effectiveI18n.myBundlesAddBundleLabel
108
+ }}</span>
102
109
  <select v-model="addForm.bundleVersionId" class="msb-input">
103
- <option value="">— bitte wählen —</option>
110
+ <option value="">
111
+ {{ effectiveI18n.myBundlesAddSelectPlaceholder }}
112
+ </option>
104
113
  <option
105
114
  v-for="b in bookable"
106
115
  :key="b.bundleVersionId"
@@ -108,54 +117,67 @@
108
117
  >
109
118
  {{ b.label }} ({{ b.bundleKey }})
110
119
  <template v-if="b.monthlyNet !== null">
111
- — {{ b.monthlyNet }} € / Mo
120
+ — {{ b.monthlyNet }}
121
+ {{ effectiveI18n.myBundlesPricePerMonthShort }}
112
122
  </template>
113
123
  </option>
114
124
  </select>
115
125
  <span v-if="hiddenBecauseIncompatible > 0" class="msb-field-hint">
116
- {{ hiddenBecauseIncompatible }} weitere Bundle(s) ausgeblendet —
117
- nicht kompatibel mit Plan <code>{{ currentPlanKey }}</code
126
+ {{ hiddenBecauseIncompatible }}
127
+ {{ effectiveI18n.myBundlesHiddenIncompatible }}
128
+ <code>{{ currentPlanKey }}</code
118
129
  >.
119
130
  </span>
120
131
  </label>
121
132
  </template>
122
133
  <template v-else>
123
134
  <label class="msb-field">
124
- <span class="msb-field-label">BundleVersion-ID</span>
135
+ <span class="msb-field-label">
136
+ {{ effectiveI18n.myBundlesBundleVersionIdLabel }}
137
+ </span>
125
138
  <input
126
139
  v-model="addForm.bundleVersionId"
127
140
  class="msb-input"
128
- placeholder="UUID der gewünschten Bundle-Version"
141
+ :placeholder="effectiveI18n.myBundlesBundleVersionIdPlaceholder"
129
142
  />
143
+ <!-- Integration hint for the embedding app, not for tenants. -->
130
144
  <span class="msb-field-hint">
131
- Aus dem öffentlichen Marketing-Catalog. Konsument kann
145
+ From the public marketing catalog. Pass
132
146
  <code>availableBundles</code>
133
- als Prop reichen, dann erscheint hier ein Dropdown.
147
+ as a prop to get a dropdown here.
134
148
  </span>
135
149
  </label>
136
150
  </template>
137
151
  <label class="msb-field">
138
- <span class="msb-field-label">Mindestlaufzeit (Monate, optional)</span>
152
+ <span class="msb-field-label">{{
153
+ effectiveI18n.myBundlesMinimumTermLabel
154
+ }}</span>
139
155
  <input
140
156
  v-model.number="addForm.minimumTermMonths"
141
157
  type="number"
142
158
  min="0"
143
159
  max="120"
144
160
  class="msb-input"
145
- placeholder="Default: 12"
161
+ :placeholder="effectiveI18n.myBundlesMinimumTermPlaceholder"
146
162
  />
147
163
  </label>
148
164
  <div v-if="addError" class="msb-error">{{ addError }}</div>
149
165
  </div>
150
166
  <div class="msb-modal-foot">
151
- <button class="msb-btn" type="button" @click="closeAddDialog">Abbrechen</button>
167
+ <button class="msb-btn" type="button" @click="closeAddDialog">
168
+ {{ effectiveI18n.bundlePreviewClose }}
169
+ </button>
152
170
  <button
153
171
  class="msb-btn msb-btn--primary"
154
172
  type="button"
155
173
  :disabled="!canSubmit || adding"
156
174
  @click="submitAdd"
157
175
  >
158
- {{ adding ? 'Buche …' : 'Bundle buchen' }}
176
+ {{
177
+ adding
178
+ ? effectiveI18n.myBundlesBookInProgress
179
+ : effectiveI18n.bundleBookAction
180
+ }}
159
181
  </button>
160
182
  </div>
161
183
  </div>
@@ -167,7 +189,9 @@
167
189
  import { computed, onMounted, reactive, ref } from 'vue';
168
190
  import type { SubscriptionBundleRecord } from '@saasicat/types';
169
191
 
170
- import { useTenantSubscriptionBundles } from '../use-tenant-subscription-bundles.js';
192
+ import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
193
+ import { useTenantSubscriptionBundles } from '../vue/use-tenant-subscription-bundles.js';
194
+ import { defaultTenantPlanSectionI18n, type TenantPlanSectionI18n } from './default-i18n.js';
171
195
 
172
196
  // MySubscriptionBundlesPage — tenant self-service page "Meine Bundles".
173
197
  // The hosting app embeds the page
@@ -208,6 +232,8 @@ const props = withDefaults(
208
232
  currentPlanKey?: string | null;
209
233
  /** Optional: auth token provider (for the Bearer header). */
210
234
  getAuthToken?: () => string | null;
235
+ /** i18n overrides — missing keys fall back to the active locale's map. */
236
+ i18n?: Partial<TenantPlanSectionI18n>;
211
237
  }>(),
212
238
  {
213
239
  bundleLabels: () => ({}),
@@ -217,6 +243,13 @@ const props = withDefaults(
217
243
  },
218
244
  );
219
245
 
246
+ const { locale, intlLocale } = useSuperAdminI18n();
247
+
248
+ const effectiveI18n = computed<TenantPlanSectionI18n>(() => ({
249
+ ...defaultTenantPlanSectionI18n(locale.value),
250
+ ...(props.i18n ?? {}),
251
+ }));
252
+
220
253
  const { bundles, loading, error, load, add, cancel } = useTenantSubscriptionBundles({
221
254
  billingEndpoint: props.billingEndpoint,
222
255
  getAuthToken: props.getAuthToken,
@@ -308,9 +341,7 @@ async function submitAdd(): Promise<void> {
308
341
  const cancellingId = ref<string | null>(null);
309
342
 
310
343
  async function onCancel(b: SubscriptionBundleRecord): Promise<void> {
311
- const ok = window.confirm(
312
- `Bundle wirklich kündigen? Die Kündigung wird zum nächstmöglichen Termin wirksam.`,
313
- );
344
+ const ok = window.confirm(effectiveI18n.value.myBundlesCancelConfirm);
314
345
  if (!ok) return;
315
346
  cancellingId.value = b.id;
316
347
  try {
@@ -336,9 +367,9 @@ function statusOf(b: SubscriptionBundleRecord): 'active' | 'canceled-pending' |
336
367
 
337
368
  function statusLabel(b: SubscriptionBundleRecord): string {
338
369
  const s = statusOf(b);
339
- if (s === 'active') return 'Aktiv';
340
- if (s === 'canceled-pending') return 'Kündigung wirksam ab …';
341
- return 'Beendet';
370
+ if (s === 'active') return effectiveI18n.value.statusActive;
371
+ if (s === 'canceled-pending') return effectiveI18n.value.myBundlesStatusCanceledPending;
372
+ return effectiveI18n.value.myBundlesStatusEnded;
342
373
  }
343
374
 
344
375
  function cardStatusClass(b: SubscriptionBundleRecord): string {
@@ -349,7 +380,11 @@ function formatDate(date: Date | string | null | undefined): string {
349
380
  if (!date) return '—';
350
381
  const d = typeof date === 'string' ? new Date(date) : date;
351
382
  if (Number.isNaN(d.getTime())) return '—';
352
- return d.toLocaleDateString('de-DE', { year: 'numeric', month: '2-digit', day: '2-digit' });
383
+ return d.toLocaleDateString(intlLocale.value, {
384
+ year: 'numeric',
385
+ month: '2-digit',
386
+ day: '2-digit',
387
+ });
353
388
  }
354
389
  </script>
355
390
 
@@ -104,9 +104,9 @@ import {
104
104
  useSubscriptionDraft,
105
105
  type PromoState,
106
106
  type SubscriptionDraft,
107
- } from '../use-subscription-draft.js';
108
- import type { CatalogPlan } from '../use-tenant-billing-catalog.js';
109
- import type { BillingCycleStr } from '../use-tenant-billing.js';
107
+ } from '../vue/use-subscription-draft.js';
108
+ import type { CatalogPlan } from '../vue/use-tenant-billing-catalog.js';
109
+ import type { BillingCycleStr } from '../vue/use-tenant-billing.js';
110
110
  import type {
111
111
  OnboardingSelectionRequest,
112
112
  OnboardingSelectionResponse,
@@ -105,7 +105,7 @@
105
105
 
106
106
  <script setup lang="ts">
107
107
  import { computed, ref } from 'vue';
108
- import type { PackageSnapshotShape } from '../use-tenant-billing.js';
108
+ import type { PackageSnapshotShape } from '../vue/use-tenant-billing.js';
109
109
  import type { TenantPlanSectionI18n } from './default-i18n.js';
110
110
 
111
111
  // PackageSnapshotPanel — P11.4 (METAMODELL §17a):
@@ -220,8 +220,9 @@ import { computed, ref, watch } from 'vue';
220
220
  import LimitsRow from './LimitsRow.vue';
221
221
  import PlanCycleToggle from '../components/plan/PlanCycleToggle.vue';
222
222
  import PlanGrid from '../components/plan/PlanGrid.vue';
223
- import type { BillingCycleStr, PlanChangePreviewShape } from '../use-tenant-billing.js';
224
- import type { CatalogPlan } from '../use-tenant-billing-catalog.js';
223
+ import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
224
+ import type { BillingCycleStr, PlanChangePreviewShape } from '../vue/use-tenant-billing.js';
225
+ import type { CatalogPlan } from '../vue/use-tenant-billing-catalog.js';
225
226
 
226
227
  // PlanChangeWizard — 3-step dialog for plan changes.
227
228
  //
@@ -239,6 +240,7 @@ interface I18nStrings {
239
240
  badgeCurrent: string;
240
241
  badgePopular: string;
241
242
  priceUnitMonthly: string;
243
+ priceUnitYearly: string;
242
244
  priceOnRequest: string;
243
245
  stepChoose: string;
244
246
  stepChooseIntro: string;
@@ -302,8 +304,8 @@ interface Props {
302
304
  formatQuotaLabel?: (key: string, value: number) => string;
303
305
  /**
304
306
  * Plain value per quota key, e.g. "200" or "10 GB" (for PlanGrid).
305
- * Optional — default: takes `value.toLocaleString('de-DE')`, appends a
306
- * `GB` suffix for `storage` keys, replaces -1 with ∞.
307
+ * Optional — default: takes `value.toLocaleString()` in the active UI
308
+ * locale, appends a `GB` suffix for `storage` keys, replaces -1 with ∞.
307
309
  */
308
310
  formatQuotaValue?: (key: string, value: number) => string;
309
311
  quotaLabel: (key: string) => string;
@@ -321,6 +323,8 @@ const emit = defineEmits<{
321
323
  submitted: [];
322
324
  }>();
323
325
 
326
+ const { intlLocale } = useSuperAdminI18n();
327
+
324
328
  const model = computed({
325
329
  get: () => props.modelValue,
326
330
  set: (v) => emit('update:modelValue', v),
@@ -430,7 +434,7 @@ function formatQuotaValueResolved(key: string, value: number | null | undefined)
430
434
  if (value === null || value === undefined || Number.isNaN(value)) return '–';
431
435
  if (value < 0) return '∞';
432
436
  if (key.toLowerCase().includes('storage')) return `${value} GB`;
433
- return value.toLocaleString('de-DE');
437
+ return value.toLocaleString(intlLocale.value);
434
438
  }
435
439
 
436
440
  const cycleI18n = computed(() => ({
@@ -443,7 +447,7 @@ const planGridI18n = computed(() => ({
443
447
  popular: props.i18n.badgePopular,
444
448
  current: props.i18n.badgeCurrent,
445
449
  perMonth: props.i18n.priceUnitMonthly,
446
- perYear: props.i18n.priceUnitMonthly,
450
+ perYear: props.i18n.priceUnitYearly,
447
451
  priceOnRequest: props.i18n.priceOnRequest,
448
452
  }));
449
453
 
@@ -122,7 +122,11 @@
122
122
  <!-- Reactivate confirmation (analogous to cancellation): deliberate action -->
123
123
  <q-dialog
124
124
  :model-value="reactivateConfirmId !== null"
125
- @update:model-value="(v) => { if (!v) closeReactivateConfirm(); }"
125
+ @update:model-value="
126
+ (v) => {
127
+ if (!v) closeReactivateConfirm();
128
+ }
129
+ "
126
130
  >
127
131
  <q-card style="min-width: 360px; max-width: 480px">
128
132
  <q-card-section>
@@ -194,7 +198,7 @@ import { computed, ref } from 'vue';
194
198
  import PackageSnapshotPanel from './PackageSnapshotPanel.vue';
195
199
  import PendingVersionBanner from './PendingVersionBanner.vue';
196
200
  import PlanChangeWizard from './PlanChangeWizard.vue';
197
- import { DEFAULT_I18N_DE, type TenantPlanSectionI18n } from './default-i18n.js';
201
+ import { defaultTenantPlanSectionI18n, type TenantPlanSectionI18n } from './default-i18n.js';
198
202
  import BundlePreviewDialog from './tenant-plan-section/BundlePreviewDialog.vue';
199
203
  import TenantBundleStore from './tenant-plan-section/TenantBundleStore.vue';
200
204
  import TenantFeatureMatrix from './tenant-plan-section/TenantFeatureMatrix.vue';
@@ -204,13 +208,14 @@ import {
204
208
  useTenantBilling,
205
209
  type BundlePreviewShape,
206
210
  type SubscriptionBundleShape,
207
- } from '../use-tenant-billing.js';
211
+ } from '../vue/use-tenant-billing.js';
208
212
  import {
209
213
  useTenantBillingCatalog,
210
214
  type CatalogBundle,
211
215
  type CatalogPlan,
212
- } from '../use-tenant-billing-catalog.js';
213
- import type { HttpClient } from '../types.js';
216
+ } from '../vue/use-tenant-billing-catalog.js';
217
+ import { useSuperAdminI18n } from '../vue/use-super-admin-i18n.js';
218
+ import type { HttpClient } from '../client/types.js';
214
219
 
215
220
  // TenantPlanSection — main component for the tenant plan/bundle self-service
216
221
  // UI. The consumer (app) embeds it in its settings page and passes through
@@ -234,7 +239,8 @@ interface Props {
234
239
  * Plain value per quota key (e.g. "200" or "10 GB") — separate from the
235
240
  * label so the wizard's `<PlanGrid>` can render value + label separately.
236
241
  * Optional; without an override the wizard default applies
237
- * (`value.toLocaleString('de-DE')` + `GB` for storage keys, ∞ for -1).
242
+ * (`value.toLocaleString()` in the active UI locale + `GB` for storage
243
+ * keys, ∞ for -1).
238
244
  */
239
245
  formatQuotaValue?: (key: string, value: number) => string;
240
246
 
@@ -245,7 +251,7 @@ interface Props {
245
251
  /** Default: storage quotas (float), all others integer. */
246
252
  isFractionalQuota?: (key: string) => boolean;
247
253
 
248
- /** i18n overrides — missing keys fall back to DEFAULT_I18N_DE. */
254
+ /** i18n overrides — missing keys fall back to the active locale's map. */
249
255
  i18n?: Partial<TenantPlanSectionI18n>;
250
256
 
251
257
  /**
@@ -264,6 +270,8 @@ interface Props {
264
270
 
265
271
  const props = defineProps<Props>();
266
272
 
273
+ const { locale } = useSuperAdminI18n();
274
+
267
275
  const billing = useTenantBilling({
268
276
  http: props.http,
269
277
  getAuthToken: props.getAuthToken,
@@ -287,8 +295,7 @@ const bundleError = ref<string | null>(null);
287
295
 
288
296
  // Bundle preview dialog state (#37/#61)
289
297
  type PendingBundleAction =
290
- | { kind: 'add'; bundleVersionId: string }
291
- | { kind: 'cancel'; subscriptionBundleId: string };
298
+ { kind: 'add'; bundleVersionId: string } | { kind: 'cancel'; subscriptionBundleId: string };
292
299
  const bundlePreviewOpen = ref(false);
293
300
  const bundlePreview = ref<BundlePreviewShape | null>(null);
294
301
  const bundlePreviewLoading = ref(false);
@@ -301,7 +308,7 @@ const loading = computed(() => billing.loading.value || catalog.loading.value);
301
308
  const error = computed(() => billing.error.value ?? catalog.error.value);
302
309
 
303
310
  const effectiveI18n = computed<TenantPlanSectionI18n>(() => ({
304
- ...DEFAULT_I18N_DE,
311
+ ...defaultTenantPlanSectionI18n(locale.value),
305
312
  ...(props.i18n ?? {}),
306
313
  }));
307
314
 
@@ -331,10 +338,7 @@ const availableBundles = computed<CatalogBundle[]>(() => catalog.bundles.value ?
331
338
  // no longer active and disappears from the list.
332
339
  function isSubscriptionBundleActive(b: SubscriptionBundleShape): boolean {
333
340
  if (b.canceledAt === null) return true;
334
- return (
335
- b.canceledEffectiveAt !== null &&
336
- new Date(b.canceledEffectiveAt).getTime() > Date.now()
337
- );
341
+ return b.canceledEffectiveAt !== null && new Date(b.canceledEffectiveAt).getTime() > Date.now();
338
342
  }
339
343
 
340
344
  const bookedBundles = computed<SubscriptionBundleShape[]>(() =>
@@ -348,8 +352,7 @@ const hasBundleStore = computed(
348
352
  const featureRegistry = computed(() => catalog.featureRegistry.value);
349
353
  const activeFeatures = computed<string[]>(() => usage.value?.limits.features ?? []);
350
354
  const hasFeatureOverview = computed(
351
- () =>
352
- Object.keys(featureRegistry.value ?? {}).length > 0 || activeFeatures.value.length > 0,
355
+ () => Object.keys(featureRegistry.value ?? {}).length > 0 || activeFeatures.value.length > 0,
353
356
  );
354
357
 
355
358
  const currentPlanName = computed(() => {
@@ -435,6 +438,7 @@ const wizardI18n = computed(() => ({
435
438
  badgeCurrent: effectiveI18n.value.wizardBadgeCurrent,
436
439
  badgePopular: effectiveI18n.value.wizardBadgePopular,
437
440
  priceUnitMonthly: effectiveI18n.value.wizardPriceUnitMonthly,
441
+ priceUnitYearly: effectiveI18n.value.wizardPriceUnitYearly,
438
442
  priceOnRequest: effectiveI18n.value.wizardPriceOnRequest,
439
443
  stepChoose: effectiveI18n.value.wizardStepChoose,
440
444
  stepChooseIntro: effectiveI18n.value.wizardStepChooseIntro,