@djangocfg/layouts 2.1.425 → 2.1.427

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 (66) hide show
  1. package/package.json +15 -17
  2. package/src/layouts/AppLayout/AppLayout.tsx +0 -7
  3. package/src/layouts/AppLayout/BaseApp.tsx +29 -52
  4. package/src/layouts/AuthLayout/components/steps/SetupStep/SetupLoading.tsx +6 -4
  5. package/src/layouts/PrivateLayout/PrivateLayout.tsx +7 -3
  6. package/src/layouts/PrivateLayout/components/PrivateContent.tsx +5 -1
  7. package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +105 -70
  8. package/src/layouts/PrivateLayout/types.ts +8 -0
  9. package/src/layouts/PublicLayout/components/UserMenu.tsx +68 -113
  10. package/src/layouts/PublicLayout/navbars/MinimalNavbar/MinimalNavbar.tsx +0 -6
  11. package/src/layouts/PublicLayout/navbars/MinimalNavbar/index.ts +1 -1
  12. package/src/layouts/SettingsLayout/README.md +258 -0
  13. package/src/layouts/SettingsLayout/SettingsDialog.tsx +101 -0
  14. package/src/layouts/SettingsLayout/SettingsForm.tsx +100 -0
  15. package/src/layouts/SettingsLayout/components/ApiKeySection/ApiKeySection.tsx +189 -0
  16. package/src/layouts/SettingsLayout/components/SettingsNav.tsx +71 -0
  17. package/src/layouts/SettingsLayout/components/SettingsNavItem.tsx +57 -0
  18. package/src/layouts/SettingsLayout/components/SettingsPanel.tsx +48 -0
  19. package/src/layouts/SettingsLayout/components/SettingsSearch.tsx +50 -0
  20. package/src/layouts/SettingsLayout/components/SettingsShell.tsx +77 -0
  21. package/src/layouts/SettingsLayout/components/SettingsTabs.tsx +56 -0
  22. package/src/layouts/{ProfileLayout → SettingsLayout}/components/TwoFactorSection/TwoFactorSection.tsx +84 -130
  23. package/src/layouts/SettingsLayout/components/index.ts +6 -0
  24. package/src/layouts/SettingsLayout/context/SettingsContext.tsx +122 -0
  25. package/src/layouts/SettingsLayout/context/index.ts +2 -0
  26. package/src/layouts/SettingsLayout/hooks/index.ts +12 -0
  27. package/src/layouts/SettingsLayout/hooks/useProfileSave.ts +95 -0
  28. package/src/layouts/SettingsLayout/hooks/useSettingsDialog.ts +52 -0
  29. package/src/layouts/SettingsLayout/hooks/useSettingsSections.ts +123 -0
  30. package/src/layouts/SettingsLayout/hooks/useSettingsUrl.ts +140 -0
  31. package/src/layouts/SettingsLayout/index.ts +67 -0
  32. package/src/layouts/SettingsLayout/sections/AccountSection.tsx +100 -0
  33. package/src/layouts/SettingsLayout/sections/ApiKeysSection.tsx +15 -0
  34. package/src/layouts/SettingsLayout/sections/DeleteAccountRow.tsx +57 -0
  35. package/src/layouts/SettingsLayout/sections/PreferencesRows.tsx +43 -0
  36. package/src/layouts/SettingsLayout/sections/SecuritySection.tsx +15 -0
  37. package/src/layouts/SettingsLayout/sections/builtins.tsx +77 -0
  38. package/src/layouts/SettingsLayout/sections/index.ts +8 -0
  39. package/src/layouts/SettingsLayout/store.ts +47 -0
  40. package/src/layouts/SettingsLayout/types.ts +107 -0
  41. package/src/layouts/index.ts +1 -1
  42. package/src/layouts/types/index.ts +0 -1
  43. package/src/layouts/types/layout.types.ts +0 -4
  44. package/src/layouts/ProfileLayout/ProfileDialog/ProfileDialog.tsx +0 -56
  45. package/src/layouts/ProfileLayout/ProfileDialog/index.ts +0 -4
  46. package/src/layouts/ProfileLayout/ProfileDialog/store.ts +0 -51
  47. package/src/layouts/ProfileLayout/ProfileForm/context.tsx +0 -123
  48. package/src/layouts/ProfileLayout/ProfileForm/index.tsx +0 -147
  49. package/src/layouts/ProfileLayout/README.md +0 -150
  50. package/src/layouts/ProfileLayout/components/ActionButton.tsx +0 -38
  51. package/src/layouts/ProfileLayout/components/ApiKeySection/ApiKeySection.tsx +0 -197
  52. package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +0 -44
  53. package/src/layouts/ProfileLayout/components/EditableField.tsx +0 -128
  54. package/src/layouts/ProfileLayout/components/PreferencesSection.tsx +0 -56
  55. package/src/layouts/ProfileLayout/components/ProfileHeader.tsx +0 -110
  56. package/src/layouts/ProfileLayout/components/ProfileTab.tsx +0 -35
  57. package/src/layouts/ProfileLayout/components/Section.tsx +0 -22
  58. package/src/layouts/ProfileLayout/components/index.ts +0 -11
  59. package/src/layouts/ProfileLayout/hooks/index.ts +0 -2
  60. package/src/layouts/ProfileLayout/hooks/useProfileTabs.ts +0 -56
  61. package/src/layouts/ProfileLayout/index.ts +0 -8
  62. package/src/layouts/ProfileLayout/types.ts +0 -48
  63. /package/src/layouts/{ProfileLayout → SettingsLayout}/components/ApiKeySection/context.tsx +0 -0
  64. /package/src/layouts/{ProfileLayout → SettingsLayout}/components/ApiKeySection/index.ts +0 -0
  65. /package/src/layouts/{ProfileLayout → SettingsLayout}/components/AvatarSection.tsx +0 -0
  66. /package/src/layouts/{ProfileLayout → SettingsLayout}/components/TwoFactorSection/index.ts +0 -0
