@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
@@ -7,12 +7,8 @@
7
7
  <q-card class="pc-dlg">
8
8
  <q-card-section class="pc-dlg__head">
9
9
  <div>
10
- <div class="pc-dlg__title">Neuer Promo-Code</div>
11
- <div class="pc-dlg__sub">
12
- {{
13
- subtitle ?? 'Code, Rabatt-Logik, Laufzeit und Plan-Zuordnung festlegen.'
14
- }}
15
- </div>
10
+ <div class="pc-dlg__title">{{ msg.createDialog.title }}</div>
11
+ <div class="pc-dlg__sub">{{ subtitle ?? msg.createDialog.subtitle }}</div>
16
12
  </div>
17
13
  <q-btn
18
14
  class="pc-dlg__close"
@@ -28,15 +24,15 @@
28
24
  <q-card-section class="pc-dlg__body">
29
25
  <!-- Section: Code & Discount -->
30
26
  <div class="pc-section">
31
- <div class="pc-section__title">Code &amp; Rabatt</div>
27
+ <div class="pc-section__title">{{ msg.form.sectionCodeDiscount }}</div>
32
28
  <div class="pc-grid pc-grid--2">
33
29
  <div class="pc-field">
34
- <div class="pc-field__label">Code</div>
30
+ <div class="pc-field__label">{{ msg.form.codeLabel }}</div>
35
31
  <div class="pc-code-input">
36
32
  <input
37
33
  v-model="form.code"
38
34
  class="pc-input pc-input--code"
39
- placeholder="z. B. WELCOME10"
35
+ :placeholder="msg.form.codePlaceholder"
40
36
  @input="onCodeInput"
41
37
  />
42
38
  <button
@@ -44,19 +40,17 @@
44
40
  class="pc-btn-mini"
45
41
  @click="form.code = randomCode()"
46
42
  >
47
- Zufall
43
+ {{ msg.form.codeRandom }}
48
44
  </button>
49
45
  </div>
50
- <div class="pc-field__hint">
51
- Großbuchstaben, Zahlen, „-" und „_" · nach Anlage stabil
52
- </div>
46
+ <div class="pc-field__hint">{{ msg.form.codeHint }}</div>
53
47
  </div>
54
48
 
55
49
  <div class="pc-field">
56
- <div class="pc-field__label">Rabatt-Typ</div>
50
+ <div class="pc-field__label">{{ msg.form.valueTypeLabel }}</div>
57
51
  <div class="pc-type-grid">
58
52
  <button
59
- v-for="o in TYPE_OPTIONS"
53
+ v-for="o in typeOptions"
60
54
  :key="o.k"
61
55
  type="button"
62
56
  class="pc-type-opt"
@@ -72,7 +66,11 @@
72
66
 
73
67
  <div class="pc-field" style="max-width: 280px">
74
68
  <div class="pc-field__label">
75
- {{ form.valueType === 'PERCENT' ? 'Rabatt in %' : 'Rabatt in €' }}
69
+ {{
70
+ form.valueType === 'PERCENT'
71
+ ? msg.form.valuePercentLabel
72
+ : msg.form.valueAbsoluteLabel
73
+ }}
76
74
  </div>
77
75
  <input
78
76
  v-model.number="form.value"
@@ -86,10 +84,10 @@
86
84
 
87
85
  <!-- Section: Validity & Duration -->
88
86
  <div class="pc-section">
89
- <div class="pc-section__title">Gültigkeit &amp; Laufzeit</div>
87
+ <div class="pc-section__title">{{ msg.form.sectionValidity }}</div>
90
88
 
91
89
  <div v-if="plans.length > 0" class="pc-field">
92
- <div class="pc-field__label">Anwendbar auf Pläne</div>
90
+ <div class="pc-field__label">{{ msg.form.plansLabel }}</div>
93
91
  <div class="pc-plan-pick">
94
92
  <button
95
93
  v-for="p in plans"
@@ -108,16 +106,20 @@
108
106
  </button>
109
107
  </div>
110
108
  <div class="pc-field__hint">
