@djangocfg/layouts 2.1.264 → 2.1.267
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 +113 -4
- package/package.json +19 -18
- package/src/hooks/index.ts +1 -1
- package/src/hooks/usePathnameWithoutLocale.ts +35 -19
- package/src/layouts/AppLayout/AppLayout.tsx +15 -4
- package/src/layouts/AuthLayout/components/steps/SetupStep/index.tsx +50 -6
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +206 -235
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -3
- package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +22 -106
- package/src/layouts/ProfileLayout/components/EditableField.tsx +15 -10
- package/src/layouts/ProfileLayout/components/Section.tsx +1 -1
- package/src/layouts/ProfileLayout/components/TwoFactorSection.tsx +255 -215
- package/src/layouts/ProfileLayout/context.tsx +108 -16
- package/src/layouts/ProfileLayout/index.ts +1 -1
- package/src/layouts/PublicLayout/PublicLayout.tsx +18 -0
- package/src/layouts/PublicLayout/components/NavActions.tsx +50 -0
- package/src/layouts/PublicLayout/components/NavBrand.tsx +26 -0
- package/src/layouts/PublicLayout/components/NavDesktopItems.tsx +207 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavbar.tsx +44 -6
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +199 -396
- package/src/layouts/PublicLayout/hooks/index.ts +5 -1
- package/src/layouts/PublicLayout/hooks/useDropdownMenu.ts +58 -0
- package/src/layouts/PublicLayout/hooks/useNavbarScroll.ts +61 -0
- package/src/layouts/PublicLayout/hooks/useNavbarViewportVars.ts +46 -0
- package/src/layouts/PublicLayout/index.ts +4 -0
- package/src/layouts/PublicLayout/navbarTypes.ts +17 -0
- package/src/utils/pathMatcher.ts +6 -3
- package/src/layouts/ProfileLayout/.claude/.sidecar/activity.jsonl +0 -2
- package/src/layouts/ProfileLayout/.claude/.sidecar/history/2026-03-15.md +0 -35
- package/src/layouts/ProfileLayout/.claude/.sidecar/review.md +0 -35
- package/src/layouts/ProfileLayout/.claude/.sidecar/scan.log +0 -3
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-001.md +0 -18
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-002.md +0 -19
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-003.md +0 -18
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-004.md +0 -18
- package/src/layouts/ProfileLayout/.claude/.sidecar/tasks/T-005.md +0 -18
- package/src/layouts/ProfileLayout/.claude/.sidecar/usage.json +0 -5
|
@@ -1,128 +1,44 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import React
|
|
3
|
+
import { Trash2 } from 'lucide-react';
|
|
4
|
+
import React from 'react';
|
|
5
5
|
|
|
6
6
|
import { useAuth, useDeleteAccount } from '@djangocfg/api/auth';
|
|
7
7
|
import { useAppT } from '@djangocfg/i18n';
|
|
8
|
-
import { Alert, AlertDescription, Button, Input } from '@djangocfg/ui-core/components';
|
|
9
8
|
|
|
10
|
-
import { useProfileContext } from '../context';
|
|
11
9
|
import { ActionButton } from './ActionButton';
|
|
12
10
|
|
|
13
|
-
// ─── Entry point: single action row on main screen ───────────────────────────
|
|
14
|
-
|
|
15
11
|
export const DeleteAccountSection: React.FC = () => {
|
|
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();
|
|
32
12
|
const { logout } = useAuth();
|
|
33
|
-
const
|
|
13
|
+
const { deleteAccount } = useDeleteAccount();
|
|
34
14
|
const t = useAppT();
|
|
35
15
|
|
|
36
|
-
const { isLoading, error, deleteAccount, clearError } = useDeleteAccount();
|
|
37
|
-
|
|
38
16
|
const confirmationWord = t('layouts.profilePage.confirmationWord');
|
|
39
|
-
const isValid = confirmationInput.toUpperCase() === confirmationWord.toUpperCase();
|
|
40
17
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
18
|
+
const handleClick = async () => {
|
|
19
|
+
const value = await window.dialog.prompt({
|
|
20
|
+
title: t('layouts.profilePage.deleteAccountTitle'),
|
|
21
|
+
message: t('layouts.profilePage.deleteAccountDesc'),
|
|
22
|
+
placeholder: confirmationWord,
|
|
23
|
+
confirmText: t('layouts.profilePage.deleteAccount'),
|
|
24
|
+
cancelText: t('layouts.profilePage.cancel'),
|
|
25
|
+
variant: 'destructive',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (value?.toUpperCase() !== confirmationWord.toUpperCase()) return;
|
|
50
29
|
|
|
51
|
-
const handleDelete = async () => {
|
|
52
|
-
clearError();
|
|
53
30
|
const result = await deleteAccount();
|
|
54
31
|
if (result.success) logout();
|
|
55
32
|
};
|
|
56
33
|
|
|
57
|
-
const handleBack = () => {
|
|
58
|
-
clearError();
|
|
59
|
-
setConfirmationInput('');
|
|
60
|
-
back();
|
|
61
|
-
};
|
|
62
|
-
|
|
63
34
|
return (
|
|
64
|
-
<
|
|
65
|
-
{
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
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>
|
|
89
|
-
</Alert>
|
|
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>
|
|
35
|
+
<ActionButton
|
|
36
|
+
icon={<Trash2 className="w-4 h-4 text-destructive" />}
|
|
37
|
+
label={<span className="text-destructive">{t('layouts.profilePage.deleteAccount')}</span>}
|
|
38
|
+
onClick={handleClick}
|
|
39
|
+
/>
|
|
127
40
|
);
|
|
128
41
|
};
|
|
42
|
+
|
|
43
|
+
// Keep export so nothing breaks — no longer used but exported for backwards compat
|
|
44
|
+
export const DeleteAccountScreen: React.FC = () => null;
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useEffect,
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { parsePhoneNumberFromString } from 'libphonenumber-js';
|
|
4
5
|
|
|
5
|
-
import { useAppT } from '@djangocfg/i18n';
|
|
6
6
|
import { Button, Input, PhoneInput } from '@djangocfg/ui-core/components';
|
|
7
7
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
8
|
|
|
9
|
+
import { useProfileContext } from '../context';
|
|
10
|
+
|
|
11
|
+
function formatPhone(raw: string): string {
|
|
12
|
+
if (!raw) return '';
|
|
13
|
+
try {
|
|
14
|
+
return parsePhoneNumberFromString(raw)?.formatInternational() ?? raw;
|
|
15
|
+
} catch {
|
|
16
|
+
return raw;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
interface EditableFieldProps {
|
|
10
21
|
label: string;
|
|
11
22
|
value: string;
|
|
@@ -23,17 +34,11 @@ export const EditableField = ({
|
|
|
23
34
|
disabled,
|
|
24
35
|
type = 'text',
|
|
25
36
|
}: EditableFieldProps) => {
|
|
26
|
-
const
|
|
37
|
+
const { labels } = useProfileContext();
|
|
27
38
|
const [isEditing, setIsEditing] = useState(false);
|
|
28
39
|
const [editValue, setEditValue] = useState(value);
|
|
29
40
|
const [isSaving, setIsSaving] = useState(false);
|
|
30
41
|
|
|
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
42
|
useEffect(() => {
|
|
38
43
|
setEditValue(value);
|
|
39
44
|
}, [value]);
|
|
@@ -112,7 +117,7 @@ export const EditableField = ({
|
|
|
112
117
|
>
|
|
113
118
|
<div className="text-[13px] text-muted-foreground mb-0.5">{label}</div>
|
|
114
119
|
<div className={cn('text-[15px]', value ? 'text-foreground' : 'text-muted-foreground/60')}>
|
|
115
|
-
{value
|
|
120
|
+
{value ? (type === 'phone' ? formatPhone(value) : value) : placeholder}
|
|
116
121
|
</div>
|
|
117
122
|
</button>
|
|
118
123
|
);
|
|
@@ -11,7 +11,7 @@ interface SectionProps {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const Section = ({ title, children, className }: SectionProps) => (
|
|
14
|
-
<div className={cn('mb-
|
|
14
|
+
<div className={cn('mb-4 md:mb-6', className)}>
|
|
15
15
|
{title && (
|
|
16
16
|
<h2 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider mb-2 px-1">
|
|
17
17
|
{title}
|