@@ -1,110 +0,0 @@
1
- 'use client';
2
-
3
- import { LogOut, MoreHorizontal, Trash2 } from 'lucide-react';
4
- import moment from 'moment';
5
- import React, { useCallback } from 'react';
6
-
7
- import { useAuth, useDeleteAccount } from '@djangocfg/api/auth';
8
- import { useAppT } from '@djangocfg/i18n';
9
- import {
10
- Button,
11
- DropdownMenu,
12
- DropdownMenuContent,
13
- DropdownMenuItem,
14
- DropdownMenuSeparator,
15
- DropdownMenuTrigger,
16
- } from '@djangocfg/ui-core/components';
17
-
18
- import { AvatarSection } from './AvatarSection';
19
- import { useProfileContext } from '../ProfileForm/context';
20
- import type { ProfileSlots } from '../types';
21
-
22
- interface ProfileHeaderProps {
23
- slots?: ProfileSlots;
24
- enableDeleteAccount?: boolean;
25
- }
26
-
27
- export const ProfileHeader: React.FC<ProfileHeaderProps> = ({ slots, enableDeleteAccount }) => {
28
- const { labels, onLogout } = useProfileContext();
29
- const { user, logout } = useAuth();
30
- const { deleteAccount } = useDeleteAccount();
31
- const t = useAppT();
32
-
33
- const handleDeleteAccount = useCallback(async () => {
34
- const confirmationWord = t('layouts.profilePage.confirmationWord');
35
- const value = await window.dialog.prompt({
36
- title: t('layouts.profilePage.deleteAccountTitle'),
37
- message: t('layouts.profilePage.deleteAccountDesc'),
38
- placeholder: confirmationWord,
39
- confirmText: t('layouts.profilePage.deleteAccount'),
40
- cancelText: t('layouts.profilePage.cancel'),
41
- variant: 'destructive',
42
- });
43
- if (value?.toUpperCase() !== confirmationWord.toUpperCase()) return;
44
- const result = await deleteAccount();
45
- if (result.success) logout();
46
- }, [t, deleteAccount, logout]);
47
-
48
- if (!user) return null;
49
-
50
- const displayName = user.full_name || user.display_username || user.email;
51
- const memberSince = user.date_joined
52
- ? moment.utc(user.date_joined).local().format('MMMM YYYY')
53
- : null;
54
-
55
- const badge = slots?.headerBadge ?? null;
56
- const menuItems = slots?.headerMenuItems ?? null;
57
- const headerAfter = slots?.headerAfter ?? null;
58
-
59
- return (
60
- <div className="pb-4 md:pb-6 border-b mb-2">
61
- <div className="flex items-center gap-3 md:gap-4">
62
- <AvatarSection />
63
-
64
- <div className="flex-1 min-w-0">
65
- <div className="flex items-center gap-2 flex-wrap">
66
- <h1 className="text-lg md:text-xl font-semibold truncate">{displayName}</h1>
67
- {badge}
68
- </div>
69
- <p className="text-sm text-muted-foreground truncate">{user.email}</p>
70
- {memberSince && (
71
- <p className="text-xs text-muted-foreground/60 mt-0.5">
72
- Member since {memberSince}
73
- </p>
74
- )}
75
- </div>
76
-
77
- <DropdownMenu>
78
- <DropdownMenuTrigger asChild>
79
- <Button variant="ghost" size="icon" className="flex-shrink-0 rounded-full">
80
- <MoreHorizontal className="w-4 h-4" />
81
- </Button>
82
- </DropdownMenuTrigger>
83
- <DropdownMenuContent align="end" className="w-48">
84
- <DropdownMenuItem onClick={onLogout} className="gap-2">
85
- <LogOut className="w-4 h-4" />
86
- {labels.signOut}
87
- </DropdownMenuItem>
88
-
89
- {menuItems && <><DropdownMenuSeparator />{menuItems}</>}
90
-
91
- {enableDeleteAccount && (
92
- <>
93
- <DropdownMenuSeparator />
94
- <DropdownMenuItem
95
- onClick={handleDeleteAccount}
96
- className="gap-2 text-destructive focus:text-destructive"
97
- >
98
- <Trash2 className="w-4 h-4" />
99
- {labels.deleteAccount}
100
- </DropdownMenuItem>
101
- </>
102
- )}
103
- </DropdownMenuContent>
104
- </DropdownMenu>
105
- </div>
106
-
107
- {headerAfter && <div className="mt-4">{headerAfter}</div>}
108
- </div>
109
- );
110
- };
@@ -1,35 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import { useAuth } from '@djangocfg/api/auth';
6
-
7
- import { EditableField, PreferencesSection, Section } from '.';
8
- import { useProfileContext } from '../ProfileForm/context';
9
-
10
- export const ProfileTab: React.FC = () => {
11
- const { labels, onFieldSave } = useProfileContext();
12
- const { user } = useAuth();
13
- if (!user) return null;
14
-
15
- return (
16
- <div className="space-y-6 pt-4">
17
- <div className="grid grid-cols-1 md:grid-cols-2 gap-3 md:gap-6">
18
- <Section title={labels.personalInfo}>
19
- <EditableField label={labels.firstName} value={user.first_name || ''} placeholder={labels.addFirstName} onSave={(v) => onFieldSave('first_name', v)} />
20
- <EditableField label={labels.lastName} value={user.last_name || ''} placeholder={labels.addLastName} onSave={(v) => onFieldSave('last_name', v)} />
21
- <EditableField label={labels.phone} value={user.phone || ''} placeholder={labels.addPhone} onSave={(v) => onFieldSave('phone', v)} type="phone" />
22
- </Section>
23
-
24
- <Section title={labels.work}>
25
- <EditableField label={labels.company} value={user.company || ''} placeholder={labels.addCompany} onSave={(v) => onFieldSave('company', v)} />
26
- <EditableField label={labels.position} value={user.position || ''} placeholder={labels.addPosition} onSave={(v) => onFieldSave('position', v)} />
27
- </Section>
28
- </div>
29
-
30
- <Section title={labels.preferences ?? 'Preferences'}>
31
- <PreferencesSection />
32
- </Section>
33
- </div>
34
- );
35
- };
@@ -1,22 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import { cn } from '@djangocfg/ui-core/lib';
6
-
7
- interface SectionProps {
8
- title?: string;
9
- children: React.ReactNode;
10
- className?: string;
11
- }
12
-
13
- export const Section = ({ title, children, className }: SectionProps) => (
14
- <div className={cn('mb-4 md:mb-6', className)}>
15
- {title && (
16
- <h2 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider mb-2 px-1">
17
- {title}
18
- </h2>
19
- )}
20
- <div className="bg-card rounded-xl px-4">{children}</div>
21
- </div>
22
- );
@@ -1,11 +0,0 @@
1
- export { ActionButton } from './ActionButton';
2
- export { ApiKeySection, ApiKeyProvider, useApiKeyContext } from './ApiKeySection';
3
- export type { ApiKeyLabels } from './ApiKeySection';
4
- export { AvatarSection } from './AvatarSection';
5
- export { DeleteAccountSection, DeleteAccountScreen } from './DeleteAccountSection';
6
- export { EditableField } from './EditableField';
7
- export { PreferencesSection } from './PreferencesSection';
8
- export { ProfileHeader } from './ProfileHeader';
9
- export { ProfileTab } from './ProfileTab';
10
- export { Section } from './Section';
11
- export { TwoFactorSection } from './TwoFactorSection';
@@ -1,2 +0,0 @@
1
- export { useProfileTabs } from './useProfileTabs';
2
- export type { ProfileTabValue, ProfileTabValueOrCustom, UseProfileTabsOptions } from './useProfileTabs';
@@ -1,56 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo, useState, useCallback } from 'react';
4
-
5
- /** Built-in tab values. */
6
- export type ProfileTabValue = 'profile' | 'security' | 'api-keys';
7
-
8
- /**
9
- * Any tab value: a built-in one, or a custom tab's `value` string.
10
- * `(string & {})` keeps the built-in literals as autocomplete hints while
11
- * still accepting arbitrary strings (custom tabs added via `tabs`).
12
- */
13
- export type ProfileTabValueOrCustom = ProfileTabValue | (string & {});
14
-
15
- export interface UseProfileTabsOptions {
16
- enable2FA?: boolean;
17
- enableAPIKeys?: boolean;
18
- extraTabValues?: string[];
19
- /** Initial active tab. Defaults to `'profile'`. Accepts custom tab values. */
20
- defaultTab?: ProfileTabValueOrCustom;
21
- }
22
-
23
- /**
24
- * Manages the active profile tab with local React state.
25
- *
26
- * Falls back to `'profile'` when `defaultTab` is missing or not allowed.
27
- *
28
- * @example
29
- * const { tab, setTab, allowed } = useProfileTabs({ enable2FA: true, defaultTab: 'security' });
30
- * <Tabs value={tab} onValueChange={setTab}>...</Tabs>
31
- */
32
- export function useProfileTabs(options: UseProfileTabsOptions = {}) {
33
- const { enable2FA, enableAPIKeys, extraTabValues = [], defaultTab } = options;
34
-
35
- const allowed = useMemo(() => {
36
- const base: ProfileTabValueOrCustom[] = ['profile'];
37
- if (enable2FA) base.push('security');
38
- if (enableAPIKeys) base.push('api-keys');
39
- return [...base, ...extraTabValues];
40
- }, [enable2FA, enableAPIKeys, extraTabValues]);
41
-
42
- const [tab, setTabState] = useState<ProfileTabValueOrCustom>(
43
- defaultTab && allowed.includes(defaultTab) ? defaultTab : 'profile',
44
- );
45
-
46
- const setTab = useCallback(
47
- (value: ProfileTabValueOrCustom) => {
48
- if (allowed.includes(value)) {
49
- setTabState(value);
50
- }
51
- },
52
- [allowed],
53
- );
54
-
55
- return { tab, setTab, allowed };
56
- }
@@ -1,8 +0,0 @@
1
- export { ProfileForm, useProfileContext } from './ProfileForm';
2
- export { ProfileProvider } from './ProfileForm/context';
3
- export { useProfileTabs } from './hooks';
4
- export { ProfileDialog, useProfileDialogStore } from './ProfileDialog';
5
- export type { ProfileDialogProps, ProfileDialogContent } from './ProfileDialog';
6
- export type { ProfileLabels } from './ProfileForm/context';
7
- export type { ProfileFormProps, ProfileLayoutProps, ProfileTab, ProfileSlots } from './types';
8
- export type { ProfileTabValue, ProfileTabValueOrCustom, UseProfileTabsOptions } from './hooks';
@@ -1,48 +0,0 @@
1
- import React from 'react';
2
-
3
- import type { ProfileTabValueOrCustom } from './hooks/useProfileTabs';
4
-
5
- // ─────────────────────────────────────────────────────────────────────────────
6
- // Slot + Tab types
7
- // ─────────────────────────────────────────────────────────────────────────────
8
-
9
- export interface ProfileTab {
10
- /** Unique key, used as Tabs value */
11
- value: string;
12
- /** Trigger label */
13
- label: React.ReactNode;
14
- /** Tab panel content */
15
- content: React.ReactNode;
16
- }
17
-
18
- export interface ProfileSlots {
19
- /** Extra items inside the ⋯ dropdown, above the separator before Delete */
20
- headerMenuItems?: React.ReactNode;
21
- /** Rendered next to the user name (e.g. plan badge, role chip) */
22
- headerBadge?: React.ReactNode;
23
- /** Rendered below the avatar row, above the tabs */
24
- headerAfter?: React.ReactNode;
25
- /** Rendered below all tab content */
26
- footer?: React.ReactNode;
27
- }
28
-
29
- export interface ProfileFormProps {
30
- onUnauthenticated?: () => void;
31
- title?: string;
32
- enable2FA?: boolean;
33
- enableAPIKeys?: boolean;
34
- enableDeleteAccount?: boolean;
35
- /** Extra tabs appended after built-in Profile / Security / API Keys tabs */
36
- tabs?: ProfileTab[];
37
- /** Named slots for additional content */
38
- slots?: ProfileSlots;
39
- /**
40
- * When provided, the active tab is controlled locally (no URL sync).
41
- * Useful for dialogs where query-string pollution is undesirable.
42
- * Accepts a built-in value or a custom tab's `value`.
43
- */
44
- defaultTab?: ProfileTabValueOrCustom;
45
- }
46
-
47
- /** @deprecated Use ProfileFormProps instead */
48
- export type ProfileLayoutProps = ProfileFormProps;