111
- Leer = alle Pläne ({{ form.appliesToPlans.length }} ausgewählt)
109
+ {{
110
+ formatMessage(msg.form.plansHint, {
111
+ count: form.appliesToPlans.length,
112
+ })
113
+ }}
112
114
  </div>
113
115
  </div>
114
116
 
115
117
  <div class="pc-grid pc-grid--2">
116
118
  <div class="pc-field">
117
- <div class="pc-field__label">Laufzeit des Rabatts</div>
119
+ <div class="pc-field__label">{{ msg.form.durationLabel }}</div>
118
120
  <div class="pc-dur">
119
121
  <button
120
- v-for="o in DURATION_OPTIONS"
122
+ v-for="o in durationOptions"
121
123
  :key="o.k"
122
124
  type="button"
123
125
  class="pc-dur-opt"
@@ -135,31 +137,35 @@
135
137
  min="1"
136
138
  style="margin-top: 8px; max-width: 120px"
137
139
  :placeholder="
138
- form.durationType === 'MONTHS' ? 'Monate' : 'Perioden'
140
+ form.durationType === 'MONTHS'
141
+ ? msg.form.durationMonthsPlaceholder
142
+ : msg.form.durationCyclesPlaceholder
139
143
  "
140
144
  />
141
145
  </div>
142
146
 
143
147
  <div class="pc-field">
144
- <div class="pc-field__label">Max. Einlösungen</div>
148
+ <div class="pc-field__label">{{ msg.form.maxRedemptionsLabel }}</div>
145
149
  <input
146
150
  v-model.number="form.maxRedemptions"
147
151
  class="pc-input"
148
152
  type="number"
149
153
  min="1"
150
- placeholder="leer = ∞"
154
+ :placeholder="msg.form.maxRedemptionsPlaceholder"
151
155
  />
152
- <div class="pc-field__hint">Leerlassen für unbegrenzt</div>
156
+ <div class="pc-field__hint">
157
+ {{ msg.form.maxRedemptionsHintCreate }}
158
+ </div>
153
159
  </div>
154
160
  </div>
155
161
 
156
162
  <div class="pc-grid pc-grid--2">
157
163
  <div class="pc-field">
158
- <div class="pc-field__label">Gültig ab</div>
164
+ <div class="pc-field__label">{{ msg.form.validFromLabel }}</div>
159
165
  <input v-model="form.validFrom" class="pc-input" type="date" />
160
166
  </div>
161
167
  <div class="pc-field">
162
- <div class="pc-field__label">Gültig bis</div>
168
+ <div class="pc-field__label">{{ msg.form.validUntilLabel }}</div>
163
169
  <input v-model="form.validUntil" class="pc-input" type="date" />
164
170
  </div>
165
171
  </div>
@@ -167,23 +173,23 @@
167
173
 
168
174
  <!-- Section: Campaign & Note -->
169
175
  <div class="pc-section">
170
- <div class="pc-section__title">Kampagne &amp; Notizen</div>
176
+ <div class="pc-section__title">{{ msg.form.sectionCampaign }}</div>
171
177
  <div v-if="showCampaignTag" class="pc-field">
172
- <div class="pc-field__label">Kampagne (optional)</div>
178
+ <div class="pc-field__label">{{ msg.form.campaignLabel }}</div>
173
179
  <input
174
180
  v-model="form.campaignTag"
175
181
  class="pc-input"
176
- placeholder="z. B. Frühjahrs-Aktion 2026"
182
+ :placeholder="msg.form.campaignPlaceholder"
177
183
  />
178
- <div class="pc-field__hint">Gruppiert mehrere Codes optisch</div>
184
+ <div class="pc-field__hint">{{ msg.form.campaignHint }}</div>
179
185
  </div>
180
186
  <div class="pc-field">
181
- <div class="pc-field__label">Notiz (intern)</div>
187
+ <div class="pc-field__label">{{ msg.form.noteLabel }}</div>
182
188
  <textarea
183
189
  v-model="form.description"
184
190
  class="pc-input"
185
191
  rows="2"
186
- placeholder="Kontext, Quelle, Sales-Bezug — wird nicht an Kunden ausgespielt."
192
+ :placeholder="msg.form.notePlaceholder"
187
193
  />
