@saasicat/ui-vue 0.23.0 → 0.24.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.
- package/dist/{chunk-XMMFN2WJ.js → chunk-VYMTWCTT.js} +51 -1
- package/dist/client/index.cjs +57 -0
- package/dist/client/index.d.cts +58 -1
- package/dist/client/index.d.ts +58 -1
- package/dist/client/index.js +15 -1
- package/dist/index.cjs +108 -0
- package/dist/index.d.cts +65 -2
- package/dist/index.d.ts +65 -2
- package/dist/index.js +65 -1
- package/package.json +2 -2
- package/src/client/identity-accents.ts +129 -0
- package/src/client/index.ts +1 -0
- package/src/components/MarketingPromotionsTab.vue +6 -2
- package/src/components/bundle-editor/BundleVersionInlineEditor.vue +1 -1
- package/src/components/dialogs/PromoCodeDialogFields.vue +3 -2
- package/src/components/plan-create-dialog/PlanCreateDialog.vue +1 -1
- package/src/components/plan-detail/PlanDetail.vue +8 -3
- package/src/components/plan-list/PlanList.vue +18 -23
- package/src/components/plan-matrix/PlanMatrix.vue +8 -25
- package/src/components/plan-review/PlanReview.vue +1 -1
- package/src/components/plan-version-editor/PlanVersionEditor.vue +22 -3
- package/src/pages-standard/BundlesPage.vue +1 -1
- package/src/pages-standard/DashboardPage.vue +5 -2
- package/src/pages-standard/MarketingCatalogPage.vue +19 -3
- package/src/pages-standard/PromoCodesPage.vue +3 -1
- package/src/pages-standard/TenantsPage.vue +5 -4
- package/src/pages-standard/discovery-page/discovery-ui.ts +10 -7
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +2 -5
- package/src/pages-standard/plan-versions/PlanDiffCard.vue +11 -4
- package/src/pages-standard/plan-versions/PlanVersionsDiff.vue +3 -1
- package/src/pages-standard/tenants/format.ts +11 -12
- package/src/pages-tenant/MySubscriptionBundlesPage.vue +9 -0
- package/src/pages-tenant/OnboardingConfigurator.vue +1 -1
- package/src/pages-tenant/PlanChangeWizard.vue +2 -49
- package/src/pages-tenant/TenantPlanSection.vue +6 -48
- package/src/pages-tenant/default-i18n.ts +113 -0
- package/src/ui/theme/_breakpoints.scss +8 -2
- package/src/ui/theme/components/card.css +26 -0
- package/src/ui/theme/tokens.primitive.css +4 -0
- package/src/ui/theme/tokens.semantic.dark.css +12 -0
- package/src/ui/theme/tokens.semantic.light.css +30 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BootLoaderOptions, ManifestLoaderOptions, BuildRouteEntry, SidebarSection, NavBuilderOptions, BatchColumnData, BatchColumnFetcherOptions, BootLoader, ManifestLoader } from './client/index.js';
|
|
2
|
-
export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, LoginBootProject, LoginBrandFallback, LoginBranding, ManifestLoadError, MessageParams, ParamStyle, PlanCounts, PlanRowLike, PlanVersionLike, ResolvePlansInput, ResolvedPlan, SidebarItem, buildRoutes, buildSidebar, countPlans, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, postJson, resolveExtension, resolveLoginBranding, resolvePlans, todayIsoDate, trimTrailingSlashes } from './client/index.js';
|
|
2
|
+
export { ADMIN_UI_VERSION, AdminPromoListFilter, AdminPromoListRow, AdminResourceClientOptions, BatchColumnDriftError, BatchColumnFetcher, BatchColumnRow, BatchColumnValue, BootLoadError, CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, LoginBootProject, LoginBrandFallback, LoginBranding, ManifestLoadError, MessageParams, ParamStyle, PlanCounts, PlanRowLike, PlanVersionLike, ResolvePlansInput, ResolvedPlan, SidebarItem, buildRoutes, buildSidebar, countPlans, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, postJson, resolveExtension, resolveLoginBranding, resolvePlans, todayIsoDate, trimTrailingSlashes } from './client/index.js';
|
|
3
3
|
import { H as HttpClient, e as ActionRegistry, A as ActionHandler, K as KvStore, S as SaLocale } from './catalog-DJcfm5N2.js';
|
|
4
4
|
export { f as ActionDefNotInManifestError, D as DEFAULT_SA_LOCALE, g as HttpResponse, M as MessageTree, h as MissingHandlerError, P as PartialMessages, R as ResolvedAction, i as SA_INTL_LOCALES, j as SA_LOCALES, k as SA_LOCALE_LABELS, l as SA_MESSAGES, m as SaBuiltinLocale, n as SaCatalog, o as SaCatalogOptions, c as SaLocaleDefinition, b as SaLocaleOption, a as SaMessages, d as SaMessagesOverrides, T as TranslationOf, p as createSaCatalog, q as defaultHttpClient, r as defaultKvStore, s as defineMessages, t as isSaBuiltinLocale, u as mergeMessages, v as resolveMessages } from './catalog-DJcfm5N2.js';
|
|
5
5
|
import { A as ActionsMap, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, c as SuperAdminLoginAdapter } from './use-sa-theme-OUBlaqgl.js';
|
|
@@ -1777,5 +1777,68 @@ declare const DEFAULT_I18N_DE: TenantPlanSectionI18n;
|
|
|
1777
1777
|
declare const DEFAULT_I18N_EN: TenantPlanSectionI18n;
|
|
1778
1778
|
/** Default map for the given UI locale — fallback layer under the `i18n` prop. */
|
|
1779
1779
|
declare function defaultTenantPlanSectionI18n(locale: SaLocale): TenantPlanSectionI18n;
|
|
1780
|
+
/**
|
|
1781
|
+
* The strings `PlanChangeWizard` takes.
|
|
1782
|
+
*
|
|
1783
|
+
* A projection of `TenantPlanSectionI18n` with the `wizard` prefix dropped, so
|
|
1784
|
+
* the wizard's own template reads `i18n.stepChoose` rather than
|
|
1785
|
+
* `i18n.wizardStepChoose`.
|
|
1786
|
+
*/
|
|
1787
|
+
interface PlanChangeWizardI18n {
|
|
1788
|
+
title: string;
|
|
1789
|
+
close: string;
|
|
1790
|
+
currentLabel: string;
|
|
1791
|
+
cycleMonthly: string;
|
|
1792
|
+
cycleYearly: string;
|
|
1793
|
+
badgeCurrent: string;
|
|
1794
|
+
badgePopular: string;
|
|
1795
|
+
priceUnitMonthly: string;
|
|
1796
|
+
priceUnitYearly: string;
|
|
1797
|
+
priceOnRequest: string;
|
|
1798
|
+
stepChoose: string;
|
|
1799
|
+
stepChooseIntro: string;
|
|
1800
|
+
stepPreview: string;
|
|
1801
|
+
stepConfirm: string;
|
|
1802
|
+
next: string;
|
|
1803
|
+
back: string;
|
|
1804
|
+
previewLoading: string;
|
|
1805
|
+
effectiveAtLabel: string;
|
|
1806
|
+
effectiveImmediate: string;
|
|
1807
|
+
prorationTitle: string;
|
|
1808
|
+
prorationLine: string;
|
|
1809
|
+
prorationDays: string;
|
|
1810
|
+
limitsTitle: string;
|
|
1811
|
+
limitsUsed: string;
|
|
1812
|
+
limitsCurrent: string;
|
|
1813
|
+
limitsTarget: string;
|
|
1814
|
+
featuresGained: string;
|
|
1815
|
+
featuresLost: string;
|
|
1816
|
+
blockersTitle: string;
|
|
1817
|
+
confirmImmediate: string;
|
|
1818
|
+
confirmScheduled: string;
|
|
1819
|
+
confirmAction: string;
|
|
1820
|
+
confirmInProgress: string;
|
|
1821
|
+
confirmPriceTitle: string;
|
|
1822
|
+
confirmProratedNow: string;
|
|
1823
|
+
confirmRecurringNext: string;
|
|
1824
|
+
confirmRecurringFrom: string;
|
|
1825
|
+
perCycleMonthly: string;
|
|
1826
|
+
perCycleYearly: string;
|
|
1827
|
+
confirmTrialNote: string;
|
|
1828
|
+
confirmRecurringTrialEnd: string;
|
|
1829
|
+
changeTypeUpgrade: string;
|
|
1830
|
+
changeTypeDowngrade: string;
|
|
1831
|
+
changeTypeCycle: string;
|
|
1832
|
+
changeTypeNoop: string;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Builds the wizard's strings from the section's.
|
|
1836
|
+
*
|
|
1837
|
+
* Here rather than inline in `TenantPlanSection.vue`, because the wizard is an
|
|
1838
|
+
* exported component: a consumer who mounts it directly would otherwise have to
|
|
1839
|
+
* reproduce all 44 keys of this mapping by hand, and a visual fixture had to do
|
|
1840
|
+
* exactly that before it moved.
|
|
1841
|
+
*/
|
|
1842
|
+
declare function planChangeWizardI18n(i18n: TenantPlanSectionI18n): PlanChangeWizardI18n;
|
|
1780
1843
|
|
|
1781
|
-
export { ALL_ROWS, ActionHandler, ActionRegistry, ActionsMap, type ApiListResponse, BatchColumnData, BatchColumnFetcherOptions, type BillingCycleStr, BootLoader, BootLoaderOptions, BuildRouteEntry, type BulkItemKind, type BulkItemStatus, type BulkPublishItem, type BundleAddPreviewShape, type BundleCancelPreviewShape, type BundlePreviewIssueShape, type BundlePreviewShape, type BundlePreviewSnapshotShape, BundlesApiError, CATALOG_DEFAULT_LOCALE, type CatalogBundle, CatalogEntriesApiError, type CatalogPlan, type ConfirmDialogState, type CreateAdminRoutesOptions, type CreateManifestStoreOptions, type CreatePlatformLoadersOptions, DEFAULT_I18N_DE, DEFAULT_I18N_EN, DEFAULT_ROWS_PER_PAGE, DEFAULT_YEARLY_FACTOR, DiscoveryLoadError, type DraftPricing, ENTITLEMENT_INJECTION_KEY, type EmailHistoryDetail, type EmailHistoryFilter, type EmailHistoryListResult, type EmailHistoryResendResult, type EmailHistoryRow, type EmailHistoryStatus, type EntitlementSnapshotShape, ExtensionsMap, type FeatureRegistryEntry, type FeatureRouterGuardOptions, type FeatureRowMarkers, HttpClient, KvStore, ManifestLoader, ManifestLoaderOptions, type ManifestStoreActions, type ManifestStoreDefinition, type ManifestStoreState, MarketingProjectionsApiError, type MfaDialogState, NavBuilderOptions, PAGE_SIZE_OPTIONS, type PackageSnapshotShape, type PilotCopy, type PilotCreatePayload, type PilotCreateResult, type PilotEditPayload, type PilotEditResult, type PlanChangePreviewShape, type PlanSnapshotShape, PlannedOnlyFeatureError, PlansApiError, type PlatformEmailProvider, type PlatformEmailTestInput, type PlatformEmailTestResult, type PlatformEmailWriteInput, type PlatformLoaders, type PlatformTenantActionRow, type PlatformTenantActionTone, type PlatformTenantActionsOptions, type PlatformTenantActionsResult, type PriceLineItem, ProjectPageHost, type PromoCodeCreatePayload, type PromoCodeDurationType, type PromoCodePlanOption, type PromoCodeUpdatePayload, type PromoCodeValueType, type PromoState, type PromoStatus, PromotionsApiError, type QuotaMeta, type RedundantFeatureHintShape, type ResolvedTenantAction, SERVER_PAGE_SIZE_OPTIONS, SaLocale, SidebarSection, type SubscriptionBundleShape, type SubscriptionDraft, SuperAdminBrand, SuperAdminEndpoints, SuperAdminLoginAdapter, type TenantActionFlowProviders, type TenantActionInput, type TenantManifestNavItem, type TenantManifestShape, type TenantPlanSectionI18n, type TenantRowLike, TenantSubscriptionBundlesApiError, type UsageSnapshotShape, type UseActionsResult, type UseApiListOptions, type UseApiListResult, type UseAuditEntriesOptions, type UseAuditEntriesResult, type UseBatchColumnsResult, type UseBulkPublishOptions, type UseBulkPublishResult, type UseBundleVersionsMapOptions, type UseBundleVersionsMapResult, type UseBundleVersionsOptions, type UseBundleVersionsResult, type UseBundlesOptions, type UseBundlesResult, type UseCatalogEntriesOptions, type UseCatalogEntriesResult, type UseDiscoveryOptions, type UseDiscoveryResult, type UseEntitlementOptions, type UseEntitlementResult, type UseLivePlanVersionsOptions, type UseLivePlanVersionsResult, type UseManifestResult, type UseMarketingProjectionsOptions, type UseMarketingProjectionsResult, type UseNavResult, type UsePaginationResult, type UsePlanEditorOptions, type UsePlanEditorResult, type UsePlanVersionsOptions, type UsePlanVersionsResult, type UsePlansOptions, type UsePlansResult, type UsePromotionsOptions, type UsePromotionsResult, type UsePublicBootResult, type UseSubscriptionDraftOptions, type UseTenantActionFlowResult, type UseTenantBillingCatalogOptions, type UseTenantBillingCatalogResult, type UseTenantBillingOptions, type UseTenantBillingResult, type UseTenantManifestOptions, type UseTenantManifestResult, type UseTenantSubscriptionBundlesOptions, type UseTenantSubscriptionBundlesResult, type UseTenantsOptions, type UseTenantsResult, buildFeatureRegistry, buildFeatureRouterGuard, buildQuotaRegistry, createAdminRoutes, createManifestStore, createPlatformLoaders, createProjectPageHostRoute, defaultIconForActionKey, defaultTenantPlanSectionI18n, defaultToneForActionKey, provideEntitlement, useActions, useApiList, useAuditEntries, useBatchColumns, useBulkPublish, useBundleVersions, useBundleVersionsMap, useBundles, useCatalogEntries, useDiscovery, useEntitlement, useInjectedEntitlement, useLivePlanVersions, useManifest, useMarketingProjections, useNav, usePagination, usePlanEditor, usePlanVersions, usePlans, usePlatformTenantActions, usePromotions, usePublicBoot, useSubscriptionDraft, useSuperAdminActions, useSuperAdminBrand, useSuperAdminEndpoints, useSuperAdminExtensions, useSuperAdminHttp, useSuperAdminLoginAdapter, useSuperAdminManifest, useTenantActionFlow, useTenantBilling, useTenantBillingCatalog, useTenantManifest, useTenantSubscriptionBundles, useTenants };
|
|
1844
|
+
export { ALL_ROWS, ActionHandler, ActionRegistry, ActionsMap, type ApiListResponse, BatchColumnData, BatchColumnFetcherOptions, type BillingCycleStr, BootLoader, BootLoaderOptions, BuildRouteEntry, type BulkItemKind, type BulkItemStatus, type BulkPublishItem, type BundleAddPreviewShape, type BundleCancelPreviewShape, type BundlePreviewIssueShape, type BundlePreviewShape, type BundlePreviewSnapshotShape, BundlesApiError, CATALOG_DEFAULT_LOCALE, type CatalogBundle, CatalogEntriesApiError, type CatalogPlan, type ConfirmDialogState, type CreateAdminRoutesOptions, type CreateManifestStoreOptions, type CreatePlatformLoadersOptions, DEFAULT_I18N_DE, DEFAULT_I18N_EN, DEFAULT_ROWS_PER_PAGE, DEFAULT_YEARLY_FACTOR, DiscoveryLoadError, type DraftPricing, ENTITLEMENT_INJECTION_KEY, type EmailHistoryDetail, type EmailHistoryFilter, type EmailHistoryListResult, type EmailHistoryResendResult, type EmailHistoryRow, type EmailHistoryStatus, type EntitlementSnapshotShape, ExtensionsMap, type FeatureRegistryEntry, type FeatureRouterGuardOptions, type FeatureRowMarkers, HttpClient, KvStore, ManifestLoader, ManifestLoaderOptions, type ManifestStoreActions, type ManifestStoreDefinition, type ManifestStoreState, MarketingProjectionsApiError, type MfaDialogState, NavBuilderOptions, PAGE_SIZE_OPTIONS, type PackageSnapshotShape, type PilotCopy, type PilotCreatePayload, type PilotCreateResult, type PilotEditPayload, type PilotEditResult, type PlanChangePreviewShape, type PlanChangeWizardI18n, type PlanSnapshotShape, PlannedOnlyFeatureError, PlansApiError, type PlatformEmailProvider, type PlatformEmailTestInput, type PlatformEmailTestResult, type PlatformEmailWriteInput, type PlatformLoaders, type PlatformTenantActionRow, type PlatformTenantActionTone, type PlatformTenantActionsOptions, type PlatformTenantActionsResult, type PriceLineItem, ProjectPageHost, type PromoCodeCreatePayload, type PromoCodeDurationType, type PromoCodePlanOption, type PromoCodeUpdatePayload, type PromoCodeValueType, type PromoState, type PromoStatus, PromotionsApiError, type QuotaMeta, type RedundantFeatureHintShape, type ResolvedTenantAction, SERVER_PAGE_SIZE_OPTIONS, SaLocale, SidebarSection, type SubscriptionBundleShape, type SubscriptionDraft, SuperAdminBrand, SuperAdminEndpoints, SuperAdminLoginAdapter, type TenantActionFlowProviders, type TenantActionInput, type TenantManifestNavItem, type TenantManifestShape, type TenantPlanSectionI18n, type TenantRowLike, TenantSubscriptionBundlesApiError, type UsageSnapshotShape, type UseActionsResult, type UseApiListOptions, type UseApiListResult, type UseAuditEntriesOptions, type UseAuditEntriesResult, type UseBatchColumnsResult, type UseBulkPublishOptions, type UseBulkPublishResult, type UseBundleVersionsMapOptions, type UseBundleVersionsMapResult, type UseBundleVersionsOptions, type UseBundleVersionsResult, type UseBundlesOptions, type UseBundlesResult, type UseCatalogEntriesOptions, type UseCatalogEntriesResult, type UseDiscoveryOptions, type UseDiscoveryResult, type UseEntitlementOptions, type UseEntitlementResult, type UseLivePlanVersionsOptions, type UseLivePlanVersionsResult, type UseManifestResult, type UseMarketingProjectionsOptions, type UseMarketingProjectionsResult, type UseNavResult, type UsePaginationResult, type UsePlanEditorOptions, type UsePlanEditorResult, type UsePlanVersionsOptions, type UsePlanVersionsResult, type UsePlansOptions, type UsePlansResult, type UsePromotionsOptions, type UsePromotionsResult, type UsePublicBootResult, type UseSubscriptionDraftOptions, type UseTenantActionFlowResult, type UseTenantBillingCatalogOptions, type UseTenantBillingCatalogResult, type UseTenantBillingOptions, type UseTenantBillingResult, type UseTenantManifestOptions, type UseTenantManifestResult, type UseTenantSubscriptionBundlesOptions, type UseTenantSubscriptionBundlesResult, type UseTenantsOptions, type UseTenantsResult, buildFeatureRegistry, buildFeatureRouterGuard, buildQuotaRegistry, createAdminRoutes, createManifestStore, createPlatformLoaders, createProjectPageHostRoute, defaultIconForActionKey, defaultTenantPlanSectionI18n, defaultToneForActionKey, planChangeWizardI18n, provideEntitlement, useActions, useApiList, useAuditEntries, useBatchColumns, useBulkPublish, useBundleVersions, useBundleVersionsMap, useBundles, useCatalogEntries, useDiscovery, useEntitlement, useInjectedEntitlement, useLivePlanVersions, useManifest, useMarketingProjections, useNav, usePagination, usePlanEditor, usePlanVersions, usePlans, usePlatformTenantActions, usePromotions, usePublicBoot, useSubscriptionDraft, useSuperAdminActions, useSuperAdminBrand, useSuperAdminEndpoints, useSuperAdminExtensions, useSuperAdminHttp, useSuperAdminLoginAdapter, useSuperAdminManifest, useTenantActionFlow, useTenantBilling, useTenantBillingCatalog, useTenantManifest, useTenantSubscriptionBundles, useTenants };
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
BootLoader,
|
|
9
9
|
DEFAULT_STANDARD_PAGE_ROUTES,
|
|
10
10
|
HttpJsonError,
|
|
11
|
+
IDENTITY_ACCENTS,
|
|
12
|
+
IDENTITY_ACCENT_VALUES,
|
|
13
|
+
IDENTITY_NEUTRAL,
|
|
14
|
+
IDENTITY_NEUTRAL_VALUE,
|
|
11
15
|
ManifestLoadError,
|
|
12
16
|
ManifestLoader,
|
|
13
17
|
MissingHandlerError,
|
|
@@ -17,6 +21,9 @@ import {
|
|
|
17
21
|
createAdminResourceClient,
|
|
18
22
|
defaultSectionOrder,
|
|
19
23
|
getJson,
|
|
24
|
+
identityAccentAt,
|
|
25
|
+
identityAccentFor,
|
|
26
|
+
identityChipStyle,
|
|
20
27
|
isCurrentlyValid,
|
|
21
28
|
isExpired,
|
|
22
29
|
isFutureScheduled,
|
|
@@ -27,7 +34,7 @@ import {
|
|
|
27
34
|
resolvePlans,
|
|
28
35
|
todayIsoDate,
|
|
29
36
|
trimTrailingSlashes
|
|
30
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-VYMTWCTT.js";
|
|
31
38
|
import {
|
|
32
39
|
SA_LOCALE_STORAGE_KEY,
|
|
33
40
|
SA_THEME_KEY,
|
|
@@ -2826,6 +2833,55 @@ var DEFAULT_I18N_EN = {
|
|
|
2826
2833
|
function defaultTenantPlanSectionI18n(locale) {
|
|
2827
2834
|
return locale === "en" ? DEFAULT_I18N_EN : DEFAULT_I18N_DE;
|
|
2828
2835
|
}
|
|
2836
|
+
function planChangeWizardI18n(i18n) {
|
|
2837
|
+
return {
|
|
2838
|
+
title: i18n.wizardTitle,
|
|
2839
|
+
close: i18n.wizardClose,
|
|
2840
|
+
currentLabel: i18n.wizardCurrent,
|
|
2841
|
+
cycleMonthly: i18n.cycleMonthly,
|
|
2842
|
+
cycleYearly: i18n.cycleYearly,
|
|
2843
|
+
badgeCurrent: i18n.wizardBadgeCurrent,
|
|
2844
|
+
badgePopular: i18n.wizardBadgePopular,
|
|
2845
|
+
priceUnitMonthly: i18n.wizardPriceUnitMonthly,
|
|
2846
|
+
priceUnitYearly: i18n.wizardPriceUnitYearly,
|
|
2847
|
+
priceOnRequest: i18n.wizardPriceOnRequest,
|
|
2848
|
+
stepChoose: i18n.wizardStepChoose,
|
|
2849
|
+
stepChooseIntro: i18n.wizardStepChooseIntro,
|
|
2850
|
+
stepPreview: i18n.wizardStepPreview,
|
|
2851
|
+
stepConfirm: i18n.wizardStepConfirm,
|
|
2852
|
+
next: i18n.wizardNext,
|
|
2853
|
+
back: i18n.wizardBack,
|
|
2854
|
+
previewLoading: i18n.wizardPreviewLoading,
|
|
2855
|
+
effectiveAtLabel: i18n.wizardEffectiveAtLabel,
|
|
2856
|
+
effectiveImmediate: i18n.wizardEffectiveImmediate,
|
|
2857
|
+
prorationTitle: i18n.wizardProrationTitle,
|
|
2858
|
+
prorationLine: i18n.wizardProrationLine,
|
|
2859
|
+
prorationDays: i18n.wizardProrationDays,
|
|
2860
|
+
limitsTitle: i18n.wizardLimitsTitle,
|
|
2861
|
+
limitsUsed: i18n.wizardLimitsUsed,
|
|
2862
|
+
limitsCurrent: i18n.wizardLimitsCurrent,
|
|
2863
|
+
limitsTarget: i18n.wizardLimitsTarget,
|
|
2864
|
+
featuresGained: i18n.wizardFeaturesGained,
|
|
2865
|
+
featuresLost: i18n.wizardFeaturesLost,
|
|
2866
|
+
blockersTitle: i18n.wizardBlockersTitle,
|
|
2867
|
+
confirmImmediate: i18n.wizardConfirmImmediate,
|
|
2868
|
+
confirmScheduled: i18n.wizardConfirmScheduled,
|
|
2869
|
+
confirmAction: i18n.wizardConfirmAction,
|
|
2870
|
+
confirmInProgress: i18n.wizardConfirmInProgress,
|
|
2871
|
+
confirmPriceTitle: i18n.wizardConfirmPriceTitle,
|
|
2872
|
+
confirmProratedNow: i18n.wizardConfirmProratedNow,
|
|
2873
|
+
confirmRecurringNext: i18n.wizardConfirmRecurringNext,
|
|
2874
|
+
confirmRecurringFrom: i18n.wizardConfirmRecurringFrom,
|
|
2875
|
+
perCycleMonthly: i18n.wizardConfirmPerCycleMonthly,
|
|
2876
|
+
perCycleYearly: i18n.wizardConfirmPerCycleYearly,
|
|
2877
|
+
confirmTrialNote: i18n.wizardConfirmTrialNote,
|
|
2878
|
+
confirmRecurringTrialEnd: i18n.wizardConfirmRecurringTrialEnd,
|
|
2879
|
+
changeTypeUpgrade: i18n.wizardChangeTypeUpgrade,
|
|
2880
|
+
changeTypeDowngrade: i18n.wizardChangeTypeDowngrade,
|
|
2881
|
+
changeTypeCycle: i18n.wizardChangeTypeCycle,
|
|
2882
|
+
changeTypeNoop: i18n.wizardChangeTypeNoop
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2829
2885
|
export {
|
|
2830
2886
|
ADMIN_UI_VERSION,
|
|
2831
2887
|
ALL_ROWS,
|
|
@@ -2847,6 +2903,10 @@ export {
|
|
|
2847
2903
|
DiscoveryLoadError,
|
|
2848
2904
|
ENTITLEMENT_INJECTION_KEY,
|
|
2849
2905
|
HttpJsonError,
|
|
2906
|
+
IDENTITY_ACCENTS,
|
|
2907
|
+
IDENTITY_ACCENT_VALUES,
|
|
2908
|
+
IDENTITY_NEUTRAL,
|
|
2909
|
+
IDENTITY_NEUTRAL_VALUE,
|
|
2850
2910
|
ManifestLoadError,
|
|
2851
2911
|
ManifestLoader,
|
|
2852
2912
|
MarketingProjectionsApiError,
|
|
@@ -2899,12 +2959,16 @@ export {
|
|
|
2899
2959
|
formatCurrency,
|
|
2900
2960
|
formatMessage,
|
|
2901
2961
|
getJson,
|
|
2962
|
+
identityAccentAt,
|
|
2963
|
+
identityAccentFor,
|
|
2964
|
+
identityChipStyle,
|
|
2902
2965
|
isCurrentlyValid,
|
|
2903
2966
|
isExpired,
|
|
2904
2967
|
isFutureScheduled,
|
|
2905
2968
|
isProductionBoot,
|
|
2906
2969
|
isSaBuiltinLocale,
|
|
2907
2970
|
mergeMessages,
|
|
2971
|
+
planChangeWizardI18n,
|
|
2908
2972
|
postJson,
|
|
2909
2973
|
provideEntitlement,
|
|
2910
2974
|
resolveExtension,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/ui-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Vue 3 components, resource clients and composables for the SuperAdmin UI shell. Provides boot and manifest loaders, navigation, actions and standard pages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"src"
|
|
62
62
|
],
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@saasicat/types": "^0.
|
|
64
|
+
"@saasicat/types": "^0.24.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"pinia": "^2.0.0 || ^3.0.0",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// The one categorical palette, and the one way to tint from it.
|
|
2
|
+
//
|
|
3
|
+
// Five components used to carry their own six-colour ramp of hex literals —
|
|
4
|
+
// `PlanList` and `PlanMatrix` byte for byte identical, `tenants/format.ts`
|
|
5
|
+
// silently different (the same plan got one colour on the plans page and
|
|
6
|
+
// another on the tenants page), `PromoCodesPage`, `MarketingPromotionsTab` and
|
|
7
|
+
// `discovery-ui.ts` each with a third opinion. Every one of them was applied
|
|
8
|
+
// through a `:style` binding, so none of them followed the theme: the plan mark
|
|
9
|
+
// measured 2.96:1 on a dark surface and was carried as a named contrast
|
|
10
|
+
// exception because there was no role to point it at.
|
|
11
|
+
//
|
|
12
|
+
// There is now one ramp, it lives in the theme, and it has a value per theme.
|
|
13
|
+
//
|
|
14
|
+
// A consumer may still hand in its own colours — `planAccents` is a public prop
|
|
15
|
+
// and takes any CSS colour. That keeps working because every helper here mixes
|
|
16
|
+
// rather than concatenates: `#7c3aed` + `'15'` needs a hex, and
|
|
17
|
+
// `color-mix(in srgb, X 8%, transparent)` needs only a colour, which a
|
|
18
|
+
// `var(--sa-color-identity-2)` also is.
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The categorical ramp, in the order an unnamed series should walk it.
|
|
22
|
+
*
|
|
23
|
+
* Seven entries, six of them hues plus the neutral. Long enough that a realistic
|
|
24
|
+
* plan list does not wrap, short enough that the colours stay tellable apart —
|
|
25
|
+
* a longer ramp is not a better one, it is a wheel of near-neighbours.
|
|
26
|
+
*/
|
|
27
|
+
export const IDENTITY_ACCENTS: readonly string[] = [
|
|
28
|
+
'var(--sa-color-identity-1)',
|
|
29
|
+
'var(--sa-color-identity-2)',
|
|
30
|
+
'var(--sa-color-identity-3)',
|
|
31
|
+
'var(--sa-color-identity-4)',
|
|
32
|
+
'var(--sa-color-identity-5)',
|
|
33
|
+
'var(--sa-color-identity-6)',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The same ramp as concrete colours, for values that are STORED rather than
|
|
38
|
+
* painted.
|
|
39
|
+
*
|
|
40
|
+
* A promotion's colour is data: the operator picks it, it goes over the wire,
|
|
41
|
+
* a database column holds it, and `CreatePromotionDto` caps it at 16
|
|
42
|
+
* characters. `var(--sa-color-identity-1)` is 28 and would fail validation on
|
|
43
|
+
* every create — which is exactly what happened when the swatches above were
|
|
44
|
+
* pointed at the token form. A token is paint; this is a value.
|
|
45
|
+
*
|
|
46
|
+
* These are the LIGHT theme's values, because a stored colour has to mean one
|
|
47
|
+
* thing wherever it is later rendered. `identity-accents-match-theme.test.js`
|
|
48
|
+
* binds each one to its role, so the two halves of the ramp cannot drift.
|
|
49
|
+
*/
|
|
50
|
+
export const IDENTITY_ACCENT_VALUES: readonly string[] = [
|
|
51
|
+
'#1d4ed8',
|
|
52
|
+
'#6d28d9',
|
|
53
|
+
'#047857',
|
|
54
|
+
'#b45309',
|
|
55
|
+
'#0369a1',
|
|
56
|
+
'#b91c1c',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/** The stored counterpart of `IDENTITY_NEUTRAL`. */
|
|
60
|
+
export const IDENTITY_NEUTRAL_VALUE = '#64748b';
|
|
61
|
+
|
|
62
|
+
/** For "no identity yet", and for the entry tier that should stay quiet. */
|
|
63
|
+
export const IDENTITY_NEUTRAL = 'var(--sa-color-identity-neutral)';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The accent for the nth member of a series, wrapping when the series is longer
|
|
67
|
+
* than the ramp.
|
|
68
|
+
*
|
|
69
|
+
* A negative or non-finite index yields the neutral rather than throwing:
|
|
70
|
+
* callers derive it from `findIndex`, which answers -1 for "not in the list",
|
|
71
|
+
* and a missing row is exactly the "no identity yet" case.
|
|
72
|
+
*/
|
|
73
|
+
export function identityAccentAt(index: number): string {
|
|
74
|
+
if (!Number.isFinite(index) || index < 0) return IDENTITY_NEUTRAL;
|
|
75
|
+
return IDENTITY_ACCENTS[Math.floor(index) % IDENTITY_ACCENTS.length]!;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The stable accent for a well-known key, so the standard plan tiers look the
|
|
80
|
+
* same on every screen that draws them.
|
|
81
|
+
*
|
|
82
|
+
* The tiers below are the ones the three former maps agreed on. `STARTER` and
|
|
83
|
+
* `BASIC` share the neutral because both are the entry tier — the earlier maps
|
|
84
|
+
* gave them two different greys, which said "these differ" about two things
|
|
85
|
+
* that do not.
|
|
86
|
+
*/
|
|
87
|
+
const WELL_KNOWN: Readonly<Record<string, string>> = {
|
|
88
|
+
STARTER: IDENTITY_NEUTRAL,
|
|
89
|
+
BASIC: IDENTITY_NEUTRAL,
|
|
90
|
+
STANDARD: 'var(--sa-color-identity-1)',
|
|
91
|
+
PRO: 'var(--sa-color-identity-2)',
|
|
92
|
+
PROFESSIONAL: 'var(--sa-color-identity-2)',
|
|
93
|
+
BUSINESS: 'var(--sa-color-identity-5)',
|
|
94
|
+
ENTERPRISE: 'var(--sa-color-identity-3)',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The accent for a key, in the order the product decides it.
|
|
99
|
+
*
|
|
100
|
+
* 1. what the consumer supplied, because it is their brand;
|
|
101
|
+
* 2. the well-known tier, so `ENTERPRISE` is the same colour everywhere;
|
|
102
|
+
* 3. the ramp position, so an unknown key still gets a stable colour;
|
|
103
|
+
* 4. the neutral.
|
|
104
|
+
*/
|
|
105
|
+
export function identityAccentFor(
|
|
106
|
+
key: string,
|
|
107
|
+
overrides: Readonly<Record<string, string>> = {},
|
|
108
|
+
index = -1,
|
|
109
|
+
): string {
|
|
110
|
+
return overrides[key] ?? WELL_KNOWN[key] ?? identityAccentAt(index);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The three-part chip style every one of these sites was hand-rolling: a wash
|
|
115
|
+
* of the accent, the accent as text, a firmer edge of it.
|
|
116
|
+
*
|
|
117
|
+
* `color-mix()` rather than the `accent + '15'` string concatenation it
|
|
118
|
+
* replaces. That trick only worked on a six-digit hex — it silently produced
|
|
119
|
+
* garbage for `rgb()`, for a named colour and for anything a consumer might
|
|
120
|
+
* reasonably pass — and it could never have worked for a `var()`, which is why
|
|
121
|
+
* these palettes could not follow the theme in the first place.
|
|
122
|
+
*/
|
|
123
|
+
export function identityChipStyle(accent: string): Record<string, string> {
|
|
124
|
+
return {
|
|
125
|
+
background: `color-mix(in srgb, ${accent} 8%, transparent)`,
|
|
126
|
+
color: accent,
|
|
127
|
+
borderColor: `color-mix(in srgb, ${accent} 20%, transparent)`,
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -361,7 +361,11 @@ const cycleOptions = computed<Array<{ id: PromotionBillingCycle; label: string }
|
|
|
361
361
|
{ id: 'yearly', label: common.value.yearly },
|
|
362
362
|
{ id: 'both', label: common.value.both },
|
|
363
363
|
]);
|
|
364
|
-
|
|
364
|
+
import { IDENTITY_ACCENT_VALUES } from '../client/identity-accents.js';
|
|
365
|
+
|
|
366
|
+
// Concrete values, not tokens: every swatch here is PATCHed to
|
|
367
|
+
// `/catalog/promotions` and stored, and the DTO caps `color` at 16 characters.
|
|
368
|
+
const COLORS = IDENTITY_ACCENT_VALUES;
|
|
365
369
|
|
|
366
370
|
const today = new Date();
|
|
367
371
|
|
|
@@ -550,7 +554,7 @@ async function onAdd(): Promise<void> {
|
|
|
550
554
|
validFrom: new Date().toISOString().slice(0, 10),
|
|
551
555
|
validTo: new Date(today.getTime() + 30 * 86400000).toISOString().slice(0, 10),
|
|
552
556
|
priority: 5,
|
|
553
|
-
color:
|
|
557
|
+
color: IDENTITY_ACCENT_VALUES[0],
|
|
554
558
|
i18n: { [DEFAULT_LOCALE]: { badge: 'Aktion' } },
|
|
555
559
|
});
|
|
556
560
|
expandedId.value = created.id;
|
|
@@ -506,7 +506,7 @@ function recordsEqual(a: Record<string, number>, b: Record<string, number>): boo
|
|
|
506
506
|
grid-template-columns: 1fr 1fr;
|
|
507
507
|
gap: 18px;
|
|
508
508
|
}
|
|
509
|
-
@media (max-width:
|
|
509
|
+
@media (max-width: 1023.98px) {
|
|
510
510
|
.bve-grid {
|
|
511
511
|
grid-template-columns: 1fr;
|
|
512
512
|
}
|
|
@@ -265,6 +265,7 @@
|
|
|
265
265
|
|
|
266
266
|
<script setup lang="ts">
|
|
267
267
|
import { computed } from 'vue';
|
|
268
|
+
import { identityChipStyle } from '../../client/identity-accents.js';
|
|
268
269
|
import { formatMessage } from '../../client/i18n/format.js';
|
|
269
270
|
import { useSaMessages } from '../../vue/use-super-admin-i18n.js';
|
|
270
271
|
import type { PromoCodeDurationType, PromoCodePlanOption, PromoCodeValueType } from './types.js';
|
|
@@ -376,7 +377,7 @@ function togglePlan(key: string): void {
|
|
|
376
377
|
|
|
377
378
|
function planChipStyle(p: PromoCodePlanOption): Record<string, string> {
|
|
378
379
|
if (!isPlanSelected(p.key) || !p.color) return {};
|
|
379
|
-
return {
|
|
380
|
+
return { ...identityChipStyle(p.color), borderColor: p.color };
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
const previewValue = computed(() => {
|
|
@@ -452,7 +453,7 @@ const advancedOpen = defineModel<boolean>('advancedOpen', { default: false });
|
|
|
452
453
|
grid-template-columns: 1fr 1fr;
|
|
453
454
|
}
|
|
454
455
|
|
|
455
|
-
@media (max-width:
|
|
456
|
+
@media (max-width: 599.98px) {
|
|
456
457
|
.pc-grid--2 {
|
|
457
458
|
grid-template-columns: 1fr;
|
|
458
459
|
}
|
|
@@ -579,10 +579,15 @@ async function executeTerminate(): Promise<void> {
|
|
|
579
579
|
font-weight: 700;
|
|
580
580
|
}
|
|
581
581
|
.pd-timeline-seg.draft {
|
|
582
|
+
/* Hatched from the two warning TINTS, not from the saturated amber.
|
|
583
|
+
* `--sa-color-warning-strong` is a border and icon tone: it put the label
|
|
584
|
+
* at 2.34:1 in light and 1.34:1 in dark on every second stripe. The tinted
|
|
585
|
+
* pair is what the guide documents for text on a status surface, and what
|
|
586
|
+
* `.live` above already uses. */
|
|
582
587
|
background: repeating-linear-gradient(
|
|
583
588
|
135deg,
|
|
584
|
-
var(--sa-color-warning-
|
|
585
|
-
var(--sa-color-warning-strong) 8px 16px
|
|
589
|
+
var(--sa-color-warning-surface) 0 8px,
|
|
590
|
+
var(--sa-color-warning-surface-strong) 8px 16px
|
|
586
591
|
);
|
|
587
592
|
color: var(--sa-color-warning-fg);
|
|
588
593
|
font-weight: 700;
|
|
@@ -1004,7 +1009,7 @@ async function executeTerminate(): Promise<void> {
|
|
|
1004
1009
|
flex: 1;
|
|
1005
1010
|
}
|
|
1006
1011
|
|
|
1007
|
-
@media (max-width:
|
|
1012
|
+
@media (max-width: 1023.98px) {
|
|
1008
1013
|
.pd-body {
|
|
1009
1014
|
grid-template-columns: 1fr;
|
|
1010
1015
|
}
|
|
@@ -45,11 +45,7 @@
|
|
|
45
45
|
<div class="sa-plan-list-plan-name">
|
|
46
46
|
<div
|
|
47
47
|
class="sa-plan-list-plan-mark"
|
|
48
|
-
:style="
|
|
49
|
-
background: planAccent(p.planKey) + '15',
|
|
50
|
-
color: planAccent(p.planKey),
|
|
51
|
-
borderColor: planAccent(p.planKey) + '33',
|
|
52
|
-
}"
|
|
48
|
+
:style="identityChipStyle(planAccent(p.planKey))"
|
|
53
49
|
>
|
|
54
50
|
{{ p.planKey.slice(0, 3) }}
|
|
55
51
|
</div>
|
|
@@ -415,6 +411,7 @@
|
|
|
415
411
|
import { resolvePlans, type ResolvedPlan } from '../../client/resolve-plans.js';
|
|
416
412
|
import { computed, ref } from 'vue';
|
|
417
413
|
import type { PlanRow, PlanVersionRow } from '@saasicat/types';
|
|
414
|
+
import { identityAccentFor, identityChipStyle } from '../../client/identity-accents.js';
|
|
418
415
|
import { formatMessage } from '../../client/i18n/format.js';
|
|
419
416
|
import { formatCurrency } from '../../client/i18n/currency.js';
|
|
420
417
|
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
@@ -456,24 +453,12 @@ const common = useSaMessages('common');
|
|
|
456
453
|
// `clearable` emits null, not '' — see Quasar's use-field clearValue().
|
|
457
454
|
const search = ref<string | null>('');
|
|
458
455
|
|
|
459
|
-
const DEFAULT_ACCENTS: Record<string, string> = {
|
|
460
|
-
STARTER: '#64748b',
|
|
461
|
-
STANDARD: '#2563eb',
|
|
462
|
-
PRO: '#7c3aed',
|
|
463
|
-
PROFESSIONAL: '#7c3aed',
|
|
464
|
-
BUSINESS: '#0ea5e9',
|
|
465
|
-
ENTERPRISE: '#0f766e',
|
|
466
|
-
BASIC: '#475569',
|
|
467
|
-
};
|
|
468
|
-
const FALLBACK_ACCENTS = ['#2563eb', '#7c3aed', '#0f766e', '#f59e0b', '#0ea5e9', '#ef4444'];
|
|
469
|
-
|
|
470
456
|
function planAccent(planKey: string): string {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return FALLBACK_ACCENTS[idx % FALLBACK_ACCENTS.length] ?? FALLBACK_ACCENTS[0]!;
|
|
457
|
+
return identityAccentFor(
|
|
458
|
+
planKey,
|
|
459
|
+
props.planAccents,
|
|
460
|
+
props.plans.findIndex((p) => p.planKey === planKey),
|
|
461
|
+
);
|
|
477
462
|
}
|
|
478
463
|
|
|
479
464
|
const resolvedPlans = computed(() =>
|
|
@@ -566,7 +551,13 @@ function hasAnyPublished(row: ResolvedPlan<PlanRow, PlanVersionRow>): boolean {
|
|
|
566
551
|
background: var(--sa-color-bg-surface);
|
|
567
552
|
border: 1px solid var(--sa-color-border);
|
|
568
553
|
border-radius: 10px;
|
|
569
|
-
overflow: hidden
|
|
554
|
+
/* Scrolls rather than clips. `overflow: hidden` was here to keep the corner
|
|
555
|
+
* radius, and it also cut the six-column grid off below ~790px — six data
|
|
556
|
+
* columns squeezed into a phone are not readable anyway, so the honest
|
|
557
|
+
* answer is the same as for the tab bar: let it scroll. `hidden` on the
|
|
558
|
+
* cross axis keeps the radius doing its job. */
|
|
559
|
+
overflow-x: auto;
|
|
560
|
+
overflow-y: hidden;
|
|
570
561
|
}
|
|
571
562
|
.sa-plan-list-toolbar {
|
|
572
563
|
display: flex;
|
|
@@ -590,6 +581,10 @@ function hasAnyPublished(row: ResolvedPlan<PlanRow, PlanVersionRow>): boolean {
|
|
|
590
581
|
.sa-plan-list-list {
|
|
591
582
|
display: grid;
|
|
592
583
|
grid-template-columns: 1.6fr 0.9fr 0.7fr 0.9fr 1.4fr 160px;
|
|
584
|
+
/* So the scroll above has something to scroll: without a content floor the
|
|
585
|
+
* tracks shrink to the container and the cells overflow individually,
|
|
586
|
+
* which is the clipped state rather than a scrollable one. */
|
|
587
|
+
min-width: max-content;
|
|
593
588
|
/* Stretch, not centre. Every row is `display: contents`, so its cells are
|
|
594
589
|
* grid items in their own right — and a centred item is only as tall as its
|
|
595
590
|
* own content. The row's hover background is painted per cell, so the band
|
|
@@ -272,10 +272,7 @@
|
|
|
272
272
|
<span
|
|
273
273
|
v-if="hasFeature(p, fKey)"
|
|
274
274
|
class="pm-check"
|
|
275
|
-
:style="
|
|
276
|
-
background: planAccent(p.planKey) + '15',
|
|
277
|
-
color: planAccent(p.planKey),
|
|
278
|
-
}"
|
|
275
|
+
:style="identityChipStyle(planAccent(p.planKey))"
|
|
279
276
|
>
|
|
280
277
|
<svg
|
|
281
278
|
width="12"
|
|
@@ -323,10 +320,7 @@
|
|
|
323
320
|
<span
|
|
324
321
|
v-if="hasBundle(p, bKey)"
|
|
325
322
|
class="pm-check"
|
|
326
|
-
:style="
|
|
327
|
-
background: planAccent(p.planKey) + '15',
|
|
328
|
-
color: planAccent(p.planKey),
|
|
329
|
-
}"
|
|
323
|
+
:style="identityChipStyle(planAccent(p.planKey))"
|
|
330
324
|
>
|
|
331
325
|
<svg
|
|
332
326
|
width="12"
|
|
@@ -384,6 +378,7 @@ import { computed } from 'vue';
|
|
|
384
378
|
*/
|
|
385
379
|
const NBSP = '\u00A0';
|
|
386
380
|
import type { PlanRow, PlanVersionRow } from '@saasicat/types';
|
|
381
|
+
import { identityAccentFor, identityChipStyle } from '../../client/identity-accents.js';
|
|
387
382
|
import { formatMessage } from '../../client/i18n/format.js';
|
|
388
383
|
import { formatCurrency } from '../../client/i18n/currency.js';
|
|
389
384
|
import { useSaMessages, useSuperAdminI18n } from '../../vue/use-super-admin-i18n.js';
|
|
@@ -477,24 +472,12 @@ function validFromLabel(validFrom: string): string {
|
|
|
477
472
|
return formatMessage(msg.value.matrix.validFrom, { date: validFrom.slice(0, 10) });
|
|
478
473
|
}
|
|
479
474
|
|
|
480
|
-
const DEFAULT_ACCENTS: Record<string, string> = {
|
|
481
|
-
STARTER: '#64748b',
|
|
482
|
-
STANDARD: '#2563eb',
|
|
483
|
-
PRO: '#7c3aed',
|
|
484
|
-
PROFESSIONAL: '#7c3aed',
|
|
485
|
-
BUSINESS: '#0ea5e9',
|
|
486
|
-
ENTERPRISE: '#0f766e',
|
|
487
|
-
BASIC: '#475569',
|
|
488
|
-
};
|
|
489
|
-
const FALLBACK_ACCENTS = ['#2563eb', '#7c3aed', '#0f766e', '#f59e0b', '#0ea5e9', '#ef4444'];
|
|
490
|
-
|
|
491
475
|
function planAccent(planKey: string): string {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return FALLBACK_ACCENTS[idx % FALLBACK_ACCENTS.length] ?? FALLBACK_ACCENTS[0]!;
|
|
476
|
+
return identityAccentFor(
|
|
477
|
+
planKey,
|
|
478
|
+
props.planAccents,
|
|
479
|
+
props.plans.findIndex((p) => p.planKey === planKey),
|
|
480
|
+
);
|
|
498
481
|
}
|
|
499
482
|
|
|
500
483
|
const resolvedPlans = computed<ResolvedPlan[]>(() =>
|
|
@@ -760,6 +760,10 @@ function emitSave(): void {
|
|
|
760
760
|
display: flex;
|
|
761
761
|
flex-direction: column;
|
|
762
762
|
box-sizing: border-box;
|
|
763
|
+
/* The editor reflows on ITS OWN width, not the window's — see the
|
|
764
|
+
`@container` rules at the end of this block. */
|
|
765
|
+
container-type: inline-size;
|
|
766
|
+
container-name: pve;
|
|
763
767
|
/* A full view rather than a modal: the columns stretch to the content area
|
|
764
768
|
AdminPage already sizes. */
|
|
765
769
|
height: 100%;
|
|
@@ -1546,13 +1550,28 @@ function emitSave(): void {
|
|
|
1546
1550
|
display: inline-flex;
|
|
1547
1551
|
}
|
|
1548
1552
|
|
|
1549
|
-
/* ── Responsive
|
|
1550
|
-
|
|
1553
|
+
/* ── Responsive ────────────────────────────────────────────────────
|
|
1554
|
+
*
|
|
1555
|
+
* On the CONTAINER's width, not the viewport's, because the viewport is not
|
|
1556
|
+
* what this grid has to fit into. The admin drawer is 240px and collapsible, so
|
|
1557
|
+
* a viewport threshold is wrong in one of its two states by construction — and
|
|
1558
|
+
* with the drawer open at 1024–1100px the old viewport rule left 784–860px for
|
|
1559
|
+
* `320px 1fr 360px`, squeezing the middle column to 104–180px and clipping its
|
|
1560
|
+
* form controls behind `overflow: hidden`.
|
|
1561
|
+
*
|
|
1562
|
+
* The thresholds are the widths the columns actually need:
|
|
1563
|
+
* 1060px → 320 + 360 fixed leaves ≥ 380px for the basket
|
|
1564
|
+
* 780px → below this the three-column form is not usable at all
|
|
1565
|
+
*
|
|
1566
|
+
* These are container queries, so they are not viewport breakpoints and the
|
|
1567
|
+
* Quasar-band rule does not reach them; `@container` needs no newer browser
|
|
1568
|
+
* than the theme already requires. */
|
|
1569
|
+
@container pve (max-width: 1060px) {
|
|
1551
1570
|
.pve-body {
|
|
1552
1571
|
grid-template-columns: 320px 1fr 360px;
|
|
1553
1572
|
}
|
|
1554
1573
|
}
|
|
1555
|
-
@
|
|
1574
|
+
@container pve (max-width: 780px) {
|
|
1556
1575
|
.pve-body {
|
|
1557
1576
|
grid-template-columns: 1fr;
|
|
1558
1577
|
overflow-y: auto;
|
|
@@ -754,7 +754,7 @@ const classifyDiff = computed(() => props.classifyDiff);
|
|
|
754
754
|
grid-template-columns: minmax(280px, 360px) 1fr;
|
|
755
755
|
gap: 18px;
|
|
756
756
|
}
|
|
757
|
-
@media (max-width:
|
|
757
|
+
@media (max-width: 1023.98px) {
|
|
758
758
|
.sa-bd-grid {
|
|
759
759
|
grid-template-columns: 1fr;
|
|
760
760
|
}
|