@djangocfg/layouts 2.1.226 → 2.1.228
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 +3 -17
- package/package.json +18 -18
- package/src/components/errors/ErrorLayout.tsx +2 -2
- package/src/components/errors/ErrorsTracker/index.ts +1 -0
- package/src/components/errors/ErrorsTracker/utils/formatters.ts +23 -1
- package/src/hooks/useLogout.ts +9 -12
- package/src/layouts/AppLayout/AppLayout.tsx +20 -8
- package/src/layouts/AppLayout/BaseApp.tsx +5 -28
- package/src/layouts/AuthLayout/AuthLayout.tsx +51 -22
- package/src/layouts/AuthLayout/README.md +78 -0
- package/src/layouts/AuthLayout/components/shared/AuthDivider.tsx +2 -2
- package/src/layouts/AuthLayout/components/shared/AuthError.tsx +10 -2
- package/src/layouts/AuthLayout/components/shared/AuthFooter.tsx +2 -2
- package/src/layouts/AuthLayout/components/shared/AuthHeader.tsx +3 -2
- package/src/layouts/AuthLayout/components/shared/AuthOTPInput.tsx +4 -1
- package/src/layouts/AuthLayout/components/shared/TermsCheckbox.tsx +2 -2
- package/src/layouts/AuthLayout/components/shared/index.ts +0 -2
- package/src/layouts/AuthLayout/components/steps/IdentifierStep.tsx +25 -80
- package/src/layouts/AuthLayout/components/steps/OTPStep.tsx +8 -13
- package/src/layouts/AuthLayout/components/steps/SetupStep/SetupComplete.tsx +2 -2
- package/src/layouts/AuthLayout/components/steps/SetupStep/SetupLoading.tsx +2 -2
- package/src/layouts/AuthLayout/components/steps/SetupStep/SetupQRCode.tsx +2 -2
- package/src/layouts/AuthLayout/components/steps/TwoFactorStep.tsx +61 -42
- package/src/layouts/AuthLayout/context.tsx +0 -2
- package/src/layouts/AuthLayout/index.ts +9 -6
- package/src/layouts/AuthLayout/styles/auth.css +265 -120
- package/src/layouts/AuthLayout/types.ts +60 -7
- package/src/layouts/ProfileLayout/.claude/.sidecar/activity.jsonl +2 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/history/2026-03-15.md +35 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/review.md +35 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/scan.log +3 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-001.md +18 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-002.md +19 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-003.md +18 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-004.md +18 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-005.md +18 -0
- package/src/layouts/ProfileLayout/.claude/.sidecar/usage.json +5 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +52 -403
- package/src/layouts/ProfileLayout/components/ActionButton.tsx +38 -0
- package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +109 -148
- package/src/layouts/ProfileLayout/components/EditableField.tsx +119 -0
- package/src/layouts/ProfileLayout/components/Section.tsx +22 -0
- package/src/layouts/ProfileLayout/components/index.ts +4 -1
- package/src/layouts/ProfileLayout/context.tsx +31 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +2 -2
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +2 -2
- package/src/layouts/_components/UserMenu.tsx +2 -2
- package/src/layouts/types/README.md +0 -20
- package/src/layouts/types/index.ts +2 -2
- package/src/layouts/types/layout.types.ts +2 -5
- package/src/layouts/types/providers.types.ts +0 -27
- package/src/snippets/AuthDialog/AuthDialog.tsx +2 -2
- package/src/snippets/Breadcrumbs.tsx +2 -2
- package/src/snippets/index.ts +0 -67
- package/src/layouts/AuthLayout/components/shared/ChannelToggle.tsx +0 -56
- package/src/snippets/McpChat/README.md +0 -441
- package/src/snippets/McpChat/components/AIChatWidget.tsx +0 -361
- package/src/snippets/McpChat/components/AskAIButton.tsx +0 -92
- package/src/snippets/McpChat/components/ChatMessages.tsx +0 -138
- package/src/snippets/McpChat/components/ChatPanel.tsx +0 -131
- package/src/snippets/McpChat/components/ChatSidebar.tsx +0 -156
- package/src/snippets/McpChat/components/ChatWidget.tsx +0 -115
- package/src/snippets/McpChat/components/MessageBubble.tsx +0 -142
- package/src/snippets/McpChat/components/MessageInput.tsx +0 -140
- package/src/snippets/McpChat/components/index.ts +0 -24
- package/src/snippets/McpChat/config.ts +0 -94
- package/src/snippets/McpChat/context/AIChatContext.tsx +0 -327
- package/src/snippets/McpChat/context/ChatContext.tsx +0 -361
- package/src/snippets/McpChat/context/index.ts +0 -7
- package/src/snippets/McpChat/hooks/index.ts +0 -6
- package/src/snippets/McpChat/hooks/useAIChat.ts +0 -503
- package/src/snippets/McpChat/hooks/useChatLayout.ts +0 -442
- package/src/snippets/McpChat/hooks/useMcpChat.ts +0 -90
- package/src/snippets/McpChat/index.ts +0 -79
- package/src/snippets/McpChat/types.ts +0 -189
- package/src/snippets/PWAInstall/@docs/README.md +0 -92
- package/src/snippets/PWAInstall/@docs/research/ios-android-install-flows.md +0 -576
- package/src/snippets/PWAInstall/README.md +0 -235
- package/src/snippets/PWAInstall/components/A2HSHint.tsx +0 -236
- package/src/snippets/PWAInstall/components/DesktopGuide.tsx +0 -234
- package/src/snippets/PWAInstall/components/IOSGuide.tsx +0 -29
- package/src/snippets/PWAInstall/components/IOSGuideDrawer.tsx +0 -103
- package/src/snippets/PWAInstall/components/IOSGuideModal.tsx +0 -103
- package/src/snippets/PWAInstall/components/PWAPageResumeManager.tsx +0 -33
- package/src/snippets/PWAInstall/context/InstallContext.tsx +0 -102
- package/src/snippets/PWAInstall/hooks/useInstallPrompt.ts +0 -168
- package/src/snippets/PWAInstall/hooks/useIsPWA.ts +0 -116
- package/src/snippets/PWAInstall/hooks/usePWAPageResume.ts +0 -163
- package/src/snippets/PWAInstall/index.ts +0 -80
- package/src/snippets/PWAInstall/types/components.ts +0 -95
- package/src/snippets/PWAInstall/types/config.ts +0 -29
- package/src/snippets/PWAInstall/types/index.ts +0 -26
- package/src/snippets/PWAInstall/types/install.ts +0 -38
- package/src/snippets/PWAInstall/types/platform.ts +0 -29
- package/src/snippets/PWAInstall/utils/localStorage.ts +0 -181
- package/src/snippets/PWAInstall/utils/logger.ts +0 -149
- package/src/snippets/PWAInstall/utils/platform.ts +0 -151
|
@@ -1,167 +1,128 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { AlertTriangle, Loader2, Trash2 } from 'lucide-react';
|
|
4
|
-
import React, { useState } from 'react';
|
|
3
|
+
import { AlertTriangle, ChevronLeft, Loader2, Trash2 } from 'lucide-react';
|
|
4
|
+
import React, { useMemo, useState } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useAuth, useDeleteAccount } from '@djangocfg/api/auth';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
CardTitle,
|
|
16
|
-
Dialog,
|
|
17
|
-
DialogContent,
|
|
18
|
-
DialogDescription,
|
|
19
|
-
DialogFooter,
|
|
20
|
-
DialogHeader,
|
|
21
|
-
DialogTitle,
|
|
22
|
-
Input,
|
|
23
|
-
} from '@djangocfg/ui-core/components';
|
|
24
|
-
|
|
25
|
-
const CONFIRMATION_TEXT = 'DELETE';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Delete Account section for ProfileLayout.
|
|
29
|
-
* Allows users to permanently delete their account.
|
|
30
|
-
*/
|
|
7
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
8
|
+
import { Alert, AlertDescription, Button, Input } from '@djangocfg/ui-core/components';
|
|
9
|
+
|
|
10
|
+
import { useProfileContext } from '../context';
|
|
11
|
+
import { ActionButton } from './ActionButton';
|
|
12
|
+
|
|
13
|
+
// ─── Entry point: single action row on main screen ───────────────────────────
|
|
14
|
+
|
|
31
15
|
export const DeleteAccountSection: React.FC = () => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
16
|
+
const { setStep } = useProfileContext();
|
|
17
|
+
const t = useAppT();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<ActionButton
|
|
21
|
+
icon={<Trash2 className="w-4 h-4 text-destructive" />}
|
|
22
|
+
label={<span className="text-destructive">{t('layouts.profilePage.deleteAccount')}</span>}
|
|
23
|
+
onClick={() => setStep('delete-account')}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ─── Full delete screen ───────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export const DeleteAccountScreen: React.FC = () => {
|
|
31
|
+
const { back } = useProfileContext();
|
|
34
32
|
const { logout } = useAuth();
|
|
33
|
+
const [confirmationInput, setConfirmationInput] = useState('');
|
|
34
|
+
const t = useAppT();
|
|
35
35
|
|
|
36
|
-
const {
|
|
37
|
-
isLoading,
|
|
38
|
-
error,
|
|
39
|
-
deleteAccount,
|
|
40
|
-
clearError,
|
|
41
|
-
} = useDeleteAccount();
|
|
36
|
+
const { isLoading, error, deleteAccount, clearError } = useDeleteAccount();
|
|
42
37
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
const confirmationWord = t('layouts.profilePage.confirmationWord');
|
|
39
|
+
const isValid = confirmationInput.toUpperCase() === confirmationWord.toUpperCase();
|
|
40
|
+
|
|
41
|
+
const labels = useMemo(() => ({
|
|
42
|
+
back: t('layouts.navigation.back'),
|
|
43
|
+
title: t('layouts.profilePage.deleteAccountTitle'),
|
|
44
|
+
desc: t('layouts.profilePage.deleteAccountDesc'),
|
|
45
|
+
typeToConfirm: t('layouts.profilePage.typeToConfirm').replace('{word}', confirmationWord),
|
|
46
|
+
deleting: t('layouts.profilePage.deleting'),
|
|
47
|
+
deleteAccount: t('layouts.profilePage.deleteAccount'),
|
|
48
|
+
cancel: t('layouts.profilePage.cancel'),
|
|
49
|
+
}), [t, confirmationWord]);
|
|
48
50
|
|
|
49
|
-
const
|
|
51
|
+
const handleDelete = async () => {
|
|
52
|
+
clearError();
|
|
50
53
|
const result = await deleteAccount();
|
|
51
|
-
if (result.success)
|
|
52
|
-
setShowDeleteDialog(false);
|
|
53
|
-
// Perform logout after successful deletion (no confirmation needed since user already confirmed deletion)
|
|
54
|
-
logout();
|
|
55
|
-
}
|
|
54
|
+
if (result.success) logout();
|
|
56
55
|
};
|
|
57
56
|
|
|
58
|
-
const
|
|
59
|
-
setShowDeleteDialog(false);
|
|
60
|
-
setConfirmationInput('');
|
|
57
|
+
const handleBack = () => {
|
|
61
58
|
clearError();
|
|
59
|
+
setConfirmationInput('');
|
|
60
|
+
back();
|
|
62
61
|
};
|
|
63
62
|
|
|
64
|
-
const isConfirmationValid = confirmationInput.toUpperCase() === CONFIRMATION_TEXT;
|
|
65
|
-
|
|
66
63
|
return (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
<div className="container mx-auto px-4 py-12 max-w-md">
|
|
65
|
+
{/* Back */}
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
onClick={handleBack}
|
|
69
|
+
className="flex items-center gap-1 text-sm text-muted-foreground mb-8 hover:text-foreground transition-colors"
|
|
70
|
+
>
|
|
71
|
+
<ChevronLeft className="w-4 h-4" />
|
|
72
|
+
{labels.back}
|
|
73
|
+
</button>
|
|
74
|
+
|
|
75
|
+
{/* Header */}
|
|
76
|
+
<div className="flex items-center gap-3 mb-2">
|
|
77
|
+
<div className="w-10 h-10 rounded-full bg-destructive/10 flex items-center justify-center">
|
|
78
|
+
<AlertTriangle className="w-5 h-5 text-destructive" />
|
|
79
|
+
</div>
|
|
80
|
+
<h1 className="text-xl font-semibold">{labels.title}</h1>
|
|
81
|
+
</div>
|
|
82
|
+
<p className="text-sm text-muted-foreground mb-8">{labels.desc}</p>
|
|
83
|
+
|
|
84
|
+
{/* Confirmation */}
|
|
85
|
+
<div className="space-y-3">
|
|
86
|
+
{error && (
|
|
87
|
+
<Alert variant="destructive">
|
|
88
|
+
<AlertDescription>{error}</AlertDescription>
|
|
85
89
|
</Alert>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
</Alert>
|
|
124
|
-
)}
|
|
125
|
-
|
|
126
|
-
<div className="space-y-2">
|
|
127
|
-
<p className="text-sm text-muted-foreground">
|
|
128
|
-
To confirm, type <span className="font-mono font-bold">{CONFIRMATION_TEXT}</span> below:
|
|
129
|
-
</p>
|
|
130
|
-
<Input
|
|
131
|
-
value={confirmationInput}
|
|
132
|
-
onChange={(e) => setConfirmationInput(e.target.value)}
|
|
133
|
-
placeholder={CONFIRMATION_TEXT}
|
|
134
|
-
disabled={isLoading}
|
|
135
|
-
autoComplete="off"
|
|
136
|
-
/>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
<DialogFooter>
|
|
141
|
-
<Button
|
|
142
|
-
variant="outline"
|
|
143
|
-
onClick={handleDeleteCancel}
|
|
144
|
-
disabled={isLoading}
|
|
145
|
-
>
|
|
146
|
-
Cancel
|
|
147
|
-
</Button>
|
|
148
|
-
<Button
|
|
149
|
-
variant="destructive"
|
|
150
|
-
onClick={handleDeleteConfirm}
|
|
151
|
-
disabled={isLoading || !isConfirmationValid}
|
|
152
|
-
>
|
|
153
|
-
{isLoading ? (
|
|
154
|
-
<>
|
|
155
|
-
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
156
|
-
Deleting...
|
|
157
|
-
</>
|
|
158
|
-
) : (
|
|
159
|
-
'Delete Account'
|
|
160
|
-
)}
|
|
161
|
-
</Button>
|
|
162
|
-
</DialogFooter>
|
|
163
|
-
</DialogContent>
|
|
164
|
-
</Dialog>
|
|
165
|
-
</>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
<p className="text-sm text-muted-foreground">{labels.typeToConfirm}</p>
|
|
93
|
+
|
|
94
|
+
<Input
|
|
95
|
+
value={confirmationInput}
|
|
96
|
+
onChange={(e) => setConfirmationInput(e.target.value)}
|
|
97
|
+
placeholder={confirmationWord}
|
|
98
|
+
disabled={isLoading}
|
|
99
|
+
autoFocus
|
|
100
|
+
autoComplete="off"
|
|
101
|
+
className="font-mono"
|
|
102
|
+
/>
|
|
103
|
+
|
|
104
|
+
<Button
|
|
105
|
+
variant="destructive"
|
|
106
|
+
className="w-full"
|
|
107
|
+
onClick={handleDelete}
|
|
108
|
+
disabled={isLoading || !isValid}
|
|
109
|
+
>
|
|
110
|
+
{isLoading ? (
|
|
111
|
+
<><Loader2 className="mr-2 h-4 w-4 animate-spin" />{labels.deleting}</>
|
|
112
|
+
) : (
|
|
113
|
+
labels.deleteAccount
|
|
114
|
+
)}
|
|
115
|
+
</Button>
|
|
116
|
+
|
|
117
|
+
<Button
|
|
118
|
+
variant="ghost"
|
|
119
|
+
className="w-full"
|
|
120
|
+
onClick={handleBack}
|
|
121
|
+
disabled={isLoading}
|
|
122
|
+
>
|
|
123
|
+
{labels.cancel}
|
|
124
|
+
</Button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
166
127
|
);
|
|
167
128
|
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
6
|
+
import { Button, Input, PhoneInput } from '@djangocfg/ui-core/components';
|
|
7
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
|
+
|
|
9
|
+
interface EditableFieldProps {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
placeholder: string;
|
|
13
|
+
onSave: (value: string) => Promise<void>;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
type?: 'text' | 'phone';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const EditableField = ({
|
|
19
|
+
label,
|
|
20
|
+
value,
|
|
21
|
+
placeholder,
|
|
22
|
+
onSave,
|
|
23
|
+
disabled,
|
|
24
|
+
type = 'text',
|
|
25
|
+
}: EditableFieldProps) => {
|
|
26
|
+
const t = useAppT();
|
|
27
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
28
|
+
const [editValue, setEditValue] = useState(value);
|
|
29
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
30
|
+
|
|
31
|
+
const labels = useMemo(() => ({
|
|
32
|
+
save: t('layouts.profilePage.save'),
|
|
33
|
+
saving: t('layouts.profilePage.saving'),
|
|
34
|
+
cancel: t('layouts.profilePage.cancel'),
|
|
35
|
+
}), [t]);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
setEditValue(value);
|
|
39
|
+
}, [value]);
|
|
40
|
+
|
|
41
|
+
const handleSave = async () => {
|
|
42
|
+
if (editValue === value) {
|
|
43
|
+
setIsEditing(false);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
setIsSaving(true);
|
|
47
|
+
try {
|
|
48
|
+
await onSave(editValue);
|
|
49
|
+
setIsEditing(false);
|
|
50
|
+
} finally {
|
|
51
|
+
setIsSaving(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleCancel = () => {
|
|
56
|
+
setEditValue(value);
|
|
57
|
+
setIsEditing(false);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
61
|
+
if (e.key === 'Enter') handleSave();
|
|
62
|
+
else if (e.key === 'Escape') handleCancel();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (isEditing) {
|
|
66
|
+
return (
|
|
67
|
+
<div className="py-4 border-b border-border/50 last:border-0">
|
|
68
|
+
<label className="block text-[13px] text-muted-foreground mb-1.5">{label}</label>
|
|
69
|
+
{type === 'phone' ? (
|
|
70
|
+
<div className="space-y-2">
|
|
71
|
+
<PhoneInput
|
|
72
|
+
value={editValue}
|
|
73
|
+
onChange={(val) => setEditValue(val ?? '')}
|
|
74
|
+
placeholder={placeholder}
|
|
75
|
+
disabled={isSaving}
|
|
76
|
+
/>
|
|
77
|
+
<div className="flex gap-2">
|
|
78
|
+
<Button size="sm" onClick={handleSave} disabled={isSaving}>
|
|
79
|
+
{isSaving ? labels.saving : labels.save}
|
|
80
|
+
</Button>
|
|
81
|
+
<Button size="sm" variant="ghost" onClick={handleCancel} disabled={isSaving}>
|
|
82
|
+
{labels.cancel}
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
) : (
|
|
87
|
+
<Input
|
|
88
|
+
value={editValue}
|
|
89
|
+
onChange={(e) => setEditValue(e.target.value)}
|
|
90
|
+
onKeyDown={handleKeyDown}
|
|
91
|
+
onBlur={handleSave}
|
|
92
|
+
placeholder={placeholder}
|
|
93
|
+
autoFocus
|
|
94
|
+
disabled={isSaving}
|
|
95
|
+
className="h-9 text-[15px]"
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onClick={() => !disabled && setIsEditing(true)}
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
className={cn(
|
|
108
|
+
'w-full py-4 border-b border-border/50 last:border-0 text-left',
|
|
109
|
+
'transition-colors hover:bg-muted/30',
|
|
110
|
+
disabled && 'cursor-default hover:bg-transparent'
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
<div className="text-[13px] text-muted-foreground mb-0.5">{label}</div>
|
|
114
|
+
<div className={cn('text-[15px]', value ? 'text-foreground' : 'text-muted-foreground/60')}>
|
|
115
|
+
{value || placeholder}
|
|
116
|
+
</div>
|
|
117
|
+
</button>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
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-10', 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,5 +1,8 @@
|
|
|
1
|
+
export { ActionButton } from './ActionButton';
|
|
1
2
|
export { AvatarSection } from './AvatarSection';
|
|
3
|
+
export { DeleteAccountSection, DeleteAccountScreen } from './DeleteAccountSection';
|
|
4
|
+
export { EditableField } from './EditableField';
|
|
2
5
|
export { ProfileForm } from './ProfileForm';
|
|
6
|
+
export { Section } from './Section';
|
|
3
7
|
export { TwoFactorSection } from './TwoFactorSection';
|
|
4
|
-
export { DeleteAccountSection } from './DeleteAccountSection';
|
|
5
8
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useCallback, useContext, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
export type ProfileStep = 'main' | 'delete-account';
|
|
6
|
+
|
|
7
|
+
interface ProfileContextValue {
|
|
8
|
+
step: ProfileStep;
|
|
9
|
+
setStep: (step: ProfileStep) => void;
|
|
10
|
+
back: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ProfileContext = createContext<ProfileContextValue>({
|
|
14
|
+
step: 'main',
|
|
15
|
+
setStep: () => {},
|
|
16
|
+
back: () => {},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const ProfileProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
20
|
+
const [step, setStep] = useState<ProfileStep>('main');
|
|
21
|
+
|
|
22
|
+
const back = useCallback(() => setStep('main'), []);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ProfileContext.Provider value={{ step, setStep, back }}>
|
|
26
|
+
{children}
|
|
27
|
+
</ProfileContext.Provider>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const useProfileContext = () => useContext(ProfileContext);
|
|
@@ -11,7 +11,7 @@ import Link from 'next/link';
|
|
|
11
11
|
import React, { useMemo } from 'react';
|
|
12
12
|
|
|
13
13
|
import { useAuth } from '@djangocfg/api/auth';
|
|
14
|
-
import {
|
|
14
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
15
15
|
import {
|
|
16
16
|
Drawer, DrawerClose, DrawerContent, DrawerHeader, DrawerTitle
|
|
17
17
|
} from '@djangocfg/ui-core/components';
|
|
@@ -39,7 +39,7 @@ export function PublicMobileDrawer({
|
|
|
39
39
|
userMenu,
|
|
40
40
|
}: PublicMobileDrawerProps) {
|
|
41
41
|
const { isAuthenticated: _isAuthenticated } = useAuth();
|
|
42
|
-
const t =
|
|
42
|
+
const t = useAppT();
|
|
43
43
|
|
|
44
44
|
const labels = useMemo(() => ({
|
|
45
45
|
closeMenu: t('layouts.mobile.closeMenu'),
|
|
@@ -11,7 +11,7 @@ import Link from 'next/link';
|
|
|
11
11
|
import React, { useMemo } from 'react';
|
|
12
12
|
|
|
13
13
|
import { useAuth } from '@djangocfg/api/auth';
|
|
14
|
-
import {
|
|
14
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
15
15
|
import { Button } from '@djangocfg/ui-core/components';
|
|
16
16
|
// useIsMobile is used for conditional rendering
|
|
17
17
|
import { useIsMobile } from '@djangocfg/ui-core/hooks';
|
|
@@ -45,7 +45,7 @@ export function PublicNavigation({
|
|
|
45
45
|
}: PublicNavigationProps) {
|
|
46
46
|
const { isAuthenticated: _isAuthenticated } = useAuth();
|
|
47
47
|
const isMobile = useIsMobile();
|
|
48
|
-
const t =
|
|
48
|
+
const t = useAppT();
|
|
49
49
|
|
|
50
50
|
const toggleMobileLabel = useMemo(() => t('layouts.navigation.toggleMobile'), [t]);
|
|
51
51
|
|
|
@@ -35,7 +35,7 @@ import Link from 'next/link';
|
|
|
35
35
|
import React, { useMemo } from 'react';
|
|
36
36
|
|
|
37
37
|
import { useAuth } from '@djangocfg/api/auth';
|
|
38
|
-
import {
|
|
38
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
39
39
|
|
|
40
40
|
import { useLogout } from '../../hooks';
|
|
41
41
|
import {
|
|
@@ -63,7 +63,7 @@ export function UserMenu({
|
|
|
63
63
|
const { user, isAuthenticated } = useAuth();
|
|
64
64
|
const handleLogout = useLogout();
|
|
65
65
|
const [mounted, setMounted] = React.useState(false);
|
|
66
|
-
const t =
|
|
66
|
+
const t = useAppT();
|
|
67
67
|
|
|
68
68
|
const labels = useMemo(() => ({
|
|
69
69
|
signIn: t('layouts.profile.login'),
|
|
@@ -15,7 +15,6 @@ This module provides shared TypeScript types used across all layout components i
|
|
|
15
15
|
- **`ErrorTrackingConfig`** - Error tracking settings
|
|
16
16
|
- **`ErrorBoundaryConfig`** - Error boundary options
|
|
17
17
|
- **`SWRConfigOptions`** - SWR data fetching configuration
|
|
18
|
-
- **`McpChatConfig`** - MCP AI chat widget settings
|
|
19
18
|
|
|
20
19
|
### Layout Component Types
|
|
21
20
|
|
|
@@ -34,7 +33,6 @@ This module provides shared TypeScript types used across all layout components i
|
|
|
34
33
|
```typescript
|
|
35
34
|
import type {
|
|
36
35
|
BaseLayoutProps,
|
|
37
|
-
McpChatConfig,
|
|
38
36
|
ThemeConfig,
|
|
39
37
|
FooterConfig,
|
|
40
38
|
} from '@djangocfg/layouts';
|
|
@@ -50,24 +48,6 @@ interface MyCustomLayoutProps extends BaseLayoutProps {
|
|
|
50
48
|
}
|
|
51
49
|
```
|
|
52
50
|
|
|
53
|
-
### MCP Chat Configuration
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { AppLayout } from '@djangocfg/layouts';
|
|
57
|
-
|
|
58
|
-
<AppLayout
|
|
59
|
-
mcpChat={{
|
|
60
|
-
enabled: true,
|
|
61
|
-
autoDetectEnvironment: true,
|
|
62
|
-
title: 'My AI Assistant',
|
|
63
|
-
position: 'bottom-right',
|
|
64
|
-
enableStreaming: true,
|
|
65
|
-
}}
|
|
66
|
-
>
|
|
67
|
-
{children}
|
|
68
|
-
</AppLayout>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
51
|
## Benefits
|
|
72
52
|
|
|
73
53
|
1. **No Duplication** - Define types once, use everywhere
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
// ============================================================================
|
|
11
11
|
|
|
12
12
|
// Local provider configs
|
|
13
|
-
export type { ThemeConfig, SWRConfigOptions,
|
|
13
|
+
export type { ThemeConfig, SWRConfigOptions, CentrifugoConfig } from './providers.types';
|
|
14
14
|
|
|
15
15
|
// External provider configs (re-export for convenience)
|
|
16
16
|
export type { AnalyticsConfig } from '../../snippets/Analytics/types';
|
|
17
|
-
export type { PwaInstallConfig } from '
|
|
17
|
+
export type { PwaInstallConfig } from '@djangocfg/ui-nextjs/pwa';
|
|
18
18
|
export type {
|
|
19
19
|
ErrorBoundaryConfig,
|
|
20
20
|
ErrorTrackingConfig,
|
|
@@ -10,11 +10,11 @@ import type { AuthConfig } from '@djangocfg/api/auth';
|
|
|
10
10
|
// Import provider configs from their modules
|
|
11
11
|
import type { DebugButtonProps } from '@djangocfg/debuger';
|
|
12
12
|
import type { AnalyticsConfig } from '../../snippets/Analytics/types';
|
|
13
|
-
import type { PwaInstallConfig } from '
|
|
13
|
+
import type { PwaInstallConfig } from '@djangocfg/ui-nextjs/pwa';
|
|
14
14
|
import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
|
|
15
15
|
|
|
16
16
|
// Import local provider configs
|
|
17
|
-
import type { ThemeConfig, SWRConfigOptions,
|
|
17
|
+
import type { ThemeConfig, SWRConfigOptions, CentrifugoConfig } from './providers.types';
|
|
18
18
|
import type { MonitorConfig } from '@djangocfg/monitor';
|
|
19
19
|
|
|
20
20
|
// ============================================================================
|
|
@@ -54,9 +54,6 @@ export interface BaseLayoutProps {
|
|
|
54
54
|
/** Error boundary configuration (from components/errors, enabled by default) */
|
|
55
55
|
errorBoundary?: ErrorBoundaryConfig;
|
|
56
56
|
|
|
57
|
-
/** MCP chat configuration */
|
|
58
|
-
mcpChat?: McpChatConfig;
|
|
59
|
-
|
|
60
57
|
/** PWA Install configuration (from snippets/PWAInstall) */
|
|
61
58
|
pwaInstall?: PwaInstallConfig;
|
|
62
59
|
|
|
@@ -24,33 +24,6 @@ export interface SWRConfigOptions {
|
|
|
24
24
|
dedupingInterval?: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// ============================================================================
|
|
28
|
-
// MCP Chat Configuration
|
|
29
|
-
// ============================================================================
|
|
30
|
-
|
|
31
|
-
export interface McpChatConfig {
|
|
32
|
-
/** Enable MCP chat widget */
|
|
33
|
-
enabled?: boolean;
|
|
34
|
-
/** API endpoint for chat */
|
|
35
|
-
apiEndpoint?: string;
|
|
36
|
-
/** Chat widget title */
|
|
37
|
-
title?: string;
|
|
38
|
-
/** Input placeholder */
|
|
39
|
-
placeholder?: string;
|
|
40
|
-
/** Greeting message */
|
|
41
|
-
greeting?: string;
|
|
42
|
-
/** Widget position */
|
|
43
|
-
position?: 'bottom-right' | 'bottom-left';
|
|
44
|
-
/** Widget variant */
|
|
45
|
-
variant?: 'default' | 'minimal';
|
|
46
|
-
/** Enable streaming responses */
|
|
47
|
-
enableStreaming?: boolean;
|
|
48
|
-
/** Auto-detect environment (dev/prod) */
|
|
49
|
-
autoDetectEnvironment?: boolean;
|
|
50
|
-
/** Custom class name */
|
|
51
|
-
className?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
27
|
// ============================================================================
|
|
55
28
|
// Centrifugo Configuration
|
|
56
29
|
// ============================================================================
|
|
@@ -4,7 +4,7 @@ import { LogIn } from 'lucide-react';
|
|
|
4
4
|
import React, { useMemo, useState } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useCfgRouter } from '@djangocfg/api/auth';
|
|
7
|
-
import {
|
|
7
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
8
8
|
import {
|
|
9
9
|
Button, Dialog, DialogContent, DialogHeader, DialogTitle
|
|
10
10
|
} from '@djangocfg/ui-core/components';
|
|
@@ -28,7 +28,7 @@ export const AuthDialog: React.FC<AuthDialogProps> = ({
|
|
|
28
28
|
authPath = '/auth'
|
|
29
29
|
}) => {
|
|
30
30
|
const [open, setOpen] = useState(false);
|
|
31
|
-
const t =
|
|
31
|
+
const t = useAppT();
|
|
32
32
|
|
|
33
33
|
const labels = useMemo(() => ({
|
|
34
34
|
authRequired: t('layouts.auth.authRequired'),
|
|
@@ -4,7 +4,7 @@ import Link from 'next/link';
|
|
|
4
4
|
import { usePathname } from 'next/navigation';
|
|
5
5
|
import React, { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { useAppT } from '@djangocfg/i18n';
|
|
8
8
|
|
|
9
9
|
export interface BreadcrumbItem {
|
|
10
10
|
path: string;
|
|
@@ -19,7 +19,7 @@ interface BreadcrumbsProps {
|
|
|
19
19
|
|
|
20
20
|
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ items, className = "" }) => {
|
|
21
21
|
const pathname = usePathname();
|
|
22
|
-
const t =
|
|
22
|
+
const t = useAppT();
|
|
23
23
|
|
|
24
24
|
const labels = useMemo(() => ({
|
|
25
25
|
home: t('layouts.navigation.home'),
|