@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
@@ -3,11 +3,8 @@
3
3
  <!-- Page header -->
4
4
  <div class="pl-page-head">
5
5
  <div>
6
- <h2 class="pl-h-title">Alle Pläne</h2>
7
- <p class="pl-h-sub">
8
- {{ resolvedPlans.length }} Pläne · {{ liveCount }} live ·
9
- {{ draftCount }} offene Drafts · {{ totalTenants }} Mandanten zugeordnet
10
- </p>
6
+ <h2 class="pl-h-title">{{ msg.list.title }}</h2>
7
+ <p class="pl-h-sub">{{ summary }}</p>
11
8
  </div>
12
9
  <div class="pl-head-actions">
13
10
  <button class="pl-btn" type="button" @click="$emit('showMatrix')">
@@ -24,7 +21,7 @@
24
21
  <rect x="3" y="14" width="7" height="7" rx="1" />
25
22
  <rect x="14" y="14" width="7" height="7" rx="1" />
26
23
  </svg>
27
- <span>Matrix-Ansicht</span>
24
+ <span>{{ msg.list.matrixView }}</span>
28
25
  </button>
29
26
  <button class="pl-btn pl-btn--primary" type="button" @click="$emit('createPlan')">
30
27
  <svg
@@ -37,7 +34,7 @@
37
34
  >
38
35
  <path d="M12 5v14M5 12h14" />
39
36
  </svg>
40
- <span>Neuer Plan</span>
37
+ <span>{{ msg.list.newPlan }}</span>
41
38
  </button>
42
39
  </div>
43
40
  </div>
@@ -59,28 +56,28 @@
59
56
  <path d="M21 21l-4.35-4.35" />
60
57
  </svg>
61
58
  </span>
62
- <input v-model="search" placeholder="Plan suchen…" />
59
+ <input v-model="search" :placeholder="msg.list.searchPlaceholder" />
63
60
  <span class="pl-kbd">⌘ K</span>
64
61
  </div>
65
62
  <div class="pl-toolbar-spacer" />
66
- <div class="pl-sortinfo">Sortiert nach: Order</div>
63
+ <div class="pl-sortinfo">{{ msg.list.sortedBy }}</div>
67
64
  </div>
68
65
 
69
66
  <div class="pl-list">
70
67
  <div class="pl-list-head">
71
- <div>Plan</div>
72
- <div>Status</div>
73
- <div>Version</div>
74
- <div>Preis</div>
75
- <div>Mandanten</div>
68
+ <div>{{ msg.list.columnPlan }}</div>
69
+ <div>{{ common.status }}</div>
70
+ <div>{{ msg.list.columnVersion }}</div>
71
+ <div>{{ msg.list.columnPrice }}</div>
72
+ <div>{{ msg.list.columnTenants }}</div>
76
73
  <div />
77
74
  </div>
78
75
 
79
76
  <div v-if="filteredPlans.length === 0" class="pl-empty">
80
77
  <template v-if="resolvedPlans.length === 0">
81
- Noch keine Pläne — über „Neuer Plan" oben rechts den ersten anlegen.
78
+ {{ msg.list.emptyNoPlans }}
82
79
  </template>
83
- <template v-else> Keine Pläne entsprechen der Suche „{{ search }}". </template>
80
+ <template v-else>{{ emptyNoMatch }}</template>
84
81
  </div>
85
82
 
86
83
  <template v-for="p in filteredPlans" :key="p.plan.id">
@@ -107,7 +104,7 @@
107
104
  <span
108
105
  v-if="highlightPlanKey === p.planKey"
109
106
  class="pl-chip pl-chip--new"
110
- >NEU</span
107
+ >{{ msg.list.badgeNew }}</span
111
108
  >
112
109
  </div>
113
110
  <div class="pl-plan-desc">{{ p.description || '—' }}</div>
@@ -117,22 +114,24 @@
117
114
 
118
115
  <div class="pl-cell pl-cell--status">
119
116
  <template v-if="p.currentLive">
120
- <span class="pl-chip pl-chip--live pl-chip--dot">live</span>
117
+ <span class="pl-chip pl-chip--live pl-chip--dot">{{
118
+ msg.list.chipLive
119
+ }}</span>
121
120
  <span
122
121
  v-if="!p.currentLive.marketed"
123
122
  class="pl-chip pl-chip--supersed pl-chip--tiny"
124
- >privat</span
123
+ >{{ msg.list.chipPrivate }}</span
125
124
  >
