@umituz/web-dashboard 3.2.4 → 4.1.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/README.md +228 -14
- package/package.json +23 -30
- package/src/domains/analytics/components/AnalyticsCard.tsx +43 -42
- package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
- package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
- package/src/domains/analytics/components/MetricCard.tsx +29 -87
- package/src/domains/analytics/hooks/index.ts +7 -1
- package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
- package/src/domains/analytics/index.ts +11 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
- package/src/domains/analytics/services/PerformanceService.ts +117 -69
- package/src/domains/analytics/types/analytics.ts +20 -6
- package/src/domains/analytics/types/index.ts +3 -0
- package/src/domains/analytics/utils/analytics.test.ts +183 -0
- package/src/domains/analytics/utils/analytics.ts +26 -9
- package/src/domains/analytics/utils/i18nKeys.ts +40 -0
- package/src/domains/analytics/utils/index.ts +2 -0
- package/src/domains/auth/components/AuthLayout.tsx +63 -54
- package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
- package/src/domains/auth/components/LoginForm.tsx +167 -226
- package/src/domains/auth/components/RegisterForm.tsx +202 -272
- package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
- package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
- package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
- package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
- package/src/domains/auth/components/shared/index.ts +20 -0
- package/src/domains/auth/hooks/index.ts +1 -0
- package/src/domains/auth/hooks/useAuth.ts +151 -232
- package/src/domains/auth/index.ts +8 -4
- package/src/domains/auth/types/auth.ts +26 -5
- package/src/domains/auth/types/index.ts +1 -0
- package/src/domains/auth/utils/auth.test.ts +182 -0
- package/src/domains/auth/utils/auth.ts +71 -130
- package/src/domains/auth/utils/i18nKeys.ts +127 -0
- package/src/domains/auth/utils/index.ts +7 -1
- package/src/domains/auth/utils/secureToken.ts +33 -0
- package/src/domains/auth/utils/validationKeys.ts +23 -0
- package/src/domains/calendar/hooks/useCalendar.ts +145 -151
- package/src/domains/calendar/index.ts +0 -1
- package/src/domains/calendar/services/CalendarService.ts +122 -179
- package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
- package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
- package/src/domains/calendar/utils/index.ts +33 -30
- package/src/domains/layouts/components/BrandLogo.tsx +12 -9
- package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
- package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
- package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
- package/src/domains/layouts/types/layout.ts +7 -1
- package/src/domains/layouts/types/user.ts +3 -1
- package/src/domains/modules/build.test.tsx +97 -0
- package/src/domains/modules/build.tsx +71 -0
- package/src/domains/modules/index.ts +2 -0
- package/src/domains/modules/types.ts +29 -0
- package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
- package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
- package/src/domains/onboarding/components/PlanStep.tsx +156 -123
- package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
- package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
- package/src/domains/onboarding/components/StepProgress.tsx +62 -0
- package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
- package/src/domains/onboarding/components/index.ts +4 -0
- package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
- package/src/domains/onboarding/index.ts +1 -0
- package/src/domains/onboarding/types/onboarding.ts +57 -17
- package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
- package/src/domains/onboarding/utils/index.ts +2 -0
- package/src/domains/permissions/PermissionsContext.tsx +103 -0
- package/src/domains/permissions/hasPermission.test.ts +50 -0
- package/src/domains/permissions/hasPermission.ts +38 -0
- package/src/domains/permissions/index.ts +19 -0
- package/src/domains/permissions/types.ts +35 -0
- package/src/domains/permissions/utils/i18nKeys.ts +11 -0
- package/src/domains/session/DemoLogin.tsx +50 -0
- package/src/domains/session/OrgSwitcher.tsx +110 -0
- package/src/domains/session/SessionContext.tsx +150 -0
- package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
- package/src/domains/session/index.ts +18 -0
- package/src/domains/session/types.ts +47 -0
- package/src/domains/session/utils/i18nKeys.ts +12 -0
- package/src/domains/settings/components/AuditLogList.tsx +59 -0
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
- package/src/domains/settings/components/SettingsLayout.tsx +75 -60
- package/src/domains/settings/components/SettingsSection.tsx +35 -22
- package/src/domains/settings/components/UserManagementTable.tsx +69 -0
- package/src/domains/settings/components/index.ts +10 -0
- package/src/domains/settings/hooks/useSettings.ts +77 -52
- package/src/domains/settings/types/settings.ts +2 -0
- package/src/domains/settings/utils/i18nKeys.ts +18 -0
- package/src/domains/settings/utils/index.ts +1 -0
- package/src/domains/settings/utils/settings.ts +11 -4
- package/src/i18n/I18nContext.tsx +33 -0
- package/src/i18n/defaultT.test.ts +43 -0
- package/src/i18n/defaultT.ts +34 -0
- package/src/i18n/index.ts +7 -0
- package/src/i18n/types.ts +13 -0
- package/src/index.ts +1 -7
- package/src/primitives/Button.tsx +24 -0
- package/src/primitives/Checkbox.tsx +17 -0
- package/src/primitives/Input.tsx +23 -0
- package/src/primitives/Skeleton.tsx +6 -0
- package/src/primitives/cn.ts +10 -0
- package/src/primitives/index.ts +23 -0
- package/src/styles/dashboard.css +2731 -0
- package/src/styles/tokens.contract.test.ts +91 -0
- package/src/styles/tokens.css +116 -0
- package/src/domains/billing/components/BillingLayout.tsx +0 -63
- package/src/domains/billing/components/BillingPage.tsx +0 -49
- package/src/domains/billing/components/BillingPortal.tsx +0 -198
- package/src/domains/billing/components/InvoiceCard.tsx +0 -143
- package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
- package/src/domains/billing/components/PlanComparison.tsx +0 -199
- package/src/domains/billing/components/UsageCard.tsx +0 -103
- package/src/domains/billing/components/index.ts +0 -13
- package/src/domains/billing/constants/billing.ts +0 -69
- package/src/domains/billing/hooks/index.ts +0 -7
- package/src/domains/billing/hooks/useBilling.ts +0 -385
- package/src/domains/billing/index.ts +0 -73
- package/src/domains/billing/types/billing.ts +0 -342
- package/src/domains/billing/types/index.ts +0 -30
- package/src/domains/billing/utils/billing.ts +0 -360
- package/src/domains/billing/utils/index.ts +0 -31
- package/src/domains/billing/utils/polar.ts +0 -63
- package/src/domains/calendar/components/index.ts +0 -7
|
@@ -1,75 +1,100 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Settings Hooks
|
|
3
3
|
*
|
|
4
|
-
* Custom React hooks for settings functionality
|
|
4
|
+
* Custom React hooks for settings functionality.
|
|
5
|
+
* Uses a single `useReducer` so the three update paths share one
|
|
6
|
+
* well-tested tree-walking helper instead of duplicating it.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
import {
|
|
9
|
+
import { useReducer, useCallback } from "react";
|
|
8
10
|
import type { SettingsConfig, SettingsItem } from "../types/settings";
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Action discriminated union for the settings reducer.
|
|
14
|
+
*/
|
|
15
|
+
type SettingsAction =
|
|
16
|
+
| { type: 'updateItem'; sectionKey: string; itemKey: string; updates: Partial<SettingsItem> }
|
|
17
|
+
| { type: 'toggleItem'; sectionKey: string; itemKey: string }
|
|
18
|
+
| { type: 'setItemBadge'; sectionKey: string; itemKey: string; badge: number | undefined };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Apply a transform to the item identified by (sectionKey, itemKey).
|
|
22
|
+
* Single source of truth for the tree-walking logic.
|
|
23
|
+
*/
|
|
24
|
+
const mapItemInTree = (
|
|
25
|
+
config: SettingsConfig,
|
|
26
|
+
sectionKey: string,
|
|
27
|
+
itemKey: string,
|
|
28
|
+
transform: (item: SettingsItem) => SettingsItem,
|
|
29
|
+
): SettingsConfig => ({
|
|
30
|
+
...config,
|
|
31
|
+
sections: config.sections.map((section) =>
|
|
32
|
+
section.key !== sectionKey
|
|
33
|
+
? section
|
|
34
|
+
: {
|
|
35
|
+
...section,
|
|
36
|
+
items: section.items.map((item) => (item.key === itemKey ? transform(item) : item)),
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const settingsReducer = (state: SettingsConfig, action: SettingsAction): SettingsConfig => {
|
|
42
|
+
switch (action.type) {
|
|
43
|
+
case 'updateItem':
|
|
44
|
+
return mapItemInTree(state, action.sectionKey, action.itemKey, (item) => ({
|
|
45
|
+
...item,
|
|
46
|
+
...action.updates,
|
|
47
|
+
}));
|
|
48
|
+
case 'toggleItem':
|
|
49
|
+
return mapItemInTree(state, action.sectionKey, action.itemKey, (item) => ({
|
|
50
|
+
...item,
|
|
51
|
+
// Tri-state semantics: undefined is treated as enabled by default.
|
|
52
|
+
enabled: item.enabled === undefined ? false : !item.enabled,
|
|
53
|
+
}));
|
|
54
|
+
case 'setItemBadge':
|
|
55
|
+
return mapItemInTree(state, action.sectionKey, action.itemKey, (item) => ({
|
|
56
|
+
...item,
|
|
57
|
+
badge: action.badge,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Public surface of the hook. Returned callbacks are stable
|
|
64
|
+
* (no deps that change between renders).
|
|
65
|
+
*/
|
|
66
|
+
export interface UseSettingsReturn {
|
|
67
|
+
config: SettingsConfig;
|
|
68
|
+
updateItem: (sectionKey: string, itemKey: string, updates: Partial<SettingsItem>) => void;
|
|
69
|
+
toggleItem: (sectionKey: string, itemKey: string) => void;
|
|
70
|
+
setItemBadge: (sectionKey: string, itemKey: string, badge: number | undefined) => void;
|
|
71
|
+
}
|
|
72
|
+
|
|
10
73
|
/**
|
|
11
74
|
* Use Settings Hook
|
|
12
75
|
*
|
|
13
|
-
* Manages settings state and actions
|
|
14
|
-
*
|
|
15
|
-
* @param initialConfig - Initial settings configuration
|
|
16
|
-
* @returns Settings state and actions
|
|
76
|
+
* Manages settings configuration state and provides update actions.
|
|
17
77
|
*/
|
|
18
|
-
export function useSettings(initialConfig: SettingsConfig) {
|
|
19
|
-
const [config,
|
|
78
|
+
export function useSettings(initialConfig: SettingsConfig): UseSettingsReturn {
|
|
79
|
+
const [config, dispatch] = useReducer(settingsReducer, initialConfig);
|
|
20
80
|
|
|
21
81
|
const updateItem = useCallback(
|
|
22
82
|
(sectionKey: string, itemKey: string, updates: Partial<SettingsItem>) => {
|
|
23
|
-
|
|
24
|
-
...prev,
|
|
25
|
-
sections: prev.sections.map((section) =>
|
|
26
|
-
section.key === sectionKey
|
|
27
|
-
? {
|
|
28
|
-
...section,
|
|
29
|
-
items: section.items.map((item) =>
|
|
30
|
-
item.key === itemKey ? { ...item, ...updates } : item
|
|
31
|
-
),
|
|
32
|
-
}
|
|
33
|
-
: section
|
|
34
|
-
),
|
|
35
|
-
}));
|
|
83
|
+
dispatch({ type: 'updateItem', sectionKey, itemKey, updates });
|
|
36
84
|
},
|
|
37
|
-
[]
|
|
85
|
+
[],
|
|
38
86
|
);
|
|
39
87
|
|
|
40
88
|
const toggleItem = useCallback((sectionKey: string, itemKey: string) => {
|
|
41
|
-
|
|
42
|
-
...prev,
|
|
43
|
-
sections: prev.sections.map((section) =>
|
|
44
|
-
section.key === sectionKey
|
|
45
|
-
? {
|
|
46
|
-
...section,
|
|
47
|
-
items: section.items.map((item) =>
|
|
48
|
-
item.key === itemKey
|
|
49
|
-
? { ...item, enabled: item.enabled === undefined ? true : !item.enabled }
|
|
50
|
-
: item
|
|
51
|
-
),
|
|
52
|
-
}
|
|
53
|
-
: section
|
|
54
|
-
),
|
|
55
|
-
}));
|
|
89
|
+
dispatch({ type: 'toggleItem', sectionKey, itemKey });
|
|
56
90
|
}, []);
|
|
57
91
|
|
|
58
|
-
const setItemBadge = useCallback(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
...section,
|
|
65
|
-
items: section.items.map((item) =>
|
|
66
|
-
item.key === itemKey ? { ...item, badge } : item
|
|
67
|
-
),
|
|
68
|
-
}
|
|
69
|
-
: section
|
|
70
|
-
),
|
|
71
|
-
}));
|
|
72
|
-
}, []);
|
|
92
|
+
const setItemBadge = useCallback(
|
|
93
|
+
(sectionKey: string, itemKey: string, badge: number | undefined) => {
|
|
94
|
+
dispatch({ type: 'setItemBadge', sectionKey, itemKey, badge });
|
|
95
|
+
},
|
|
96
|
+
[],
|
|
97
|
+
);
|
|
73
98
|
|
|
74
99
|
return {
|
|
75
100
|
config,
|
|
@@ -20,6 +20,8 @@ export interface SettingsItem {
|
|
|
20
20
|
icon?: LucideIcon;
|
|
21
21
|
/** Route path for this setting */
|
|
22
22
|
path?: string;
|
|
23
|
+
/** Click handler when no route is available */
|
|
24
|
+
onClick?: () => void;
|
|
23
25
|
/** Whether this item is enabled */
|
|
24
26
|
enabled?: boolean;
|
|
25
27
|
/** Required permission to access */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings domain i18n keys (admin skeleton screens) — the
|
|
3
|
+
* documented string contract. Column titles and role labels come
|
|
4
|
+
* from props; only empty/denied states fall back to these keys.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const SETTINGS_ADMIN_KEYS = {
|
|
8
|
+
users: {
|
|
9
|
+
empty: "settings.users.empty",
|
|
10
|
+
},
|
|
11
|
+
matrix: {
|
|
12
|
+
granted: "settings.matrix.granted",
|
|
13
|
+
denied: "settings.matrix.denied",
|
|
14
|
+
},
|
|
15
|
+
audit: {
|
|
16
|
+
empty: "settings.audit.empty",
|
|
17
|
+
},
|
|
18
|
+
} as const;
|
|
@@ -51,13 +51,20 @@ export function filterSettingsByPermission<T extends SettingsItem>(
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Fallback route when no default or first available route can be resolved.
|
|
56
|
+
* Caller should treat this as an explicit "no route configured" signal
|
|
57
|
+
* rather than a silent success.
|
|
58
|
+
*/
|
|
59
|
+
export const SETTINGS_FALLBACK_ROUTE = "/settings";
|
|
60
|
+
|
|
54
61
|
/**
|
|
55
62
|
* Get default route from settings config
|
|
56
63
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
64
|
+
* Returns null when no default route is resolvable. The caller is
|
|
65
|
+
* responsible for handling that case (e.g. redirect, error).
|
|
59
66
|
*/
|
|
60
|
-
export function getSettingsDefaultRoute(config: SettingsConfig): string {
|
|
67
|
+
export function getSettingsDefaultRoute(config: SettingsConfig): string | null {
|
|
61
68
|
if (config.defaultRoute) return config.defaultRoute;
|
|
62
69
|
|
|
63
70
|
for (const section of config.sections) {
|
|
@@ -65,7 +72,7 @@ export function getSettingsDefaultRoute(config: SettingsConfig): string {
|
|
|
65
72
|
if (firstEnabled?.path) return firstEnabled.path;
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
return
|
|
75
|
+
return null;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
/**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard i18n injection.
|
|
3
|
+
*
|
|
4
|
+
* The package ships no i18n runtime. Hosts that have one wrap their
|
|
5
|
+
* app once: `<DashboardI18nProvider t={i18n.t}>` — the signature
|
|
6
|
+
* already matches i18next (and the fleet's custom `t()` helpers).
|
|
7
|
+
* Without a provider, `useT()` falls back to `defaultT`, which
|
|
8
|
+
* humanizes catalog keys into readable English.
|
|
9
|
+
*
|
|
10
|
+
* Components consume only `useT()`; the key catalogs (i18nKeys.ts)
|
|
11
|
+
* remain the documented string contract.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createContext, useContext, type ReactNode } from "react";
|
|
15
|
+
import { defaultT } from "./defaultT";
|
|
16
|
+
import type { TranslateFn } from "./types";
|
|
17
|
+
|
|
18
|
+
export interface DashboardI18nProviderProps {
|
|
19
|
+
/** Host translation function (e.g. i18next's `t`). */
|
|
20
|
+
t: TranslateFn;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const I18nContext = createContext<TranslateFn | null>(null);
|
|
25
|
+
|
|
26
|
+
export const DashboardI18nProvider = ({
|
|
27
|
+
t,
|
|
28
|
+
children,
|
|
29
|
+
}: DashboardI18nProviderProps) => (
|
|
30
|
+
<I18nContext.Provider value={t}>{children}</I18nContext.Provider>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const useT = (): TranslateFn => useContext(I18nContext) ?? defaultT;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defaultT } from "./defaultT";
|
|
3
|
+
|
|
4
|
+
describe("defaultT", () => {
|
|
5
|
+
it("humanizes the last key segment", () => {
|
|
6
|
+
expect(defaultT("auth.login.welcomeBack")).toBe("Welcome back");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("splits camelCase into words", () => {
|
|
10
|
+
expect(defaultT("auth.passwordStrength.passwordsDoNotMatch")).toBe(
|
|
11
|
+
"Passwords do not match",
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("splits snake_case and kebab-case segments", () => {
|
|
16
|
+
expect(defaultT("common.error_code")).toBe("Error code");
|
|
17
|
+
expect(defaultT("common.error-code")).toBe("Error code");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("keeps digits attached to their word", () => {
|
|
21
|
+
expect(defaultT("analytics.common.last7Days")).toBe("Last7 days");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("interpolates {{param}} placeholders the text carries", () => {
|
|
25
|
+
expect(defaultT("custom.{{brand}} console", { brand: "Acme" })).toBe(
|
|
26
|
+
"Acme console",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("ignores params the humanized text cannot place", () => {
|
|
31
|
+
expect(defaultT("auth.login.signInTo", { brand: "Acme" })).toBe(
|
|
32
|
+
"Sign in to",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("returns the raw key when it has no segments", () => {
|
|
37
|
+
expect(defaultT("")).toBe("");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("handles single-segment keys", () => {
|
|
41
|
+
expect(defaultT("settings")).toBe("Settings");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default translator — the zero-config fallback when no host i18n
|
|
3
|
+
* is provided. Humanizes catalog keys into sentence case
|
|
4
|
+
* ("auth.login.welcomeBack" → "Welcome back") and interpolates
|
|
5
|
+
* `{{param}}` placeholders when the humanized text carries one.
|
|
6
|
+
* Params the text cannot place are ignored — real interpolation is
|
|
7
|
+
* the host runtime's job via the DashboardI18nProvider bridge.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { TranslateFn } from "./types";
|
|
11
|
+
|
|
12
|
+
/** Split a key segment into words: camelCase, snake_case, kebab-case. */
|
|
13
|
+
const WORD_SPLIT = /[._\-]+|(?<=[a-z0-9])(?=[A-Z])/g;
|
|
14
|
+
|
|
15
|
+
const humanizeSegment = (segment: string): string => {
|
|
16
|
+
const words = segment.split(WORD_SPLIT).filter(Boolean);
|
|
17
|
+
if (words.length === 0) return segment;
|
|
18
|
+
const sentence = words.join(" ").toLowerCase();
|
|
19
|
+
return sentence.charAt(0).toUpperCase() + sentence.slice(1);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Replace `{{param}}` placeholders with param values. */
|
|
23
|
+
const interpolate = (text: string, params?: Record<string, unknown>): string => {
|
|
24
|
+
if (!params) return text;
|
|
25
|
+
return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name: string) =>
|
|
26
|
+
name in params ? String(params[name]) : match,
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const defaultT: TranslateFn = (key, params) => {
|
|
31
|
+
const segments = key.split(".").filter(Boolean);
|
|
32
|
+
const source = segments.length > 0 ? segments[segments.length - 1] : key;
|
|
33
|
+
return interpolate(humanizeSegment(source), params);
|
|
34
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation function contract.
|
|
3
|
+
*
|
|
4
|
+
* Keyed by the package's i18n catalog constants (e.g. AUTH_KEYS);
|
|
5
|
+
* `params` feeds `{{placeholder}}` interpolation. The signature is
|
|
6
|
+
* deliberately compatible with i18next's `t` so hosts can bridge
|
|
7
|
+
* their own setup: `<DashboardI18nProvider t={i18n.t}>`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type TranslateFn = (
|
|
11
|
+
key: string,
|
|
12
|
+
params?: Record<string, unknown>,
|
|
13
|
+
) => string;
|
package/src/index.ts
CHANGED
|
@@ -7,15 +7,9 @@
|
|
|
7
7
|
* @version 3.0.0
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Core domains with complete public API
|
|
11
11
|
export * from './domains/layouts';
|
|
12
12
|
export * from './domains/settings';
|
|
13
|
-
export * from './domains/billing';
|
|
14
|
-
|
|
15
|
-
// Domains - using selective exports to avoid conflicts
|
|
16
|
-
export * from './domains/layouts';
|
|
17
|
-
export * from './domains/settings';
|
|
18
|
-
export * from './domains/billing';
|
|
19
13
|
export * from './domains/calendar';
|
|
20
14
|
export {
|
|
21
15
|
OnboardingWizard,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Button — delegates to the design system.
|
|
3
|
+
* Dashboard surface: variants default/destructive/outline/ghost,
|
|
4
|
+
* sizes default/sm/icon. Call-site API unchanged.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { forwardRef } from 'react';
|
|
8
|
+
import { Button as DSButton } from '@umituz/web-design-system/atoms';
|
|
9
|
+
|
|
10
|
+
export type ButtonVariant = 'default' | 'destructive' | 'outline' | 'ghost';
|
|
11
|
+
export type ButtonSize = 'default' | 'sm' | 'icon';
|
|
12
|
+
|
|
13
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
14
|
+
variant?: ButtonVariant;
|
|
15
|
+
size?: ButtonSize;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
19
|
+
({ variant = 'default', size = 'default', ...props }, ref) => (
|
|
20
|
+
<DSButton ref={ref} variant={variant} size={size} {...props} />
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
Button.displayName = 'Button';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox — delegates to the design system (native + onCheckedChange).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { forwardRef } from 'react';
|
|
6
|
+
import { Checkbox as DSCheckbox } from '@umituz/web-design-system/atoms';
|
|
7
|
+
|
|
8
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
9
|
+
checked?: boolean;
|
|
10
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => (
|
|
14
|
+
<DSCheckbox ref={ref} {...props} />
|
|
15
|
+
));
|
|
16
|
+
|
|
17
|
+
Checkbox.displayName = 'Checkbox';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input — delegates to the design system.
|
|
3
|
+
* Native `size` (character width) has no DS counterpart; it maps to a
|
|
4
|
+
* `ch`-based width so the original prop API keeps working.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { forwardRef } from 'react';
|
|
8
|
+
import { Input as DSInput } from '@umituz/web-design-system/atoms';
|
|
9
|
+
|
|
10
|
+
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
|
11
|
+
|
|
12
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
13
|
+
({ type = 'text', size: nativeSize, style, ...props }, ref) => (
|
|
14
|
+
<DSInput
|
|
15
|
+
ref={ref}
|
|
16
|
+
type={type}
|
|
17
|
+
{...props}
|
|
18
|
+
style={nativeSize != null ? { width: `${nativeSize}ch`, ...style } : style}
|
|
19
|
+
/>
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
Input.displayName = 'Input';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cn — conditional className join, delegated to the design system.
|
|
3
|
+
*
|
|
4
|
+
* The design system's `cn` (clsx + tailwind-merge) understands both the
|
|
5
|
+
* `udb-*` vocabulary (unknown classes pass through untouched) and real
|
|
6
|
+
* Tailwind utilities a host may pass, resolving conflicts by last-wins.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { cn } from '@umituz/web-design-system/utils/cn';
|
|
10
|
+
export type { ClassValue } from '@umituz/web-design-system/utils/cn';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local primitives — the package's only UI atoms.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the `@umituz/web-design-system` dependency (v3) with a
|
|
5
|
+
* camp-agnostic set: `udb-*` classes + `--u-*` tokens, no utility
|
|
6
|
+
* framework, no cva, no radix. Import as `@umituz/web-dashboard/primitives`
|
|
7
|
+
* or relatively from inside the package.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { cn } from './cn';
|
|
11
|
+
export type { ClassValue } from './cn';
|
|
12
|
+
|
|
13
|
+
export { Button } from './Button';
|
|
14
|
+
export type { ButtonProps, ButtonVariant, ButtonSize } from './Button';
|
|
15
|
+
|
|
16
|
+
export { Input } from './Input';
|
|
17
|
+
export type { InputProps } from './Input';
|
|
18
|
+
|
|
19
|
+
export { Checkbox } from './Checkbox';
|
|
20
|
+
export type { CheckboxProps } from './Checkbox';
|
|
21
|
+
|
|
22
|
+
export { Skeleton } from './Skeleton';
|
|
23
|
+
export type { SkeletonProps } from './Skeleton';
|