@djangocfg/layouts 2.1.110 → 2.1.112
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 +69 -0
- package/package.json +14 -12
- package/src/components/errors/ErrorBoundary.tsx +12 -6
- package/src/components/errors/ErrorLayout.tsx +19 -9
- package/src/components/errors/errorConfig.ts +28 -22
- package/src/layouts/AppLayout/AppLayout.tsx +42 -17
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +8 -1
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +14 -1
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +128 -56
- package/src/layouts/PublicLayout/PublicLayout.tsx +6 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +12 -4
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +20 -2
- package/src/layouts/_components/LocaleSwitcher.tsx +114 -0
- package/src/layouts/_components/UserMenu.tsx +14 -6
- package/src/layouts/_components/index.ts +3 -0
- package/src/layouts/index.ts +2 -2
- package/src/snippets/AuthDialog/AuthDialog.tsx +15 -6
- package/src/snippets/Breadcrumbs.tsx +19 -8
- package/src/snippets/McpChat/components/ChatPanel.tsx +16 -6
- package/src/snippets/McpChat/components/ChatSidebar.tsx +20 -8
- package/src/snippets/PWAInstall/components/A2HSHint.tsx +23 -10
- package/src/snippets/PWAInstall/components/DesktopGuide.tsx +44 -32
- package/src/snippets/PWAInstall/components/IOSGuideDrawer.tsx +34 -25
- package/src/snippets/PWAInstall/components/IOSGuideModal.tsx +34 -25
- package/src/snippets/PushNotifications/components/PushPrompt.tsx +16 -6
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { ArrowDownToLine, Check, Menu, Monitor, Plus, Search } from 'lucide-react';
|
|
12
12
|
import React, { useMemo } from 'react';
|
|
13
13
|
|
|
14
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
14
15
|
import {
|
|
15
16
|
Button, Card, CardContent, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader,
|
|
16
17
|
DialogTitle
|
|
@@ -32,28 +33,29 @@ function getBrowserCategory(browser: {
|
|
|
32
33
|
return 'unknown';
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
type TranslationFn = (key: string) => string;
|
|
37
|
+
|
|
38
|
+
function getBrowserSteps(category: BrowserCategory, t: TranslationFn): InstallStep[] {
|
|
36
39
|
switch (category) {
|
|
37
40
|
case 'chromium':
|
|
38
|
-
// Chrome, Edge, Brave, Arc, Vivaldi, Opera, Yandex, etc.
|
|
39
41
|
return [
|
|
40
42
|
{
|
|
41
43
|
number: 1,
|
|
42
|
-
title: '
|
|
44
|
+
title: t('layouts.pwa.chromiumStep1Title'),
|
|
43
45
|
icon: ArrowDownToLine,
|
|
44
|
-
description: '
|
|
46
|
+
description: t('layouts.pwa.chromiumStep1Desc'),
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
49
|
number: 2,
|
|
48
|
-
title: '
|
|
50
|
+
title: t('layouts.pwa.chromiumStep2Title'),
|
|
49
51
|
icon: Plus,
|
|
50
|
-
description: '
|
|
52
|
+
description: t('layouts.pwa.chromiumStep2Desc'),
|
|
51
53
|
},
|
|
52
54
|
{
|
|
53
55
|
number: 3,
|
|
54
|
-
title: '
|
|
56
|
+
title: t('layouts.pwa.chromiumStep3Title'),
|
|
55
57
|
icon: Check,
|
|
56
|
-
description: '
|
|
58
|
+
description: t('layouts.pwa.chromiumStep3Desc'),
|
|
57
59
|
},
|
|
58
60
|
];
|
|
59
61
|
|
|
@@ -61,21 +63,21 @@ function getBrowserSteps(category: BrowserCategory, browserName: string): Instal
|
|
|
61
63
|
return [
|
|
62
64
|
{
|
|
63
65
|
number: 1,
|
|
64
|
-
title: '
|
|
66
|
+
title: t('layouts.pwa.firefoxStep1Title'),
|
|
65
67
|
icon: Menu,
|
|
66
|
-
description: '
|
|
68
|
+
description: t('layouts.pwa.firefoxStep1Desc'),
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
number: 2,
|
|
70
|
-
title: '
|
|
72
|
+
title: t('layouts.pwa.firefoxStep2Title'),
|
|
71
73
|
icon: Search,
|
|
72
|
-
description: '
|
|
74
|
+
description: t('layouts.pwa.firefoxStep2Desc'),
|
|
73
75
|
},
|
|
74
76
|
{
|
|
75
77
|
number: 3,
|
|
76
|
-
title: '
|
|
78
|
+
title: t('layouts.pwa.firefoxStep3Title'),
|
|
77
79
|
icon: Check,
|
|
78
|
-
description: '
|
|
80
|
+
description: t('layouts.pwa.firefoxStep3Desc'),
|
|
79
81
|
},
|
|
80
82
|
];
|
|
81
83
|
|
|
@@ -83,15 +85,15 @@ function getBrowserSteps(category: BrowserCategory, browserName: string): Instal
|
|
|
83
85
|
return [
|
|
84
86
|
{
|
|
85
87
|
number: 1,
|
|
86
|
-
title: '
|
|
88
|
+
title: t('layouts.pwa.safariStep1Title'),
|
|
87
89
|
icon: Monitor,
|
|
88
|
-
description: '
|
|
90
|
+
description: t('layouts.pwa.safariStep1Desc'),
|
|
89
91
|
},
|
|
90
92
|
{
|
|
91
93
|
number: 2,
|
|
92
|
-
title: '
|
|
94
|
+
title: t('layouts.pwa.safariStep2Title'),
|
|
93
95
|
icon: ArrowDownToLine,
|
|
94
|
-
description: '
|
|
96
|
+
description: t('layouts.pwa.safariStep2Desc'),
|
|
95
97
|
},
|
|
96
98
|
];
|
|
97
99
|
|
|
@@ -99,21 +101,21 @@ function getBrowserSteps(category: BrowserCategory, browserName: string): Instal
|
|
|
99
101
|
return [
|
|
100
102
|
{
|
|
101
103
|
number: 1,
|
|
102
|
-
title: '
|
|
104
|
+
title: t('layouts.pwa.unknownStep1Title'),
|
|
103
105
|
icon: ArrowDownToLine,
|
|
104
|
-
description: '
|
|
106
|
+
description: t('layouts.pwa.unknownStep1Desc'),
|
|
105
107
|
},
|
|
106
108
|
{
|
|
107
109
|
number: 2,
|
|
108
|
-
title: '
|
|
110
|
+
title: t('layouts.pwa.unknownStep2Title'),
|
|
109
111
|
icon: Menu,
|
|
110
|
-
description: '
|
|
112
|
+
description: t('layouts.pwa.unknownStep2Desc'),
|
|
111
113
|
},
|
|
112
114
|
{
|
|
113
115
|
number: 3,
|
|
114
|
-
title: '
|
|
116
|
+
title: t('layouts.pwa.unknownStep3Title'),
|
|
115
117
|
icon: Check,
|
|
116
|
-
description: '
|
|
118
|
+
description: t('layouts.pwa.unknownStep3Desc'),
|
|
117
119
|
},
|
|
118
120
|
];
|
|
119
121
|
}
|
|
@@ -157,15 +159,25 @@ export function DesktopGuide({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
|
157
159
|
isOpera,
|
|
158
160
|
isYandex,
|
|
159
161
|
} = useInstall();
|
|
162
|
+
const t = useTypedT<I18nTranslations>();
|
|
163
|
+
|
|
164
|
+
const labels = useMemo(() => ({
|
|
165
|
+
title: t('layouts.pwa.desktopTitle'),
|
|
166
|
+
description: t('layouts.pwa.desktopDescription'),
|
|
167
|
+
descSafari: t('layouts.pwa.desktopDescSafari'),
|
|
168
|
+
tip: t('layouts.pwa.desktopTip'),
|
|
169
|
+
firefoxNote: t('layouts.pwa.desktopFirefoxNote'),
|
|
170
|
+
gotIt: t('layouts.pwa.gotIt'),
|
|
171
|
+
}), [t]);
|
|
160
172
|
|
|
161
173
|
const category = useMemo(
|
|
162
174
|
() => getBrowserCategory({ isChromium, isFirefox, isSafari }),
|
|
163
175
|
[isChromium, isFirefox, isSafari]
|
|
164
176
|
);
|
|
165
177
|
|
|
166
|
-
const steps = useMemo(() => getBrowserSteps(category,
|
|
178
|
+
const steps = useMemo(() => getBrowserSteps(category, t), [category, t]);
|
|
167
179
|
|
|
168
|
-
// Get specific browser display name
|
|
180
|
+
// Get specific browser display name
|
|
169
181
|
const displayName = useMemo(() => {
|
|
170
182
|
if (isEdge) return 'Edge';
|
|
171
183
|
if (isBrave) return 'Brave';
|
|
@@ -182,12 +194,12 @@ export function DesktopGuide({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
|
182
194
|
<DialogHeader className="text-left">
|
|
183
195
|
<DialogTitle className="flex items-center gap-2">
|
|
184
196
|
<Monitor className="w-5 h-5 text-primary" />
|
|
185
|
-
|
|
197
|
+
{labels.title}
|
|
186
198
|
</DialogTitle>
|
|
187
199
|
<DialogDescription className="text-left">
|
|
188
200
|
{isSafari
|
|
189
|
-
?
|
|
190
|
-
:
|
|
201
|
+
? labels.descSafari
|
|
202
|
+
: labels.description.replace('{browser}', displayName)
|
|
191
203
|
}
|
|
192
204
|
</DialogDescription>
|
|
193
205
|
</DialogHeader>
|
|
@@ -200,20 +212,20 @@ export function DesktopGuide({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
|
200
212
|
|
|
201
213
|
{category === 'chromium' && (
|
|
202
214
|
<div className="p-3 bg-muted/30 rounded-lg text-xs text-muted-foreground">
|
|
203
|
-
💡
|
|
215
|
+
💡 {labels.tip.replace('{browser}', displayName)}
|
|
204
216
|
</div>
|
|
205
217
|
)}
|
|
206
218
|
|
|
207
219
|
{category === 'firefox' && (
|
|
208
220
|
<div className="p-3 bg-amber-500/10 rounded-lg text-xs text-amber-700 dark:text-amber-400">
|
|
209
|
-
ℹ️
|
|
221
|
+
ℹ️ {labels.firefoxNote}
|
|
210
222
|
</div>
|
|
211
223
|
)}
|
|
212
224
|
|
|
213
225
|
<DialogFooter>
|
|
214
226
|
<Button onClick={onDismiss} variant="default" className="w-full">
|
|
215
227
|
<Check className="w-4 h-4 mr-2" />
|
|
216
|
-
|
|
228
|
+
{labels.gotIt}
|
|
217
229
|
</Button>
|
|
218
230
|
</DialogFooter>
|
|
219
231
|
</DialogContent>
|
|
@@ -8,35 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { ArrowDown, ArrowUpRight, Check, CheckCircle, Share } from 'lucide-react';
|
|
11
|
-
import React from 'react';
|
|
11
|
+
import React, { useMemo } from 'react';
|
|
12
12
|
|
|
13
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
13
14
|
import {
|
|
14
15
|
Button, Card, CardContent, Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle
|
|
15
16
|
} from '@djangocfg/ui-core/components';
|
|
16
17
|
|
|
17
18
|
import type { IOSGuideModalProps, InstallStep } from '../types';
|
|
18
19
|
|
|
19
|
-
const steps: InstallStep[] = [
|
|
20
|
-
{
|
|
21
|
-
number: 1,
|
|
22
|
-
title: 'Tap Share',
|
|
23
|
-
icon: ArrowUpRight,
|
|
24
|
-
description: 'At the bottom of Safari',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
number: 2,
|
|
28
|
-
title: 'Scroll & Tap',
|
|
29
|
-
icon: ArrowDown,
|
|
30
|
-
description: '"Add to Home Screen"',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
number: 3,
|
|
34
|
-
title: 'Confirm',
|
|
35
|
-
icon: CheckCircle,
|
|
36
|
-
description: 'Tap "Add" in top-right',
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
20
|
function StepCard({ step }: { step: InstallStep }) {
|
|
41
21
|
return (
|
|
42
22
|
<Card className="border border-border">
|
|
@@ -63,16 +43,45 @@ function StepCard({ step }: { step: InstallStep }) {
|
|
|
63
43
|
}
|
|
64
44
|
|
|
65
45
|
export function IOSGuideDrawer({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
46
|
+
const t = useTypedT<I18nTranslations>();
|
|
47
|
+
|
|
48
|
+
const labels = useMemo(() => ({
|
|
49
|
+
title: t('layouts.pwa.iosTitle'),
|
|
50
|
+
description: t('layouts.pwa.iosDescription'),
|
|
51
|
+
gotIt: t('layouts.pwa.gotIt'),
|
|
52
|
+
}), [t]);
|
|
53
|
+
|
|
54
|
+
const steps: InstallStep[] = useMemo(() => [
|
|
55
|
+
{
|
|
56
|
+
number: 1,
|
|
57
|
+
title: t('layouts.pwa.iosStep1Title'),
|
|
58
|
+
icon: ArrowUpRight,
|
|
59
|
+
description: t('layouts.pwa.iosStep1Desc'),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
number: 2,
|
|
63
|
+
title: t('layouts.pwa.iosStep2Title'),
|
|
64
|
+
icon: ArrowDown,
|
|
65
|
+
description: t('layouts.pwa.iosStep2Desc'),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
number: 3,
|
|
69
|
+
title: t('layouts.pwa.iosStep3Title'),
|
|
70
|
+
icon: CheckCircle,
|
|
71
|
+
description: t('layouts.pwa.iosStep3Desc'),
|
|
72
|
+
},
|
|
73
|
+
], [t]);
|
|
74
|
+
|
|
66
75
|
return (
|
|
67
76
|
<Drawer open={open} onOpenChange={(isOpen) => !isOpen && onDismiss()}>
|
|
68
77
|
<DrawerContent>
|
|
69
78
|
<DrawerHeader className="text-left">
|
|
70
79
|
<DrawerTitle className="flex items-center gap-2">
|
|
71
80
|
<Share className="w-5 h-5 text-primary" />
|
|
72
|
-
|
|
81
|
+
{labels.title}
|
|
73
82
|
</DrawerTitle>
|
|
74
83
|
<DrawerDescription className="text-left">
|
|
75
|
-
|
|
84
|
+
{labels.description}
|
|
76
85
|
</DrawerDescription>
|
|
77
86
|
</DrawerHeader>
|
|
78
87
|
|
|
@@ -85,7 +94,7 @@ export function IOSGuideDrawer({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
|
85
94
|
<div className="p-4 pt-0">
|
|
86
95
|
<Button onClick={onDismiss} variant="default" className="w-full">
|
|
87
96
|
<Check className="w-4 h-4 mr-2" />
|
|
88
|
-
|
|
97
|
+
{labels.gotIt}
|
|
89
98
|
</Button>
|
|
90
99
|
</div>
|
|
91
100
|
</DrawerContent>
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ArrowDown, ArrowUpRight, Check, CheckCircle, Share } from 'lucide-react';
|
|
10
|
-
import React from 'react';
|
|
10
|
+
import React, { useMemo } from 'react';
|
|
11
11
|
|
|
12
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
12
13
|
import {
|
|
13
14
|
Button, Card, CardContent, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader,
|
|
14
15
|
DialogTitle
|
|
@@ -16,27 +17,6 @@ import {
|
|
|
16
17
|
|
|
17
18
|
import type { IOSGuideModalProps, InstallStep } from '../types';
|
|
18
19
|
|
|
19
|
-
const steps: InstallStep[] = [
|
|
20
|
-
{
|
|
21
|
-
number: 1,
|
|
22
|
-
title: 'Tap Share',
|
|
23
|
-
icon: ArrowUpRight,
|
|
24
|
-
description: 'At the bottom of Safari',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
number: 2,
|
|
28
|
-
title: 'Scroll & Tap',
|
|
29
|
-
icon: ArrowDown,
|
|
30
|
-
description: '"Add to Home Screen"',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
number: 3,
|
|
34
|
-
title: 'Confirm',
|
|
35
|
-
icon: CheckCircle,
|
|
36
|
-
description: 'Tap "Add" in top-right',
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
20
|
function StepCard({ step }: { step: InstallStep }) {
|
|
41
21
|
return (
|
|
42
22
|
<Card className="border border-border">
|
|
@@ -63,16 +43,45 @@ function StepCard({ step }: { step: InstallStep }) {
|
|
|
63
43
|
}
|
|
64
44
|
|
|
65
45
|
export function IOSGuideModal({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
46
|
+
const t = useTypedT<I18nTranslations>();
|
|
47
|
+
|
|
48
|
+
const labels = useMemo(() => ({
|
|
49
|
+
title: t('layouts.pwa.iosTitle'),
|
|
50
|
+
description: t('layouts.pwa.iosDescription'),
|
|
51
|
+
gotIt: t('layouts.pwa.gotIt'),
|
|
52
|
+
}), [t]);
|
|
53
|
+
|
|
54
|
+
const steps: InstallStep[] = useMemo(() => [
|
|
55
|
+
{
|
|
56
|
+
number: 1,
|
|
57
|
+
title: t('layouts.pwa.iosStep1Title'),
|
|
58
|
+
icon: ArrowUpRight,
|
|
59
|
+
description: t('layouts.pwa.iosStep1Desc'),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
number: 2,
|
|
63
|
+
title: t('layouts.pwa.iosStep2Title'),
|
|
64
|
+
icon: ArrowDown,
|
|
65
|
+
description: t('layouts.pwa.iosStep2Desc'),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
number: 3,
|
|
69
|
+
title: t('layouts.pwa.iosStep3Title'),
|
|
70
|
+
icon: CheckCircle,
|
|
71
|
+
description: t('layouts.pwa.iosStep3Desc'),
|
|
72
|
+
},
|
|
73
|
+
], [t]);
|
|
74
|
+
|
|
66
75
|
return (
|
|
67
76
|
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onDismiss()}>
|
|
68
77
|
<DialogContent className="sm:max-w-md">
|
|
69
78
|
<DialogHeader className="text-left">
|
|
70
79
|
<DialogTitle className="flex items-center gap-2">
|
|
71
80
|
<Share className="w-5 h-5 text-primary" />
|
|
72
|
-
|
|
81
|
+
{labels.title}
|
|
73
82
|
</DialogTitle>
|
|
74
83
|
<DialogDescription className="text-left">
|
|
75
|
-
|
|
84
|
+
{labels.description}
|
|
76
85
|
</DialogDescription>
|
|
77
86
|
</DialogHeader>
|
|
78
87
|
|
|
@@ -85,7 +94,7 @@ export function IOSGuideModal({ onDismiss, open = true }: IOSGuideModalProps) {
|
|
|
85
94
|
<DialogFooter>
|
|
86
95
|
<Button onClick={onDismiss} variant="default" className="w-full">
|
|
87
96
|
<Check className="w-4 h-4 mr-2" />
|
|
88
|
-
|
|
97
|
+
{labels.gotIt}
|
|
89
98
|
</Button>
|
|
90
99
|
</DialogFooter>
|
|
91
100
|
</DialogContent>
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Bell, X } from 'lucide-react';
|
|
11
|
-
import React, { useEffect, useState } from 'react';
|
|
11
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
12
12
|
|
|
13
13
|
import { useAuth } from '@djangocfg/api/auth';
|
|
14
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
14
15
|
import { Button } from '@djangocfg/ui-core';
|
|
15
16
|
|
|
16
17
|
import { usePushNotifications } from '../hooks/usePushNotifications';
|
|
@@ -66,10 +67,19 @@ export function PushPrompt({
|
|
|
66
67
|
vapidPublicKey,
|
|
67
68
|
subscribeEndpoint,
|
|
68
69
|
});
|
|
70
|
+
const t = useTypedT<I18nTranslations>();
|
|
69
71
|
|
|
70
72
|
const [show, setShow] = useState(false);
|
|
71
73
|
const [enabling, setEnabling] = useState(false);
|
|
72
74
|
|
|
75
|
+
const labels = useMemo(() => ({
|
|
76
|
+
enableNotifications: t('layouts.push.enableNotifications'),
|
|
77
|
+
stayUpdated: t('layouts.push.stayUpdated'),
|
|
78
|
+
enable: t('layouts.push.enable'),
|
|
79
|
+
notNow: t('layouts.push.notNow'),
|
|
80
|
+
dismiss: t('layouts.push.dismiss'),
|
|
81
|
+
}), [t]);
|
|
82
|
+
|
|
73
83
|
// Check if should show
|
|
74
84
|
useEffect(() => {
|
|
75
85
|
// Wait for auth to complete, don't show for unauthenticated users
|
|
@@ -132,9 +142,9 @@ export function PushPrompt({
|
|
|
132
142
|
|
|
133
143
|
{/* Content */}
|
|
134
144
|
<div className="flex-1 min-w-0">
|
|
135
|
-
<p className="text-sm font-medium text-white mb-1">
|
|
145
|
+
<p className="text-sm font-medium text-white mb-1">{labels.enableNotifications}</p>
|
|
136
146
|
<p className="text-xs text-zinc-400 mb-3">
|
|
137
|
-
|
|
147
|
+
{labels.stayUpdated}
|
|
138
148
|
</p>
|
|
139
149
|
|
|
140
150
|
{/* Actions */}
|
|
@@ -145,14 +155,14 @@ export function PushPrompt({
|
|
|
145
155
|
size="sm"
|
|
146
156
|
variant="default"
|
|
147
157
|
>
|
|
148
|
-
|
|
158
|
+
{labels.enable}
|
|
149
159
|
</Button>
|
|
150
160
|
<Button
|
|
151
161
|
onClick={handleDismiss}
|
|
152
162
|
size="sm"
|
|
153
163
|
variant="ghost"
|
|
154
164
|
>
|
|
155
|
-
|
|
165
|
+
{labels.notNow}
|
|
156
166
|
</Button>
|
|
157
167
|
</div>
|
|
158
168
|
</div>
|
|
@@ -163,7 +173,7 @@ export function PushPrompt({
|
|
|
163
173
|
size="sm"
|
|
164
174
|
variant="ghost"
|
|
165
175
|
className="flex-shrink-0 p-1"
|
|
166
|
-
aria-label=
|
|
176
|
+
aria-label={labels.dismiss}
|
|
167
177
|
>
|
|
168
178
|
<X className="w-4 h-4" />
|
|
169
179
|
</Button>
|