126
125
  </template>
127
126
  <template v-else-if="p.primary && p.primary.publishedAt">
128
127
  <span class="pl-chip pl-chip--scheduled pl-chip--dot">
129
- ab {{ p.primary.validFrom?.slice(0, 10) }}
128
+ {{ validFromLabel(p.primary.validFrom) }}
130
129
  </span>
131
130
  </template>
132
131
  <template v-else>
133
- <span class="pl-chip pl-chip--supersed pl-chip--dot"
134
- >keine live</span
135
- >
132
+ <span class="pl-chip pl-chip--supersed pl-chip--dot">{{
133
+ msg.list.chipNoLive
134
+ }}</span>
136
135
  </template>
137
136
  </div>
138
137
 
@@ -142,11 +141,13 @@
142
141
  </div>
143
142
  <div v-else class="pl-version-num pl-version-num--muted">—</div>
144
143
  <div v-if="p.primary?.validFrom" class="pl-version-sub">
145
- {{ p.currentLive ? 'seit' : 'ab' }}
146
- {{ p.primary.validFrom.slice(0, 10)
147
- }}<template v-if="p.primary.validUntil">
148
- bis {{ p.primary.validUntil.slice(0, 10) }}</template
149
- >
144
+ {{
145
+ validityRange(
146
+ p.currentLive ? msg.list.since : msg.list.from,
147
+ p.primary.validFrom,
148
+ p.primary.validUntil,
149
+ )
150
+ }}
150
151
  </div>
151
152
  </div>
152
153
 
@@ -160,17 +161,20 @@
160
161
  Number(p.primary.yearlyNet) === 0
161
162
  "
162
163
  >
163
- <span class="pl-price-text">Kostenlos</span>
164
+ <span class="pl-price-text">{{ msg.list.priceFree }}</span>
164
165
  </template>
165
166
  <template v-else>
166
167
  <div>
167
168
  <span class="pl-price-big">{{
168
169
  formatMoney(p.primary.monthlyNet)
169
170
  }}</span>
170
- <span class="pl-price-unit"> / Mo</span>
171
+ <span class="pl-price-unit">{{
172
+ ' ' + msg.list.perMonthShort
173
+ }}</span>
171
174
  </div>
172
175
  <div class="pl-price-sub">
173
- {{ formatMoney(p.primary.yearlyNet) }} / Jahr
176
+ {{ formatMoney(p.primary.yearlyNet) }}
177
+ {{ msg.list.perYearShort }}
174
178
  </div>
175
179
  </template>
176
180
  </div>
@@ -194,7 +198,7 @@
194
198
  class="pl-btn pl-btn--sm pl-btn--ghost"
195
199
  type="button"
196
200
  disabled
197
- title="Plan hat published Versionen — kann nicht gelöscht werden (Vertragsschutz P1)"
201
+ :title="msg.list.actionDeleteBlocked"
198
202
  >
199
203
  <svg
200
204
  width="14"
@@ -213,7 +217,7 @@
213
217
  v-else
214
218
  class="pl-btn pl-btn--sm pl-btn--ghost pl-btn--danger"
215
219
  type="button"
216
- title="Plan komplett löschen"
220
+ :title="msg.list.actionDeletePlan"
217
221
  @click="$emit('archivePlan', p.plan, false)"
218
222
  >
219
223
  <svg
@@ -232,7 +236,7 @@
232
236
  <button
233
237
  class="pl-btn pl-btn--sm pl-btn--ghost"
234
238
  type="button"
235
- title="Plan klonen"
239
+ :title="msg.list.actionClonePlan"
236
240
  @click="$emit('clonePlan', p.plan)"
237
241
  >
238
242
  <svg
@@ -250,7 +254,7 @@
250
254
  <button
251
255
  class="pl-btn pl-btn--sm pl-btn--ghost"
252
256
  type="button"
253
- title="Neue Version anlegen"
257
+ :title="msg.list.actionNewVersion"
254
258
  :disabled="!!p.draft"
255
259
  @click="onNewVersion(p)"
256
260
  >
@@ -270,7 +274,7 @@
270
274
  <button
271
275
  class="pl-btn pl-btn--sm"
272
276
  type="button"
273
- title="Plan öffnen"
277
+ :title="msg.list.actionOpenPlan"
274
278
  @click="$emit('openPlan', p.plan)"
275
279
  >
276
280
  <svg
