@saasicat/ui-vue 0.2.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 (193) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +47 -0
  3. package/dist/index.cjs +3776 -0
  4. package/dist/index.d.cts +2395 -0
  5. package/dist/index.d.ts +2395 -0
  6. package/dist/index.js +3659 -0
  7. package/dist/testing-e2e/admin-pages-suite.cjs +134 -0
  8. package/dist/testing-e2e/admin-pages-suite.d.cts +37 -0
  9. package/dist/testing-e2e/admin-pages-suite.d.ts +37 -0
  10. package/dist/testing-e2e/admin-pages-suite.js +109 -0
  11. package/package.json +87 -0
  12. package/src/action-registry.ts +110 -0
  13. package/src/batch-column-fetcher.ts +152 -0
  14. package/src/boot-loader.ts +60 -0
  15. package/src/components/BundleVersionPublishDialog.vue +359 -0
  16. package/src/components/BusinessTypeVersionEditorDialog.vue +417 -0
  17. package/src/components/BusinessTypeVersionPublishDialog.vue +258 -0
  18. package/src/components/FeatureGate.vue +56 -0
  19. package/src/components/KpiCard.vue +53 -0
  20. package/src/components/KvBlock.vue +36 -0
  21. package/src/components/MarketingPromotionsTab.vue +861 -0
  22. package/src/components/MfaPromptDialog.vue +104 -0
  23. package/src/components/TenantActionConfirmDialog.vue +252 -0
  24. package/src/components/VersionDiffPreview.vue +150 -0
  25. package/src/components/bundle-editor/BundleCreatePanel.vue +681 -0
  26. package/src/components/bundle-editor/BundleFeaturesEditor.vue +215 -0
  27. package/src/components/bundle-editor/BundlePlanCompatPicker.vue +316 -0
  28. package/src/components/bundle-editor/BundleQuotasEditor.vue +219 -0
  29. package/src/components/bundle-editor/BundleStatusBanner.vue +189 -0
  30. package/src/components/bundle-editor/BundleVersionInlineEditor.vue +659 -0
  31. package/src/components/bundle-editor/BundleVersionStrip.vue +207 -0
  32. package/src/components/bundle-editor/bundle-version-status.ts +164 -0
  33. package/src/components/bundle-editor/catalog-i18n.ts +85 -0
  34. package/src/components/dialogs/PilotCreateDialog.vue +780 -0
  35. package/src/components/dialogs/PilotEditDialog.vue +548 -0
  36. package/src/components/dialogs/PromoCodeCreateDialog.vue +737 -0
  37. package/src/components/dialogs/PromoCodeEditDialog.vue +859 -0
  38. package/src/components/dialogs/types.ts +126 -0
  39. package/src/components/plan/PlanCycleToggle.vue +99 -0
  40. package/src/components/plan/PlanGrid.vue +211 -0
  41. package/src/components/plan/PriceSummary.vue +293 -0
  42. package/src/components/plan/PromoCodeInput.vue +192 -0
  43. package/src/components/plan/PublicBundleGrid.vue +287 -0
  44. package/src/components/plan-cockpit/PlanCockpit.vue +993 -0
  45. package/src/components/plan-cockpit/PlanCockpitAuditLog.vue +65 -0
  46. package/src/components/plan-cockpit/PlanCockpitDiffPanel.vue +91 -0
  47. package/src/components/plan-cockpit/PlanCockpitHeader.vue +98 -0
  48. package/src/components/plan-cockpit/PlanCockpitImpactPanel.vue +99 -0
  49. package/src/components/plan-cockpit/PlanCockpitKpis.vue +57 -0
  50. package/src/components/plan-cockpit/PlanCockpitVersions.vue +226 -0
  51. package/src/components/plan-cockpit/types.ts +72 -0
  52. package/src/components/plan-create-dialog/PlanCreateDialog.vue +508 -0
  53. package/src/components/plan-detail/PlanAuditLog.vue +59 -0
  54. package/src/components/plan-detail/PlanDetail.vue +1195 -0
  55. package/src/components/plan-detail/PlanDetailHeader.vue +185 -0
  56. package/src/components/plan-detail/PlanDetailKpis.vue +57 -0
  57. package/src/components/plan-detail/PlanTerminateDialog.vue +76 -0
  58. package/src/components/plan-detail/PlanVersionDiffPanel.vue +149 -0
  59. package/src/components/plan-detail/PlanVersionsPanel.vue +255 -0
  60. package/src/components/plan-detail/types.ts +59 -0
  61. package/src/components/plan-list/PlanList.vue +1067 -0
  62. package/src/components/plan-matrix/PlanMatrix.vue +1113 -0
  63. package/src/components/plan-review/PlanReview.vue +742 -0
  64. package/src/components/plan-version-editor/PlanCatalogPreview.vue +184 -0
  65. package/src/components/plan-version-editor/PlanComponentPool.vue +272 -0
  66. package/src/components/plan-version-editor/PlanVersionBasket.vue +281 -0
  67. package/src/components/plan-version-editor/PlanVersionDiffDialog.vue +84 -0
  68. package/src/components/plan-version-editor/PlanVersionEditor.vue +1728 -0
  69. package/src/components/plan-version-editor/PlanVersionEditorHeader.vue +94 -0
  70. package/src/components/plan-version-editor/types.ts +96 -0
  71. package/src/components/wizard-stepper/WizardStepper.vue +107 -0
  72. package/src/create-admin-routes.ts +68 -0
  73. package/src/create-super-admin-app.ts +345 -0
  74. package/src/entitlement-provider.ts +36 -0
  75. package/src/feature-router-guard.ts +77 -0
  76. package/src/http-json.ts +62 -0
  77. package/src/index.ts +98 -0
  78. package/src/manifest-loader.ts +139 -0
  79. package/src/manifest-store-factory.ts +114 -0
  80. package/src/nav-builder.ts +274 -0
  81. package/src/pages-standard/AdminLayout.vue +411 -0
  82. package/src/pages-standard/AdminManifestErrorPage.vue +79 -0
  83. package/src/pages-standard/AuditPage.vue +223 -0
  84. package/src/pages-standard/BundlesPage.vue +910 -0
  85. package/src/pages-standard/BusinessTypesPage.vue +662 -0
  86. package/src/pages-standard/DashboardPage.vue +595 -0
  87. package/src/pages-standard/DiscoveryPage.vue +663 -0
  88. package/src/pages-standard/EmailHistoryPage.vue +576 -0
  89. package/src/pages-standard/MarketingCatalogPage.vue +1771 -0
  90. package/src/pages-standard/PilotsPage.vue +577 -0
  91. package/src/pages-standard/PlanVersionsPage.vue +216 -0
  92. package/src/pages-standard/PlansPage.vue +1222 -0
  93. package/src/pages-standard/PlatformEmailPage.vue +434 -0
  94. package/src/pages-standard/PromoCodeDetailPage.vue +331 -0
  95. package/src/pages-standard/PromoCodesPage.vue +589 -0
  96. package/src/pages-standard/SubscriptionsPage.vue +159 -0
  97. package/src/pages-standard/SuperAdminLoginPage.vue +299 -0
  98. package/src/pages-standard/SuperAdminSetupWizard.vue +405 -0
  99. package/src/pages-standard/TenantDetailPage.vue +432 -0
  100. package/src/pages-standard/TenantsPage.vue +732 -0
  101. package/src/pages-standard/UsersPage.vue +504 -0
  102. package/src/pages-standard/bundles-page/BundleAccordionList.vue +116 -0
  103. package/src/pages-standard/bundles-page/BundleDetailPanel.vue +193 -0
  104. package/src/pages-standard/bundles-page/BundlesFilterBar.vue +48 -0
  105. package/src/pages-standard/bundles-page/BundlesHeader.vue +67 -0
  106. package/src/pages-standard/bundles-page/BundlesKpis.vue +39 -0
  107. package/src/pages-standard/bundles-page/types.ts +15 -0
  108. package/src/pages-standard/discovery-page/CatalogEntryTransPanel.vue +246 -0
  109. package/src/pages-standard/discovery-page/DiscoveryCapList.vue +171 -0
  110. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +410 -0
  111. package/src/pages-standard/discovery-page/DiscoveryHeader.vue +31 -0
  112. package/src/pages-standard/discovery-page/DiscoveryKpis.vue +47 -0
  113. package/src/pages-standard/discovery-page/DiscoveryMetaBanner.vue +26 -0
  114. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +247 -0
  115. package/src/pages-standard/discovery-page/DiscoveryStatusControl.vue +73 -0
  116. package/src/pages-standard/discovery-page/discovery-ui.ts +166 -0
  117. package/src/pages-standard/email-history.types.ts +48 -0
  118. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +448 -0
  119. package/src/pages-standard/marketing-catalog/MarketingCatalogHeader.vue +93 -0
  120. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +138 -0
  121. package/src/pages-standard/marketing-catalog/MarketingCatalogToolbar.vue +58 -0
  122. package/src/pages-standard/marketing-catalog/types.ts +43 -0
  123. package/src/pages-standard/plan-versions/PlanDiffCard.vue +132 -0
  124. package/src/pages-standard/plan-versions/PlanVersionHeader.vue +293 -0
  125. package/src/pages-standard/plan-versions/PlanVersionStatusBadge.vue +48 -0
  126. package/src/pages-standard/plan-versions/PlanVersionsAudit.vue +307 -0
  127. package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +269 -0
  128. package/src/pages-standard/plan-versions/PlanVersionsKpi.vue +65 -0
  129. package/src/pages-standard/plan-versions/PlanVersionsList.vue +292 -0
  130. package/src/pages-standard/plan-versions/PlanVersionsMatrix.vue +261 -0
  131. package/src/pages-standard/plan-versions/PlanVersionsTimeline.vue +300 -0
  132. package/src/pages-standard/plan-versions/VersionDiffPreview.vue +167 -0
  133. package/src/pages-standard/plan-versions/format.ts +53 -0
  134. package/src/pages-standard/plan-versions/types.ts +33 -0
  135. package/src/pages-standard/plans-page/PlanArchiveDialog.vue +50 -0
  136. package/src/pages-standard/plans-page/PlanBundleOverview.vue +147 -0
  137. package/src/pages-standard/plans-page/PlanDiscardDraftDialog.vue +46 -0
  138. package/src/pages-standard/plans-page/PlanPublishDialog.vue +106 -0
  139. package/src/pages-standard/plans-page/PlansPageToast.vue +69 -0
  140. package/src/pages-standard/plans-page/types.ts +18 -0
  141. package/src/pages-standard/platform-email.types.ts +41 -0
  142. package/src/pages-standard/sa-theme.css +223 -0
  143. package/src/pages-standard/tenants/StatusPill.vue +62 -0
  144. package/src/pages-standard/tenants/format.ts +38 -0
  145. package/src/pages-tenant/LimitsRow.vue +67 -0
  146. package/src/pages-tenant/MySubscriptionBundlesPage.vue +604 -0
  147. package/src/pages-tenant/OnboardingConfigurator.vue +417 -0
  148. package/src/pages-tenant/PackageSnapshotPanel.vue +241 -0
  149. package/src/pages-tenant/PendingVersionBanner.vue +126 -0
  150. package/src/pages-tenant/PlanChangeWizard.vue +630 -0
  151. package/src/pages-tenant/TenantPlanSection.vue +729 -0
  152. package/src/pages-tenant/UsageBar.vue +107 -0
  153. package/src/pages-tenant/default-i18n.ts +280 -0
  154. package/src/pages-tenant/tenant-plan-section/BundlePreviewDialog.vue +304 -0
  155. package/src/pages-tenant/tenant-plan-section/TenantBundleStore.vue +323 -0
  156. package/src/pages-tenant/tenant-plan-section/TenantFeatureMatrix.vue +122 -0
  157. package/src/pages-tenant/tenant-plan-section/TenantPlanCardHeader.vue +67 -0
  158. package/src/pages-tenant/tenant-plan-section/TenantUsageGrid.vue +31 -0
  159. package/src/plan-versions-catalog.ts +368 -0
  160. package/src/platform-loaders.ts +68 -0
  161. package/src/project-page-host.ts +108 -0
  162. package/src/testing-e2e/admin-pages-suite.ts +187 -0
  163. package/src/types.ts +58 -0
  164. package/src/use-actions.ts +25 -0
  165. package/src/use-api-list.ts +151 -0
  166. package/src/use-audit-entries.ts +48 -0
  167. package/src/use-batch-columns.ts +59 -0
  168. package/src/use-bulk-publish.ts +150 -0
  169. package/src/use-bundle-versions-map.ts +112 -0
  170. package/src/use-bundles.ts +297 -0
  171. package/src/use-business-types.ts +269 -0
  172. package/src/use-catalog-entries.ts +243 -0
  173. package/src/use-discovery.ts +158 -0
  174. package/src/use-entitlement.ts +87 -0
  175. package/src/use-live-plan-versions.ts +126 -0
  176. package/src/use-manifest.ts +57 -0
  177. package/src/use-marketing-projections.ts +160 -0
  178. package/src/use-nav.ts +33 -0
  179. package/src/use-plan-editor.ts +162 -0
  180. package/src/use-plan-versions.ts +83 -0
  181. package/src/use-plans.ts +375 -0
  182. package/src/use-platform-tenant-actions.ts +286 -0
  183. package/src/use-promotions.ts +126 -0
  184. package/src/use-public-boot.ts +40 -0
  185. package/src/use-subscription-draft.ts +355 -0
  186. package/src/use-super-admin-context.ts +93 -0
  187. package/src/use-tenant-action-flow.ts +238 -0
  188. package/src/use-tenant-billing-catalog.ts +160 -0
  189. package/src/use-tenant-billing.ts +453 -0
  190. package/src/use-tenant-manifest.ts +94 -0
  191. package/src/use-tenant-subscription-bundles.ts +148 -0
  192. package/src/use-tenants.ts +61 -0
  193. package/src/version.ts +10 -0
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <div :class="['bd-quotas', { 'bd-locked': locked }]">
3
+ <div
4
+ v-for="q in availableQuotas"
5
+ :key="q.quotaKey"
6
+ class="bd-quota-row"
7
+ :class="{
8
+ on: q.quotaKey in quotas,
9
+ overlap: overlapKeys.includes(q.quotaKey),
10
+ }"
11
+ >
12
+ <button
13
+ type="button"
14
+ class="bd-quota-toggle"
15
+ :disabled="locked"
16
+ :title="
17
+ locked
18
+ ? 'Live-Version ist read-only'
19
+ : q.quotaKey in quotas
20
+ ? 'Quota entfernen'
21
+ : 'Quota aufnehmen'
22
+ "
23
+ @click="$emit('toggle', q.quotaKey)"
24
+ >
25
+ <span class="bd-quota-tick" aria-hidden="true">
26
+ <svg
27
+ v-if="q.quotaKey in quotas"
28
+ width="11"
29
+ height="11"
30
+ viewBox="0 0 24 24"
31
+ fill="none"
32
+ stroke="currentColor"
33
+ stroke-width="3"
34
+ >
35
+ <path d="M5 13l4 4L19 7" />
36
+ </svg>
37
+ <svg
38
+ v-else
39
+ width="11"
40
+ height="11"
41
+ viewBox="0 0 24 24"
42
+ fill="none"
43
+ stroke="currentColor"
44
+ stroke-width="2"
45
+ >
46
+ <path d="M12 5v14M5 12h14" />
47
+ </svg>
48
+ </span>
49
+ </button>
50
+ <div class="bd-quota-main">
51
+ <div class="bd-quota-label">{{ quotaLabel(q) }}</div>
52
+ <div class="bd-quota-key">{{ q.quotaKey }}</div>
53
+ </div>
54
+ <div class="bd-quota-valwrap">
55
+ <input
56
+ type="number"
57
+ class="bd-quota-val"
58
+ :value="quotas[q.quotaKey] ?? ''"
59
+ :disabled="!(q.quotaKey in quotas) || locked"
60
+ :placeholder="String(0)"
61
+ @input="
62
+ $emit(
63
+ 'setValue',
64
+ q.quotaKey,
65
+ Number(($event.target as HTMLInputElement).value),
66
+ )
67
+ "
68
+ />
69
+ <span class="bd-quota-unit">{{ quotaUnit(q) }}</span>
70
+ </div>
71
+ </div>
72
+ <div v-if="availableQuotas.length === 0" class="bd-quotas-empty">
73
+ Keine Quotas im Discovery-Snapshot.
74
+ </div>
75
+ </div>
76
+ </template>
77
+
78
+ <script setup lang="ts">
79
+ import { computed } from 'vue';
80
+ import type { DiscoveredQuota } from '@saasicat/types';
81
+ import type { QuotaMeta } from './catalog-i18n.js';
82
+
83
+ // BundleQuotasEditor — toggle + numeric per Quota, against the discovery
84
+ // snapshot as a library (after plan simulation). `quotas[key]` is the
85
+ // configured amount; if the key is missing, the Quota is not in the bundle.
86
+ //
87
+ // `overlapKeys` marks Quotas that the selected compatible plan already
88
+ // carries — warn about double counting.
89
+ //
90
+ // `quotaRegistry` provides the label/unit resolved in the display locale
91
+ // (from the Quota catalog). If an entry is missing, the discovery value applies.
92
+
93
+ const props = defineProps<{
94
+ availableQuotas: DiscoveredQuota[];
95
+ quotas: Record<string, number>;
96
+ locked?: boolean;
97
+ overlapKeys?: string[];
98
+ quotaRegistry?: Record<string, QuotaMeta>;
99
+ }>();
100
+
101
+ defineEmits<{
102
+ (e: 'toggle', quotaKey: string): void;
103
+ (e: 'setValue', quotaKey: string, value: number): void;
104
+ }>();
105
+
106
+ const overlapKeys = computed(() => props.overlapKeys ?? []);
107
+
108
+ function quotaLabel(q: DiscoveredQuota): string {
109
+ return props.quotaRegistry?.[q.quotaKey]?.label ?? q.label ?? q.quotaKey;
110
+ }
111
+
112
+ function quotaUnit(q: DiscoveredQuota): string {
113
+ return props.quotaRegistry?.[q.quotaKey]?.unit ?? q.unit;
114
+ }
115
+ </script>
116
+
117
+ <style scoped>
118
+ .bd-quotas {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: 6px;
122
+ }
123
+ .bd-quotas.bd-locked {
124
+ opacity: 0.7;
125
+ }
126
+ .bd-quota-row {
127
+ display: grid;
128
+ grid-template-columns: 28px 1fr auto;
129
+ align-items: center;
130
+ gap: 10px;
131
+ padding: 8px 10px;
132
+ background: #fff;
133
+ border: 1px solid #e2e8f0;
134
+ border-radius: 8px;
135
+ transition:
136
+ background 0.12s,
137
+ border-color 0.12s;
138
+ }
139
+ .bd-quota-row.on {
140
+ background: #f0f9ff;
141
+ border-color: #bae6fd;
142
+ }
143
+ .bd-quota-row.overlap {
144
+ border-color: #fecaca;
145
+ background: #fef2f2;
146
+ }
147
+ .bd-quota-toggle {
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ width: 22px;
152
+ height: 22px;
153
+ background: #fff;
154
+ border: 1px solid #cbd5e1;
155
+ border-radius: 6px;
156
+ cursor: pointer;
157
+ color: #475569;
158
+ }
159
+ .bd-quota-row.on .bd-quota-toggle {
160
+ background: #2563eb;
161
+ border-color: #2563eb;
162
+ color: #fff;
163
+ }
164
+ .bd-quota-toggle:disabled {
165
+ cursor: not-allowed;
166
+ }
167
+ .bd-quota-tick {
168
+ display: inline-flex;
169
+ }
170
+ .bd-quota-main {
171
+ min-width: 0;
172
+ }
173
+ .bd-quota-label {
174
+ font-size: 13px;
175
+ font-weight: 600;
176
+ color: #0f172a;
177
+ }
178
+ .bd-quota-key {
179
+ font:
180
+ 500 10.5px 'JetBrains Mono',
181
+ ui-monospace,
182
+ monospace;
183
+ color: #94a3b8;
184
+ }
185
+ .bd-quota-valwrap {
186
+ display: inline-flex;
187
+ align-items: center;
188
+ gap: 6px;
189
+ }
190
+ .bd-quota-val {
191
+ width: 90px;
192
+ padding: 5px 8px;
193
+ border: 1px solid #cbd5e1;
194
+ border-radius: 6px;
195
+ font:
196
+ 600 13px 'JetBrains Mono',
197
+ ui-monospace,
198
+ monospace;
199
+ text-align: right;
200
+ background: #fff;
201
+ color: #0f172a;
202
+ }
203
+ .bd-quota-val:disabled {
204
+ background: #f1f5f9;
205
+ color: #94a3b8;
206
+ cursor: not-allowed;
207
+ }
208
+ .bd-quota-unit {
209
+ font-size: 11.5px;
210
+ color: #64748b;
211
+ min-width: 32px;
212
+ }
213
+ .bd-quotas-empty {
214
+ padding: 12px;
215
+ color: #94a3b8;
216
+ font-style: italic;
217
+ font-size: 12.5px;
218
+ }
219
+ </style>
@@ -0,0 +1,189 @@
1
+ <template>
2
+ <div :class="['bv-status-banner', `bv-status-${status}`]">
3
+ <span class="bv-status-icon" aria-hidden="true">
4
+ <svg
5
+ v-if="status === 'live'"
6
+ width="16"
7
+ height="16"
8
+ viewBox="0 0 24 24"
9
+ fill="none"
10
+ stroke="currentColor"
11
+ stroke-width="2"
12
+ >
13
+ <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
14
+ </svg>
15
+ <svg
16
+ v-else-if="status === 'scheduled'"
17
+ width="16"
18
+ height="16"
19
+ viewBox="0 0 24 24"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ stroke-width="2"
23
+ >
24
+ <circle cx="12" cy="12" r="9" />
25
+ <path d="M12 7v5l3 2" />
26
+ </svg>
27
+ <svg
28
+ v-else-if="status === 'superseded'"
29
+ width="16"
30
+ height="16"
31
+ viewBox="0 0 24 24"
32
+ fill="none"
33
+ stroke="currentColor"
34
+ stroke-width="2"
35
+ >
36
+ <path
37
+ d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M5 6l1 14a2 2 0 002 2h8a2 2 0 002-2l1-14"
38
+ />
39
+ </svg>
40
+ <svg
41
+ v-else
42
+ width="16"
43
+ height="16"
44
+ viewBox="0 0 24 24"
45
+ fill="none"
46
+ stroke="currentColor"
47
+ stroke-width="2"
48
+ >
49
+ <path d="M12 20h9M16.5 3.5a2.12 2.12 0 113 3L7 19l-4 1 1-4 12.5-12.5z" />
50
+ </svg>
51
+ </span>
52
+ <div class="bv-status-text">
53
+ <template v-if="status === 'live'">
54
+ <b>v{{ version.version }}</b> ist <b>live</b> seit
55
+ {{ formatDateDE(version.validFrom) }} — wird aktuell als Add-On angeboten.
56
+ <span class="bv-status-warn">
57
+ Inhalt &amp; Preis sind read-only (laufende Verträge). Für Änderungen eine neue
58
+ Version anlegen.
59
+ </span>
60
+ </template>
61
+ <template v-else-if="status === 'scheduled'">
62
+ <b>v{{ version.version }}</b> ist <b>geplant</b> für
63
+ {{ formatDateDE(version.validFrom) }} — wird ab dann verkaufbar.
64
+ <span class="bv-status-ok">Frei editierbar bis dahin.</span>
65
+ </template>
66
+ <template v-else-if="status === 'superseded'">
67
+ <b>v{{ version.version }}</b> ist <b>abgelöst</b> ({{
68
+ formatDateDE(version.validFrom)
69
+ }}
70
+ – {{ formatDateDE(version.validUntil) }}) — wird nicht mehr angeboten, Bestand
71
+ bleibt für Abrechnung erhalten.
72
+ </template>
73
+ <template v-else>
74
+ <b>v{{ version.version }}</b> ist <b>Draft</b> — noch nicht published, frei
75
+ editierbar.
76
+ </template>
77
+ </div>
78
+ <button
79
+ v-if="status === 'scheduled' || status === 'draft'"
80
+ class="bv-status-discard"
81
+ type="button"
82
+ title="Geplante Version verwerfen"
83
+ @click="$emit('discard')"
84
+ >
85
+ <svg
86
+ width="14"
87
+ height="14"
88
+ viewBox="0 0 24 24"
89
+ fill="none"
90
+ stroke="currentColor"
91
+ stroke-width="2"
92
+ >
93
+ <path
94
+ d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"
95
+ />
96
+ </svg>
97
+ <span>Verwerfen</span>
98
+ </button>
99
+ </div>
100
+ </template>
101
+
102
+ <script setup lang="ts">
103
+ import { computed } from 'vue';
104
+ import type { BundleVersionRow } from '@saasicat/types';
105
+
106
+ import { bundleVersionStatus, formatDateDE } from './bundle-version-status';
107
+
108
+ // BundleStatusBanner — inline hint per bundle version with plain-text info on
109
+ // editability (after plan simulation). Shows the status + the business
110
+ // consequence (live = read-only, scheduled = editable).
111
+
112
+ const props = defineProps<{
113
+ version: BundleVersionRow;
114
+ /** Optional: reference point in time for the status check (tests). */
115
+ now?: Date;
116
+ }>();
117
+
118
+ defineEmits<{
119
+ (e: 'discard'): void;
120
+ }>();
121
+
122
+ const status = computed(() => bundleVersionStatus(props.version, props.now));
123
+ </script>
124
+
125
+ <style scoped>
126
+ .bv-status-banner {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 12px;
130
+ padding: 12px 14px;
131
+ border-radius: 8px;
132
+ border: 1px solid transparent;
133
+ font-size: 13px;
134
+ line-height: 1.4;
135
+ }
136
+ .bv-status-live {
137
+ background: #ecfdf5;
138
+ border-color: #a7f3d0;
139
+ color: #065f46;
140
+ }
141
+ .bv-status-scheduled {
142
+ background: #fffbeb;
143
+ border-color: #fde68a;
144
+ color: #92400e;
145
+ }
146
+ .bv-status-superseded {
147
+ background: #f1f5f9;
148
+ border-color: #cbd5e1;
149
+ color: #475569;
150
+ }
151
+ .bv-status-draft {
152
+ background: #eff6ff;
153
+ border-color: #bfdbfe;
154
+ color: #1e40af;
155
+ }
156
+ .bv-status-icon {
157
+ flex: 0 0 auto;
158
+ display: inline-flex;
159
+ }
160
+ .bv-status-text {
161
+ flex: 1;
162
+ min-width: 0;
163
+ }
164
+ .bv-status-warn {
165
+ color: #b45309;
166
+ font-weight: 500;
167
+ }
168
+ .bv-status-ok {
169
+ color: #047857;
170
+ font-weight: 500;
171
+ }
172
+ .bv-status-discard {
173
+ display: inline-flex;
174
+ align-items: center;
175
+ gap: 5px;
176
+ padding: 6px 10px;
177
+ background: #fff;
178
+ border: 1px solid #e2e8f0;
179
+ border-radius: 6px;
180
+ cursor: pointer;
181
+ font-size: 12px;
182
+ color: #b91c1c;
183
+ font-family: inherit;
184
+ }
185
+ .bv-status-discard:hover {
186
+ background: #fef2f2;
187
+ border-color: #fecaca;
188
+ }
189
+ </style>