188
194
  </div>
189
195
  </div>
@@ -199,45 +205,47 @@
199
205
  :name="advancedOpen ? 'expand_more' : 'chevron_right'"
200
206
  size="16px"
201
207
  />
202
- Erweiterte Einschränkungen
208
+ {{ msg.form.advancedToggle }}
203
209
  </button>
204
210
  <div v-if="advancedOpen" class="pc-advanced">
205
211
  <div class="pc-grid pc-grid--2">
206
212
  <div class="pc-field">
207
- <div class="pc-field__label">Nur für Abrechnungs-Zyklus</div>
213
+ <div class="pc-field__label">{{ msg.form.billingCycleLabel }}</div>
208
214
  <select v-model="form.appliesToBilling" class="pc-input">
209
- <option :value="undefined">Beide</option>
210
- <option value="MONTHLY">Monatlich</option>
211
- <option value="YEARLY">Jährlich</option>
215
+ <option :value="undefined">
216
+ {{ common.both }}
217
+ </option>
218
+ <option value="MONTHLY">{{ common.monthly }}</option>
219
+ <option value="YEARLY">{{ common.yearly }}</option>
212
220
  </select>
213
221
  </div>
214
222
  <div class="pc-field">
215
- <div class="pc-field__label">Mindest-Plan-Betrag (€ brutto)</div>
223
+ <div class="pc-field__label">{{ msg.form.minAmountLabel }}</div>
216
224
  <input
217
225
  v-model.number="form.minimumPlanAmountGross"
218
226
  class="pc-input"
219
227
  type="number"
220
228
  min="0"
221
- placeholder="leer = keine Schwelle"
229
+ :placeholder="msg.form.minAmountPlaceholder"
222
230
  />
223
231
  </div>
224
232
  </div>
225
233
  <div class="pc-grid pc-grid--2">
226
234
  <label class="pc-check">
227
235
  <input v-model="form.firstTimeCustomersOnly" type="checkbox" />
228
- <span>Nur Neukunden</span>
236
+ <span>{{ msg.form.firstTimeOnly }}</span>
229
237
  </label>
230
238
  <label class="pc-check">
231
239
  <input v-model="form.allowZeroInvoice" type="checkbox" />
232
- <span>0-€-Rechnung zulassen</span>
240
+ <span>{{ msg.form.allowZeroInvoice }}</span>
233
241
  </label>
234
242
  </div>
235
243
  <div class="pc-field">
236
- <div class="pc-field__label">Erlös-Minderungs-Konto (Buchhaltung)</div>
244
+ <div class="pc-field__label">{{ msg.form.revenueAccountLabel }}</div>
237
245
  <input
238
246
  v-model="form.revenueDeductionAccount"
239
247
  class="pc-input"
240
- placeholder="z. B. 8736"
248
+ :placeholder="msg.form.revenueAccountPlaceholder"
241
249
  />
242
250
  </div>
243
251
  </div>
@@ -245,7 +253,7 @@
245
253
 
246
254
  <!-- Live preview -->
247
255
  <div class="pc-preview">
248
- <div class="pc-preview__eyebrow">Vorschau im Catalog</div>
256
+ <div class="pc-preview__eyebrow">{{ msg.form.previewEyebrow }}</div>
249
257
  <div class="pc-preview__body">
250
258
  <code class="pc-preview__code">{{ form.code || 'CODE' }}</code>
251
259
  <span class="pc-preview__disc">{{ previewValue }}</span>
@@ -257,11 +265,11 @@
257
265
  </q-card-section>
258
266
 
259
267
  <q-card-actions align="right" class="pc-dlg__foot">
260
- <q-btn flat label="Abbrechen" v-close-popup :disable="loading" />
268
+ <q-btn flat :label="common.cancel" v-close-popup :disable="loading" />
261
269
  <q-btn
262
270
  unelevated
263
271
  color="primary"
264
- label="Anlegen"
272
+ :label="common.create"
265
273
  :loading="loading"
266
274
  :disable="!isValid"
267
275
  @click="onSubmit"
@@ -273,6 +281,8 @@
273
281
 