@@ -304,18 +308,18 @@
304
308
  <template v-if="sub.publishedAt === null">
305
309
  <span
306
310
  class="pl-chip pl-chip--draft pl-chip--dot pl-chip--tiny"
307
- >Draft</span
311
+ >{{ msg.list.chipDraft }}</span
308
312
  >
309
313
  </template>
310
314
  <template v-else>
311
315
  <span
312
316
  class="pl-chip pl-chip--scheduled pl-chip--dot pl-chip--tiny"
313
- >geplant</span
317
+ >{{ msg.list.chipScheduled }}</span
314
318
  >
315
319
  </template>
316
320
  </div>
317
321
  <div class="pl-sub-desc">
318
- {{ sub.changeNote || 'keine Change-Note' }}
322
+ {{ sub.changeNote || msg.list.noChangeNote }}
319
323
  </div>
320
324
  </div>
321
325
  </div>
@@ -324,22 +328,19 @@
324
328
  <span
325
329
  v-if="sub.publishedAt === null"
326
330
  class="pl-chip pl-chip--draft pl-chip--dot pl-chip--tiny"
327
- >Draft</span
331
+ >{{ msg.list.chipDraft }}</span
328
332
  >
329
333
  <span
330
334
  v-else
331
335
  class="pl-chip pl-chip--scheduled pl-chip--dot pl-chip--tiny"
332
- >ab {{ sub.validFrom?.slice(0, 10) }}</span
336
+ >{{ validFromLabel(sub.validFrom) }}</span
333
337
  >
334
338
  </div>
335
339
 
336
340
  <div class="pl-cell">
337
341
  <div class="pl-version-num pl-version-num--sub">v{{ sub.version }}</div>
338
342
  <div v-if="sub.validFrom" class="pl-version-sub">
339
- ab {{ sub.validFrom.slice(0, 10)
340
- }}<template v-if="sub.validUntil">
341
- bis {{ sub.validUntil.slice(0, 10) }}</template
342
- >
343
+ {{ validityRange(msg.list.from, sub.validFrom, sub.validUntil) }}
343
344
  </div>
344
345
  </div>
345
346
 
@@ -347,23 +348,27 @@
347
348
  <template
348
349
  v-if="Number(sub.monthlyNet) === 0 && Number(sub.yearlyNet) === 0"
349
350
  >
350
- <span class="pl-price-text">Kostenlos</span>
351
+ <span class="pl-price-text">{{ msg.list.priceFree }}</span>
351
352
  </template>
352
353
  <template v-else>
353
354
  <div>
354
355
  <span class="pl-price-big">{{
355
356
  formatMoney(sub.monthlyNet)
356
357
  }}</span>
357
- <span class="pl-price-unit"> / Mo</span>
358
+ <span class="pl-price-unit">{{
359
+ ' ' + msg.list.perMonthShort
360
+ }}</span>
358
361
  </div>
359
362
  <div class="pl-price-sub">
360
- {{ formatMoney(sub.yearlyNet) }} / Jahr
363
+ {{ formatMoney(sub.yearlyNet) }} {{ msg.list.perYearShort }}
361
364
  </div>
362
365
  </template>
363
366
  </div>
364
367
 
365
368
  <div class="pl-cell pl-cell--sub-impact">
366
- <span class="pl-version-sub">{{ p.tenantCount }} Mandanten</span>
369
+ <span class="pl-version-sub">{{
370
+ tenantCountLabel(p.tenantCount)
371
+ }}</span>
367
372
  </div>
368
373
 
369
374
  <div class="pl-cell pl-cell--actions" @click.stop>
@@ -371,7 +376,7 @@
371
376
  v-if="sub.publishedAt === null"
372
377
  class="pl-btn pl-btn--sm pl-btn--ghost pl-btn--danger"
373
378
  type="button"
374
- :title="`Draft v${sub.version} verwerfen`"
379
+ :title="discardDraftTitle(sub.version)"
375
380
  @click="$emit('discardDraft', p.plan, sub)"
376
381
  >
377
382
  <svg
@@ -391,7 +396,7 @@
391
396
  v-if="sub.publishedAt === null"
392
397
  class="pl-btn pl-btn--sm pl-btn--ghost"
393
398
  type="button"
394
- title="Draft bearbeiten"
399
+ :title="msg.list.actionEditDraft"
395
400
  @click="$emit('editDraft', p.plan, sub)"
396
401
  >
