@djangocfg/layouts 2.1.426 → 2.1.428
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 +29 -21
- package/package.json +15 -17
- package/src/components/errors/ErrorsTracker/components/ErrorToast.tsx +19 -0
- package/src/components/errors/ErrorsTracker/utils/curl-generator.ts +24 -10
- package/src/components/errors/README.md +63 -0
- package/src/layouts/AppLayout/BaseApp.tsx +36 -52
- package/src/layouts/AppLayout/README.md +79 -64
- package/src/layouts/AppLayout/index.ts +12 -19
- package/src/layouts/AuthLayout/components/steps/SetupStep/SetupLoading.tsx +6 -4
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +7 -4
- package/src/layouts/PrivateLayout/README.md +30 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +6 -2
- package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +105 -70
- package/src/layouts/PrivateLayout/hooks/useAuthGuard.ts +12 -3
- package/src/layouts/PrivateLayout/types.ts +8 -3
- package/src/layouts/PublicLayout/components/UserMenu.tsx +68 -113
- package/src/layouts/PublicLayout/navbars/MinimalNavbar/MinimalNavbar.tsx +0 -6
- package/src/layouts/PublicLayout/navbars/MinimalNavbar/index.ts +1 -1
- package/src/layouts/SettingsLayout/README.md +258 -0
- package/src/layouts/SettingsLayout/SettingsDialog.tsx +101 -0
- package/src/layouts/SettingsLayout/SettingsForm.tsx +100 -0
- package/src/layouts/SettingsLayout/components/ApiKeySection/ApiKeySection.tsx +192 -0
- package/src/layouts/SettingsLayout/components/SettingsNav.tsx +71 -0
- package/src/layouts/SettingsLayout/components/SettingsNavItem.tsx +57 -0
- package/src/layouts/SettingsLayout/components/SettingsPanel.tsx +48 -0
- package/src/layouts/SettingsLayout/components/SettingsSearch.tsx +50 -0
- package/src/layouts/SettingsLayout/components/SettingsShell.tsx +77 -0
- package/src/layouts/SettingsLayout/components/SettingsTabs.tsx +56 -0
- package/src/layouts/{ProfileLayout → SettingsLayout}/components/TwoFactorSection/TwoFactorSection.tsx +84 -130
- package/src/layouts/SettingsLayout/components/index.ts +6 -0
- package/src/layouts/SettingsLayout/context/SettingsContext.tsx +122 -0
- package/src/layouts/SettingsLayout/context/index.ts +2 -0
- package/src/layouts/SettingsLayout/hooks/index.ts +12 -0
- package/src/layouts/SettingsLayout/hooks/useProfileSave.ts +95 -0
- package/src/layouts/SettingsLayout/hooks/useSettingsDialog.ts +52 -0
- package/src/layouts/SettingsLayout/hooks/useSettingsSections.ts +123 -0
- package/src/layouts/SettingsLayout/hooks/useSettingsUrl.ts +140 -0
- package/src/layouts/SettingsLayout/index.ts +67 -0
- package/src/layouts/SettingsLayout/sections/AccountSection.tsx +100 -0
- package/src/layouts/SettingsLayout/sections/ApiKeysSection.tsx +15 -0
- package/src/layouts/SettingsLayout/sections/DeleteAccountRow.tsx +57 -0
- package/src/layouts/SettingsLayout/sections/PreferencesRows.tsx +43 -0
- package/src/layouts/SettingsLayout/sections/SecuritySection.tsx +15 -0
- package/src/layouts/SettingsLayout/sections/builtins.tsx +77 -0
- package/src/layouts/SettingsLayout/sections/index.ts +8 -0
- package/src/layouts/SettingsLayout/store.ts +47 -0
- package/src/layouts/SettingsLayout/types.ts +107 -0
- package/src/layouts/index.ts +1 -2
- package/src/layouts/types/index.ts +0 -1
- package/src/layouts/types/layout.types.ts +0 -4
- package/src/utils/logger.ts +9 -4
- package/src/layouts/AdminLayout/AdminLayout.tsx +0 -57
- package/src/layouts/AdminLayout/index.ts +0 -7
- package/src/layouts/AppLayout/AppLayout.tsx +0 -520
- package/src/layouts/ProfileLayout/ProfileDialog/ProfileDialog.tsx +0 -56
- package/src/layouts/ProfileLayout/ProfileDialog/index.ts +0 -4
- package/src/layouts/ProfileLayout/ProfileDialog/store.ts +0 -51
- package/src/layouts/ProfileLayout/ProfileForm/context.tsx +0 -123
- package/src/layouts/ProfileLayout/ProfileForm/index.tsx +0 -147
- package/src/layouts/ProfileLayout/README.md +0 -150
- package/src/layouts/ProfileLayout/components/ActionButton.tsx +0 -38
- package/src/layouts/ProfileLayout/components/ApiKeySection/ApiKeySection.tsx +0 -197
- package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +0 -44
- package/src/layouts/ProfileLayout/components/EditableField.tsx +0 -128
- package/src/layouts/ProfileLayout/components/PreferencesSection.tsx +0 -56
- package/src/layouts/ProfileLayout/components/ProfileHeader.tsx +0 -110
- package/src/layouts/ProfileLayout/components/ProfileTab.tsx +0 -35
- package/src/layouts/ProfileLayout/components/Section.tsx +0 -22
- package/src/layouts/ProfileLayout/components/index.ts +0 -11
- package/src/layouts/ProfileLayout/hooks/index.ts +0 -2
- package/src/layouts/ProfileLayout/hooks/useProfileTabs.ts +0 -56
- package/src/layouts/ProfileLayout/index.ts +0 -8
- package/src/layouts/ProfileLayout/types.ts +0 -48
- /package/src/layouts/{ProfileLayout → SettingsLayout}/components/ApiKeySection/context.tsx +0 -0
- /package/src/layouts/{ProfileLayout → SettingsLayout}/components/ApiKeySection/index.ts +0 -0
- /package/src/layouts/{ProfileLayout → SettingsLayout}/components/AvatarSection.tsx +0 -0
- /package/src/layouts/{ProfileLayout → SettingsLayout}/components/TwoFactorSection/index.ts +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SettingsDialog store.
|
|
5
|
+
*
|
|
6
|
+
* Holds only transient dialog state: whether it's open and which section is
|
|
7
|
+
* active. Section *definitions* are NOT stored here — they're passed as props
|
|
8
|
+
* to `<SettingsDialog />` so they can include live JSX/handlers from the host
|
|
9
|
+
* app. Consumers open the dialog imperatively via `useSettingsDialog()` or
|
|
10
|
+
* declaratively via the URL hash (see `useSettingsUrl`).
|
|
11
|
+
*
|
|
12
|
+
* Mirrors the established `useProfileDialogStore` pattern so both dialogs feel
|
|
13
|
+
* identical to wire up. ProfileLayout's store stays separate and untouched.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { create } from 'zustand';
|
|
17
|
+
|
|
18
|
+
interface SettingsDialogState {
|
|
19
|
+
/** Whether the dialog is visible. */
|
|
20
|
+
isOpen: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Active section id, or null to mean "fall back to the first section".
|
|
23
|
+
* Kept separate from isOpen so a deep-link can preset the section before open.
|
|
24
|
+
*/
|
|
25
|
+
activeSection: string | null;
|
|
26
|
+
/** Open the dialog, optionally on a specific section. */
|
|
27
|
+
open: (sectionId?: string) => void;
|
|
28
|
+
/** Close the dialog. Leaves activeSection as-is (cheap to reopen same spot). */
|
|
29
|
+
close: () => void;
|
|
30
|
+
/** Toggle visibility. */
|
|
31
|
+
toggle: () => void;
|
|
32
|
+
/** Switch the active section (no-op on open state). */
|
|
33
|
+
setSection: (sectionId: string) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const useSettingsDialogStore = create<SettingsDialogState>((set) => ({
|
|
37
|
+
isOpen: false,
|
|
38
|
+
activeSection: null,
|
|
39
|
+
open: (sectionId) =>
|
|
40
|
+
set((state) => ({
|
|
41
|
+
isOpen: true,
|
|
42
|
+
activeSection: sectionId ?? state.activeSection,
|
|
43
|
+
})),
|
|
44
|
+
close: () => set({ isOpen: false }),
|
|
45
|
+
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
|
|
46
|
+
setSection: (sectionId) => set({ activeSection: sectionId }),
|
|
47
|
+
}));
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SettingsLayout — public types.
|
|
3
|
+
*
|
|
4
|
+
* A settings surface modeled on the Claude.ai modal: a left section-nav rail
|
|
5
|
+
* (grouped, searchable) and a right scrollable detail panel, rendered as a
|
|
6
|
+
* dialog and/or inline page. Sections are plain config objects so apps extend
|
|
7
|
+
* the surface by appending to an array — no JSX registry, no context plumbing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type * as React from 'react';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A logical grouping of sections in the nav rail. Groups render as a labelled
|
|
14
|
+
* cluster (e.g. "Account", "Workspace"). Unknown / omitted groups fall into the
|
|
15
|
+
* implicit default group, which renders first with no header.
|
|
16
|
+
*/
|
|
17
|
+
export interface SettingsGroup {
|
|
18
|
+
/** Stable key, referenced by `SettingsSection.group`. */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Header shown above the group's items. Omit for an unlabelled cluster. */
|
|
21
|
+
label?: React.ReactNode;
|
|
22
|
+
/** Sort order among groups (ascending). Defaults to declaration order. */
|
|
23
|
+
order?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One settings section = one nav entry + one detail panel.
|
|
28
|
+
*
|
|
29
|
+
* Mirrors the old `ProfileTab` shape (`{ value, label, content }`) so migrating
|
|
30
|
+
* a ProfileLayout tab into a SettingsLayout section is a near-rename.
|
|
31
|
+
*/
|
|
32
|
+
export interface SettingsSection {
|
|
33
|
+
/** Unique id. Used as the active key and the URL hash segment (`#settings/<id>`). */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Nav label. */
|
|
36
|
+
label: React.ReactNode;
|
|
37
|
+
/** Optional icon (lucide component or any SVG-ish element type). */
|
|
38
|
+
icon?: React.ComponentType<{ className?: string }>;
|
|
39
|
+
/** Group id this section belongs to. Falls into the default group when omitted. */
|
|
40
|
+
group?: string;
|
|
41
|
+
/** Heading shown at the top of the detail panel. Defaults to `label`. */
|
|
42
|
+
title?: React.ReactNode;
|
|
43
|
+
/** Sub-heading under the panel title. */
|
|
44
|
+
description?: React.ReactNode;
|
|
45
|
+
/** The panel body. Rendered only while this section is active (Radix unmounts closed dialogs). */
|
|
46
|
+
content: React.ReactNode;
|
|
47
|
+
/** Extra terms the search box matches against, beyond the label. */
|
|
48
|
+
keywords?: string[];
|
|
49
|
+
/** Sort order within the group (ascending). Defaults to declaration order. */
|
|
50
|
+
order?: number;
|
|
51
|
+
/** Optional trailing accessory in the nav row (badge, chevron, count). */
|
|
52
|
+
badge?: React.ReactNode;
|
|
53
|
+
/** Hide from the rail without removing (e.g. gated by plan). Still reachable by id. */
|
|
54
|
+
hidden?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Toggle flags for the built-in sections shipped by SettingsLayout. */
|
|
58
|
+
export interface SettingsBuiltins {
|
|
59
|
+
/** Account / profile fields + preferences. Default: true. */
|
|
60
|
+
account?: boolean;
|
|
61
|
+
/** Two-factor / security. Default: false (opt-in, needs API support). */
|
|
62
|
+
security?: boolean;
|
|
63
|
+
/** API keys. Default: true. */
|
|
64
|
+
apiKeys?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Shared configuration consumed by both the dialog and the inline form. */
|
|
68
|
+
export interface SettingsContent {
|
|
69
|
+
/** Dialog/page title. Default: "Settings". */
|
|
70
|
+
title?: React.ReactNode;
|
|
71
|
+
/** App-provided sections, merged after the enabled built-ins. */
|
|
72
|
+
sections?: SettingsSection[];
|
|
73
|
+
/** Group definitions for ordering/labelling. Optional — sections can name ad-hoc groups. */
|
|
74
|
+
groups?: SettingsGroup[];
|
|
75
|
+
/** Which built-in sections to include. */
|
|
76
|
+
builtins?: SettingsBuiltins;
|
|
77
|
+
/** Section id to open first. Defaults to the first visible section. */
|
|
78
|
+
initialSection?: string;
|
|
79
|
+
/** Show the search box above the nav rail. Default: true. */
|
|
80
|
+
searchable?: boolean;
|
|
81
|
+
/** Allow account deletion in the account section. Default: true. */
|
|
82
|
+
enableDeleteAccount?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Props for the inline (non-dialog) `<SettingsForm />`. */
|
|
86
|
+
export interface SettingsFormProps extends SettingsContent {
|
|
87
|
+
/** Controlled active section. When set, the form does not own selection state. */
|
|
88
|
+
activeSection?: string;
|
|
89
|
+
/** Called when the user picks a section (controlled mode). */
|
|
90
|
+
onSectionChange?: (id: string) => void;
|
|
91
|
+
/** Fired when an unauthenticated user reaches the surface. */
|
|
92
|
+
onUnauthenticated?: () => void;
|
|
93
|
+
/** Drop the outer card chrome (border/rounding) — used when embedded in a dialog. */
|
|
94
|
+
bare?: boolean;
|
|
95
|
+
className?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Props for the globally-mounted `<SettingsDialog />`. */
|
|
99
|
+
export interface SettingsDialogProps extends SettingsContent {
|
|
100
|
+
/**
|
|
101
|
+
* Sync open/close + active section with the URL hash (`#settings/<id>`).
|
|
102
|
+
* Lets users deep-link and the back button close. Default: true.
|
|
103
|
+
*/
|
|
104
|
+
syncUrl?: boolean;
|
|
105
|
+
/** Hash namespace; the segment before the section id. Default: "settings". */
|
|
106
|
+
urlKey?: string;
|
|
107
|
+
}
|
package/src/layouts/index.ts
CHANGED
|
@@ -27,7 +27,6 @@ export * from './AppLayout';
|
|
|
27
27
|
export * from './PublicLayout';
|
|
28
28
|
export * from './PrivateLayout';
|
|
29
29
|
export * from './AuthLayout';
|
|
30
|
-
export * from './AdminLayout';
|
|
31
30
|
|
|
32
31
|
// Additional layouts
|
|
33
|
-
export * from './
|
|
32
|
+
export * from './SettingsLayout';
|
|
@@ -22,7 +22,6 @@ export type {
|
|
|
22
22
|
|
|
23
23
|
// External provider configs (re-export for convenience)
|
|
24
24
|
export type { AnalyticsConfig } from '../../snippets/Analytics/types';
|
|
25
|
-
export type { PwaInstallConfig } from '@djangocfg/ui-nextjs/pwa';
|
|
26
25
|
export type {
|
|
27
26
|
ErrorBoundaryConfig,
|
|
28
27
|
ErrorTrackingConfig,
|
|
@@ -9,7 +9,6 @@ import type { AuthConfig } from '@djangocfg/api/auth';
|
|
|
9
9
|
|
|
10
10
|
// Import provider configs from their modules
|
|
11
11
|
import type { AnalyticsConfig } from '../../snippets/Analytics/types';
|
|
12
|
-
import type { PwaInstallConfig } from '@djangocfg/ui-nextjs/pwa';
|
|
13
12
|
import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
|
|
14
13
|
|
|
15
14
|
/**
|
|
@@ -62,9 +61,6 @@ export interface BaseLayoutProps {
|
|
|
62
61
|
/** Error boundary configuration (from components/errors, enabled by default) */
|
|
63
62
|
errorBoundary?: ErrorBoundaryConfig;
|
|
64
63
|
|
|
65
|
-
/** PWA Install configuration (from snippets/PWAInstall) */
|
|
66
|
-
pwaInstall?: PwaInstallConfig;
|
|
67
|
-
|
|
68
64
|
/** Monitor configuration — initialises window.monitor + auto-captures JS errors & console */
|
|
69
65
|
monitor?: MonitorConfig;
|
|
70
66
|
|
package/src/utils/logger.ts
CHANGED
|
@@ -11,15 +11,20 @@ import { createConsola } from 'consola';
|
|
|
11
11
|
* - 3: log, info
|
|
12
12
|
* - 4: debug
|
|
13
13
|
* - 5: trace, verbose
|
|
14
|
+
*
|
|
15
|
+
* Level is driven by the global runtime controller in @djangocfg/api so admins
|
|
16
|
+
* get verbose logs (even in prod) and regular users keep a clean console.
|
|
14
17
|
*/
|
|
15
|
-
import {
|
|
16
|
-
const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
|
|
17
|
-
const showLogs = isDevelopment || isStaticBuild;
|
|
18
|
+
import { getLogLevel, onLogLevelChange } from '@djangocfg/api';
|
|
18
19
|
|
|
19
20
|
export const logger = createConsola({
|
|
20
|
-
level:
|
|
21
|
+
level: getLogLevel(),
|
|
21
22
|
}).withTag('layouts');
|
|
22
23
|
|
|
24
|
+
onLogLevelChange((level) => {
|
|
25
|
+
logger.level = level;
|
|
26
|
+
});
|
|
27
|
+
|
|
23
28
|
// ─────────────────────────────────────────────────────────────────────────
|
|
24
29
|
// Module-specific loggers
|
|
25
30
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin Layout
|
|
3
|
-
*
|
|
4
|
-
* Layout for admin dashboard pages
|
|
5
|
-
* Import and use directly with props - no complex configs needed!
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```tsx
|
|
9
|
-
* import { AdminLayout } from '@djangocfg/layouts';
|
|
10
|
-
*
|
|
11
|
-
* <AdminLayout
|
|
12
|
-
* sidebar={{
|
|
13
|
-
* items: [
|
|
14
|
-
* { label: 'Overview', href: '/admin', icon: 'LayoutDashboard' },
|
|
15
|
-
* { label: 'Users', href: '/admin/users', icon: 'Users' }
|
|
16
|
-
* ]
|
|
17
|
-
* }}
|
|
18
|
-
* header={{
|
|
19
|
-
* title: 'Admin Dashboard',
|
|
20
|
-
* groups: [
|
|
21
|
-
* {
|
|
22
|
-
* title: 'Admin',
|
|
23
|
-
* items: [
|
|
24
|
-
* { label: 'Profile', href: '/profile' },
|
|
25
|
-
* { label: 'Settings', href: '/settings' }
|
|
26
|
-
* ]
|
|
27
|
-
* }
|
|
28
|
-
* ],
|
|
29
|
-
* authPath: '/auth'
|
|
30
|
-
* }}
|
|
31
|
-
* >
|
|
32
|
-
* {children}
|
|
33
|
-
* </AdminLayout>
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
'use client';
|
|
38
|
-
|
|
39
|
-
import { ReactNode } from 'react';
|
|
40
|
-
|
|
41
|
-
import { PrivateLayout } from '../PrivateLayout';
|
|
42
|
-
import type { PrivateLayoutProps } from '../PrivateLayout';
|
|
43
|
-
|
|
44
|
-
export interface AdminLayoutProps extends PrivateLayoutProps {
|
|
45
|
-
children: ReactNode;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Admin Layout Component
|
|
50
|
-
*
|
|
51
|
-
* Wrapper around PrivateLayout with admin-specific defaults
|
|
52
|
-
* Same API as PrivateLayout - just a convenience export
|
|
53
|
-
*/
|
|
54
|
-
export function AdminLayout(props: AdminLayoutProps) {
|
|
55
|
-
return <PrivateLayout {...props} />;
|
|
56
|
-
}
|
|
57
|
-
|