274
282
  <script setup lang="ts">
275
283
  import { computed, reactive, ref, watch } from 'vue';
284
+ import { formatMessage } from '../../client/i18n/format.js';
285
+ import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
276
286
  import type {
277
287
  PromoCodeCreatePayload,
278
288
  PromoCodeDurationType,
@@ -304,16 +314,29 @@ const emit = defineEmits<{
304
314
  (e: 'created'): void;
305
315
  }>();
306
316
 
307
- const TYPE_OPTIONS: ReadonlyArray<{ k: PromoCodeValueType; label: string; sub: string }> = [
308
- { k: 'PERCENT', label: 'Prozent', sub: '−x %' },
309
- { k: 'ABSOLUTE', label: 'Fester €', sub: '−x €' },
310
- ];
317
+ const msg = useSaMessages('promos');
318
+ const common = useSaMessages('common');
319
+
320
+ const typeOptions = computed<ReadonlyArray<{ k: PromoCodeValueType; label: string; sub: string }>>(
321
+ () => [
322
+ {
323
+ k: 'PERCENT',
324
+ label: msg.value.form.valueTypePercent,
325
+ sub: msg.value.form.valueTypePercentSub,
326
+ },
327
+ {
328
+ k: 'ABSOLUTE',
329
+ label: msg.value.form.valueTypeAbsolute,
330
+ sub: msg.value.form.valueTypeAbsoluteSub,
331
+ },
332
+ ],
333
+ );
311
334
 
312
- const DURATION_OPTIONS: ReadonlyArray<{ k: PromoCodeDurationType; label: string }> = [
313
- { k: 'ONCE', label: 'Einmalig' },
314
- { k: 'MONTHS', label: 'N Monate' },
315
- { k: 'BILLING_CYCLES', label: 'N Perioden' },
316
- ];
335
+ const durationOptions = computed<ReadonlyArray<{ k: PromoCodeDurationType; label: string }>>(() => [
336
+ { k: 'ONCE', label: msg.value.form.durationOnce },
337
+ { k: 'MONTHS', label: msg.value.form.durationMonths },
338
+ { k: 'BILLING_CYCLES', label: msg.value.form.durationBillingCycles },
339
+ ]);
317
340
 
318
341
  function emptyForm() {
319
342
  return {
@@ -378,17 +401,20 @@ const previewValue = computed(() => {
378
401
 
379
402
  const previewMeta = computed(() => {
380
403
  const parts: string[] = [];
381
- if (form.durationType === 'ONCE') parts.push('einmalig');
382
- else if (form.durationType === 'MONTHS') parts.push(`${form.durationValue || 0} Monate`);
383
- else parts.push(`${form.durationValue || 0} Perioden`);
404
+ const count = form.durationValue || 0;
405
+ if (form.durationType === 'ONCE') parts.push(msg.value.form.previewOnce);
406
+ else if (form.durationType === 'MONTHS')
407
+ parts.push(formatMessage(msg.value.form.previewMonths, { count }));
408
+ else parts.push(formatMessage(msg.value.form.previewCycles, { count }));
384
409
  parts.push(
385
410
  form.appliesToPlans.length > 0
386
411
  ? form.appliesToPlans.join(', ')
387
412
  : props.plans.length > 0
388
- ? 'alle Pläne'
389
- : 'kein Plan-Filter',
413
+ ? msg.value.form.previewAllPlans
414
+ : msg.value.form.previewNoPlanFilter,
390
415
  );
391
- if (form.maxRedemptions) parts.push(`max. ${form.maxRedemptions}`);
416
+ if (form.maxRedemptions)
417
+ parts.push(formatMessage(msg.value.form.previewMax, { count: form.maxRedemptions }));
392
418
  return parts.join(' · ');
393
419
  });
394
420
 
@@ -441,7 +467,7 @@ async function onSubmit() {
441
467
  error.value =
442
468
  (err as { response?: { data?: { message?: string } } }).response?.data?.message ??
443
469
  (err as Error).message ??
444
- 'Anlegen fehlgeschlagen';
470
+ common.value.createFailed;
445
471
  } finally {
446
472
  loading.value = false;
447
473
  }