397
402
  <svg
@@ -410,7 +415,7 @@
410
415
  <button
411
416
  class="pl-btn pl-btn--sm"
412
417
  type="button"
413
- title="Im Cockpit öffnen"
418
+ :title="msg.list.actionOpenInCockpit"
414
419
  @click="$emit('openPlan', p.plan)"
415
420
  >
416
421
  <svg
@@ -435,6 +440,9 @@
435
440
  <script setup lang="ts">
436
441
  import { computed, ref } from 'vue';
437
442
  import type { PlanRow, PlanVersionRow } from '@saasicat/types';
443
+ import { formatMessage } from '../../client/i18n/format.js';
444
+ import { formatCurrency } from '../../client/i18n/currency.js';
445
+ import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
438
446
 
439
447
  // PlanList — list view of all plans (default view in PlansPage,
440
448
  // corresponds to the ListScreen from the plan simulation). One row per
@@ -468,6 +476,10 @@ const emit = defineEmits<{
468
476
  (e: 'showMatrix'): void;
469
477
  }>();
470
478
 
479
+ const msg = useSaMessages('plans');
480
+ const { locale, intlLocale } = useSuperAdminI18n();
481
+ const common = useSaMessages('common');
482
+
471
483
  const search = ref('');
472
484
 
473
485
  interface ResolvedPlan {
@@ -594,12 +606,12 @@ const filteredPlans = computed(() => {
594
606
  // (SPEC_V2 §4.2.1: only currently-valid + future ones stay visible
595
607
  // in the admin listing).
596
608
  const base = resolvedPlans.value.filter((p) => !p.allExpired);
597
- const q = search.value.trim().toLocaleLowerCase('de-DE');
609
+ const q = search.value.trim().toLocaleLowerCase(intlLocale.value);
598
610
  if (!q) return base;
599
611
  return base.filter(
600
612
  (p) =>
601
- p.planKey.toLocaleLowerCase('de-DE').includes(q) ||
602
- p.label.toLocaleLowerCase('de-DE').includes(q),
613
+ p.planKey.toLocaleLowerCase(intlLocale.value).includes(q) ||
614
+ p.label.toLocaleLowerCase(intlLocale.value).includes(q),
603
615
  );
604
616
  });
605
617
 
@@ -607,11 +619,41 @@ const liveCount = computed(() => resolvedPlans.value.filter((p) => p.currentLive
607
619
  const draftCount = computed(() => resolvedPlans.value.filter((p) => p.draft !== null).length);
608
620
  const totalTenants = computed(() => resolvedPlans.value.reduce((sum, p) => sum + p.tenantCount, 0));
609
621
 
622
+ const summary = computed(() =>
623
+ formatMessage(msg.value.list.summary, {
624
+ plans: resolvedPlans.value.length,
625
+ live: liveCount.value,
626
+ drafts: draftCount.value,
627
+ tenants: totalTenants.value,
628
+ }),
629
+ );
630
+
631
+ const emptyNoMatch = computed(() =>
632
+ formatMessage(msg.value.list.emptyNoMatch, { query: search.value }),
633
+ );
634
+
635
+ function validFromLabel(validFrom: string | null | undefined): string {
636
+ return formatMessage(msg.value.list.validFrom, { date: validFrom?.slice(0, 10) ?? '' });
637
+ }
638
+
639
+ function validityRange(prefix: string, validFrom: string, validUntil: string | null): string {
640
+ const range = `${prefix} ${validFrom.slice(0, 10)}`;
641
+ if (!validUntil) return range;
642
+ return `${range} ${msg.value.list.until} ${validUntil.slice(0, 10)}`;
643
+ }
644
+
645
+ function tenantCountLabel(count: number): string {
646
+ return formatMessage(msg.value.list.tenantCount, { count });
647
+ }
648
+
649
+ function discardDraftTitle(version: number): string {
650
+ return formatMessage(msg.value.list.actionDiscardDraft, { version });
651
+ }
652
+
610
653
  function formatMoney(raw: string | number): string {
611
654
  const num = typeof raw === 'string' ? Number(raw) : raw;
612
655
  if (!Number.isFinite(num)) return String(raw);
613
- if (Number.isInteger(num)) return `${num} €`;
614
- return `${num.toFixed(2).replace('.', ',')} €`;
656
+ return formatCurrency(num, locale.value);
615
657
  }
616
658
 
617
659
  function tenantBarWidth(count: number): string {