@djangocfg/layouts 2.1.103 → 2.1.104
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/package.json +33 -37
- package/src/components/RedirectPage/RedirectPage.tsx +2 -2
- package/src/components/core/ClientOnly.tsx +1 -1
- package/src/components/errors/ErrorLayout.tsx +1 -1
- package/src/components/errors/ErrorsTracker/components/ErrorButtons.tsx +1 -1
- package/src/components/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/layouts/AuthLayout/components/AuthHelp.tsx +1 -1
- package/src/layouts/AuthLayout/components/AuthSuccess.tsx +1 -1
- package/src/layouts/AuthLayout/components/IdentifierForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/OTPForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/TwoFactorForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/TwoFactorSetup.tsx +1 -1
- package/src/layouts/AuthLayout/components/oauth/OAuthCallback.tsx +1 -1
- package/src/layouts/AuthLayout/components/oauth/OAuthProviders.tsx +1 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +3 -2
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +2 -2
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +1 -1
- package/src/layouts/ProfileLayout/__tests__/TwoFactorSection.test.tsx +1 -1
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +1 -1
- package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +1 -1
- package/src/layouts/ProfileLayout/components/ProfileForm.tsx +1 -1
- package/src/layouts/ProfileLayout/components/TwoFactorSection.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicFooter/PublicFooter.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +2 -2
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +2 -0
- package/src/pages/index.ts +2 -0
- package/src/pages/legal/LegalPage.tsx +1 -1
- package/src/snippets/AuthDialog/AuthDialog.tsx +3 -2
- package/src/snippets/McpChat/components/AIChatWidget.tsx +1 -1
- package/src/snippets/McpChat/components/AskAIButton.tsx +1 -1
- package/src/snippets/McpChat/components/ChatMessages.tsx +1 -1
- package/src/snippets/McpChat/components/ChatPanel.tsx +1 -1
- package/src/snippets/McpChat/components/ChatSidebar.tsx +1 -1
- package/src/snippets/McpChat/components/ChatWidget.tsx +1 -1
- package/src/snippets/McpChat/components/MessageBubble.tsx +1 -1
- package/src/snippets/McpChat/components/MessageInput.tsx +1 -1
- package/src/snippets/McpChat/context/AIChatContext.tsx +1 -1
- package/src/snippets/McpChat/context/ChatContext.tsx +1 -1
- package/src/snippets/McpChat/hooks/useChatLayout.ts +1 -1
- package/src/snippets/PWAInstall/components/A2HSHint.tsx +0 -1
- package/src/snippets/PWAInstall/components/DesktopGuide.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuide.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuideDrawer.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuideModal.tsx +1 -1
- package/src/snippets/PWAInstall/hooks/useInstallPrompt.ts +2 -2
- package/src/snippets/PushNotifications/components/PushPrompt.tsx +1 -1
- package/src/snippets/index.ts +1 -0
- package/dist/AIChatWidget-LUPM7S2O.mjs +0 -1644
- package/dist/AIChatWidget-LUPM7S2O.mjs.map +0 -1
- package/dist/AIChatWidget-O23TJJ7C.mjs +0 -3
- package/dist/AIChatWidget-O23TJJ7C.mjs.map +0 -1
- package/dist/chunk-53YKWR6F.mjs +0 -6
- package/dist/chunk-53YKWR6F.mjs.map +0 -1
- package/dist/chunk-EI7TDN2G.mjs +0 -1652
- package/dist/chunk-EI7TDN2G.mjs.map +0 -1
- package/dist/components.cjs +0 -925
- package/dist/components.cjs.map +0 -1
- package/dist/components.d.mts +0 -583
- package/dist/components.d.ts +0 -583
- package/dist/components.mjs +0 -879
- package/dist/components.mjs.map +0 -1
- package/dist/index.cjs +0 -7573
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.mts +0 -2376
- package/dist/index.d.ts +0 -2376
- package/dist/index.mjs +0 -5673
- package/dist/index.mjs.map +0 -1
- package/dist/layouts.cjs +0 -6530
- package/dist/layouts.cjs.map +0 -1
- package/dist/layouts.d.mts +0 -748
- package/dist/layouts.d.ts +0 -748
- package/dist/layouts.mjs +0 -4741
- package/dist/layouts.mjs.map +0 -1
- package/dist/pages.cjs +0 -178
- package/dist/pages.cjs.map +0 -1
- package/dist/pages.d.mts +0 -57
- package/dist/pages.d.ts +0 -57
- package/dist/pages.mjs +0 -168
- package/dist/pages.mjs.map +0 -1
- package/dist/snippets.cjs +0 -3793
- package/dist/snippets.cjs.map +0 -1
- package/dist/snippets.d.mts +0 -1192
- package/dist/snippets.d.ts +0 -1192
- package/dist/snippets.mjs +0 -3738
- package/dist/snippets.mjs.map +0 -1
- package/dist/utils.cjs +0 -34
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.d.mts +0 -40
- package/dist/utils.d.ts +0 -40
- package/dist/utils.mjs +0 -25
- package/dist/utils.mjs.map +0 -1
package/dist/layouts.mjs
DELETED
|
@@ -1,4741 +0,0 @@
|
|
|
1
|
-
import { __name } from './chunk-53YKWR6F.mjs';
|
|
2
|
-
import * as LucideIcons from 'lucide-react';
|
|
3
|
-
import { LogOut, Copy, Terminal, Bell, X, ArrowDownToLine, Menu, Check, Monitor, Search, Plus, Share, ArrowUpRight, ArrowDown, CheckCircle, Mail, MessageSquare, Instagram, Facebook, Youtube, MessageCircle, Twitter, Linkedin, Github, LogIn, ChevronRight, Download, HelpCircle, Loader2, AlertCircle, User, Phone, Send, ShieldCheck, ArrowLeft, RotateCw, EyeOff, Eye, KeyRound, Camera, Building2, Briefcase, Shield, Trash2 } from 'lucide-react';
|
|
4
|
-
import Link from 'next/link';
|
|
5
|
-
import React, { createContext, useState, useEffect, Suspense as Suspense$1, Component, useCallback, useContext, useMemo, useRef } from 'react';
|
|
6
|
-
import { useAuth, AuthProvider, useAuthForm, useGithubAuth, useTwoFactorSetup, useTwoFactorStatus, PatchedUserProfileUpdateRequestSchema, useDeleteAccount } from '@djangocfg/api/auth';
|
|
7
|
-
import { Button, Avatar, AvatarImage, AvatarFallback, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem, Drawer as Drawer$1, DrawerContent as DrawerContent$1, DrawerHeader as DrawerHeader$1, DrawerTitle as DrawerTitle$1, DrawerClose, useSidebar, Sidebar, SidebarHeader, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarMenuBadge, SidebarTrigger, Separator, Preloader, SidebarProvider, SidebarInset, Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, Card as Card$1, CardHeader, CardTitle, CardDescription, CardContent as CardContent$1, Tabs, TabsList, TabsTrigger, TabsContent, Label, Input, PhoneInput, Checkbox, OTPInput, Alert, AlertDescription, CardFooter, DialogFooter as DialogFooter$1, DialogDescription as DialogDescription$1 } from '@djangocfg/ui-nextjs/components';
|
|
8
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
-
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
10
|
-
import { cn } from '@djangocfg/ui-core/lib';
|
|
11
|
-
import dynamic from 'next/dynamic';
|
|
12
|
-
import NextTopLoader from 'nextjs-toploader';
|
|
13
|
-
import { SWRConfig } from 'swr';
|
|
14
|
-
import { getCentrifugoAuthTokenRetrieve } from '@djangocfg/api';
|
|
15
|
-
import { CentrifugoProvider } from '@djangocfg/centrifugo';
|
|
16
|
-
import { TooltipProvider, Toaster } from '@djangocfg/ui-core/components';
|
|
17
|
-
import { ThemeProvider, ThemeToggle } from '@djangocfg/ui-nextjs/theme';
|
|
18
|
-
import { useCopy, toast, events } from '@djangocfg/ui-core/hooks';
|
|
19
|
-
import { Button as Button$1, useBrowserDetect, useDeviceDetect, Card, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription } from '@djangocfg/ui-nextjs';
|
|
20
|
-
import consola, { createConsola, consola as consola$1 } from 'consola';
|
|
21
|
-
import ReactGA from 'react-ga4';
|
|
22
|
-
import { useIsMobile, useCfgRouter, useEventListener } from '@djangocfg/ui-nextjs/hooks';
|
|
23
|
-
import { apiWebPush } from '@djangocfg/api/clients';
|
|
24
|
-
import { QRCodeSVG } from 'qrcode.react';
|
|
25
|
-
import moment from 'moment';
|
|
26
|
-
import { useForm } from 'react-hook-form';
|
|
27
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
28
|
-
|
|
29
|
-
function UserMenu({
|
|
30
|
-
variant = "desktop",
|
|
31
|
-
groups,
|
|
32
|
-
authPath = "/auth"
|
|
33
|
-
}) {
|
|
34
|
-
const { user, isAuthenticated, logout } = useAuth();
|
|
35
|
-
const [mounted, setMounted] = React.useState(false);
|
|
36
|
-
React.useEffect(() => {
|
|
37
|
-
setMounted(true);
|
|
38
|
-
}, []);
|
|
39
|
-
const menuGroups = React.useMemo(() => {
|
|
40
|
-
const allGroups = [];
|
|
41
|
-
if (groups && groups.length > 0) {
|
|
42
|
-
allGroups.push(...groups);
|
|
43
|
-
}
|
|
44
|
-
allGroups.push({
|
|
45
|
-
items: [
|
|
46
|
-
{
|
|
47
|
-
label: "Sign Out",
|
|
48
|
-
onClick: /* @__PURE__ */ __name(() => logout(), "onClick"),
|
|
49
|
-
icon: LogOut,
|
|
50
|
-
variant: "destructive"
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
});
|
|
54
|
-
return allGroups;
|
|
55
|
-
}, [groups, logout]);
|
|
56
|
-
if (!mounted) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
if (!isAuthenticated || !user) {
|
|
60
|
-
if (variant === "mobile") {
|
|
61
|
-
return /* @__PURE__ */ jsx(Link, { href: authPath, children: /* @__PURE__ */ jsx(Button, { variant: "default", className: "w-full", children: "Sign In" }) });
|
|
62
|
-
}
|
|
63
|
-
return /* @__PURE__ */ jsx(Link, { href: authPath, children: /* @__PURE__ */ jsx(Button, { variant: "default", size: "sm", children: "Sign In" }) });
|
|
64
|
-
}
|
|
65
|
-
const displayName = user.display_username || user.email || "User";
|
|
66
|
-
const userInitial = displayName.charAt(0).toUpperCase();
|
|
67
|
-
const userAvatar = user.avatar || "";
|
|
68
|
-
if (variant === "mobile") {
|
|
69
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
70
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
|
|
71
|
-
/* @__PURE__ */ jsxs(Avatar, { className: "h-10 w-10", children: [
|
|
72
|
-
/* @__PURE__ */ jsx(AvatarImage, { src: userAvatar, alt: displayName }),
|
|
73
|
-
/* @__PURE__ */ jsx(AvatarFallback, { children: userInitial })
|
|
74
|
-
] }),
|
|
75
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
76
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground truncate", children: displayName }),
|
|
77
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: user.email })
|
|
78
|
-
] })
|
|
79
|
-
] }),
|
|
80
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: menuGroups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { children: [
|
|
81
|
-
group.title && /* @__PURE__ */ jsx("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group.title }) }),
|
|
82
|
-
group.items.map((item, itemIndex) => {
|
|
83
|
-
const Icon = item.icon;
|
|
84
|
-
const isDestructive = item.variant === "destructive";
|
|
85
|
-
const baseClasses = `flex items-center gap-3 px-4 py-3 text-sm font-medium rounded-sm transition-colors w-full text-left ${isDestructive ? "text-destructive hover:bg-destructive/10" : "text-foreground hover:bg-accent hover:text-accent-foreground"}`;
|
|
86
|
-
if (item.onClick) {
|
|
87
|
-
return /* @__PURE__ */ jsxs(
|
|
88
|
-
"button",
|
|
89
|
-
{
|
|
90
|
-
onClick: item.onClick,
|
|
91
|
-
className: baseClasses,
|
|
92
|
-
children: [
|
|
93
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
94
|
-
item.label
|
|
95
|
-
]
|
|
96
|
-
},
|
|
97
|
-
itemIndex
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (item.href) {
|
|
101
|
-
return /* @__PURE__ */ jsxs(Link, { href: item.href, className: baseClasses, children: [
|
|
102
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
103
|
-
item.label
|
|
104
|
-
] }, itemIndex);
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
})
|
|
108
|
-
] }, groupIndex)) })
|
|
109
|
-
] });
|
|
110
|
-
}
|
|
111
|
-
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
112
|
-
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "rounded-full", children: [
|
|
113
|
-
/* @__PURE__ */ jsxs(Avatar, { className: "h-8 w-8", children: [
|
|
114
|
-
/* @__PURE__ */ jsx(AvatarImage, { src: userAvatar, alt: displayName }),
|
|
115
|
-
/* @__PURE__ */ jsx(AvatarFallback, { children: userInitial })
|
|
116
|
-
] }),
|
|
117
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "User menu" })
|
|
118
|
-
] }) }),
|
|
119
|
-
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
|
|
120
|
-
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-1", children: [
|
|
121
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none", children: displayName }),
|
|
122
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs leading-none text-muted-foreground", children: user.email })
|
|
123
|
-
] }) }),
|
|
124
|
-
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
125
|
-
menuGroups.map((group, groupIndex) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
126
|
-
groupIndex > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
127
|
-
/* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
|
|
128
|
-
group.title && /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group.title }),
|
|
129
|
-
group.items.map((item, itemIndex) => {
|
|
130
|
-
const Icon = item.icon;
|
|
131
|
-
const isDestructive = item.variant === "destructive";
|
|
132
|
-
if (item.onClick) {
|
|
133
|
-
return /* @__PURE__ */ jsxs(
|
|
134
|
-
DropdownMenuItem,
|
|
135
|
-
{
|
|
136
|
-
onClick: item.onClick,
|
|
137
|
-
className: isDestructive ? "text-destructive focus:text-destructive" : "",
|
|
138
|
-
children: [
|
|
139
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "mr-2 h-4 w-4" }),
|
|
140
|
-
/* @__PURE__ */ jsx("span", { children: item.label })
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
itemIndex
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
if (item.href) {
|
|
147
|
-
return /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
148
|
-
Link,
|
|
149
|
-
{
|
|
150
|
-
href: item.href,
|
|
151
|
-
className: `flex items-center ${isDestructive ? "text-destructive" : ""}`,
|
|
152
|
-
children: [
|
|
153
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "mr-2 h-4 w-4" }),
|
|
154
|
-
/* @__PURE__ */ jsx("span", { children: item.label })
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
) }, itemIndex);
|
|
158
|
-
}
|
|
159
|
-
return null;
|
|
160
|
-
})
|
|
161
|
-
] })
|
|
162
|
-
] }, groupIndex))
|
|
163
|
-
] })
|
|
164
|
-
] });
|
|
165
|
-
}
|
|
166
|
-
__name(UserMenu, "UserMenu");
|
|
167
|
-
var defaultFallback = /* @__PURE__ */ jsx(
|
|
168
|
-
Preloader,
|
|
169
|
-
{
|
|
170
|
-
variant: "fullscreen",
|
|
171
|
-
text: "Loading...",
|
|
172
|
-
size: "lg",
|
|
173
|
-
backdrop: true,
|
|
174
|
-
backdropOpacity: 80
|
|
175
|
-
}
|
|
176
|
-
);
|
|
177
|
-
function ClientOnly({
|
|
178
|
-
children,
|
|
179
|
-
fallback = defaultFallback
|
|
180
|
-
}) {
|
|
181
|
-
const [mounted, setMounted] = useState(false);
|
|
182
|
-
useEffect(() => {
|
|
183
|
-
setMounted(true);
|
|
184
|
-
}, []);
|
|
185
|
-
if (!mounted) {
|
|
186
|
-
return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
187
|
-
}
|
|
188
|
-
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
189
|
-
}
|
|
190
|
-
__name(ClientOnly, "ClientOnly");
|
|
191
|
-
function getLucideIcon(icon, fallback = LucideIcons.CloudLightning) {
|
|
192
|
-
if (!icon) {
|
|
193
|
-
return fallback;
|
|
194
|
-
}
|
|
195
|
-
if (typeof icon === "string") {
|
|
196
|
-
const IconComponent = LucideIcons[icon];
|
|
197
|
-
return IconComponent || fallback;
|
|
198
|
-
}
|
|
199
|
-
return icon;
|
|
200
|
-
}
|
|
201
|
-
__name(getLucideIcon, "getLucideIcon");
|
|
202
|
-
function LucideIcon({
|
|
203
|
-
icon,
|
|
204
|
-
fallback = LucideIcons.CloudLightning,
|
|
205
|
-
className,
|
|
206
|
-
...props
|
|
207
|
-
}) {
|
|
208
|
-
const IconComponent = getLucideIcon(icon, fallback);
|
|
209
|
-
return /* @__PURE__ */ jsx(IconComponent, { className: cn(className), ...props });
|
|
210
|
-
}
|
|
211
|
-
__name(LucideIcon, "LucideIcon");
|
|
212
|
-
var defaultFallback2 = /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
213
|
-
/* @__PURE__ */ jsx("div", { className: "inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]" }),
|
|
214
|
-
/* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: "Loading..." })
|
|
215
|
-
] }) });
|
|
216
|
-
function Suspense({ children, fallback = defaultFallback2 }) {
|
|
217
|
-
return /* @__PURE__ */ jsx(Suspense$1, { fallback, children });
|
|
218
|
-
}
|
|
219
|
-
__name(Suspense, "Suspense");
|
|
220
|
-
var _ErrorBoundary = class _ErrorBoundary extends Component {
|
|
221
|
-
constructor(props) {
|
|
222
|
-
super(props);
|
|
223
|
-
this.state = { hasError: false, error: null };
|
|
224
|
-
}
|
|
225
|
-
static getDerivedStateFromError(error) {
|
|
226
|
-
return { hasError: true, error };
|
|
227
|
-
}
|
|
228
|
-
componentDidCatch(error, errorInfo) {
|
|
229
|
-
if (this.props.onError) {
|
|
230
|
-
this.props.onError(error, errorInfo);
|
|
231
|
-
}
|
|
232
|
-
{
|
|
233
|
-
console.error("ErrorBoundary caught an error:", error, errorInfo);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
render() {
|
|
237
|
-
if (this.state.hasError) {
|
|
238
|
-
return /* @__PURE__ */ jsx("div", { className: "flex min-h-screen items-center justify-center bg-background p-4", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md w-full space-y-4 text-center", children: [
|
|
239
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Something went wrong" }),
|
|
240
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "We're sorry, but something unexpected happened. Please try refreshing the page." }),
|
|
241
|
-
this.props.supportEmail && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
242
|
-
"If the problem persists, please contact",
|
|
243
|
-
" ",
|
|
244
|
-
/* @__PURE__ */ jsx(
|
|
245
|
-
"a",
|
|
246
|
-
{
|
|
247
|
-
href: `mailto:${this.props.supportEmail}`,
|
|
248
|
-
className: "text-primary hover:underline",
|
|
249
|
-
children: this.props.supportEmail
|
|
250
|
-
}
|
|
251
|
-
)
|
|
252
|
-
] }),
|
|
253
|
-
/* @__PURE__ */ jsx(
|
|
254
|
-
"button",
|
|
255
|
-
{
|
|
256
|
-
onClick: () => window.location.reload(),
|
|
257
|
-
className: "px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90",
|
|
258
|
-
children: "Refresh Page"
|
|
259
|
-
}
|
|
260
|
-
)
|
|
261
|
-
] }) });
|
|
262
|
-
}
|
|
263
|
-
return this.props.children;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
__name(_ErrorBoundary, "ErrorBoundary");
|
|
267
|
-
var ErrorBoundary = _ErrorBoundary;
|
|
268
|
-
|
|
269
|
-
// src/components/errors/ErrorsTracker/utils/formatters.ts
|
|
270
|
-
function formatZodIssues(error, maxIssues = 3) {
|
|
271
|
-
const issues = error.issues.slice(0, maxIssues);
|
|
272
|
-
const formatted = issues.map((issue) => {
|
|
273
|
-
const path = issue.path.join(".") || "root";
|
|
274
|
-
return `${path}: ${issue.message}`;
|
|
275
|
-
});
|
|
276
|
-
if (error.issues.length > maxIssues) {
|
|
277
|
-
formatted.push(`... and ${error.issues.length - maxIssues} more`);
|
|
278
|
-
}
|
|
279
|
-
return formatted.join(", ");
|
|
280
|
-
}
|
|
281
|
-
__name(formatZodIssues, "formatZodIssues");
|
|
282
|
-
function formatValidationErrorForClipboard(detail) {
|
|
283
|
-
const errorData = {
|
|
284
|
-
type: "validation",
|
|
285
|
-
timestamp: detail.timestamp.toISOString(),
|
|
286
|
-
operation: detail.operation,
|
|
287
|
-
endpoint: {
|
|
288
|
-
method: detail.method,
|
|
289
|
-
path: detail.path
|
|
290
|
-
},
|
|
291
|
-
validation_errors: detail.error.issues.map((issue) => ({
|
|
292
|
-
path: issue.path.join(".") || "root",
|
|
293
|
-
message: issue.message,
|
|
294
|
-
code: issue.code,
|
|
295
|
-
..."expected" in issue && { expected: issue.expected },
|
|
296
|
-
..."received" in issue && { received: issue.received },
|
|
297
|
-
..."minimum" in issue && { minimum: issue.minimum },
|
|
298
|
-
..."maximum" in issue && { maximum: issue.maximum }
|
|
299
|
-
})),
|
|
300
|
-
response: detail.response,
|
|
301
|
-
total_errors: detail.error.issues.length
|
|
302
|
-
};
|
|
303
|
-
return JSON.stringify(errorData, null, 2);
|
|
304
|
-
}
|
|
305
|
-
__name(formatValidationErrorForClipboard, "formatValidationErrorForClipboard");
|
|
306
|
-
function formatCORSErrorForClipboard(detail) {
|
|
307
|
-
const errorData = {
|
|
308
|
-
type: "cors",
|
|
309
|
-
timestamp: detail.timestamp.toISOString(),
|
|
310
|
-
url: detail.url,
|
|
311
|
-
method: detail.method,
|
|
312
|
-
error: detail.error
|
|
313
|
-
};
|
|
314
|
-
return JSON.stringify(errorData, null, 2);
|
|
315
|
-
}
|
|
316
|
-
__name(formatCORSErrorForClipboard, "formatCORSErrorForClipboard");
|
|
317
|
-
function formatNetworkErrorForClipboard(detail) {
|
|
318
|
-
const errorData = {
|
|
319
|
-
type: "network",
|
|
320
|
-
timestamp: detail.timestamp.toISOString(),
|
|
321
|
-
url: detail.url,
|
|
322
|
-
method: detail.method,
|
|
323
|
-
error: detail.error,
|
|
324
|
-
...detail.statusCode && { statusCode: detail.statusCode }
|
|
325
|
-
};
|
|
326
|
-
return JSON.stringify(errorData, null, 2);
|
|
327
|
-
}
|
|
328
|
-
__name(formatNetworkErrorForClipboard, "formatNetworkErrorForClipboard");
|
|
329
|
-
function formatCentrifugoErrorForClipboard(detail) {
|
|
330
|
-
const errorData = {
|
|
331
|
-
type: "centrifugo",
|
|
332
|
-
timestamp: detail.timestamp.toISOString(),
|
|
333
|
-
method: detail.method,
|
|
334
|
-
error: detail.error,
|
|
335
|
-
...detail.code !== void 0 && { code: detail.code },
|
|
336
|
-
...detail.data && { data: detail.data }
|
|
337
|
-
};
|
|
338
|
-
return JSON.stringify(errorData, null, 2);
|
|
339
|
-
}
|
|
340
|
-
__name(formatCentrifugoErrorForClipboard, "formatCentrifugoErrorForClipboard");
|
|
341
|
-
function extractDomain(url) {
|
|
342
|
-
try {
|
|
343
|
-
const urlObj = new URL(url);
|
|
344
|
-
return urlObj.origin;
|
|
345
|
-
} catch {
|
|
346
|
-
return url;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
__name(extractDomain, "extractDomain");
|
|
350
|
-
function formatErrorTitle(detail) {
|
|
351
|
-
switch (detail.type) {
|
|
352
|
-
case "validation":
|
|
353
|
-
return `\u274C Validation Error in ${detail.operation}`;
|
|
354
|
-
case "cors":
|
|
355
|
-
return "\u{1F6AB} CORS Error";
|
|
356
|
-
case "network":
|
|
357
|
-
return detail.statusCode ? `\u26A0\uFE0F Network Error (${detail.statusCode})` : "\u26A0\uFE0F Network Error";
|
|
358
|
-
case "centrifugo":
|
|
359
|
-
return detail.code !== void 0 ? `\u{1F50C} Centrifugo Error (${detail.code})` : "\u{1F50C} Centrifugo Error";
|
|
360
|
-
default:
|
|
361
|
-
return "\u274C Error";
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
__name(formatErrorTitle, "formatErrorTitle");
|
|
365
|
-
function getAuthToken() {
|
|
366
|
-
if (typeof window === "undefined") return null;
|
|
367
|
-
try {
|
|
368
|
-
const token = localStorage.getItem("access_token") || localStorage.getItem("token") || localStorage.getItem("auth_token");
|
|
369
|
-
return token;
|
|
370
|
-
} catch (error) {
|
|
371
|
-
consola.error("Failed to get auth token:", error);
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
__name(getAuthToken, "getAuthToken");
|
|
376
|
-
function formatHeaders(headers) {
|
|
377
|
-
return Object.entries(headers).map(
|
|
378
|
-
([key, value]) => `-H '${key}: ${value}'`
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
__name(formatHeaders, "formatHeaders");
|
|
382
|
-
function escapeShell(str) {
|
|
383
|
-
return str.replace(/'/g, "'\\''");
|
|
384
|
-
}
|
|
385
|
-
__name(escapeShell, "escapeShell");
|
|
386
|
-
function generateCurl(options) {
|
|
387
|
-
const {
|
|
388
|
-
method,
|
|
389
|
-
path,
|
|
390
|
-
token = getAuthToken() || void 0,
|
|
391
|
-
// Auto-fetch if not provided
|
|
392
|
-
body,
|
|
393
|
-
headers = {},
|
|
394
|
-
baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"
|
|
395
|
-
} = options;
|
|
396
|
-
const curlParts = ["curl"];
|
|
397
|
-
const url = `${baseUrl}${path}`;
|
|
398
|
-
curlParts.push(`'${url}'`);
|
|
399
|
-
if (method.toUpperCase() !== "GET") {
|
|
400
|
-
curlParts.push(`-X ${method.toUpperCase()}`);
|
|
401
|
-
}
|
|
402
|
-
const allHeaders = {
|
|
403
|
-
"Accept": "*/*",
|
|
404
|
-
"Content-Type": "application/json",
|
|
405
|
-
...headers
|
|
406
|
-
};
|
|
407
|
-
if (token) {
|
|
408
|
-
allHeaders["Authorization"] = `Bearer ${token}`;
|
|
409
|
-
}
|
|
410
|
-
const headerStrings = formatHeaders(allHeaders);
|
|
411
|
-
curlParts.push(...headerStrings);
|
|
412
|
-
if (body && method.toUpperCase() !== "GET") {
|
|
413
|
-
const bodyJson = typeof body === "string" ? body : JSON.stringify(body, null, 2);
|
|
414
|
-
curlParts.push(`-d '${escapeShell(bodyJson)}'`);
|
|
415
|
-
}
|
|
416
|
-
return curlParts.join(" \\\n ");
|
|
417
|
-
}
|
|
418
|
-
__name(generateCurl, "generateCurl");
|
|
419
|
-
function generateCurlFromError(detail) {
|
|
420
|
-
return generateCurl({
|
|
421
|
-
method: detail.method,
|
|
422
|
-
path: detail.path
|
|
423
|
-
// token is auto-fetched in generateCurl
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
__name(generateCurlFromError, "generateCurlFromError");
|
|
427
|
-
function formatErrorForClipboard(detail) {
|
|
428
|
-
switch (detail.type) {
|
|
429
|
-
case "validation":
|
|
430
|
-
return formatValidationErrorForClipboard(detail);
|
|
431
|
-
case "cors":
|
|
432
|
-
return formatCORSErrorForClipboard(detail);
|
|
433
|
-
case "network":
|
|
434
|
-
return formatNetworkErrorForClipboard(detail);
|
|
435
|
-
case "centrifugo":
|
|
436
|
-
return formatCentrifugoErrorForClipboard(detail);
|
|
437
|
-
default:
|
|
438
|
-
return JSON.stringify(detail, null, 2);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
__name(formatErrorForClipboard, "formatErrorForClipboard");
|
|
442
|
-
function supportsCurl(detail) {
|
|
443
|
-
return detail.type === "validation";
|
|
444
|
-
}
|
|
445
|
-
__name(supportsCurl, "supportsCurl");
|
|
446
|
-
function ErrorButtons({ detail }) {
|
|
447
|
-
const { copyToClipboard } = useCopy();
|
|
448
|
-
const handleCopyError = /* @__PURE__ */ __name(async (e) => {
|
|
449
|
-
e.preventDefault();
|
|
450
|
-
e.stopPropagation();
|
|
451
|
-
const formattedError = formatErrorForClipboard(detail);
|
|
452
|
-
await copyToClipboard(formattedError, "\u2705 Error details copied");
|
|
453
|
-
}, "handleCopyError");
|
|
454
|
-
const handleCopyCurl = /* @__PURE__ */ __name(async (e) => {
|
|
455
|
-
e.preventDefault();
|
|
456
|
-
e.stopPropagation();
|
|
457
|
-
if (detail.type === "validation") {
|
|
458
|
-
const curl = generateCurlFromError({
|
|
459
|
-
method: detail.method,
|
|
460
|
-
path: detail.path,
|
|
461
|
-
response: detail.response
|
|
462
|
-
});
|
|
463
|
-
await copyToClipboard(curl, "\u2705 cURL command copied");
|
|
464
|
-
}
|
|
465
|
-
}, "handleCopyCurl");
|
|
466
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-2", children: [
|
|
467
|
-
/* @__PURE__ */ jsxs(
|
|
468
|
-
Button$1,
|
|
469
|
-
{
|
|
470
|
-
size: "sm",
|
|
471
|
-
variant: "secondary",
|
|
472
|
-
onClick: handleCopyError,
|
|
473
|
-
className: "h-8 text-xs bg-background hover:bg-background/80 text-foreground border border-border gap-1.5",
|
|
474
|
-
children: [
|
|
475
|
-
/* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }),
|
|
476
|
-
"Copy Error"
|
|
477
|
-
]
|
|
478
|
-
}
|
|
479
|
-
),
|
|
480
|
-
supportsCurl(detail) && /* @__PURE__ */ jsxs(
|
|
481
|
-
Button$1,
|
|
482
|
-
{
|
|
483
|
-
size: "sm",
|
|
484
|
-
variant: "secondary",
|
|
485
|
-
onClick: handleCopyCurl,
|
|
486
|
-
className: "h-8 text-xs bg-background hover:bg-background/80 text-foreground border border-border gap-1.5",
|
|
487
|
-
children: [
|
|
488
|
-
/* @__PURE__ */ jsx(Terminal, { className: "h-3.5 w-3.5" }),
|
|
489
|
-
"Copy cURL"
|
|
490
|
-
]
|
|
491
|
-
}
|
|
492
|
-
)
|
|
493
|
-
] });
|
|
494
|
-
}
|
|
495
|
-
__name(ErrorButtons, "ErrorButtons");
|
|
496
|
-
function buildValidationDescription(detail, config2) {
|
|
497
|
-
const descriptionParts = [];
|
|
498
|
-
if (config2.showPath) {
|
|
499
|
-
descriptionParts.push(`${detail.method} ${detail.path}`);
|
|
500
|
-
}
|
|
501
|
-
if (config2.showErrorCount) {
|
|
502
|
-
const count = detail.error.issues.length;
|
|
503
|
-
const plural = count === 1 ? "error" : "errors";
|
|
504
|
-
descriptionParts.push(`${count} ${plural}`);
|
|
505
|
-
}
|
|
506
|
-
const issuesText = formatZodIssues(detail.error, config2.maxIssuesInToast);
|
|
507
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-sm", children: [
|
|
508
|
-
descriptionParts.length > 0 && /* @__PURE__ */ jsx("div", { className: "font-mono text-xs opacity-90", children: descriptionParts.join(" \u2022 ") }),
|
|
509
|
-
/* @__PURE__ */ jsx("div", { className: "opacity-90", children: issuesText }),
|
|
510
|
-
/* @__PURE__ */ jsx(ErrorButtons, { detail })
|
|
511
|
-
] });
|
|
512
|
-
}
|
|
513
|
-
__name(buildValidationDescription, "buildValidationDescription");
|
|
514
|
-
function buildCORSDescription(detail, config2) {
|
|
515
|
-
const domain = extractDomain(detail.url);
|
|
516
|
-
const parts = [];
|
|
517
|
-
if (config2.showMethod && config2.showUrl) {
|
|
518
|
-
parts.push(`${detail.method} ${detail.url}`);
|
|
519
|
-
} else if (config2.showUrl) {
|
|
520
|
-
parts.push(detail.url);
|
|
521
|
-
} else if (config2.showMethod) {
|
|
522
|
-
parts.push(`${detail.method} request blocked`);
|
|
523
|
-
}
|
|
524
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-sm", children: [
|
|
525
|
-
parts.length > 0 && /* @__PURE__ */ jsx("div", { className: "font-mono text-xs opacity-90", children: parts.join(" \u2022 ") }),
|
|
526
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
527
|
-
/* @__PURE__ */ jsx("div", { className: "font-medium", children: "Request blocked by CORS policy" }),
|
|
528
|
-
/* @__PURE__ */ jsxs("div", { className: "text-xs opacity-75", children: [
|
|
529
|
-
"Check CORS configuration on ",
|
|
530
|
-
domain
|
|
531
|
-
] })
|
|
532
|
-
] }),
|
|
533
|
-
/* @__PURE__ */ jsx(ErrorButtons, { detail })
|
|
534
|
-
] });
|
|
535
|
-
}
|
|
536
|
-
__name(buildCORSDescription, "buildCORSDescription");
|
|
537
|
-
function buildNetworkDescription(detail, config2) {
|
|
538
|
-
const parts = [];
|
|
539
|
-
if (config2.showMethod && config2.showUrl) {
|
|
540
|
-
parts.push(`${detail.method} ${detail.url}`);
|
|
541
|
-
} else if (config2.showUrl) {
|
|
542
|
-
parts.push(detail.url);
|
|
543
|
-
} else if (config2.showMethod) {
|
|
544
|
-
parts.push(`${detail.method} request failed`);
|
|
545
|
-
}
|
|
546
|
-
if (config2.showStatusCode && detail.statusCode) {
|
|
547
|
-
parts.push(`Status: ${detail.statusCode}`);
|
|
548
|
-
}
|
|
549
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-sm", children: [
|
|
550
|
-
parts.length > 0 && /* @__PURE__ */ jsx("div", { className: "font-mono text-xs opacity-90", children: parts.join(" \u2022 ") }),
|
|
551
|
-
/* @__PURE__ */ jsx("div", { className: "opacity-90", children: detail.error }),
|
|
552
|
-
/* @__PURE__ */ jsx(ErrorButtons, { detail })
|
|
553
|
-
] });
|
|
554
|
-
}
|
|
555
|
-
__name(buildNetworkDescription, "buildNetworkDescription");
|
|
556
|
-
function buildCentrifugoDescription(detail, config2) {
|
|
557
|
-
const parts = [];
|
|
558
|
-
if (config2.showMethod) {
|
|
559
|
-
parts.push(`RPC: ${detail.method}`);
|
|
560
|
-
}
|
|
561
|
-
if (config2.showCode && detail.code !== void 0) {
|
|
562
|
-
parts.push(`Code: ${detail.code}`);
|
|
563
|
-
}
|
|
564
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-sm", children: [
|
|
565
|
-
parts.length > 0 && /* @__PURE__ */ jsx("div", { className: "font-mono text-xs opacity-90", children: parts.join(" \u2022 ") }),
|
|
566
|
-
/* @__PURE__ */ jsx("div", { className: "opacity-90", children: detail.error }),
|
|
567
|
-
/* @__PURE__ */ jsx(ErrorButtons, { detail })
|
|
568
|
-
] });
|
|
569
|
-
}
|
|
570
|
-
__name(buildCentrifugoDescription, "buildCentrifugoDescription");
|
|
571
|
-
function createErrorToast(detail, config2) {
|
|
572
|
-
let description;
|
|
573
|
-
if (detail.type === "validation") {
|
|
574
|
-
description = buildValidationDescription(detail, config2);
|
|
575
|
-
} else if (detail.type === "cors") {
|
|
576
|
-
description = buildCORSDescription(detail, config2);
|
|
577
|
-
} else if (detail.type === "centrifugo") {
|
|
578
|
-
description = buildCentrifugoDescription(detail, config2);
|
|
579
|
-
} else {
|
|
580
|
-
description = buildNetworkDescription(detail, config2);
|
|
581
|
-
}
|
|
582
|
-
return {
|
|
583
|
-
title: formatErrorTitle(detail),
|
|
584
|
-
description,
|
|
585
|
-
variant: "destructive",
|
|
586
|
-
duration: config2.duration
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
__name(createErrorToast, "createErrorToast");
|
|
590
|
-
|
|
591
|
-
// src/components/errors/ErrorsTracker/types.ts
|
|
592
|
-
var ERROR_EVENTS = {
|
|
593
|
-
VALIDATION: "zod-validation-error",
|
|
594
|
-
CORS: "cors-error",
|
|
595
|
-
NETWORK: "network-error",
|
|
596
|
-
/** Unified Centrifugo event - filter by detail.type === 'error' */
|
|
597
|
-
CENTRIFUGO: "centrifugo"
|
|
598
|
-
};
|
|
599
|
-
var DEFAULT_ERROR_CONFIG = {
|
|
600
|
-
enabled: true,
|
|
601
|
-
showToast: true,
|
|
602
|
-
maxErrors: 50,
|
|
603
|
-
duration: 8e3
|
|
604
|
-
};
|
|
605
|
-
var DEFAULT_VALIDATION_CONFIG = {
|
|
606
|
-
...DEFAULT_ERROR_CONFIG,
|
|
607
|
-
showOperation: true,
|
|
608
|
-
showPath: true,
|
|
609
|
-
showErrorCount: true,
|
|
610
|
-
maxIssuesInToast: 3
|
|
611
|
-
};
|
|
612
|
-
var DEFAULT_CORS_CONFIG = {
|
|
613
|
-
...DEFAULT_ERROR_CONFIG,
|
|
614
|
-
duration: 0,
|
|
615
|
-
// Don't auto-dismiss CORS errors
|
|
616
|
-
showUrl: true,
|
|
617
|
-
showMethod: true
|
|
618
|
-
};
|
|
619
|
-
var DEFAULT_NETWORK_CONFIG = {
|
|
620
|
-
...DEFAULT_ERROR_CONFIG,
|
|
621
|
-
duration: 0,
|
|
622
|
-
// Don't auto-dismiss network errors
|
|
623
|
-
showUrl: true,
|
|
624
|
-
showMethod: true,
|
|
625
|
-
showStatusCode: true
|
|
626
|
-
};
|
|
627
|
-
var DEFAULT_CENTRIFUGO_CONFIG = {
|
|
628
|
-
...DEFAULT_ERROR_CONFIG,
|
|
629
|
-
duration: 0,
|
|
630
|
-
// Don't auto-dismiss centrifugo errors
|
|
631
|
-
showMethod: true,
|
|
632
|
-
showCode: true
|
|
633
|
-
};
|
|
634
|
-
var ErrorTrackingContext = createContext(void 0);
|
|
635
|
-
var errorIdCounter = 0;
|
|
636
|
-
function generateErrorId(type) {
|
|
637
|
-
return `${type}-error-${Date.now()}-${++errorIdCounter}`;
|
|
638
|
-
}
|
|
639
|
-
__name(generateErrorId, "generateErrorId");
|
|
640
|
-
function ErrorTrackingProvider({
|
|
641
|
-
children,
|
|
642
|
-
validation: userValidationConfig,
|
|
643
|
-
cors: userCorsConfig,
|
|
644
|
-
network: userNetworkConfig,
|
|
645
|
-
centrifugo: userCentrifugoConfig,
|
|
646
|
-
onError
|
|
647
|
-
}) {
|
|
648
|
-
const [errors, setErrors] = useState([]);
|
|
649
|
-
const validationConfig = {
|
|
650
|
-
...DEFAULT_VALIDATION_CONFIG,
|
|
651
|
-
...userValidationConfig
|
|
652
|
-
};
|
|
653
|
-
const corsConfig = {
|
|
654
|
-
...DEFAULT_CORS_CONFIG,
|
|
655
|
-
...userCorsConfig
|
|
656
|
-
};
|
|
657
|
-
const networkConfig = {
|
|
658
|
-
...DEFAULT_NETWORK_CONFIG,
|
|
659
|
-
...userNetworkConfig
|
|
660
|
-
};
|
|
661
|
-
const centrifugoConfig = {
|
|
662
|
-
...DEFAULT_CENTRIFUGO_CONFIG,
|
|
663
|
-
...userCentrifugoConfig
|
|
664
|
-
};
|
|
665
|
-
const clearErrors = useCallback(() => {
|
|
666
|
-
setErrors([]);
|
|
667
|
-
}, []);
|
|
668
|
-
const clearErrorsByType = useCallback((type) => {
|
|
669
|
-
setErrors((prev) => prev.filter((error) => error.type !== type));
|
|
670
|
-
}, []);
|
|
671
|
-
const clearError = useCallback((id) => {
|
|
672
|
-
setErrors((prev) => prev.filter((error) => error.id !== id));
|
|
673
|
-
}, []);
|
|
674
|
-
const handleError = useCallback(
|
|
675
|
-
(detail, config2) => {
|
|
676
|
-
const storedError = {
|
|
677
|
-
...detail,
|
|
678
|
-
id: generateErrorId(detail.type)
|
|
679
|
-
};
|
|
680
|
-
setErrors((prev) => {
|
|
681
|
-
const updated = [storedError, ...prev];
|
|
682
|
-
return updated.slice(0, config2.maxErrors);
|
|
683
|
-
});
|
|
684
|
-
const shouldShowToast = onError?.(detail) !== false;
|
|
685
|
-
if (config2.showToast && shouldShowToast) {
|
|
686
|
-
const toastOptions = createErrorToast(detail, config2);
|
|
687
|
-
toast.error(toastOptions.title, {
|
|
688
|
-
description: toastOptions.description,
|
|
689
|
-
duration: toastOptions.duration
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
|
-
[onError]
|
|
694
|
-
);
|
|
695
|
-
useEffect(() => {
|
|
696
|
-
if (typeof window === "undefined") return;
|
|
697
|
-
const handlers = [];
|
|
698
|
-
if (validationConfig.enabled) {
|
|
699
|
-
const handler = /* @__PURE__ */ __name((event) => {
|
|
700
|
-
if (!(event instanceof CustomEvent)) return;
|
|
701
|
-
const detail = {
|
|
702
|
-
...event.detail,
|
|
703
|
-
type: "validation"
|
|
704
|
-
};
|
|
705
|
-
handleError(detail, validationConfig);
|
|
706
|
-
}, "handler");
|
|
707
|
-
window.addEventListener(ERROR_EVENTS.VALIDATION, handler);
|
|
708
|
-
handlers.push({ event: ERROR_EVENTS.VALIDATION, handler });
|
|
709
|
-
}
|
|
710
|
-
if (corsConfig.enabled) {
|
|
711
|
-
const handler = /* @__PURE__ */ __name((event) => {
|
|
712
|
-
if (!(event instanceof CustomEvent)) return;
|
|
713
|
-
const detail = {
|
|
714
|
-
...event.detail,
|
|
715
|
-
type: "cors"
|
|
716
|
-
};
|
|
717
|
-
handleError(detail, corsConfig);
|
|
718
|
-
}, "handler");
|
|
719
|
-
window.addEventListener(ERROR_EVENTS.CORS, handler);
|
|
720
|
-
handlers.push({ event: ERROR_EVENTS.CORS, handler });
|
|
721
|
-
}
|
|
722
|
-
if (networkConfig.enabled) {
|
|
723
|
-
const handler = /* @__PURE__ */ __name((event) => {
|
|
724
|
-
if (!(event instanceof CustomEvent)) return;
|
|
725
|
-
const detail = {
|
|
726
|
-
...event.detail,
|
|
727
|
-
type: "network"
|
|
728
|
-
};
|
|
729
|
-
handleError(detail, networkConfig);
|
|
730
|
-
}, "handler");
|
|
731
|
-
window.addEventListener(ERROR_EVENTS.NETWORK, handler);
|
|
732
|
-
handlers.push({ event: ERROR_EVENTS.NETWORK, handler });
|
|
733
|
-
}
|
|
734
|
-
if (centrifugoConfig.enabled) {
|
|
735
|
-
const handler = /* @__PURE__ */ __name((event) => {
|
|
736
|
-
if (!(event instanceof CustomEvent)) return;
|
|
737
|
-
if (event.detail?.type !== "error") return;
|
|
738
|
-
const detail = {
|
|
739
|
-
type: "centrifugo",
|
|
740
|
-
method: event.detail.data?.method || "unknown",
|
|
741
|
-
error: event.detail.data?.error || "Unknown error",
|
|
742
|
-
code: event.detail.data?.code,
|
|
743
|
-
data: event.detail.data?.data,
|
|
744
|
-
timestamp: event.detail.timestamp || /* @__PURE__ */ new Date()
|
|
745
|
-
};
|
|
746
|
-
handleError(detail, centrifugoConfig);
|
|
747
|
-
}, "handler");
|
|
748
|
-
window.addEventListener(ERROR_EVENTS.CENTRIFUGO, handler);
|
|
749
|
-
handlers.push({ event: ERROR_EVENTS.CENTRIFUGO, handler });
|
|
750
|
-
}
|
|
751
|
-
return () => {
|
|
752
|
-
handlers.forEach(({ event, handler }) => {
|
|
753
|
-
window.removeEventListener(event, handler);
|
|
754
|
-
});
|
|
755
|
-
};
|
|
756
|
-
}, [handleError, validationConfig, corsConfig, networkConfig, centrifugoConfig]);
|
|
757
|
-
const validationErrors = errors.filter((e) => e.type === "validation");
|
|
758
|
-
const corsErrors = errors.filter((e) => e.type === "cors");
|
|
759
|
-
const networkErrors = errors.filter((e) => e.type === "network");
|
|
760
|
-
const centrifugoErrors = errors.filter((e) => e.type === "centrifugo");
|
|
761
|
-
const value = {
|
|
762
|
-
errors,
|
|
763
|
-
validationErrors,
|
|
764
|
-
corsErrors,
|
|
765
|
-
networkErrors,
|
|
766
|
-
centrifugoErrors,
|
|
767
|
-
clearErrors,
|
|
768
|
-
clearErrorsByType,
|
|
769
|
-
clearError,
|
|
770
|
-
errorCount: errors.length,
|
|
771
|
-
latestError: errors[0] || null,
|
|
772
|
-
config: {
|
|
773
|
-
validation: validationConfig,
|
|
774
|
-
cors: corsConfig,
|
|
775
|
-
network: networkConfig,
|
|
776
|
-
centrifugo: centrifugoConfig
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
return /* @__PURE__ */ jsx(ErrorTrackingContext.Provider, { value, children });
|
|
780
|
-
}
|
|
781
|
-
__name(ErrorTrackingProvider, "ErrorTrackingProvider");
|
|
782
|
-
var isProduction = false;
|
|
783
|
-
var Analytics = {
|
|
784
|
-
/**
|
|
785
|
-
* Initialize Google Analytics (called automatically by useAnalytics hook)
|
|
786
|
-
*/
|
|
787
|
-
init: /* @__PURE__ */ __name((trackingId) => {
|
|
788
|
-
return;
|
|
789
|
-
}, "init"),
|
|
790
|
-
/**
|
|
791
|
-
* Check if Analytics is enabled and initialized
|
|
792
|
-
*/
|
|
793
|
-
isEnabled: /* @__PURE__ */ __name(() => isProduction, "isEnabled"),
|
|
794
|
-
/**
|
|
795
|
-
* Track a page view
|
|
796
|
-
*/
|
|
797
|
-
pageview: /* @__PURE__ */ __name((path) => {
|
|
798
|
-
if (!Analytics.isEnabled()) return;
|
|
799
|
-
ReactGA.send({ hitType: "pageview", page: path });
|
|
800
|
-
}, "pageview"),
|
|
801
|
-
/**
|
|
802
|
-
* Track a custom event
|
|
803
|
-
* @param name - Event name (action)
|
|
804
|
-
* @param params - Optional event parameters
|
|
805
|
-
*/
|
|
806
|
-
event: /* @__PURE__ */ __name((name, params = {}) => {
|
|
807
|
-
if (!Analytics.isEnabled()) return;
|
|
808
|
-
ReactGA.event(name, params);
|
|
809
|
-
}, "event"),
|
|
810
|
-
/**
|
|
811
|
-
* Set user ID for tracking
|
|
812
|
-
*/
|
|
813
|
-
setUser: /* @__PURE__ */ __name((userId) => {
|
|
814
|
-
if (!Analytics.isEnabled()) return;
|
|
815
|
-
ReactGA.set({ user_id: userId });
|
|
816
|
-
}, "setUser"),
|
|
817
|
-
/**
|
|
818
|
-
* Set custom dimensions/metrics
|
|
819
|
-
*/
|
|
820
|
-
set: /* @__PURE__ */ __name((fieldsObject) => {
|
|
821
|
-
if (!Analytics.isEnabled()) return;
|
|
822
|
-
ReactGA.set(fieldsObject);
|
|
823
|
-
}, "set")
|
|
824
|
-
};
|
|
825
|
-
function useAnalytics(trackingIdProp) {
|
|
826
|
-
const pathname = usePathname();
|
|
827
|
-
const { user, isAuthenticated } = useAuth();
|
|
828
|
-
const trackingId = trackingIdProp;
|
|
829
|
-
const isEnabled = isProduction;
|
|
830
|
-
useEffect(() => {
|
|
831
|
-
return;
|
|
832
|
-
}, [isEnabled, trackingId]);
|
|
833
|
-
useEffect(() => {
|
|
834
|
-
return;
|
|
835
|
-
}, [isEnabled, isAuthenticated, user?.id]);
|
|
836
|
-
useEffect(() => {
|
|
837
|
-
return;
|
|
838
|
-
}, [pathname, isEnabled]);
|
|
839
|
-
return {
|
|
840
|
-
isEnabled,
|
|
841
|
-
trackingId,
|
|
842
|
-
pageview: Analytics.pageview,
|
|
843
|
-
event: Analytics.event,
|
|
844
|
-
setUser: Analytics.setUser,
|
|
845
|
-
set: Analytics.set
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
__name(useAnalytics, "useAnalytics");
|
|
849
|
-
function AnalyticsProvider({ children, trackingId }) {
|
|
850
|
-
useAnalytics(trackingId);
|
|
851
|
-
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
852
|
-
}
|
|
853
|
-
__name(AnalyticsProvider, "AnalyticsProvider");
|
|
854
|
-
var DIALOG_EVENTS = {
|
|
855
|
-
OPEN_AUTH_DIALOG: "OPEN_AUTH_DIALOG",
|
|
856
|
-
CLOSE_AUTH_DIALOG: "CLOSE_AUTH_DIALOG"};
|
|
857
|
-
var AuthDialog = /* @__PURE__ */ __name(({
|
|
858
|
-
onAuthRequired,
|
|
859
|
-
authPath = "/auth"
|
|
860
|
-
}) => {
|
|
861
|
-
const [open, setOpen] = useState(false);
|
|
862
|
-
const [message, setMessage] = useState("Please sign in to continue");
|
|
863
|
-
const router = useCfgRouter();
|
|
864
|
-
useEventListener(DIALOG_EVENTS.OPEN_AUTH_DIALOG, (payload) => {
|
|
865
|
-
if (payload?.message) {
|
|
866
|
-
setMessage(payload.message);
|
|
867
|
-
}
|
|
868
|
-
setOpen(true);
|
|
869
|
-
});
|
|
870
|
-
useEventListener(DIALOG_EVENTS.CLOSE_AUTH_DIALOG, () => {
|
|
871
|
-
setOpen(false);
|
|
872
|
-
});
|
|
873
|
-
const handleClose = /* @__PURE__ */ __name(() => {
|
|
874
|
-
setMessage("Please sign in to continue");
|
|
875
|
-
setOpen(false);
|
|
876
|
-
}, "handleClose");
|
|
877
|
-
const handleGoToAuth = /* @__PURE__ */ __name(() => {
|
|
878
|
-
if (typeof window !== "undefined") {
|
|
879
|
-
sessionStorage.setItem("redirectAfterAuth", window.location.pathname);
|
|
880
|
-
}
|
|
881
|
-
if (onAuthRequired) {
|
|
882
|
-
onAuthRequired();
|
|
883
|
-
} else {
|
|
884
|
-
router.push(authPath);
|
|
885
|
-
}
|
|
886
|
-
handleClose();
|
|
887
|
-
}, "handleGoToAuth");
|
|
888
|
-
return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent$1, { className: "max-w-sm", children: [
|
|
889
|
-
/* @__PURE__ */ jsx(DialogHeader$1, { children: /* @__PURE__ */ jsx(DialogTitle$1, { children: "Authentication Required" }) }),
|
|
890
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
891
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: message }),
|
|
892
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleGoToAuth, className: "w-full", children: [
|
|
893
|
-
/* @__PURE__ */ jsx(LogIn, { className: "h-4 w-4 mr-2" }),
|
|
894
|
-
"Go to Sign In"
|
|
895
|
-
] })
|
|
896
|
-
] })
|
|
897
|
-
] }) });
|
|
898
|
-
}, "AuthDialog");
|
|
899
|
-
|
|
900
|
-
// src/snippets/AuthDialog/events.ts
|
|
901
|
-
var AUTH_EVENTS = {
|
|
902
|
-
OPEN_AUTH_DIALOG: "OPEN_AUTH_DIALOG",
|
|
903
|
-
CLOSE_AUTH_DIALOG: "CLOSE_AUTH_DIALOG"};
|
|
904
|
-
function useAuthDialog() {
|
|
905
|
-
const openAuthDialog = useCallback((options) => {
|
|
906
|
-
events.publish({
|
|
907
|
-
type: AUTH_EVENTS.OPEN_AUTH_DIALOG,
|
|
908
|
-
payload: options
|
|
909
|
-
});
|
|
910
|
-
}, []);
|
|
911
|
-
const closeAuthDialog = useCallback(() => {
|
|
912
|
-
events.publish({
|
|
913
|
-
type: AUTH_EVENTS.CLOSE_AUTH_DIALOG
|
|
914
|
-
});
|
|
915
|
-
}, []);
|
|
916
|
-
return {
|
|
917
|
-
openAuthDialog,
|
|
918
|
-
closeAuthDialog
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
__name(useAuthDialog, "useAuthDialog");
|
|
922
|
-
function isDebugEnabled() {
|
|
923
|
-
if (typeof window === "undefined") return false;
|
|
924
|
-
try {
|
|
925
|
-
return localStorage.getItem("pwa_debug") === "true";
|
|
926
|
-
} catch {
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
__name(isDebugEnabled, "isDebugEnabled");
|
|
931
|
-
var pwaLogger = {
|
|
932
|
-
/**
|
|
933
|
-
* Info level logging
|
|
934
|
-
* Only logs in development or when debug is enabled
|
|
935
|
-
*/
|
|
936
|
-
info: /* @__PURE__ */ __name((...args) => {
|
|
937
|
-
{
|
|
938
|
-
consola$1.info(...args);
|
|
939
|
-
}
|
|
940
|
-
}, "info"),
|
|
941
|
-
/**
|
|
942
|
-
* Warning level logging
|
|
943
|
-
* Only logs in development or when debug is enabled
|
|
944
|
-
*/
|
|
945
|
-
warn: /* @__PURE__ */ __name((...args) => {
|
|
946
|
-
{
|
|
947
|
-
consola$1.warn(...args);
|
|
948
|
-
}
|
|
949
|
-
}, "warn"),
|
|
950
|
-
/**
|
|
951
|
-
* Error level logging
|
|
952
|
-
* Always logs (production + development)
|
|
953
|
-
*/
|
|
954
|
-
error: /* @__PURE__ */ __name((...args) => {
|
|
955
|
-
consola$1.error(...args);
|
|
956
|
-
}, "error"),
|
|
957
|
-
/**
|
|
958
|
-
* Debug level logging
|
|
959
|
-
* Only logs when debug is explicitly enabled
|
|
960
|
-
*/
|
|
961
|
-
debug: /* @__PURE__ */ __name((...args) => {
|
|
962
|
-
if (isDebugEnabled()) {
|
|
963
|
-
consola$1.debug(...args);
|
|
964
|
-
}
|
|
965
|
-
}, "debug"),
|
|
966
|
-
/**
|
|
967
|
-
* Success level logging
|
|
968
|
-
* Only logs in development or when debug is enabled
|
|
969
|
-
*/
|
|
970
|
-
success: /* @__PURE__ */ __name((...args) => {
|
|
971
|
-
{
|
|
972
|
-
consola$1.success(...args);
|
|
973
|
-
}
|
|
974
|
-
}, "success")
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
// src/snippets/PushNotifications/utils/platform.ts
|
|
978
|
-
function checkBrowserPushSupport() {
|
|
979
|
-
if (typeof window === "undefined") {
|
|
980
|
-
return { isSupported: false, browserName: "unknown", reason: "Server-side rendering" };
|
|
981
|
-
}
|
|
982
|
-
const ua = window.navigator.userAgent.toLowerCase();
|
|
983
|
-
if (ua.includes("fban") || ua.includes("fbav") || ua.includes("fb_iab")) {
|
|
984
|
-
return { isSupported: false, browserName: "Facebook In-App", reason: "In-app browsers do not support push notifications" };
|
|
985
|
-
}
|
|
986
|
-
if (ua.includes("instagram")) {
|
|
987
|
-
return { isSupported: false, browserName: "Instagram In-App", reason: "In-app browsers do not support push notifications" };
|
|
988
|
-
}
|
|
989
|
-
if (ua.includes("tiktok") || ua.includes("bytedancewebview") || ua.includes("bytelocale")) {
|
|
990
|
-
return { isSupported: false, browserName: "TikTok In-App", reason: "In-app browsers do not support push notifications" };
|
|
991
|
-
}
|
|
992
|
-
if (ua.includes("snapchat")) {
|
|
993
|
-
return { isSupported: false, browserName: "Snapchat In-App", reason: "In-app browsers do not support push notifications" };
|
|
994
|
-
}
|
|
995
|
-
if (ua.includes("micromessenger")) {
|
|
996
|
-
return { isSupported: false, browserName: "WeChat In-App", reason: "In-app browsers do not support push notifications" };
|
|
997
|
-
}
|
|
998
|
-
if (ua.includes("barcelona")) {
|
|
999
|
-
return { isSupported: false, browserName: "Threads In-App", reason: "In-app browsers do not support push notifications" };
|
|
1000
|
-
}
|
|
1001
|
-
if (ua.includes("pinterest")) {
|
|
1002
|
-
return { isSupported: false, browserName: "Pinterest In-App", reason: "In-app browsers do not support push notifications" };
|
|
1003
|
-
}
|
|
1004
|
-
if (ua.includes("telegram")) {
|
|
1005
|
-
return { isSupported: false, browserName: "Telegram In-App", reason: "In-app browsers do not support push notifications" };
|
|
1006
|
-
}
|
|
1007
|
-
if (ua.includes("line/")) {
|
|
1008
|
-
return { isSupported: false, browserName: "Line In-App", reason: "In-app browsers do not support push notifications" };
|
|
1009
|
-
}
|
|
1010
|
-
if (ua.includes("kakaotalk")) {
|
|
1011
|
-
return { isSupported: false, browserName: "KakaoTalk In-App", reason: "In-app browsers do not support push notifications" };
|
|
1012
|
-
}
|
|
1013
|
-
const isIOSDevice = ua.includes("iphone") || ua.includes("ipad") || ua.includes("ipod");
|
|
1014
|
-
if (ua.includes("linkedinapp") && isIOSDevice) {
|
|
1015
|
-
return { isSupported: false, browserName: "LinkedIn In-App", reason: "LinkedIn In-App on iOS does not support push notifications" };
|
|
1016
|
-
}
|
|
1017
|
-
if (ua.includes("twitter") && isIOSDevice) {
|
|
1018
|
-
return { isSupported: false, browserName: "Twitter In-App", reason: "Twitter In-App on iOS does not support push notifications" };
|
|
1019
|
-
}
|
|
1020
|
-
if (ua.includes("opera mini") || ua.includes("opios")) {
|
|
1021
|
-
return { isSupported: false, browserName: "Opera Mini", reason: "Opera Mini does not support service workers" };
|
|
1022
|
-
}
|
|
1023
|
-
if (ua.includes("msie") || ua.includes("trident/")) {
|
|
1024
|
-
return { isSupported: false, browserName: "Internet Explorer", reason: "Internet Explorer does not support Push API" };
|
|
1025
|
-
}
|
|
1026
|
-
if (ua.includes("ucbrowser") || ua.includes("uc browser")) {
|
|
1027
|
-
return { isSupported: false, browserName: "UC Browser", reason: "UC Browser has unreliable push notification support" };
|
|
1028
|
-
}
|
|
1029
|
-
const isWebView = ua.includes("wv)") || ua.includes("webview") || ua.includes("; wv") || ua.includes("iphone") && !ua.includes("safari") || ua.includes("ipad") && !ua.includes("safari");
|
|
1030
|
-
if (isWebView) {
|
|
1031
|
-
return { isSupported: false, browserName: "WebView", reason: "WebViews do not support push notifications" };
|
|
1032
|
-
}
|
|
1033
|
-
let browserName = "unknown";
|
|
1034
|
-
if (ua.includes("comet") || ua.includes("perplexity")) browserName = "Comet";
|
|
1035
|
-
else if (ua.includes("edg/") || ua.includes("edge/")) browserName = "Edge";
|
|
1036
|
-
else if (window.navigator.brave) browserName = "Brave";
|
|
1037
|
-
else if (ua.includes("arc/")) browserName = "Arc";
|
|
1038
|
-
else if (ua.includes("vivaldi")) browserName = "Vivaldi";
|
|
1039
|
-
else if (ua.includes("yabrowser")) browserName = "Yandex";
|
|
1040
|
-
else if (ua.includes("samsungbrowser")) browserName = "Samsung Internet";
|
|
1041
|
-
else if (ua.includes("opr/") || ua.includes("opera")) browserName = "Opera";
|
|
1042
|
-
else if (ua.includes("firefox")) browserName = "Firefox";
|
|
1043
|
-
else if (ua.includes("chrome")) browserName = "Chrome";
|
|
1044
|
-
else if (ua.includes("safari") && ua.includes("version/")) browserName = "Safari";
|
|
1045
|
-
return { isSupported: true, browserName };
|
|
1046
|
-
}
|
|
1047
|
-
__name(checkBrowserPushSupport, "checkBrowserPushSupport");
|
|
1048
|
-
function isStandalone() {
|
|
1049
|
-
if (typeof window === "undefined") return false;
|
|
1050
|
-
if (!window.matchMedia) {
|
|
1051
|
-
const nav2 = navigator;
|
|
1052
|
-
return nav2.standalone === true;
|
|
1053
|
-
}
|
|
1054
|
-
const isStandaloneDisplay = window.matchMedia("(display-mode: standalone)").matches;
|
|
1055
|
-
const nav = navigator;
|
|
1056
|
-
const isStandaloneNavigator = nav.standalone === true;
|
|
1057
|
-
return isStandaloneDisplay || isStandaloneNavigator;
|
|
1058
|
-
}
|
|
1059
|
-
__name(isStandalone, "isStandalone");
|
|
1060
|
-
|
|
1061
|
-
// src/snippets/PushNotifications/utils/vapid.ts
|
|
1062
|
-
var _VapidKeyError = class _VapidKeyError extends Error {
|
|
1063
|
-
constructor(message, code) {
|
|
1064
|
-
super(message);
|
|
1065
|
-
this.code = code;
|
|
1066
|
-
this.name = "VapidKeyError";
|
|
1067
|
-
}
|
|
1068
|
-
};
|
|
1069
|
-
__name(_VapidKeyError, "VapidKeyError");
|
|
1070
|
-
var VapidKeyError = _VapidKeyError;
|
|
1071
|
-
function urlBase64ToUint8Array(base64String) {
|
|
1072
|
-
if (!base64String) {
|
|
1073
|
-
throw new VapidKeyError("VAPID public key is required", "VAPID_EMPTY");
|
|
1074
|
-
}
|
|
1075
|
-
if (typeof base64String !== "string") {
|
|
1076
|
-
throw new VapidKeyError("VAPID public key must be a string", "VAPID_INVALID_TYPE");
|
|
1077
|
-
}
|
|
1078
|
-
const padding = "=".repeat((4 - base64String.length % 4) % 4);
|
|
1079
|
-
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
1080
|
-
let rawData;
|
|
1081
|
-
try {
|
|
1082
|
-
rawData = window.atob(base64);
|
|
1083
|
-
} catch (e) {
|
|
1084
|
-
throw new VapidKeyError(
|
|
1085
|
-
`Invalid base64url format: ${e instanceof Error ? e.message : String(e)}`,
|
|
1086
|
-
"VAPID_INVALID_BASE64"
|
|
1087
|
-
);
|
|
1088
|
-
}
|
|
1089
|
-
const outputArray = new Uint8Array(rawData.length);
|
|
1090
|
-
for (let i = 0; i < rawData.length; i++) {
|
|
1091
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
1092
|
-
}
|
|
1093
|
-
if (outputArray.length !== 65) {
|
|
1094
|
-
throw new VapidKeyError(
|
|
1095
|
-
`Invalid key length: expected 65 bytes (P-256 uncompressed), got ${outputArray.length} bytes`,
|
|
1096
|
-
"VAPID_INVALID_LENGTH"
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1099
|
-
if (outputArray[0] !== 4) {
|
|
1100
|
-
throw new VapidKeyError(
|
|
1101
|
-
`Invalid key format: must start with 0x04 (uncompressed P-256 point), got 0x${outputArray[0].toString(16).padStart(2, "0")}`,
|
|
1102
|
-
"VAPID_INVALID_FORMAT"
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
return outputArray;
|
|
1106
|
-
}
|
|
1107
|
-
__name(urlBase64ToUint8Array, "urlBase64ToUint8Array");
|
|
1108
|
-
|
|
1109
|
-
// src/snippets/PushNotifications/hooks/usePushNotifications.ts
|
|
1110
|
-
function usePushNotifications(options) {
|
|
1111
|
-
const [state, setState] = useState({
|
|
1112
|
-
isSupported: false,
|
|
1113
|
-
permission: "default",
|
|
1114
|
-
isSubscribed: false,
|
|
1115
|
-
subscription: null
|
|
1116
|
-
});
|
|
1117
|
-
useEffect(() => {
|
|
1118
|
-
if (typeof window === "undefined") return;
|
|
1119
|
-
const browserSupport = checkBrowserPushSupport();
|
|
1120
|
-
if (!browserSupport.isSupported) {
|
|
1121
|
-
pwaLogger.info(`[usePushNotifications] Browser does not support push: ${browserSupport.browserName} - ${browserSupport.reason}`);
|
|
1122
|
-
setState((prev) => ({
|
|
1123
|
-
...prev,
|
|
1124
|
-
isSupported: false,
|
|
1125
|
-
permission: "denied"
|
|
1126
|
-
}));
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
const isSupported = "serviceWorker" in navigator && "PushManager" in window && "Notification" in window;
|
|
1130
|
-
setState((prev) => ({
|
|
1131
|
-
...prev,
|
|
1132
|
-
isSupported,
|
|
1133
|
-
permission: isSupported ? Notification.permission : "denied"
|
|
1134
|
-
}));
|
|
1135
|
-
if (isSupported) {
|
|
1136
|
-
navigator.serviceWorker.ready.then((registration) => registration.pushManager.getSubscription()).then((subscription) => {
|
|
1137
|
-
setState((prev) => ({
|
|
1138
|
-
...prev,
|
|
1139
|
-
isSubscribed: !!subscription,
|
|
1140
|
-
subscription
|
|
1141
|
-
}));
|
|
1142
|
-
}).catch((error) => {
|
|
1143
|
-
pwaLogger.error("[usePushNotifications] Failed to get subscription:", error);
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
}, []);
|
|
1147
|
-
const subscribe = /* @__PURE__ */ __name(async () => {
|
|
1148
|
-
if (!state.isSupported) {
|
|
1149
|
-
pwaLogger.warn("[usePushNotifications] Push notifications not supported");
|
|
1150
|
-
return null;
|
|
1151
|
-
}
|
|
1152
|
-
if (!options?.vapidPublicKey) {
|
|
1153
|
-
pwaLogger.error("[usePushNotifications] VAPID public key required");
|
|
1154
|
-
return null;
|
|
1155
|
-
}
|
|
1156
|
-
try {
|
|
1157
|
-
pwaLogger.debug("[usePushNotifications] Running pre-flight checks...");
|
|
1158
|
-
if (!navigator.onLine) {
|
|
1159
|
-
pwaLogger.error("[usePushNotifications] No internet connection");
|
|
1160
|
-
throw new Error("No internet connection. Please check your network and try again.");
|
|
1161
|
-
}
|
|
1162
|
-
const permission = await Notification.requestPermission();
|
|
1163
|
-
setState((prev) => ({ ...prev, permission }));
|
|
1164
|
-
if (permission !== "granted") {
|
|
1165
|
-
pwaLogger.warn("[usePushNotifications] Permission not granted:", permission);
|
|
1166
|
-
return null;
|
|
1167
|
-
}
|
|
1168
|
-
const registration = await navigator.serviceWorker.ready;
|
|
1169
|
-
let applicationServerKey;
|
|
1170
|
-
try {
|
|
1171
|
-
pwaLogger.debug("[usePushNotifications] Converting VAPID key...");
|
|
1172
|
-
applicationServerKey = urlBase64ToUint8Array(options.vapidPublicKey);
|
|
1173
|
-
pwaLogger.info("[usePushNotifications] VAPID key validated successfully");
|
|
1174
|
-
} catch (e) {
|
|
1175
|
-
if (e instanceof VapidKeyError) {
|
|
1176
|
-
pwaLogger.error(`[usePushNotifications] Invalid VAPID key: ${e.message} (code: ${e.code})`);
|
|
1177
|
-
} else {
|
|
1178
|
-
pwaLogger.error("[usePushNotifications] Failed to convert VAPID key:", e);
|
|
1179
|
-
}
|
|
1180
|
-
return null;
|
|
1181
|
-
}
|
|
1182
|
-
pwaLogger.debug("[usePushNotifications] Service Worker state:", {
|
|
1183
|
-
controller: navigator.serviceWorker.controller ? "active" : "none",
|
|
1184
|
-
registrationActive: registration.active ? "yes" : "no",
|
|
1185
|
-
permission: Notification.permission
|
|
1186
|
-
});
|
|
1187
|
-
const existingSub = await registration.pushManager.getSubscription();
|
|
1188
|
-
if (existingSub) {
|
|
1189
|
-
pwaLogger.debug("[usePushNotifications] Unsubscribing from existing subscription...");
|
|
1190
|
-
await existingSub.unsubscribe();
|
|
1191
|
-
}
|
|
1192
|
-
const subscribeOptions = {
|
|
1193
|
-
userVisibleOnly: true,
|
|
1194
|
-
applicationServerKey
|
|
1195
|
-
};
|
|
1196
|
-
pwaLogger.debug("[usePushNotifications] Subscribing with VAPID key...");
|
|
1197
|
-
const subscription = await registration.pushManager.subscribe(subscribeOptions);
|
|
1198
|
-
if (options.subscribeEndpoint) {
|
|
1199
|
-
await fetch(options.subscribeEndpoint, {
|
|
1200
|
-
method: "POST",
|
|
1201
|
-
headers: { "Content-Type": "application/json" },
|
|
1202
|
-
body: JSON.stringify(subscription)
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
setState((prev) => ({
|
|
1206
|
-
...prev,
|
|
1207
|
-
isSubscribed: true,
|
|
1208
|
-
subscription
|
|
1209
|
-
}));
|
|
1210
|
-
pwaLogger.success("[usePushNotifications] Successfully subscribed to push notifications");
|
|
1211
|
-
return subscription;
|
|
1212
|
-
} catch (error) {
|
|
1213
|
-
pwaLogger.error("[usePushNotifications] Subscribe failed:", error);
|
|
1214
|
-
if (error.name === "AbortError" || error.message?.includes("push service error")) {
|
|
1215
|
-
pwaLogger.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1216
|
-
pwaLogger.error("\u274C PUSH SERVICE ERROR - Cannot connect to FCM");
|
|
1217
|
-
pwaLogger.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1218
|
-
pwaLogger.error("");
|
|
1219
|
-
pwaLogger.error("\u{1F50D} This is NOT a code bug - it's a network/security block.");
|
|
1220
|
-
pwaLogger.error("");
|
|
1221
|
-
pwaLogger.error("\u2705 Quick Fixes (try in order):");
|
|
1222
|
-
pwaLogger.error(" 1. Disable VPN/Proxy and refresh page");
|
|
1223
|
-
pwaLogger.error(" 2. Open Incognito window (Cmd+Shift+N)");
|
|
1224
|
-
pwaLogger.error(" 3. Try different browser (Safari, Firefox)");
|
|
1225
|
-
pwaLogger.error(" 4. Use mobile hotspot instead of WiFi");
|
|
1226
|
-
pwaLogger.error("");
|
|
1227
|
-
pwaLogger.error("\u{1F527} Technical Details:");
|
|
1228
|
-
pwaLogger.error(" \u2022 Browser tries to connect to FCM (ports 5228-5230)");
|
|
1229
|
-
pwaLogger.error(" \u2022 VPN/Firewall/Privacy settings may block these ports");
|
|
1230
|
-
pwaLogger.error(' \u2022 Check browser console for "AbortError" details');
|
|
1231
|
-
pwaLogger.error("");
|
|
1232
|
-
pwaLogger.error("\u{1F4DA} Learn more: https://web.dev/push-notifications-overview/");
|
|
1233
|
-
pwaLogger.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1234
|
-
} else if (error.message?.includes("No internet connection")) {
|
|
1235
|
-
pwaLogger.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1236
|
-
pwaLogger.error("\u274C NO INTERNET CONNECTION");
|
|
1237
|
-
pwaLogger.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1238
|
-
pwaLogger.error("Please check your network connection and try again.");
|
|
1239
|
-
} else {
|
|
1240
|
-
pwaLogger.error("Unknown error:", error.name, error.message);
|
|
1241
|
-
}
|
|
1242
|
-
return null;
|
|
1243
|
-
}
|
|
1244
|
-
}, "subscribe");
|
|
1245
|
-
const unsubscribe = /* @__PURE__ */ __name(async () => {
|
|
1246
|
-
if (!state.subscription) {
|
|
1247
|
-
pwaLogger.warn("[usePushNotifications] No active subscription to unsubscribe");
|
|
1248
|
-
return false;
|
|
1249
|
-
}
|
|
1250
|
-
try {
|
|
1251
|
-
await state.subscription.unsubscribe();
|
|
1252
|
-
setState((prev) => ({
|
|
1253
|
-
...prev,
|
|
1254
|
-
isSubscribed: false,
|
|
1255
|
-
subscription: null
|
|
1256
|
-
}));
|
|
1257
|
-
pwaLogger.info("[usePushNotifications] Successfully unsubscribed from push notifications");
|
|
1258
|
-
return true;
|
|
1259
|
-
} catch (error) {
|
|
1260
|
-
pwaLogger.error("[usePushNotifications] Unsubscribe failed:", error);
|
|
1261
|
-
return false;
|
|
1262
|
-
}
|
|
1263
|
-
}, "unsubscribe");
|
|
1264
|
-
return {
|
|
1265
|
-
...state,
|
|
1266
|
-
subscribe,
|
|
1267
|
-
unsubscribe
|
|
1268
|
-
};
|
|
1269
|
-
}
|
|
1270
|
-
__name(usePushNotifications, "usePushNotifications");
|
|
1271
|
-
|
|
1272
|
-
// src/snippets/PushNotifications/hooks/useDjangoPush.ts
|
|
1273
|
-
function useDjangoPush(options) {
|
|
1274
|
-
const { onSubscribed, onSubscribeError, onUnsubscribed, ...pushOptions } = options;
|
|
1275
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
1276
|
-
const [error, setError] = useState(null);
|
|
1277
|
-
const { isAuthenticated } = useAuth();
|
|
1278
|
-
const { openAuthDialog } = useAuthDialog();
|
|
1279
|
-
const pushNotifications = usePushNotifications(pushOptions);
|
|
1280
|
-
const subscribe = useCallback(async () => {
|
|
1281
|
-
if (!isAuthenticated) {
|
|
1282
|
-
openAuthDialog({
|
|
1283
|
-
message: "Please sign in to enable push notifications"
|
|
1284
|
-
});
|
|
1285
|
-
return false;
|
|
1286
|
-
}
|
|
1287
|
-
setIsLoading(true);
|
|
1288
|
-
setError(null);
|
|
1289
|
-
try {
|
|
1290
|
-
const subscription = await pushNotifications.subscribe();
|
|
1291
|
-
if (!subscription) {
|
|
1292
|
-
const err = new Error("Browser subscription failed");
|
|
1293
|
-
setError(err);
|
|
1294
|
-
onSubscribeError?.(err);
|
|
1295
|
-
return false;
|
|
1296
|
-
}
|
|
1297
|
-
pwaLogger.info("[useDjangoPush] Browser subscription created");
|
|
1298
|
-
const result = await apiWebPush.web_push.webpushSubscribeCreate({
|
|
1299
|
-
endpoint: subscription.endpoint,
|
|
1300
|
-
keys: {
|
|
1301
|
-
p256dh: arrayBufferToBase64(subscription.getKey("p256dh")),
|
|
1302
|
-
auth: arrayBufferToBase64(subscription.getKey("auth"))
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1305
|
-
pwaLogger.info("[useDjangoPush] Subscription saved to Django:", result);
|
|
1306
|
-
onSubscribed?.(subscription);
|
|
1307
|
-
toast.success("Push notifications enabled");
|
|
1308
|
-
return true;
|
|
1309
|
-
} catch (err) {
|
|
1310
|
-
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1311
|
-
pwaLogger.error("[useDjangoPush] Subscribe failed:", error2);
|
|
1312
|
-
setError(error2);
|
|
1313
|
-
onSubscribeError?.(error2);
|
|
1314
|
-
toast.error("Subscription Failed", {
|
|
1315
|
-
description: "Could not save subscription to server. Please try again.",
|
|
1316
|
-
duration: 5e3
|
|
1317
|
-
});
|
|
1318
|
-
return false;
|
|
1319
|
-
} finally {
|
|
1320
|
-
setIsLoading(false);
|
|
1321
|
-
}
|
|
1322
|
-
}, [isAuthenticated, openAuthDialog, pushNotifications.subscribe, onSubscribed, onSubscribeError]);
|
|
1323
|
-
const unsubscribe = useCallback(async () => {
|
|
1324
|
-
setIsLoading(true);
|
|
1325
|
-
setError(null);
|
|
1326
|
-
try {
|
|
1327
|
-
const success = await pushNotifications.unsubscribe();
|
|
1328
|
-
if (!success) {
|
|
1329
|
-
return false;
|
|
1330
|
-
}
|
|
1331
|
-
pwaLogger.info("[useDjangoPush] Browser unsubscribed");
|
|
1332
|
-
onUnsubscribed?.();
|
|
1333
|
-
toast.success("Push notifications disabled");
|
|
1334
|
-
return true;
|
|
1335
|
-
} catch (err) {
|
|
1336
|
-
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1337
|
-
pwaLogger.error("[useDjangoPush] Unsubscribe failed:", error2);
|
|
1338
|
-
setError(error2);
|
|
1339
|
-
return false;
|
|
1340
|
-
} finally {
|
|
1341
|
-
setIsLoading(false);
|
|
1342
|
-
}
|
|
1343
|
-
}, [pushNotifications.unsubscribe, onUnsubscribed]);
|
|
1344
|
-
const sendTestPush = useCallback(
|
|
1345
|
-
async (message) => {
|
|
1346
|
-
if (!pushNotifications.isSubscribed) {
|
|
1347
|
-
const err = new Error("Not subscribed");
|
|
1348
|
-
setError(err);
|
|
1349
|
-
return false;
|
|
1350
|
-
}
|
|
1351
|
-
setIsLoading(true);
|
|
1352
|
-
setError(null);
|
|
1353
|
-
try {
|
|
1354
|
-
const result = await apiWebPush.web_push.webpushSendCreate({
|
|
1355
|
-
title: message.title,
|
|
1356
|
-
body: message.body,
|
|
1357
|
-
url: message.url || "/",
|
|
1358
|
-
icon: "/icon.png"
|
|
1359
|
-
});
|
|
1360
|
-
pwaLogger.info("[useDjangoPush] Test push sent:", result);
|
|
1361
|
-
return result.success;
|
|
1362
|
-
} catch (err) {
|
|
1363
|
-
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1364
|
-
pwaLogger.error("[useDjangoPush] Send test push failed:", error2);
|
|
1365
|
-
setError(error2);
|
|
1366
|
-
return false;
|
|
1367
|
-
} finally {
|
|
1368
|
-
setIsLoading(false);
|
|
1369
|
-
}
|
|
1370
|
-
},
|
|
1371
|
-
[pushNotifications.isSubscribed]
|
|
1372
|
-
);
|
|
1373
|
-
return {
|
|
1374
|
-
// State from usePushNotifications
|
|
1375
|
-
isSupported: pushNotifications.isSupported,
|
|
1376
|
-
permission: pushNotifications.permission,
|
|
1377
|
-
isSubscribed: pushNotifications.isSubscribed,
|
|
1378
|
-
subscription: pushNotifications.subscription,
|
|
1379
|
-
// Local state
|
|
1380
|
-
isLoading,
|
|
1381
|
-
error,
|
|
1382
|
-
// Actions
|
|
1383
|
-
subscribe,
|
|
1384
|
-
unsubscribe,
|
|
1385
|
-
sendTestPush
|
|
1386
|
-
};
|
|
1387
|
-
}
|
|
1388
|
-
__name(useDjangoPush, "useDjangoPush");
|
|
1389
|
-
function arrayBufferToBase64(buffer) {
|
|
1390
|
-
if (!buffer) return "";
|
|
1391
|
-
const bytes = new Uint8Array(buffer);
|
|
1392
|
-
let binary = "";
|
|
1393
|
-
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1394
|
-
binary += String.fromCharCode(bytes[i]);
|
|
1395
|
-
}
|
|
1396
|
-
return window.btoa(binary);
|
|
1397
|
-
}
|
|
1398
|
-
__name(arrayBufferToBase64, "arrayBufferToBase64");
|
|
1399
|
-
var DjangoPushContext = createContext(void 0);
|
|
1400
|
-
function DjangoPushProvider({
|
|
1401
|
-
children,
|
|
1402
|
-
vapidPublicKey,
|
|
1403
|
-
autoSubscribe = false,
|
|
1404
|
-
onSubscribed,
|
|
1405
|
-
onSubscribeError,
|
|
1406
|
-
onUnsubscribed
|
|
1407
|
-
}) {
|
|
1408
|
-
const djangoPush = useDjangoPush({
|
|
1409
|
-
vapidPublicKey,
|
|
1410
|
-
onSubscribed,
|
|
1411
|
-
onSubscribeError,
|
|
1412
|
-
onUnsubscribed
|
|
1413
|
-
});
|
|
1414
|
-
useEffect(() => {
|
|
1415
|
-
if (autoSubscribe && djangoPush.isSupported && djangoPush.permission === "granted" && !djangoPush.isSubscribed && !djangoPush.isLoading) {
|
|
1416
|
-
pwaLogger.info("[DjangoPushProvider] Auto-subscribing (permission already granted)");
|
|
1417
|
-
djangoPush.subscribe();
|
|
1418
|
-
}
|
|
1419
|
-
}, [autoSubscribe, djangoPush.isSupported, djangoPush.permission, djangoPush.isSubscribed, djangoPush.isLoading]);
|
|
1420
|
-
const [pushes, setPushes] = useState([]);
|
|
1421
|
-
useEffect(() => {
|
|
1422
|
-
if (typeof window === "undefined" || !("serviceWorker" in navigator)) {
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
const handleMessage = /* @__PURE__ */ __name((event) => {
|
|
1426
|
-
if (event.data && event.data.type === "PUSH_RECEIVED") {
|
|
1427
|
-
const push = {
|
|
1428
|
-
id: crypto.randomUUID(),
|
|
1429
|
-
timestamp: Date.now(),
|
|
1430
|
-
...event.data.notification
|
|
1431
|
-
};
|
|
1432
|
-
setPushes((prev) => [push, ...prev]);
|
|
1433
|
-
pwaLogger.info("[DjangoPushProvider] Push received:", push);
|
|
1434
|
-
}
|
|
1435
|
-
}, "handleMessage");
|
|
1436
|
-
navigator.serviceWorker.addEventListener("message", handleMessage);
|
|
1437
|
-
return () => navigator.serviceWorker.removeEventListener("message", handleMessage);
|
|
1438
|
-
}, []);
|
|
1439
|
-
const sendPush = useCallback(
|
|
1440
|
-
async (message) => {
|
|
1441
|
-
await djangoPush.sendTestPush({
|
|
1442
|
-
title: message.title,
|
|
1443
|
-
body: message.body,
|
|
1444
|
-
url: message.data?.url
|
|
1445
|
-
});
|
|
1446
|
-
},
|
|
1447
|
-
[djangoPush]
|
|
1448
|
-
);
|
|
1449
|
-
const clearPushes = useCallback(() => {
|
|
1450
|
-
setPushes([]);
|
|
1451
|
-
pwaLogger.info("[DjangoPushProvider] Push history cleared");
|
|
1452
|
-
}, []);
|
|
1453
|
-
const removePush = useCallback((id) => {
|
|
1454
|
-
setPushes((prev) => prev.filter((p) => p.id !== id));
|
|
1455
|
-
pwaLogger.info("[DjangoPushProvider] Push removed:", id);
|
|
1456
|
-
}, []);
|
|
1457
|
-
const value = {
|
|
1458
|
-
...djangoPush,
|
|
1459
|
-
pushes,
|
|
1460
|
-
sendPush,
|
|
1461
|
-
clearPushes,
|
|
1462
|
-
removePush
|
|
1463
|
-
};
|
|
1464
|
-
return /* @__PURE__ */ jsx(DjangoPushContext.Provider, { value, children });
|
|
1465
|
-
}
|
|
1466
|
-
__name(DjangoPushProvider, "DjangoPushProvider");
|
|
1467
|
-
|
|
1468
|
-
// src/snippets/PushNotifications/utils/localStorage.ts
|
|
1469
|
-
var STORAGE_KEYS = {
|
|
1470
|
-
PUSH_DISMISSED: "pwa_push_dismissed_at"
|
|
1471
|
-
};
|
|
1472
|
-
function isPushDismissedRecently(resetDays = 7) {
|
|
1473
|
-
return isDismissedRecentlyHelper(resetDays, STORAGE_KEYS.PUSH_DISMISSED);
|
|
1474
|
-
}
|
|
1475
|
-
__name(isPushDismissedRecently, "isPushDismissedRecently");
|
|
1476
|
-
function markPushDismissed() {
|
|
1477
|
-
if (typeof window === "undefined") return;
|
|
1478
|
-
try {
|
|
1479
|
-
localStorage.setItem(STORAGE_KEYS.PUSH_DISMISSED, Date.now().toString());
|
|
1480
|
-
} catch {
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
__name(markPushDismissed, "markPushDismissed");
|
|
1484
|
-
function isDismissedRecentlyHelper(resetDays, key) {
|
|
1485
|
-
if (typeof window === "undefined") return false;
|
|
1486
|
-
try {
|
|
1487
|
-
const dismissed = localStorage.getItem(key);
|
|
1488
|
-
if (!dismissed) return false;
|
|
1489
|
-
const dismissedAt = parseInt(dismissed, 10);
|
|
1490
|
-
const daysSince = (Date.now() - dismissedAt) / (1e3 * 60 * 60 * 24);
|
|
1491
|
-
return daysSince < resetDays;
|
|
1492
|
-
} catch {
|
|
1493
|
-
return false;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
__name(isDismissedRecentlyHelper, "isDismissedRecentlyHelper");
|
|
1497
|
-
var DEFAULT_RESET_DAYS = 7;
|
|
1498
|
-
function PushPrompt({
|
|
1499
|
-
vapidPublicKey,
|
|
1500
|
-
subscribeEndpoint = "/api/push/subscribe",
|
|
1501
|
-
requirePWA = true,
|
|
1502
|
-
delayMs = 5e3,
|
|
1503
|
-
resetAfterDays = DEFAULT_RESET_DAYS,
|
|
1504
|
-
onEnabled,
|
|
1505
|
-
onDismissed
|
|
1506
|
-
}) {
|
|
1507
|
-
const { isAuthenticated, isLoading: isAuthLoading } = useAuth();
|
|
1508
|
-
const { isSupported, permission, isSubscribed, subscribe } = usePushNotifications({
|
|
1509
|
-
vapidPublicKey,
|
|
1510
|
-
subscribeEndpoint
|
|
1511
|
-
});
|
|
1512
|
-
const [show, setShow] = useState(false);
|
|
1513
|
-
const [enabling, setEnabling] = useState(false);
|
|
1514
|
-
useEffect(() => {
|
|
1515
|
-
if (isAuthLoading || !isAuthenticated) {
|
|
1516
|
-
return;
|
|
1517
|
-
}
|
|
1518
|
-
if (!isSupported || isSubscribed || permission === "denied") {
|
|
1519
|
-
return;
|
|
1520
|
-
}
|
|
1521
|
-
if (requirePWA && !isStandalone()) {
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
if (typeof window !== "undefined") {
|
|
1525
|
-
if (isPushDismissedRecently(resetAfterDays)) {
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
const timer = setTimeout(() => setShow(true), delayMs);
|
|
1530
|
-
return () => clearTimeout(timer);
|
|
1531
|
-
}, [isAuthLoading, isAuthenticated, isSupported, isSubscribed, permission, requirePWA, resetAfterDays, delayMs]);
|
|
1532
|
-
const handleEnable = /* @__PURE__ */ __name(async () => {
|
|
1533
|
-
setEnabling(true);
|
|
1534
|
-
try {
|
|
1535
|
-
const success = await subscribe();
|
|
1536
|
-
if (success) {
|
|
1537
|
-
setShow(false);
|
|
1538
|
-
onEnabled?.();
|
|
1539
|
-
}
|
|
1540
|
-
} catch (error) {
|
|
1541
|
-
pwaLogger.error("[PushPrompt] Enable failed:", error);
|
|
1542
|
-
} finally {
|
|
1543
|
-
setEnabling(false);
|
|
1544
|
-
}
|
|
1545
|
-
}, "handleEnable");
|
|
1546
|
-
const handleDismiss = /* @__PURE__ */ __name(() => {
|
|
1547
|
-
setShow(false);
|
|
1548
|
-
markPushDismissed();
|
|
1549
|
-
onDismissed?.();
|
|
1550
|
-
}, "handleDismiss");
|
|
1551
|
-
if (!show) return null;
|
|
1552
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed bottom-4 left-4 right-4 z-50 animate-in slide-in-from-bottom-4 duration-300", children: /* @__PURE__ */ jsx("div", { className: "bg-zinc-900 border border-zinc-700 rounded-lg p-4 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
1553
|
-
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(Bell, { className: "w-5 h-5 text-blue-400" }) }),
|
|
1554
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1555
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-white mb-1", children: "Enable notifications" }),
|
|
1556
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-zinc-400 mb-3", children: "Stay updated with important updates and alerts" }),
|
|
1557
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
1558
|
-
/* @__PURE__ */ jsx(
|
|
1559
|
-
Button$1,
|
|
1560
|
-
{
|
|
1561
|
-
onClick: handleEnable,
|
|
1562
|
-
loading: enabling,
|
|
1563
|
-
size: "sm",
|
|
1564
|
-
variant: "default",
|
|
1565
|
-
children: "Enable"
|
|
1566
|
-
}
|
|
1567
|
-
),
|
|
1568
|
-
/* @__PURE__ */ jsx(
|
|
1569
|
-
Button$1,
|
|
1570
|
-
{
|
|
1571
|
-
onClick: handleDismiss,
|
|
1572
|
-
size: "sm",
|
|
1573
|
-
variant: "ghost",
|
|
1574
|
-
children: "Not now"
|
|
1575
|
-
}
|
|
1576
|
-
)
|
|
1577
|
-
] })
|
|
1578
|
-
] }),
|
|
1579
|
-
/* @__PURE__ */ jsx(
|
|
1580
|
-
Button$1,
|
|
1581
|
-
{
|
|
1582
|
-
onClick: handleDismiss,
|
|
1583
|
-
size: "sm",
|
|
1584
|
-
variant: "ghost",
|
|
1585
|
-
className: "flex-shrink-0 p-1",
|
|
1586
|
-
"aria-label": "Dismiss",
|
|
1587
|
-
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
|
|
1588
|
-
}
|
|
1589
|
-
)
|
|
1590
|
-
] }) }) });
|
|
1591
|
-
}
|
|
1592
|
-
__name(PushPrompt, "PushPrompt");
|
|
1593
|
-
|
|
1594
|
-
// src/snippets/PushNotifications/config.ts
|
|
1595
|
-
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY || "";
|
|
1596
|
-
|
|
1597
|
-
// src/snippets/PWAInstall/utils/localStorage.ts
|
|
1598
|
-
var STORAGE_KEYS2 = {
|
|
1599
|
-
IOS_GUIDE_DISMISSED: "pwa_ios_guide_dismissed_at",
|
|
1600
|
-
APP_INSTALLED: "pwa_app_installed",
|
|
1601
|
-
A2HS_DISMISSED: "pwa_a2hs_dismissed_at"
|
|
1602
|
-
};
|
|
1603
|
-
function clearIOSGuideDismissal() {
|
|
1604
|
-
if (typeof window === "undefined") return;
|
|
1605
|
-
try {
|
|
1606
|
-
localStorage.removeItem(STORAGE_KEYS2.IOS_GUIDE_DISMISSED);
|
|
1607
|
-
} catch {
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
__name(clearIOSGuideDismissal, "clearIOSGuideDismissal");
|
|
1611
|
-
function markAppInstalled() {
|
|
1612
|
-
if (typeof window === "undefined") return;
|
|
1613
|
-
try {
|
|
1614
|
-
localStorage.setItem(STORAGE_KEYS2.APP_INSTALLED, "true");
|
|
1615
|
-
clearIOSGuideDismissal();
|
|
1616
|
-
} catch {
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
__name(markAppInstalled, "markAppInstalled");
|
|
1620
|
-
function isA2HSDismissedRecently(resetDays = 3) {
|
|
1621
|
-
return isDismissedRecentlyHelper2(resetDays, STORAGE_KEYS2.A2HS_DISMISSED);
|
|
1622
|
-
}
|
|
1623
|
-
__name(isA2HSDismissedRecently, "isA2HSDismissedRecently");
|
|
1624
|
-
function markA2HSDismissed() {
|
|
1625
|
-
if (typeof window === "undefined") return;
|
|
1626
|
-
try {
|
|
1627
|
-
localStorage.setItem(STORAGE_KEYS2.A2HS_DISMISSED, Date.now().toString());
|
|
1628
|
-
} catch {
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
__name(markA2HSDismissed, "markA2HSDismissed");
|
|
1632
|
-
function isDismissedRecentlyHelper2(resetDays, key) {
|
|
1633
|
-
if (typeof window === "undefined") return false;
|
|
1634
|
-
try {
|
|
1635
|
-
const dismissed = localStorage.getItem(key);
|
|
1636
|
-
if (!dismissed) return false;
|
|
1637
|
-
const dismissedAt = parseInt(dismissed, 10);
|
|
1638
|
-
const daysSince = (Date.now() - dismissedAt) / (1e3 * 60 * 60 * 24);
|
|
1639
|
-
return daysSince < resetDays;
|
|
1640
|
-
} catch {
|
|
1641
|
-
return false;
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
__name(isDismissedRecentlyHelper2, "isDismissedRecentlyHelper");
|
|
1645
|
-
function isDebugEnabled2() {
|
|
1646
|
-
if (typeof window === "undefined") return false;
|
|
1647
|
-
try {
|
|
1648
|
-
return localStorage.getItem("pwa_debug") === "true";
|
|
1649
|
-
} catch {
|
|
1650
|
-
return false;
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
__name(isDebugEnabled2, "isDebugEnabled");
|
|
1654
|
-
var pwaLogger2 = {
|
|
1655
|
-
/**
|
|
1656
|
-
* Info level logging
|
|
1657
|
-
* Only logs in development or when debug is enabled
|
|
1658
|
-
*/
|
|
1659
|
-
info: /* @__PURE__ */ __name((...args) => {
|
|
1660
|
-
{
|
|
1661
|
-
consola$1.info(...args);
|
|
1662
|
-
}
|
|
1663
|
-
}, "info"),
|
|
1664
|
-
/**
|
|
1665
|
-
* Warning level logging
|
|
1666
|
-
* Only logs in development or when debug is enabled
|
|
1667
|
-
*/
|
|
1668
|
-
warn: /* @__PURE__ */ __name((...args) => {
|
|
1669
|
-
{
|
|
1670
|
-
consola$1.warn(...args);
|
|
1671
|
-
}
|
|
1672
|
-
}, "warn"),
|
|
1673
|
-
/**
|
|
1674
|
-
* Error level logging
|
|
1675
|
-
* Always logs (production + development)
|
|
1676
|
-
*/
|
|
1677
|
-
error: /* @__PURE__ */ __name((...args) => {
|
|
1678
|
-
consola$1.error(...args);
|
|
1679
|
-
}, "error"),
|
|
1680
|
-
/**
|
|
1681
|
-
* Debug level logging
|
|
1682
|
-
* Only logs when debug is explicitly enabled
|
|
1683
|
-
*/
|
|
1684
|
-
debug: /* @__PURE__ */ __name((...args) => {
|
|
1685
|
-
if (isDebugEnabled2()) {
|
|
1686
|
-
consola$1.debug(...args);
|
|
1687
|
-
}
|
|
1688
|
-
}, "debug"),
|
|
1689
|
-
/**
|
|
1690
|
-
* Success level logging
|
|
1691
|
-
* Only logs in development or when debug is enabled
|
|
1692
|
-
*/
|
|
1693
|
-
success: /* @__PURE__ */ __name((...args) => {
|
|
1694
|
-
{
|
|
1695
|
-
consola$1.success(...args);
|
|
1696
|
-
}
|
|
1697
|
-
}, "success")
|
|
1698
|
-
};
|
|
1699
|
-
|
|
1700
|
-
// src/snippets/PWAInstall/utils/platform.ts
|
|
1701
|
-
function isStandalone2() {
|
|
1702
|
-
if (typeof window === "undefined") return false;
|
|
1703
|
-
if (!window.matchMedia) {
|
|
1704
|
-
const nav2 = navigator;
|
|
1705
|
-
return nav2.standalone === true;
|
|
1706
|
-
}
|
|
1707
|
-
const isStandaloneDisplay = window.matchMedia("(display-mode: standalone)").matches;
|
|
1708
|
-
const nav = navigator;
|
|
1709
|
-
const isStandaloneNavigator = nav.standalone === true;
|
|
1710
|
-
return isStandaloneDisplay || isStandaloneNavigator;
|
|
1711
|
-
}
|
|
1712
|
-
__name(isStandalone2, "isStandalone");
|
|
1713
|
-
function isMobileDevice2() {
|
|
1714
|
-
if (typeof window === "undefined") return false;
|
|
1715
|
-
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
1716
|
-
}
|
|
1717
|
-
__name(isMobileDevice2, "isMobileDevice");
|
|
1718
|
-
function hasValidManifest() {
|
|
1719
|
-
if (typeof document === "undefined") return false;
|
|
1720
|
-
const manifestLink = document.querySelector('link[rel="manifest"]');
|
|
1721
|
-
return !!manifestLink;
|
|
1722
|
-
}
|
|
1723
|
-
__name(hasValidManifest, "hasValidManifest");
|
|
1724
|
-
function isStandaloneReliable2() {
|
|
1725
|
-
const standalone = isStandalone2();
|
|
1726
|
-
if (!standalone) return false;
|
|
1727
|
-
if (isMobileDevice2()) return true;
|
|
1728
|
-
return hasValidManifest();
|
|
1729
|
-
}
|
|
1730
|
-
__name(isStandaloneReliable2, "isStandaloneReliable");
|
|
1731
|
-
function onDisplayModeChange(callback) {
|
|
1732
|
-
if (typeof window === "undefined" || !window.matchMedia) {
|
|
1733
|
-
return () => {
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
const mediaQuery = window.matchMedia("(display-mode: standalone)");
|
|
1737
|
-
const handleChange = /* @__PURE__ */ __name((e) => {
|
|
1738
|
-
callback(e.matches);
|
|
1739
|
-
}, "handleChange");
|
|
1740
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
1741
|
-
return () => {
|
|
1742
|
-
mediaQuery.removeEventListener("change", handleChange);
|
|
1743
|
-
};
|
|
1744
|
-
}
|
|
1745
|
-
__name(onDisplayModeChange, "onDisplayModeChange");
|
|
1746
|
-
|
|
1747
|
-
// src/snippets/PWAInstall/hooks/useInstallPrompt.ts
|
|
1748
|
-
function useInstallPrompt() {
|
|
1749
|
-
const browser = useBrowserDetect();
|
|
1750
|
-
const device = useDeviceDetect();
|
|
1751
|
-
const [state, setState] = useState(() => {
|
|
1752
|
-
if (typeof window === "undefined") {
|
|
1753
|
-
return {
|
|
1754
|
-
isIOS: false,
|
|
1755
|
-
isAndroid: false,
|
|
1756
|
-
isSafari: false,
|
|
1757
|
-
isChrome: false,
|
|
1758
|
-
isInstalled: false,
|
|
1759
|
-
canPrompt: false,
|
|
1760
|
-
deferredPrompt: null
|
|
1761
|
-
};
|
|
1762
|
-
}
|
|
1763
|
-
const isSafari = browser.isSafari && !browser.isChromium;
|
|
1764
|
-
return {
|
|
1765
|
-
isIOS: device.isIOS,
|
|
1766
|
-
isAndroid: device.isAndroid,
|
|
1767
|
-
isSafari,
|
|
1768
|
-
isChrome: browser.isChrome || browser.isChromium,
|
|
1769
|
-
isInstalled: isStandalone2(),
|
|
1770
|
-
canPrompt: false,
|
|
1771
|
-
deferredPrompt: null
|
|
1772
|
-
};
|
|
1773
|
-
});
|
|
1774
|
-
useEffect(() => {
|
|
1775
|
-
const isSafari = browser.isSafari && !browser.isChromium;
|
|
1776
|
-
setState((prev) => ({
|
|
1777
|
-
...prev,
|
|
1778
|
-
isIOS: device.isIOS,
|
|
1779
|
-
isAndroid: device.isAndroid,
|
|
1780
|
-
isSafari,
|
|
1781
|
-
isChrome: browser.isChrome || browser.isChromium,
|
|
1782
|
-
isInstalled: isStandalone2()
|
|
1783
|
-
}));
|
|
1784
|
-
}, [browser, device]);
|
|
1785
|
-
useEffect(() => {
|
|
1786
|
-
if (typeof window === "undefined") return;
|
|
1787
|
-
const handleBeforeInstallPrompt = /* @__PURE__ */ __name((e) => {
|
|
1788
|
-
e.preventDefault();
|
|
1789
|
-
const event = e;
|
|
1790
|
-
setState((prev) => ({
|
|
1791
|
-
...prev,
|
|
1792
|
-
canPrompt: true,
|
|
1793
|
-
deferredPrompt: event
|
|
1794
|
-
}));
|
|
1795
|
-
}, "handleBeforeInstallPrompt");
|
|
1796
|
-
window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
|
|
1797
|
-
return () => {
|
|
1798
|
-
window.removeEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
|
|
1799
|
-
};
|
|
1800
|
-
}, []);
|
|
1801
|
-
useEffect(() => {
|
|
1802
|
-
if (typeof window === "undefined") return;
|
|
1803
|
-
const handleAppInstalled = /* @__PURE__ */ __name(() => {
|
|
1804
|
-
setState((prev) => ({
|
|
1805
|
-
...prev,
|
|
1806
|
-
canPrompt: false,
|
|
1807
|
-
deferredPrompt: null,
|
|
1808
|
-
isInstalled: true
|
|
1809
|
-
}));
|
|
1810
|
-
markAppInstalled();
|
|
1811
|
-
}, "handleAppInstalled");
|
|
1812
|
-
window.addEventListener("appinstalled", handleAppInstalled);
|
|
1813
|
-
return () => {
|
|
1814
|
-
window.removeEventListener("appinstalled", handleAppInstalled);
|
|
1815
|
-
};
|
|
1816
|
-
}, []);
|
|
1817
|
-
useEffect(() => {
|
|
1818
|
-
const cleanup = onDisplayModeChange((isStandaloneMode) => {
|
|
1819
|
-
if (isStandaloneMode) {
|
|
1820
|
-
setState((prev) => ({
|
|
1821
|
-
...prev,
|
|
1822
|
-
isInstalled: true,
|
|
1823
|
-
canPrompt: false,
|
|
1824
|
-
deferredPrompt: null
|
|
1825
|
-
}));
|
|
1826
|
-
markAppInstalled();
|
|
1827
|
-
}
|
|
1828
|
-
});
|
|
1829
|
-
return cleanup;
|
|
1830
|
-
}, []);
|
|
1831
|
-
const promptInstall = /* @__PURE__ */ __name(async () => {
|
|
1832
|
-
if (!state.deferredPrompt) {
|
|
1833
|
-
pwaLogger2.warn("[PWA Install] No deferred prompt available");
|
|
1834
|
-
return null;
|
|
1835
|
-
}
|
|
1836
|
-
try {
|
|
1837
|
-
await state.deferredPrompt.prompt();
|
|
1838
|
-
const { outcome } = await state.deferredPrompt.userChoice;
|
|
1839
|
-
pwaLogger2.info("[PWA Install] User choice:", outcome);
|
|
1840
|
-
setState((prev) => ({
|
|
1841
|
-
...prev,
|
|
1842
|
-
deferredPrompt: null,
|
|
1843
|
-
canPrompt: false
|
|
1844
|
-
}));
|
|
1845
|
-
return outcome;
|
|
1846
|
-
} catch (error) {
|
|
1847
|
-
pwaLogger2.error("[PWA Install] Error showing install prompt:", error);
|
|
1848
|
-
return null;
|
|
1849
|
-
}
|
|
1850
|
-
}, "promptInstall");
|
|
1851
|
-
return {
|
|
1852
|
-
...state,
|
|
1853
|
-
promptInstall,
|
|
1854
|
-
// Expose full browser info
|
|
1855
|
-
browser,
|
|
1856
|
-
device
|
|
1857
|
-
};
|
|
1858
|
-
}
|
|
1859
|
-
__name(useInstallPrompt, "useInstallPrompt");
|
|
1860
|
-
var PwaContext = createContext(void 0);
|
|
1861
|
-
function PwaProvider({ children, ...config2 }) {
|
|
1862
|
-
if (config2.enabled === false) {
|
|
1863
|
-
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1864
|
-
}
|
|
1865
|
-
const prompt = useInstallPrompt();
|
|
1866
|
-
const value = {
|
|
1867
|
-
// Platform
|
|
1868
|
-
isIOS: prompt.isIOS,
|
|
1869
|
-
isAndroid: prompt.isAndroid,
|
|
1870
|
-
isDesktop: !prompt.isIOS && !prompt.isAndroid,
|
|
1871
|
-
// Browsers (from useBrowserDetect)
|
|
1872
|
-
isSafari: prompt.browser.isSafari && !prompt.browser.isChromium,
|
|
1873
|
-
// Real Safari only
|
|
1874
|
-
isChrome: prompt.browser.isChrome,
|
|
1875
|
-
isFirefox: prompt.browser.isFirefox,
|
|
1876
|
-
isEdge: prompt.browser.isEdge,
|
|
1877
|
-
isOpera: prompt.browser.isOpera,
|
|
1878
|
-
isBrave: prompt.browser.isBrave,
|
|
1879
|
-
isArc: prompt.browser.isArc,
|
|
1880
|
-
isVivaldi: prompt.browser.isVivaldi,
|
|
1881
|
-
isYandex: prompt.browser.isYandex,
|
|
1882
|
-
isSamsungBrowser: prompt.browser.isSamsungBrowser,
|
|
1883
|
-
isUCBrowser: prompt.browser.isUCBrowser,
|
|
1884
|
-
isChromium: prompt.browser.isChromium,
|
|
1885
|
-
browserName: prompt.browser.browserName,
|
|
1886
|
-
// State
|
|
1887
|
-
isInstalled: prompt.isInstalled,
|
|
1888
|
-
canPrompt: prompt.canPrompt,
|
|
1889
|
-
// Actions
|
|
1890
|
-
install: prompt.promptInstall
|
|
1891
|
-
};
|
|
1892
|
-
return /* @__PURE__ */ jsx(PwaContext.Provider, { value, children });
|
|
1893
|
-
}
|
|
1894
|
-
__name(PwaProvider, "PwaProvider");
|
|
1895
|
-
function useInstall() {
|
|
1896
|
-
const context = useContext(PwaContext);
|
|
1897
|
-
if (context === void 0) {
|
|
1898
|
-
throw new Error("useInstall must be used within <PwaProvider>");
|
|
1899
|
-
}
|
|
1900
|
-
return context;
|
|
1901
|
-
}
|
|
1902
|
-
__name(useInstall, "useInstall");
|
|
1903
|
-
function getBrowserCategory(browser) {
|
|
1904
|
-
if (browser.isChromium) return "chromium";
|
|
1905
|
-
if (browser.isFirefox) return "firefox";
|
|
1906
|
-
if (browser.isSafari) return "safari";
|
|
1907
|
-
return "unknown";
|
|
1908
|
-
}
|
|
1909
|
-
__name(getBrowserCategory, "getBrowserCategory");
|
|
1910
|
-
function getBrowserSteps(category, browserName) {
|
|
1911
|
-
switch (category) {
|
|
1912
|
-
case "chromium":
|
|
1913
|
-
return [
|
|
1914
|
-
{
|
|
1915
|
-
number: 1,
|
|
1916
|
-
title: "Find Install Icon",
|
|
1917
|
-
icon: ArrowDownToLine,
|
|
1918
|
-
description: "Look for install icon in address bar (right side)"
|
|
1919
|
-
},
|
|
1920
|
-
{
|
|
1921
|
-
number: 2,
|
|
1922
|
-
title: "Click Install",
|
|
1923
|
-
icon: Plus,
|
|
1924
|
-
description: 'Click the icon and select "Install"'
|
|
1925
|
-
},
|
|
1926
|
-
{
|
|
1927
|
-
number: 3,
|
|
1928
|
-
title: "Confirm",
|
|
1929
|
-
icon: Check,
|
|
1930
|
-
description: 'Click "Install" in the popup dialog'
|
|
1931
|
-
}
|
|
1932
|
-
];
|
|
1933
|
-
case "firefox":
|
|
1934
|
-
return [
|
|
1935
|
-
{
|
|
1936
|
-
number: 1,
|
|
1937
|
-
title: "Open Menu",
|
|
1938
|
-
icon: Menu,
|
|
1939
|
-
description: "Click the menu button (three lines)"
|
|
1940
|
-
},
|
|
1941
|
-
{
|
|
1942
|
-
number: 2,
|
|
1943
|
-
title: "Find Install Option",
|
|
1944
|
-
icon: Search,
|
|
1945
|
-
description: 'Look for "Install" or "Add to Home Screen"'
|
|
1946
|
-
},
|
|
1947
|
-
{
|
|
1948
|
-
number: 3,
|
|
1949
|
-
title: "Confirm",
|
|
1950
|
-
icon: Check,
|
|
1951
|
-
description: "Follow the installation prompts"
|
|
1952
|
-
}
|
|
1953
|
-
];
|
|
1954
|
-
case "safari":
|
|
1955
|
-
return [
|
|
1956
|
-
{
|
|
1957
|
-
number: 1,
|
|
1958
|
-
title: "Limited Support",
|
|
1959
|
-
icon: Monitor,
|
|
1960
|
-
description: "Safari on macOS has limited PWA support"
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
number: 2,
|
|
1964
|
-
title: "Use Chromium Browser",
|
|
1965
|
-
icon: ArrowDownToLine,
|
|
1966
|
-
description: "Consider using Chrome, Edge, or Brave for full PWA experience"
|
|
1967
|
-
}
|
|
1968
|
-
];
|
|
1969
|
-
default:
|
|
1970
|
-
return [
|
|
1971
|
-
{
|
|
1972
|
-
number: 1,
|
|
1973
|
-
title: "Check Address Bar",
|
|
1974
|
-
icon: ArrowDownToLine,
|
|
1975
|
-
description: "Look for an install or download icon"
|
|
1976
|
-
},
|
|
1977
|
-
{
|
|
1978
|
-
number: 2,
|
|
1979
|
-
title: "Or Use Menu",
|
|
1980
|
-
icon: Menu,
|
|
1981
|
-
description: "Check browser menu for install option"
|
|
1982
|
-
},
|
|
1983
|
-
{
|
|
1984
|
-
number: 3,
|
|
1985
|
-
title: "Confirm",
|
|
1986
|
-
icon: Check,
|
|
1987
|
-
description: "Follow the installation prompts"
|
|
1988
|
-
}
|
|
1989
|
-
];
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
__name(getBrowserSteps, "getBrowserSteps");
|
|
1993
|
-
function StepCard({ step }) {
|
|
1994
|
-
return /* @__PURE__ */ jsx(Card, { className: "border border-border", children: /* @__PURE__ */ jsx(CardContent, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
1995
|
-
/* @__PURE__ */ jsx(
|
|
1996
|
-
"div",
|
|
1997
|
-
{
|
|
1998
|
-
className: "flex items-center justify-center rounded-full bg-primary text-primary-foreground flex-shrink-0",
|
|
1999
|
-
style: { width: "32px", height: "32px" },
|
|
2000
|
-
children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: step.number })
|
|
2001
|
-
}
|
|
2002
|
-
),
|
|
2003
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2004
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
2005
|
-
/* @__PURE__ */ jsx(step.icon, { className: "w-5 h-5 text-primary" }),
|
|
2006
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground", children: step.title })
|
|
2007
|
-
] }),
|
|
2008
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: step.description })
|
|
2009
|
-
] })
|
|
2010
|
-
] }) }) });
|
|
2011
|
-
}
|
|
2012
|
-
__name(StepCard, "StepCard");
|
|
2013
|
-
function DesktopGuide({ onDismiss, open = true }) {
|
|
2014
|
-
const {
|
|
2015
|
-
browserName,
|
|
2016
|
-
isChromium,
|
|
2017
|
-
isFirefox,
|
|
2018
|
-
isSafari,
|
|
2019
|
-
isEdge,
|
|
2020
|
-
isBrave,
|
|
2021
|
-
isArc,
|
|
2022
|
-
isVivaldi,
|
|
2023
|
-
isOpera,
|
|
2024
|
-
isYandex
|
|
2025
|
-
} = useInstall();
|
|
2026
|
-
const category = useMemo(
|
|
2027
|
-
() => getBrowserCategory({ isChromium, isFirefox, isSafari }),
|
|
2028
|
-
[isChromium, isFirefox, isSafari]
|
|
2029
|
-
);
|
|
2030
|
-
const steps3 = useMemo(() => getBrowserSteps(category), [category, browserName]);
|
|
2031
|
-
const displayName = useMemo(() => {
|
|
2032
|
-
if (isEdge) return "Edge";
|
|
2033
|
-
if (isBrave) return "Brave";
|
|
2034
|
-
if (isArc) return "Arc";
|
|
2035
|
-
if (isVivaldi) return "Vivaldi";
|
|
2036
|
-
if (isOpera) return "Opera";
|
|
2037
|
-
if (isYandex) return "Yandex Browser";
|
|
2038
|
-
return browserName;
|
|
2039
|
-
}, [browserName, isEdge, isBrave, isArc, isVivaldi, isOpera, isYandex]);
|
|
2040
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && onDismiss(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
2041
|
-
/* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
|
|
2042
|
-
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2043
|
-
/* @__PURE__ */ jsx(Monitor, { className: "w-5 h-5 text-primary" }),
|
|
2044
|
-
"Install App on Desktop"
|
|
2045
|
-
] }),
|
|
2046
|
-
/* @__PURE__ */ jsx(DialogDescription, { className: "text-left", children: isSafari ? "Safari on macOS has limited PWA support. For the best experience, use Chrome, Edge, or Brave." : `Install this app on ${displayName} for quick access from your desktop` })
|
|
2047
|
-
] }),
|
|
2048
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-3 py-4", children: steps3.map((step) => /* @__PURE__ */ jsx(StepCard, { step }, step.number)) }),
|
|
2049
|
-
category === "chromium" && /* @__PURE__ */ jsxs("div", { className: "p-3 bg-muted/30 rounded-lg text-xs text-muted-foreground", children: [
|
|
2050
|
-
'\u{1F4A1} Tip: You can also right-click the page and look for "Install" option in ',
|
|
2051
|
-
displayName
|
|
2052
|
-
] }),
|
|
2053
|
-
category === "firefox" && /* @__PURE__ */ jsx("div", { className: "p-3 bg-amber-500/10 rounded-lg text-xs text-amber-700 dark:text-amber-400", children: "\u2139\uFE0F Note: Firefox has limited PWA support. Some features may not work as expected." }),
|
|
2054
|
-
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsxs(Button$1, { onClick: onDismiss, variant: "default", className: "w-full", children: [
|
|
2055
|
-
/* @__PURE__ */ jsx(Check, { className: "w-4 h-4 mr-2" }),
|
|
2056
|
-
"Got It"
|
|
2057
|
-
] }) })
|
|
2058
|
-
] }) });
|
|
2059
|
-
}
|
|
2060
|
-
__name(DesktopGuide, "DesktopGuide");
|
|
2061
|
-
var steps = [
|
|
2062
|
-
{
|
|
2063
|
-
number: 1,
|
|
2064
|
-
title: "Tap Share",
|
|
2065
|
-
icon: ArrowUpRight,
|
|
2066
|
-
description: "At the bottom of Safari"
|
|
2067
|
-
},
|
|
2068
|
-
{
|
|
2069
|
-
number: 2,
|
|
2070
|
-
title: "Scroll & Tap",
|
|
2071
|
-
icon: ArrowDown,
|
|
2072
|
-
description: '"Add to Home Screen"'
|
|
2073
|
-
},
|
|
2074
|
-
{
|
|
2075
|
-
number: 3,
|
|
2076
|
-
title: "Confirm",
|
|
2077
|
-
icon: CheckCircle,
|
|
2078
|
-
description: 'Tap "Add" in top-right'
|
|
2079
|
-
}
|
|
2080
|
-
];
|
|
2081
|
-
function StepCard2({ step }) {
|
|
2082
|
-
return /* @__PURE__ */ jsx(Card, { className: "border border-border", children: /* @__PURE__ */ jsx(CardContent, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
2083
|
-
/* @__PURE__ */ jsx(
|
|
2084
|
-
"div",
|
|
2085
|
-
{
|
|
2086
|
-
className: "flex items-center justify-center rounded-full bg-primary text-primary-foreground flex-shrink-0",
|
|
2087
|
-
style: { width: "32px", height: "32px" },
|
|
2088
|
-
children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: step.number })
|
|
2089
|
-
}
|
|
2090
|
-
),
|
|
2091
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2092
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
2093
|
-
/* @__PURE__ */ jsx(step.icon, { className: "w-5 h-5 text-primary" }),
|
|
2094
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground", children: step.title })
|
|
2095
|
-
] }),
|
|
2096
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: step.description })
|
|
2097
|
-
] })
|
|
2098
|
-
] }) }) });
|
|
2099
|
-
}
|
|
2100
|
-
__name(StepCard2, "StepCard");
|
|
2101
|
-
function IOSGuideDrawer({ onDismiss, open = true }) {
|
|
2102
|
-
return /* @__PURE__ */ jsx(Drawer, { open, onOpenChange: (isOpen) => !isOpen && onDismiss(), children: /* @__PURE__ */ jsxs(DrawerContent, { children: [
|
|
2103
|
-
/* @__PURE__ */ jsxs(DrawerHeader, { className: "text-left", children: [
|
|
2104
|
-
/* @__PURE__ */ jsxs(DrawerTitle, { className: "flex items-center gap-2", children: [
|
|
2105
|
-
/* @__PURE__ */ jsx(Share, { className: "w-5 h-5 text-primary" }),
|
|
2106
|
-
"Add to Home Screen"
|
|
2107
|
-
] }),
|
|
2108
|
-
/* @__PURE__ */ jsx(DrawerDescription, { className: "text-left", children: "Install this app on your iPhone for quick access and a better experience" })
|
|
2109
|
-
] }),
|
|
2110
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-3 p-4", children: steps.map((step) => /* @__PURE__ */ jsx(StepCard2, { step }, step.number)) }),
|
|
2111
|
-
/* @__PURE__ */ jsx("div", { className: "p-4 pt-0", children: /* @__PURE__ */ jsxs(Button$1, { onClick: onDismiss, variant: "default", className: "w-full", children: [
|
|
2112
|
-
/* @__PURE__ */ jsx(Check, { className: "w-4 h-4 mr-2" }),
|
|
2113
|
-
"Got It"
|
|
2114
|
-
] }) })
|
|
2115
|
-
] }) });
|
|
2116
|
-
}
|
|
2117
|
-
__name(IOSGuideDrawer, "IOSGuideDrawer");
|
|
2118
|
-
var steps2 = [
|
|
2119
|
-
{
|
|
2120
|
-
number: 1,
|
|
2121
|
-
title: "Tap Share",
|
|
2122
|
-
icon: ArrowUpRight,
|
|
2123
|
-
description: "At the bottom of Safari"
|
|
2124
|
-
},
|
|
2125
|
-
{
|
|
2126
|
-
number: 2,
|
|
2127
|
-
title: "Scroll & Tap",
|
|
2128
|
-
icon: ArrowDown,
|
|
2129
|
-
description: '"Add to Home Screen"'
|
|
2130
|
-
},
|
|
2131
|
-
{
|
|
2132
|
-
number: 3,
|
|
2133
|
-
title: "Confirm",
|
|
2134
|
-
icon: CheckCircle,
|
|
2135
|
-
description: 'Tap "Add" in top-right'
|
|
2136
|
-
}
|
|
2137
|
-
];
|
|
2138
|
-
function StepCard3({ step }) {
|
|
2139
|
-
return /* @__PURE__ */ jsx(Card, { className: "border border-border", children: /* @__PURE__ */ jsx(CardContent, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
2140
|
-
/* @__PURE__ */ jsx(
|
|
2141
|
-
"div",
|
|
2142
|
-
{
|
|
2143
|
-
className: "flex items-center justify-center rounded-full bg-primary text-primary-foreground flex-shrink-0",
|
|
2144
|
-
style: { width: "32px", height: "32px" },
|
|
2145
|
-
children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: step.number })
|
|
2146
|
-
}
|
|
2147
|
-
),
|
|
2148
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2149
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
2150
|
-
/* @__PURE__ */ jsx(step.icon, { className: "w-5 h-5 text-primary" }),
|
|
2151
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground", children: step.title })
|
|
2152
|
-
] }),
|
|
2153
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: step.description })
|
|
2154
|
-
] })
|
|
2155
|
-
] }) }) });
|
|
2156
|
-
}
|
|
2157
|
-
__name(StepCard3, "StepCard");
|
|
2158
|
-
function IOSGuideModal({ onDismiss, open = true }) {
|
|
2159
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (isOpen) => !isOpen && onDismiss(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
2160
|
-
/* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
|
|
2161
|
-
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2162
|
-
/* @__PURE__ */ jsx(Share, { className: "w-5 h-5 text-primary" }),
|
|
2163
|
-
"Add to Home Screen"
|
|
2164
|
-
] }),
|
|
2165
|
-
/* @__PURE__ */ jsx(DialogDescription, { className: "text-left", children: "Install this app on your iPhone for quick access and a better experience" })
|
|
2166
|
-
] }),
|
|
2167
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-3 py-4", children: steps2.map((step) => /* @__PURE__ */ jsx(StepCard3, { step }, step.number)) }),
|
|
2168
|
-
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsxs(Button$1, { onClick: onDismiss, variant: "default", className: "w-full", children: [
|
|
2169
|
-
/* @__PURE__ */ jsx(Check, { className: "w-4 h-4 mr-2" }),
|
|
2170
|
-
"Got It"
|
|
2171
|
-
] }) })
|
|
2172
|
-
] }) });
|
|
2173
|
-
}
|
|
2174
|
-
__name(IOSGuideModal, "IOSGuideModal");
|
|
2175
|
-
function IOSGuide(props) {
|
|
2176
|
-
const isMobile = useIsMobile();
|
|
2177
|
-
if (isMobile) {
|
|
2178
|
-
return /* @__PURE__ */ jsx(IOSGuideDrawer, { ...props });
|
|
2179
|
-
}
|
|
2180
|
-
return /* @__PURE__ */ jsx(IOSGuideModal, { ...props });
|
|
2181
|
-
}
|
|
2182
|
-
__name(IOSGuide, "IOSGuide");
|
|
2183
|
-
var DEFAULT_RESET_DAYS2 = 3;
|
|
2184
|
-
function A2HSHint({
|
|
2185
|
-
className,
|
|
2186
|
-
resetAfterDays = DEFAULT_RESET_DAYS2,
|
|
2187
|
-
delayMs = 3e3,
|
|
2188
|
-
demo = false,
|
|
2189
|
-
logo
|
|
2190
|
-
} = {}) {
|
|
2191
|
-
const { isIOS, isDesktop, isInstalled, canPrompt, install } = useInstall();
|
|
2192
|
-
const [show, setShow] = useState(false);
|
|
2193
|
-
const [showGuide, setShowGuide] = useState(false);
|
|
2194
|
-
const [installing, setInstalling] = useState(false);
|
|
2195
|
-
const shouldShow = demo ? !isInstalled : !isInstalled && (isIOS || canPrompt);
|
|
2196
|
-
useEffect(() => {
|
|
2197
|
-
if (!shouldShow) return;
|
|
2198
|
-
if (!demo && typeof window !== "undefined") {
|
|
2199
|
-
if (resetAfterDays === null) {
|
|
2200
|
-
if (isA2HSDismissedRecently(Number.MAX_SAFE_INTEGER)) {
|
|
2201
|
-
return;
|
|
2202
|
-
}
|
|
2203
|
-
} else if (isA2HSDismissedRecently(resetAfterDays)) {
|
|
2204
|
-
return;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
const timer = setTimeout(() => setShow(true), delayMs);
|
|
2208
|
-
return () => clearTimeout(timer);
|
|
2209
|
-
}, [shouldShow, resetAfterDays, delayMs, demo]);
|
|
2210
|
-
const handleDismiss = /* @__PURE__ */ __name(() => {
|
|
2211
|
-
setShow(false);
|
|
2212
|
-
if (!demo) {
|
|
2213
|
-
markA2HSDismissed();
|
|
2214
|
-
}
|
|
2215
|
-
}, "handleDismiss");
|
|
2216
|
-
const handleGuideDismiss = /* @__PURE__ */ __name(() => {
|
|
2217
|
-
setShowGuide(false);
|
|
2218
|
-
if (!demo) {
|
|
2219
|
-
handleDismiss();
|
|
2220
|
-
}
|
|
2221
|
-
}, "handleGuideDismiss");
|
|
2222
|
-
const handleClick = /* @__PURE__ */ __name(async () => {
|
|
2223
|
-
if (isIOS || isDesktop) {
|
|
2224
|
-
setShowGuide(true);
|
|
2225
|
-
} else if (canPrompt) {
|
|
2226
|
-
setInstalling(true);
|
|
2227
|
-
try {
|
|
2228
|
-
await install();
|
|
2229
|
-
handleDismiss();
|
|
2230
|
-
} catch (error) {
|
|
2231
|
-
pwaLogger2.error("[A2HSHint] Install error:", error);
|
|
2232
|
-
} finally {
|
|
2233
|
-
setInstalling(false);
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
}, "handleClick");
|
|
2237
|
-
if (!show) return null;
|
|
2238
|
-
let title;
|
|
2239
|
-
let subtitle;
|
|
2240
|
-
if (isIOS) {
|
|
2241
|
-
title = "Add to Home Screen";
|
|
2242
|
-
subtitle = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2243
|
-
"Tap to learn how ",
|
|
2244
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3" })
|
|
2245
|
-
] });
|
|
2246
|
-
} else if (isDesktop) {
|
|
2247
|
-
title = "Install App";
|
|
2248
|
-
subtitle = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2249
|
-
"Click to see desktop guide ",
|
|
2250
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3" })
|
|
2251
|
-
] });
|
|
2252
|
-
} else {
|
|
2253
|
-
title = "Install App";
|
|
2254
|
-
subtitle = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2255
|
-
"Tap to install ",
|
|
2256
|
-
/* @__PURE__ */ jsx(Download, { className: "w-3 h-3" })
|
|
2257
|
-
] });
|
|
2258
|
-
}
|
|
2259
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2260
|
-
/* @__PURE__ */ jsx("div", { className: cn(
|
|
2261
|
-
"fixed bottom-4 left-4 right-4 z-50 animate-in slide-in-from-bottom-4 duration-300",
|
|
2262
|
-
demo && "relative inset-auto z-auto",
|
|
2263
|
-
// Demo mode: remove fixed positioning
|
|
2264
|
-
className
|
|
2265
|
-
), children: /* @__PURE__ */ jsx(
|
|
2266
|
-
"div",
|
|
2267
|
-
{
|
|
2268
|
-
role: "button",
|
|
2269
|
-
tabIndex: 0,
|
|
2270
|
-
onClick: handleClick,
|
|
2271
|
-
onKeyDown: (e) => {
|
|
2272
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
2273
|
-
e.preventDefault();
|
|
2274
|
-
handleClick();
|
|
2275
|
-
}
|
|
2276
|
-
},
|
|
2277
|
-
className: cn(
|
|
2278
|
-
"w-full bg-zinc-900 border border-zinc-700 rounded-lg p-4 shadow-lg cursor-pointer hover:bg-zinc-800 transition-colors",
|
|
2279
|
-
installing && "opacity-70 cursor-not-allowed"
|
|
2280
|
-
),
|
|
2281
|
-
"aria-disabled": installing,
|
|
2282
|
-
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2283
|
-
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "App logo", className: "w-10 h-10 rounded-lg" }) : /* @__PURE__ */ jsx(Share, { className: "w-5 h-5 text-blue-400" }) }),
|
|
2284
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2285
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-white mb-1", children: title }),
|
|
2286
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-zinc-400 flex items-center gap-1", children: installing ? "Installing..." : subtitle })
|
|
2287
|
-
] }),
|
|
2288
|
-
/* @__PURE__ */ jsx(
|
|
2289
|
-
"button",
|
|
2290
|
-
{
|
|
2291
|
-
onClick: (e) => {
|
|
2292
|
-
e.stopPropagation();
|
|
2293
|
-
handleDismiss();
|
|
2294
|
-
},
|
|
2295
|
-
className: "flex-shrink-0 p-1 hover:bg-zinc-700 rounded transition-colors",
|
|
2296
|
-
"aria-label": "Dismiss",
|
|
2297
|
-
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 text-zinc-400" })
|
|
2298
|
-
}
|
|
2299
|
-
)
|
|
2300
|
-
] })
|
|
2301
|
-
}
|
|
2302
|
-
) }),
|
|
2303
|
-
isIOS && /* @__PURE__ */ jsx(IOSGuide, { open: showGuide, onDismiss: handleGuideDismiss }),
|
|
2304
|
-
isDesktop && /* @__PURE__ */ jsx(DesktopGuide, { open: showGuide, onDismiss: handleGuideDismiss })
|
|
2305
|
-
] });
|
|
2306
|
-
}
|
|
2307
|
-
__name(A2HSHint, "A2HSHint");
|
|
2308
|
-
var CACHE_KEY = "pwa_is_standalone";
|
|
2309
|
-
function useIsPWA(options) {
|
|
2310
|
-
const checkFunction = options?.reliable ? isStandaloneReliable2 : isStandalone2;
|
|
2311
|
-
const [isPWA, setIsPWA] = useState(() => {
|
|
2312
|
-
if (typeof window !== "undefined") {
|
|
2313
|
-
try {
|
|
2314
|
-
const cached = sessionStorage.getItem(CACHE_KEY);
|
|
2315
|
-
if (cached !== null) {
|
|
2316
|
-
return cached === "true";
|
|
2317
|
-
}
|
|
2318
|
-
} catch {
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
return checkFunction();
|
|
2322
|
-
});
|
|
2323
|
-
useEffect(() => {
|
|
2324
|
-
const isStandaloneMode = checkFunction();
|
|
2325
|
-
setIsPWA(isStandaloneMode);
|
|
2326
|
-
if (typeof window !== "undefined") {
|
|
2327
|
-
try {
|
|
2328
|
-
sessionStorage.setItem(CACHE_KEY, String(isStandaloneMode));
|
|
2329
|
-
} catch {
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
const cleanup = onDisplayModeChange((newValue) => {
|
|
2333
|
-
setIsPWA(newValue);
|
|
2334
|
-
if (typeof window !== "undefined") {
|
|
2335
|
-
try {
|
|
2336
|
-
sessionStorage.setItem(CACHE_KEY, String(newValue));
|
|
2337
|
-
} catch {
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
});
|
|
2341
|
-
return cleanup;
|
|
2342
|
-
}, [checkFunction]);
|
|
2343
|
-
return isPWA;
|
|
2344
|
-
}
|
|
2345
|
-
__name(useIsPWA, "useIsPWA");
|
|
2346
|
-
var STORAGE_KEY = "pwa_last_page";
|
|
2347
|
-
var TTL_24_HOURS = 24 * 60 * 60 * 1e3;
|
|
2348
|
-
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
2349
|
-
"/auth",
|
|
2350
|
-
"/login",
|
|
2351
|
-
"/register",
|
|
2352
|
-
"/error",
|
|
2353
|
-
"/404",
|
|
2354
|
-
"/500",
|
|
2355
|
-
"/oauth",
|
|
2356
|
-
"/callback"
|
|
2357
|
-
];
|
|
2358
|
-
function isExcludedPath(pathname) {
|
|
2359
|
-
return DEFAULT_EXCLUDE_PATTERNS.some(
|
|
2360
|
-
(pattern) => pathname === pattern || pathname.startsWith(`${pattern}/`)
|
|
2361
|
-
);
|
|
2362
|
-
}
|
|
2363
|
-
__name(isExcludedPath, "isExcludedPath");
|
|
2364
|
-
function readLastPage() {
|
|
2365
|
-
if (typeof window === "undefined") return null;
|
|
2366
|
-
try {
|
|
2367
|
-
const item = localStorage.getItem(STORAGE_KEY);
|
|
2368
|
-
if (!item) return null;
|
|
2369
|
-
const parsed = JSON.parse(item);
|
|
2370
|
-
if (parsed && typeof parsed === "object" && "_meta" in parsed && "_value" in parsed) {
|
|
2371
|
-
const age = Date.now() - parsed._meta.createdAt;
|
|
2372
|
-
if (age > parsed._meta.ttl) {
|
|
2373
|
-
localStorage.removeItem(STORAGE_KEY);
|
|
2374
|
-
return null;
|
|
2375
|
-
}
|
|
2376
|
-
return parsed._value;
|
|
2377
|
-
}
|
|
2378
|
-
return item;
|
|
2379
|
-
} catch {
|
|
2380
|
-
return null;
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
__name(readLastPage, "readLastPage");
|
|
2384
|
-
function saveLastPage(pathname) {
|
|
2385
|
-
if (typeof window === "undefined") return;
|
|
2386
|
-
try {
|
|
2387
|
-
const wrapped = {
|
|
2388
|
-
_meta: {
|
|
2389
|
-
createdAt: Date.now(),
|
|
2390
|
-
ttl: TTL_24_HOURS
|
|
2391
|
-
},
|
|
2392
|
-
_value: pathname
|
|
2393
|
-
};
|
|
2394
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(wrapped));
|
|
2395
|
-
} catch {
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
__name(saveLastPage, "saveLastPage");
|
|
2399
|
-
function usePWAPageResume(options = {}) {
|
|
2400
|
-
const { enabled = true } = options;
|
|
2401
|
-
const pathname = usePathname();
|
|
2402
|
-
const router = useRouter();
|
|
2403
|
-
const isPWA = useIsPWA();
|
|
2404
|
-
const hasResumed = useRef(false);
|
|
2405
|
-
useEffect(() => {
|
|
2406
|
-
if (!enabled || !isPWA || hasResumed.current) return;
|
|
2407
|
-
hasResumed.current = true;
|
|
2408
|
-
const lastPage = readLastPage();
|
|
2409
|
-
if (lastPage && lastPage !== pathname && !isExcludedPath(lastPage)) {
|
|
2410
|
-
router.replace(lastPage);
|
|
2411
|
-
}
|
|
2412
|
-
}, [isPWA, enabled]);
|
|
2413
|
-
useEffect(() => {
|
|
2414
|
-
if (!enabled) return;
|
|
2415
|
-
if (isExcludedPath(pathname)) return;
|
|
2416
|
-
saveLastPage(pathname);
|
|
2417
|
-
}, [pathname, enabled]);
|
|
2418
|
-
return { isPWA };
|
|
2419
|
-
}
|
|
2420
|
-
__name(usePWAPageResume, "usePWAPageResume");
|
|
2421
|
-
|
|
2422
|
-
// src/snippets/PWAInstall/components/PWAPageResumeManager.tsx
|
|
2423
|
-
function PWAPageResumeManager({ enabled = true }) {
|
|
2424
|
-
usePWAPageResume({ enabled });
|
|
2425
|
-
return null;
|
|
2426
|
-
}
|
|
2427
|
-
__name(PWAPageResumeManager, "PWAPageResumeManager");
|
|
2428
|
-
var AIChatWidget = dynamic(
|
|
2429
|
-
() => import('./AIChatWidget-LUPM7S2O.mjs').then((mod) => ({ default: mod.AIChatWidget })),
|
|
2430
|
-
{ ssr: false }
|
|
2431
|
-
);
|
|
2432
|
-
function BaseApp({
|
|
2433
|
-
children,
|
|
2434
|
-
theme,
|
|
2435
|
-
auth,
|
|
2436
|
-
analytics,
|
|
2437
|
-
centrifugo,
|
|
2438
|
-
errorTracking,
|
|
2439
|
-
errorBoundary,
|
|
2440
|
-
swr,
|
|
2441
|
-
mcpChat,
|
|
2442
|
-
pwaInstall,
|
|
2443
|
-
pushNotifications
|
|
2444
|
-
}) {
|
|
2445
|
-
const enableErrorBoundary = errorBoundary?.enabled !== false;
|
|
2446
|
-
const pwaInstallEnabled = pwaInstall?.enabled !== false;
|
|
2447
|
-
const showInstallHint = pwaInstallEnabled && pwaInstall?.showInstallHint !== false;
|
|
2448
|
-
const pushEnabled = pushNotifications?.enabled !== false && !!pushNotifications?.vapidPublicKey;
|
|
2449
|
-
const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
|
|
2450
|
-
const centrifugoEnabled = centrifugo?.enabled !== false;
|
|
2451
|
-
const content = /* @__PURE__ */ jsx(
|
|
2452
|
-
ThemeProvider,
|
|
2453
|
-
{
|
|
2454
|
-
defaultTheme: theme?.defaultTheme || "system",
|
|
2455
|
-
storageKey: theme?.storageKey,
|
|
2456
|
-
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(
|
|
2457
|
-
SWRConfig,
|
|
2458
|
-
{
|
|
2459
|
-
value: {
|
|
2460
|
-
revalidateOnFocus: swr?.revalidateOnFocus ?? true,
|
|
2461
|
-
revalidateOnReconnect: swr?.revalidateOnReconnect ?? true,
|
|
2462
|
-
dedupingInterval: swr?.dedupingInterval ?? 2e3
|
|
2463
|
-
},
|
|
2464
|
-
children: /* @__PURE__ */ jsx(AuthProvider, { config: auth, children: /* @__PURE__ */ jsx(AnalyticsProvider, { trackingId: analytics?.googleTrackingId, children: /* @__PURE__ */ jsx(
|
|
2465
|
-
CentrifugoProvider,
|
|
2466
|
-
{
|
|
2467
|
-
enabled: centrifugoEnabled,
|
|
2468
|
-
autoConnect: centrifugoEnabled && centrifugo?.autoConnect,
|
|
2469
|
-
url: centrifugoUrl,
|
|
2470
|
-
onTokenRefresh: async () => {
|
|
2471
|
-
const response = await getCentrifugoAuthTokenRetrieve();
|
|
2472
|
-
return response.token;
|
|
2473
|
-
},
|
|
2474
|
-
children: /* @__PURE__ */ jsx(PwaProvider, { enabled: pwaInstallEnabled, children: /* @__PURE__ */ jsx(
|
|
2475
|
-
DjangoPushProvider,
|
|
2476
|
-
{
|
|
2477
|
-
vapidPublicKey: pushNotifications?.vapidPublicKey || "",
|
|
2478
|
-
autoSubscribe: pushNotifications?.autoSubscribe,
|
|
2479
|
-
children: /* @__PURE__ */ jsxs(
|
|
2480
|
-
ErrorTrackingProvider,
|
|
2481
|
-
{
|
|
2482
|
-
validation: errorTracking?.validation,
|
|
2483
|
-
cors: errorTracking?.cors,
|
|
2484
|
-
network: errorTracking?.network,
|
|
2485
|
-
onError: errorTracking?.onError,
|
|
2486
|
-
children: [
|
|
2487
|
-
children,
|
|
2488
|
-
/* @__PURE__ */ jsx(
|
|
2489
|
-
NextTopLoader,
|
|
2490
|
-
{
|
|
2491
|
-
color: "hsl(var(--primary))",
|
|
2492
|
-
height: 3,
|
|
2493
|
-
showSpinner: false,
|
|
2494
|
-
shadow: "0 0 10px hsl(var(--primary)), 0 0 5px hsl(var(--primary))"
|
|
2495
|
-
}
|
|
2496
|
-
),
|
|
2497
|
-
/* @__PURE__ */ jsx(Toaster, {}),
|
|
2498
|
-
showInstallHint && /* @__PURE__ */ jsx(
|
|
2499
|
-
A2HSHint,
|
|
2500
|
-
{
|
|
2501
|
-
resetAfterDays: pwaInstall?.resetAfterDays,
|
|
2502
|
-
delayMs: pwaInstall?.delayMs,
|
|
2503
|
-
logo: pwaInstall?.logo
|
|
2504
|
-
}
|
|
2505
|
-
),
|
|
2506
|
-
pwaInstallEnabled && pwaInstall?.resumeLastPage && /* @__PURE__ */ jsx(PWAPageResumeManager, { enabled: true }),
|
|
2507
|
-
pushEnabled && /* @__PURE__ */ jsx(
|
|
2508
|
-
PushPrompt,
|
|
2509
|
-
{
|
|
2510
|
-
vapidPublicKey: pushNotifications.vapidPublicKey,
|
|
2511
|
-
subscribeEndpoint: pushNotifications?.subscribeEndpoint,
|
|
2512
|
-
requirePWA: pushNotifications?.requirePWA ?? true,
|
|
2513
|
-
delayMs: pushNotifications?.delayMs,
|
|
2514
|
-
resetAfterDays: pushNotifications?.resetAfterDays
|
|
2515
|
-
}
|
|
2516
|
-
),
|
|
2517
|
-
mcpChat?.enabled && /* @__PURE__ */ jsx(
|
|
2518
|
-
AIChatWidget,
|
|
2519
|
-
{
|
|
2520
|
-
apiEndpoint: mcpChat.apiEndpoint,
|
|
2521
|
-
title: mcpChat.title,
|
|
2522
|
-
placeholder: mcpChat.placeholder,
|
|
2523
|
-
greeting: mcpChat.greeting,
|
|
2524
|
-
position: mcpChat.position,
|
|
2525
|
-
variant: mcpChat.variant,
|
|
2526
|
-
enableStreaming: mcpChat.enableStreaming,
|
|
2527
|
-
autoDetectEnvironment: mcpChat.autoDetectEnvironment,
|
|
2528
|
-
className: mcpChat.className
|
|
2529
|
-
}
|
|
2530
|
-
),
|
|
2531
|
-
/* @__PURE__ */ jsx(AuthDialog, { authPath: auth?.routes?.auth })
|
|
2532
|
-
]
|
|
2533
|
-
}
|
|
2534
|
-
)
|
|
2535
|
-
}
|
|
2536
|
-
) })
|
|
2537
|
-
}
|
|
2538
|
-
) }) })
|
|
2539
|
-
}
|
|
2540
|
-
) })
|
|
2541
|
-
}
|
|
2542
|
-
);
|
|
2543
|
-
if (enableErrorBoundary) {
|
|
2544
|
-
return /* @__PURE__ */ jsx(
|
|
2545
|
-
ErrorBoundary,
|
|
2546
|
-
{
|
|
2547
|
-
supportEmail: errorBoundary?.supportEmail,
|
|
2548
|
-
onError: errorBoundary?.onError,
|
|
2549
|
-
children: content
|
|
2550
|
-
}
|
|
2551
|
-
);
|
|
2552
|
-
}
|
|
2553
|
-
return content;
|
|
2554
|
-
}
|
|
2555
|
-
__name(BaseApp, "BaseApp");
|
|
2556
|
-
function matchesPath(pathname, enabledPath) {
|
|
2557
|
-
if (!enabledPath) return false;
|
|
2558
|
-
if (typeof enabledPath === "string") {
|
|
2559
|
-
return pathname === enabledPath || pathname.startsWith(enabledPath + "/");
|
|
2560
|
-
}
|
|
2561
|
-
return enabledPath.some((path) => pathname === path || pathname.startsWith(path + "/"));
|
|
2562
|
-
}
|
|
2563
|
-
__name(matchesPath, "matchesPath");
|
|
2564
|
-
function determineLayoutMode(pathname, adminLayout, privateLayout, publicLayout) {
|
|
2565
|
-
if (adminLayout && matchesPath(pathname, adminLayout.enabledPath)) return "admin";
|
|
2566
|
-
if (privateLayout && matchesPath(pathname, privateLayout.enabledPath)) return "private";
|
|
2567
|
-
if (publicLayout && matchesPath(pathname, publicLayout.enabledPath)) return "public";
|
|
2568
|
-
return "public";
|
|
2569
|
-
}
|
|
2570
|
-
__name(determineLayoutMode, "determineLayoutMode");
|
|
2571
|
-
function AppLayoutContent({
|
|
2572
|
-
children,
|
|
2573
|
-
publicLayout,
|
|
2574
|
-
privateLayout,
|
|
2575
|
-
adminLayout,
|
|
2576
|
-
noLayoutPaths
|
|
2577
|
-
}) {
|
|
2578
|
-
const pathname = usePathname();
|
|
2579
|
-
const shouldSkipLayout = useMemo(
|
|
2580
|
-
() => matchesPath(pathname, noLayoutPaths),
|
|
2581
|
-
[pathname, noLayoutPaths]
|
|
2582
|
-
);
|
|
2583
|
-
const layoutMode = useMemo(
|
|
2584
|
-
() => determineLayoutMode(
|
|
2585
|
-
pathname,
|
|
2586
|
-
adminLayout,
|
|
2587
|
-
privateLayout,
|
|
2588
|
-
publicLayout
|
|
2589
|
-
),
|
|
2590
|
-
[pathname, adminLayout, privateLayout, publicLayout]
|
|
2591
|
-
);
|
|
2592
|
-
const renderLayout = /* @__PURE__ */ __name(() => {
|
|
2593
|
-
if (shouldSkipLayout) {
|
|
2594
|
-
return children;
|
|
2595
|
-
}
|
|
2596
|
-
switch (layoutMode) {
|
|
2597
|
-
case "admin":
|
|
2598
|
-
if (!adminLayout && privateLayout) {
|
|
2599
|
-
return /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(privateLayout.component, { children }) }) });
|
|
2600
|
-
}
|
|
2601
|
-
if (!adminLayout) {
|
|
2602
|
-
return children;
|
|
2603
|
-
}
|
|
2604
|
-
return /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(adminLayout.component, { children }) }) });
|
|
2605
|
-
case "private":
|
|
2606
|
-
if (!privateLayout) {
|
|
2607
|
-
if (publicLayout) {
|
|
2608
|
-
return /* @__PURE__ */ jsx(publicLayout.component, { children });
|
|
2609
|
-
}
|
|
2610
|
-
return children;
|
|
2611
|
-
}
|
|
2612
|
-
return /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(privateLayout.component, { children }) }) });
|
|
2613
|
-
case "public":
|
|
2614
|
-
default:
|
|
2615
|
-
if (!publicLayout) {
|
|
2616
|
-
return children;
|
|
2617
|
-
}
|
|
2618
|
-
return /* @__PURE__ */ jsx(publicLayout.component, { children });
|
|
2619
|
-
}
|
|
2620
|
-
}, "renderLayout");
|
|
2621
|
-
return renderLayout();
|
|
2622
|
-
}
|
|
2623
|
-
__name(AppLayoutContent, "AppLayoutContent");
|
|
2624
|
-
function AppLayout(props) {
|
|
2625
|
-
const {
|
|
2626
|
-
theme,
|
|
2627
|
-
auth,
|
|
2628
|
-
analytics,
|
|
2629
|
-
centrifugo,
|
|
2630
|
-
errorTracking,
|
|
2631
|
-
errorBoundary,
|
|
2632
|
-
swr,
|
|
2633
|
-
mcpChat,
|
|
2634
|
-
pwaInstall,
|
|
2635
|
-
pushNotifications
|
|
2636
|
-
} = props;
|
|
2637
|
-
return /* @__PURE__ */ jsx(
|
|
2638
|
-
BaseApp,
|
|
2639
|
-
{
|
|
2640
|
-
theme,
|
|
2641
|
-
auth,
|
|
2642
|
-
analytics,
|
|
2643
|
-
centrifugo,
|
|
2644
|
-
errorTracking,
|
|
2645
|
-
errorBoundary,
|
|
2646
|
-
swr,
|
|
2647
|
-
mcpChat,
|
|
2648
|
-
pwaInstall,
|
|
2649
|
-
pushNotifications,
|
|
2650
|
-
children: /* @__PURE__ */ jsx(AppLayoutContent, { ...props })
|
|
2651
|
-
}
|
|
2652
|
-
);
|
|
2653
|
-
}
|
|
2654
|
-
__name(AppLayout, "AppLayout");
|
|
2655
|
-
function PublicNavigation({
|
|
2656
|
-
logo,
|
|
2657
|
-
siteName,
|
|
2658
|
-
navigation,
|
|
2659
|
-
userMenu,
|
|
2660
|
-
onMobileMenuClick
|
|
2661
|
-
}) {
|
|
2662
|
-
const { isAuthenticated } = useAuth();
|
|
2663
|
-
const isMobile = useIsMobile();
|
|
2664
|
-
const navClass = "sticky top-0 w-full z-50 border-b bg-background";
|
|
2665
|
-
return /* @__PURE__ */ jsx("nav", { className: navClass, style: { backgroundColor: "hsl(var(--background) / 0.6)", backdropFilter: "blur(10px)" }, children: /* @__PURE__ */ jsx("div", { className: "w-full px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-4", children: [
|
|
2666
|
-
/* @__PURE__ */ jsxs(Link, { href: "/", className: "flex items-center gap-2", children: [
|
|
2667
|
-
logo && /* @__PURE__ */ jsx("img", { src: logo, alt: siteName, className: "h-6 w-auto object-contain" }),
|
|
2668
|
-
/* @__PURE__ */ jsx("span", { className: "font-bold text-lg", children: siteName })
|
|
2669
|
-
] }),
|
|
2670
|
-
/* @__PURE__ */ jsx("div", { className: "hidden md:flex items-center gap-6", children: navigation.map((item) => /* @__PURE__ */ jsx(
|
|
2671
|
-
Link,
|
|
2672
|
-
{
|
|
2673
|
-
href: item.href,
|
|
2674
|
-
className: "text-sm font-medium hover:text-primary transition-colors",
|
|
2675
|
-
children: item.label
|
|
2676
|
-
},
|
|
2677
|
-
item.href
|
|
2678
|
-
)) }),
|
|
2679
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
2680
|
-
!isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2681
|
-
/* @__PURE__ */ jsx(ThemeToggle, {}),
|
|
2682
|
-
/* @__PURE__ */ jsx(
|
|
2683
|
-
UserMenu,
|
|
2684
|
-
{
|
|
2685
|
-
variant: "desktop",
|
|
2686
|
-
groups: userMenu?.groups,
|
|
2687
|
-
authPath: userMenu?.authPath
|
|
2688
|
-
}
|
|
2689
|
-
)
|
|
2690
|
-
] }),
|
|
2691
|
-
isMobile && /* @__PURE__ */ jsx(
|
|
2692
|
-
Button,
|
|
2693
|
-
{
|
|
2694
|
-
variant: "ghost",
|
|
2695
|
-
size: "icon",
|
|
2696
|
-
onClick: onMobileMenuClick,
|
|
2697
|
-
"aria-label": "Toggle mobile menu",
|
|
2698
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
2699
|
-
}
|
|
2700
|
-
)
|
|
2701
|
-
] })
|
|
2702
|
-
] }) }) });
|
|
2703
|
-
}
|
|
2704
|
-
__name(PublicNavigation, "PublicNavigation");
|
|
2705
|
-
function PublicMobileDrawer({
|
|
2706
|
-
isOpen,
|
|
2707
|
-
onClose,
|
|
2708
|
-
logo,
|
|
2709
|
-
siteName,
|
|
2710
|
-
navigation,
|
|
2711
|
-
userMenu
|
|
2712
|
-
}) {
|
|
2713
|
-
const { isAuthenticated } = useAuth();
|
|
2714
|
-
return /* @__PURE__ */ jsx(Drawer$1, { open: isOpen, onOpenChange: (open) => !open && onClose(), direction: "right", children: /* @__PURE__ */ jsxs(DrawerContent$1, { direction: "right", className: "w-80 lg:hidden", children: [
|
|
2715
|
-
/* @__PURE__ */ jsxs(DrawerHeader$1, { className: "flex flex-row items-center justify-between p-4 border-b border-border/30", children: [
|
|
2716
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2717
|
-
logo && /* @__PURE__ */ jsx(
|
|
2718
|
-
"img",
|
|
2719
|
-
{
|
|
2720
|
-
src: logo,
|
|
2721
|
-
alt: `${siteName} Logo`,
|
|
2722
|
-
className: "h-6 w-auto object-contain"
|
|
2723
|
-
}
|
|
2724
|
-
),
|
|
2725
|
-
/* @__PURE__ */ jsx(DrawerTitle$1, { className: "text-lg font-bold text-foreground", children: siteName })
|
|
2726
|
-
] }),
|
|
2727
|
-
/* @__PURE__ */ jsxs(DrawerClose, { className: "p-2 rounded-sm transition-colors hover:bg-accent/50", children: [
|
|
2728
|
-
/* @__PURE__ */ jsx(X, { className: "size-5" }),
|
|
2729
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close menu" })
|
|
2730
|
-
] })
|
|
2731
|
-
] }),
|
|
2732
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-6", children: [
|
|
2733
|
-
/* @__PURE__ */ jsx(
|
|
2734
|
-
UserMenu,
|
|
2735
|
-
{
|
|
2736
|
-
variant: "mobile",
|
|
2737
|
-
groups: userMenu?.groups,
|
|
2738
|
-
authPath: userMenu?.authPath
|
|
2739
|
-
}
|
|
2740
|
-
),
|
|
2741
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2742
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsx("h3", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: "Menu" }) }),
|
|
2743
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: navigation.map((item) => /* @__PURE__ */ jsx(
|
|
2744
|
-
Link,
|
|
2745
|
-
{
|
|
2746
|
-
href: item.href,
|
|
2747
|
-
className: "block px-4 py-3 rounded-sm text-sm font-medium transition-colors text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
2748
|
-
children: item.label
|
|
2749
|
-
},
|
|
2750
|
-
item.href
|
|
2751
|
-
)) })
|
|
2752
|
-
] })
|
|
2753
|
-
] }),
|
|
2754
|
-
/* @__PURE__ */ jsx("div", { className: "border-t border-border/30 p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [
|
|
2755
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: "Theme" }),
|
|
2756
|
-
/* @__PURE__ */ jsx(ThemeToggle, {})
|
|
2757
|
-
] }) })
|
|
2758
|
-
] }) });
|
|
2759
|
-
}
|
|
2760
|
-
__name(PublicMobileDrawer, "PublicMobileDrawer");
|
|
2761
|
-
function DjangoCFGLogo({ className, size }) {
|
|
2762
|
-
return /* @__PURE__ */ jsxs(
|
|
2763
|
-
"svg",
|
|
2764
|
-
{
|
|
2765
|
-
width: size,
|
|
2766
|
-
height: size,
|
|
2767
|
-
viewBox: "0 0 541 536",
|
|
2768
|
-
fill: "none",
|
|
2769
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2770
|
-
className,
|
|
2771
|
-
children: [
|
|
2772
|
-
/* @__PURE__ */ jsx(
|
|
2773
|
-
"path",
|
|
2774
|
-
{
|
|
2775
|
-
d: "M159.958 408.555C129.071 408.555 102.501 403.407 80.2482 393.111C57.9958 382.483 40.8913 366.707 28.9348 345.783C16.9783 324.527 11 297.791 11 265.575C11 236.348 16.9783 211.272 28.9348 190.348C40.8913 169.092 57.6637 152.818 79.2519 141.526C100.84 130.233 126.248 124.421 155.475 124.089C167.431 124.089 177.893 125.085 186.861 127.078C196.16 128.739 202.969 130.566 207.286 132.558L206.29 185.864C201.972 184.204 196.492 182.543 189.85 180.883C183.207 179.222 174.904 178.392 164.94 178.392C137.374 178.392 116.616 186.031 102.667 201.308C89.0496 216.586 82.241 238.008 82.241 265.575C82.241 284.506 85.0641 300.614 90.7102 313.899C96.6885 327.184 105.656 337.314 117.612 344.289C129.569 350.931 144.847 354.252 163.446 354.252C173.41 354.252 182.045 353.422 189.352 351.761C196.991 350.101 203.633 348.108 209.279 345.783L202.803 364.216C200.146 358.57 198.485 351.761 197.821 343.79C197.157 335.819 196.824 327.184 196.824 317.885V113.627C196.824 105.656 196.658 96.0244 196.326 84.7322C195.994 73.4399 195.496 62.4797 194.832 51.8517C194.167 41.2237 193.171 33.2526 191.843 27.9386L193.337 23.9531H266.571V304.433C266.571 311.076 266.571 319.711 266.571 330.339C266.903 340.635 267.235 350.765 267.567 360.729C268.231 370.693 269.228 378.664 270.556 384.642L269.062 388.627C255.112 394.273 239.004 398.923 220.737 402.577C202.803 406.562 182.543 408.555 159.958 408.555Z",
|
|
2776
|
-
fill: "currentColor"
|
|
2777
|
-
}
|
|
2778
|
-
),
|
|
2779
|
-
/* @__PURE__ */ jsx(
|
|
2780
|
-
"path",
|
|
2781
|
-
{
|
|
2782
|
-
d: "M193.337 23.9531L191.843 27.9385C193.171 33.2525 194.168 41.2236 194.832 51.8516C195.496 62.4796 195.994 73.4401 196.326 84.7324C196.658 96.0245 196.824 105.656 196.824 113.627V129.152C193.901 128.435 190.58 127.742 186.86 127.078C177.893 125.085 167.431 124.089 155.475 124.089C126.248 124.421 100.84 130.233 79.252 141.525L78.2432 142.059C57.1405 153.328 40.7042 169.425 28.9346 190.349C16.9782 211.272 11 236.348 11 265.575L11.0039 267.081C11.1878 298.625 17.1649 324.859 28.9346 345.783C40.8911 366.707 57.9956 382.483 80.248 393.111C102.5 403.407 129.07 408.555 159.958 408.555C182.543 408.555 202.803 406.562 220.737 402.576C239.004 398.923 255.112 394.273 269.062 388.627L270.557 384.642C269.228 378.663 268.232 370.692 267.567 360.729C267.235 350.765 266.903 340.635 266.571 330.339V23.9531H193.337ZM82.2412 265.575C82.2412 238.009 89.0498 216.586 102.667 201.309C116.616 186.031 137.374 178.392 164.94 178.392L166.789 178.401C175.935 178.499 183.622 179.326 189.85 180.883C192.337 181.505 194.663 182.126 196.824 182.748V317.885L196.828 319.62C196.867 328.261 197.199 336.317 197.821 343.79C197.985 345.757 198.21 347.653 198.495 349.478C195.625 350.297 192.577 351.06 189.352 351.762C182.045 353.422 173.409 354.252 163.445 354.252V343.252C172.677 343.252 180.348 342.49 186.584 341.106C186.076 333.821 185.824 326.078 185.824 317.885V191.237C180.514 190.065 173.614 189.392 164.94 189.392C139.406 189.392 122.05 196.41 110.814 208.698C99.5767 221.349 93.2412 239.809 93.2412 265.575C93.2413 283.41 95.8996 297.933 100.788 309.487C105.861 320.71 113.267 328.992 123.06 334.729C132.888 340.16 146.134 343.252 163.445 343.252V354.252C144.846 354.252 129.569 350.931 117.612 344.288C105.656 337.313 96.6882 327.184 90.71 313.899C85.2402 301.029 82.4199 285.51 82.249 267.341L82.2412 265.575ZM277.571 330.152C277.899 340.322 278.227 350.329 278.555 360.174C279.202 369.794 280.145 377.085 281.294 382.256L282.003 385.445L277.666 397.012L273.188 398.824C258.493 404.772 241.727 409.592 222.969 413.348C204.136 417.518 183.107 419.555 159.958 419.555C127.877 419.555 99.6539 414.211 75.6289 403.095L75.5684 403.066L75.5078 403.037C51.2848 391.468 32.4787 374.156 19.3838 351.24L19.3652 351.208L19.3477 351.176C6.21456 327.828 6.03515e-05 299.093 0 265.575C0 234.84 6.29459 207.797 19.3838 184.891C32.3805 161.813 50.6989 144.047 74.1533 131.778C97.5551 119.537 124.732 113.438 155.35 113.09L155.412 113.089H155.475C166.591 113.089 176.734 113.908 185.824 115.636V113.627C185.824 105.798 185.661 96.2805 185.331 85.0557C185.002 73.8772 184.51 63.0382 183.854 52.5381C183.2 42.0782 182.249 34.9186 181.171 30.6064L180.34 27.2832L185.714 12.9531H277.571V330.152Z",
|
|
2783
|
-
fill: "currentColor",
|
|
2784
|
-
opacity: "0.2"
|
|
2785
|
-
}
|
|
2786
|
-
),
|
|
2787
|
-
/* @__PURE__ */ jsx(
|
|
2788
|
-
"path",
|
|
2789
|
-
{
|
|
2790
|
-
d: "M326.086 273.578L489.086 2.57812L415.586 204.578H532.086L333.586 533.078L415.586 273.578H326.086Z",
|
|
2791
|
-
fill: "#FFEF0B"
|
|
2792
|
-
}
|
|
2793
|
-
),
|
|
2794
|
-
/* @__PURE__ */ jsx(
|
|
2795
|
-
"path",
|
|
2796
|
-
{
|
|
2797
|
-
d: "M493.784 4.28711L422.726 199.577H540.949L337.865 535.663L328.818 531.57L408.763 278.577H317.244L484.801 0L493.784 4.28711ZM334.929 268.577H422.409L350.98 494.621L523.223 209.577H408.446L466.683 49.5254L334.929 268.577Z",
|
|
2798
|
-
fill: "currentColor"
|
|
2799
|
-
}
|
|
2800
|
-
)
|
|
2801
|
-
]
|
|
2802
|
-
}
|
|
2803
|
-
);
|
|
2804
|
-
}
|
|
2805
|
-
__name(DjangoCFGLogo, "DjangoCFGLogo");
|
|
2806
|
-
function FooterBottom({
|
|
2807
|
-
copyright,
|
|
2808
|
-
credits,
|
|
2809
|
-
links = [],
|
|
2810
|
-
variant = "desktop"
|
|
2811
|
-
}) {
|
|
2812
|
-
const isMobile = variant === "mobile";
|
|
2813
|
-
if (isMobile) {
|
|
2814
|
-
return /* @__PURE__ */ jsx("div", { className: "border-t border-border pt-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center space-y-2", children: [
|
|
2815
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: copyright }),
|
|
2816
|
-
credits && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground flex items-center justify-center gap-1.5", children: credits.url ? /* @__PURE__ */ jsxs(
|
|
2817
|
-
"a",
|
|
2818
|
-
{
|
|
2819
|
-
href: credits.url,
|
|
2820
|
-
target: "_blank",
|
|
2821
|
-
rel: "noopener noreferrer",
|
|
2822
|
-
className: "hover:text-primary transition-colors flex items-center gap-1.5",
|
|
2823
|
-
children: [
|
|
2824
|
-
/* @__PURE__ */ jsx(DjangoCFGLogo, { size: 12, className: "text-foreground" }),
|
|
2825
|
-
credits.text
|
|
2826
|
-
]
|
|
2827
|
-
}
|
|
2828
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2829
|
-
/* @__PURE__ */ jsx(DjangoCFGLogo, { size: 12, className: "text-foreground" }),
|
|
2830
|
-
credits.text
|
|
2831
|
-
] }) })
|
|
2832
|
-
] }) });
|
|
2833
|
-
}
|
|
2834
|
-
return /* @__PURE__ */ jsx("div", { className: "border-t border-border mt-8 pt-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row justify-between items-center space-y-3 md:space-y-0 gap-4", children: [
|
|
2835
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: copyright }),
|
|
2836
|
-
credits && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground flex items-center gap-1.5", children: credits.url ? /* @__PURE__ */ jsxs(
|
|
2837
|
-
"a",
|
|
2838
|
-
{
|
|
2839
|
-
href: credits.url,
|
|
2840
|
-
target: "_blank",
|
|
2841
|
-
rel: "noopener noreferrer",
|
|
2842
|
-
className: "hover:text-primary transition-colors flex items-center gap-1.5",
|
|
2843
|
-
children: [
|
|
2844
|
-
/* @__PURE__ */ jsx(DjangoCFGLogo, { size: 14, className: "text-foreground" }),
|
|
2845
|
-
credits.text
|
|
2846
|
-
]
|
|
2847
|
-
}
|
|
2848
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2849
|
-
/* @__PURE__ */ jsx(DjangoCFGLogo, { size: 14, className: "text-foreground" }),
|
|
2850
|
-
credits.text
|
|
2851
|
-
] }) }),
|
|
2852
|
-
links.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-3 md:gap-4 justify-center md:justify-end", children: links.map(
|
|
2853
|
-
(link) => link.external ? /* @__PURE__ */ jsx(
|
|
2854
|
-
"a",
|
|
2855
|
-
{
|
|
2856
|
-
href: link.path,
|
|
2857
|
-
target: "_blank",
|
|
2858
|
-
rel: "noopener noreferrer",
|
|
2859
|
-
className: "text-xs text-muted-foreground hover:text-primary transition-colors",
|
|
2860
|
-
children: link.label
|
|
2861
|
-
},
|
|
2862
|
-
link.path
|
|
2863
|
-
) : /* @__PURE__ */ jsx(
|
|
2864
|
-
Link,
|
|
2865
|
-
{
|
|
2866
|
-
href: link.path,
|
|
2867
|
-
className: "text-xs text-muted-foreground hover:text-primary transition-colors",
|
|
2868
|
-
children: link.label
|
|
2869
|
-
},
|
|
2870
|
-
link.path
|
|
2871
|
-
)
|
|
2872
|
-
) })
|
|
2873
|
-
] }) });
|
|
2874
|
-
}
|
|
2875
|
-
__name(FooterBottom, "FooterBottom");
|
|
2876
|
-
function FooterMenuSections({ menuSections }) {
|
|
2877
|
-
if (menuSections.length === 0) return null;
|
|
2878
|
-
const gapPx = 48;
|
|
2879
|
-
const sectionCount = menuSections.length;
|
|
2880
|
-
const totalGap = (sectionCount - 1) * gapPx;
|
|
2881
|
-
const sectionWidth = `calc(25% - ${totalGap / sectionCount}px)`;
|
|
2882
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-1 gap-8 lg:gap-x-12 justify-end", children: menuSections.map((section) => /* @__PURE__ */ jsxs(
|
|
2883
|
-
"div",
|
|
2884
|
-
{
|
|
2885
|
-
className: "flex-shrink-0 min-w-0",
|
|
2886
|
-
style: { width: sectionWidth },
|
|
2887
|
-
children: [
|
|
2888
|
-
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-foreground mb-3", children: section.title }),
|
|
2889
|
-
/* @__PURE__ */ jsx("ul", { className: "space-y-2", children: section.items.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
2890
|
-
Link,
|
|
2891
|
-
{
|
|
2892
|
-
href: item.path,
|
|
2893
|
-
className: "text-muted-foreground hover:text-primary text-sm transition-colors",
|
|
2894
|
-
children: item.label
|
|
2895
|
-
}
|
|
2896
|
-
) }, item.path)) })
|
|
2897
|
-
]
|
|
2898
|
-
},
|
|
2899
|
-
section.title
|
|
2900
|
-
)) });
|
|
2901
|
-
}
|
|
2902
|
-
__name(FooterMenuSections, "FooterMenuSections");
|
|
2903
|
-
var socialIconsMap = {
|
|
2904
|
-
github: { icon: Github, title: "GitHub" },
|
|
2905
|
-
linkedin: { icon: Linkedin, title: "LinkedIn" },
|
|
2906
|
-
twitter: { icon: Twitter, title: "Twitter" },
|
|
2907
|
-
telegram: { icon: MessageCircle, title: "Telegram" },
|
|
2908
|
-
youtube: { icon: Youtube, title: "YouTube" },
|
|
2909
|
-
facebook: { icon: Facebook, title: "Facebook" },
|
|
2910
|
-
instagram: { icon: Instagram, title: "Instagram" },
|
|
2911
|
-
whatsapp: { icon: MessageSquare, title: "WhatsApp" },
|
|
2912
|
-
email: { icon: Mail, title: "Email" }
|
|
2913
|
-
};
|
|
2914
|
-
function FooterSocialLinksComponent({
|
|
2915
|
-
socialLinks,
|
|
2916
|
-
className = "flex space-x-4",
|
|
2917
|
-
iconClassName = "w-5 h-5"
|
|
2918
|
-
}) {
|
|
2919
|
-
const socialLinksData = socialLinks ? Object.entries(socialLinks).filter(([_, url]) => url).map(([platform, url]) => {
|
|
2920
|
-
const social = socialIconsMap[platform];
|
|
2921
|
-
if (!social) return null;
|
|
2922
|
-
return {
|
|
2923
|
-
platform,
|
|
2924
|
-
url,
|
|
2925
|
-
icon: social.icon,
|
|
2926
|
-
title: social.title
|
|
2927
|
-
};
|
|
2928
|
-
}).filter((item) => item !== null) : [];
|
|
2929
|
-
if (socialLinksData.length === 0) return null;
|
|
2930
|
-
return /* @__PURE__ */ jsx("div", { className, children: socialLinksData.map((social) => {
|
|
2931
|
-
const Icon = social.icon;
|
|
2932
|
-
return /* @__PURE__ */ jsx(
|
|
2933
|
-
"a",
|
|
2934
|
-
{
|
|
2935
|
-
href: social.url,
|
|
2936
|
-
target: "_blank",
|
|
2937
|
-
rel: "noopener noreferrer",
|
|
2938
|
-
className: "text-muted-foreground hover:text-primary transition-colors",
|
|
2939
|
-
title: social.title,
|
|
2940
|
-
children: /* @__PURE__ */ jsx(Icon, { className: iconClassName })
|
|
2941
|
-
},
|
|
2942
|
-
social.platform
|
|
2943
|
-
);
|
|
2944
|
-
}) });
|
|
2945
|
-
}
|
|
2946
|
-
__name(FooterSocialLinksComponent, "FooterSocialLinksComponent");
|
|
2947
|
-
function FooterProjectInfo({
|
|
2948
|
-
siteName,
|
|
2949
|
-
description,
|
|
2950
|
-
logo,
|
|
2951
|
-
badge,
|
|
2952
|
-
socialLinks,
|
|
2953
|
-
variant = "desktop"
|
|
2954
|
-
}) {
|
|
2955
|
-
const isMobile = variant === "mobile";
|
|
2956
|
-
return /* @__PURE__ */ jsxs("div", { className: isMobile ? "text-center space-y-4 mb-6" : "space-y-4 lg:flex-shrink-0 lg:w-80", children: [
|
|
2957
|
-
/* @__PURE__ */ jsxs("div", { className: isMobile ? "flex items-center justify-center gap-2" : "flex items-center gap-2", children: [
|
|
2958
|
-
logo ? /* @__PURE__ */ jsx("div", { className: isMobile ? "w-6 h-6 flex items-center justify-center" : "w-8 h-8 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
2959
|
-
"img",
|
|
2960
|
-
{
|
|
2961
|
-
src: logo,
|
|
2962
|
-
alt: `${siteName} Logo`,
|
|
2963
|
-
className: "w-full h-full object-contain"
|
|
2964
|
-
}
|
|
2965
|
-
) }) : /* @__PURE__ */ jsx(DjangoCFGLogo, { size: isMobile ? 24 : 32, className: "text-foreground" }),
|
|
2966
|
-
/* @__PURE__ */ jsx("span", { className: isMobile ? "text-lg font-bold text-foreground" : "text-xl font-bold text-foreground", children: siteName })
|
|
2967
|
-
] }),
|
|
2968
|
-
description && /* @__PURE__ */ jsx("p", { className: isMobile ? "text-muted-foreground text-sm leading-relaxed max-w-md mx-auto" : "text-muted-foreground text-sm leading-relaxed", children: description }),
|
|
2969
|
-
badge && !isMobile && /* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 px-3 py-1 rounded-full bg-gradient-to-r from-primary/80 to-secondary/60 border border-primary/30 shadow-brand text-xs font-semibold text-primary-foreground", children: [
|
|
2970
|
-
/* @__PURE__ */ jsx(badge.icon, { className: "w-4 h-4" }),
|
|
2971
|
-
badge.text
|
|
2972
|
-
] }) }),
|
|
2973
|
-
socialLinks && /* @__PURE__ */ jsx(
|
|
2974
|
-
FooterSocialLinksComponent,
|
|
2975
|
-
{
|
|
2976
|
-
socialLinks,
|
|
2977
|
-
className: isMobile ? "flex justify-center space-x-6" : "flex space-x-4 pt-4"
|
|
2978
|
-
}
|
|
2979
|
-
)
|
|
2980
|
-
] });
|
|
2981
|
-
}
|
|
2982
|
-
__name(FooterProjectInfo, "FooterProjectInfo");
|
|
2983
|
-
function PublicFooter({
|
|
2984
|
-
siteName,
|
|
2985
|
-
description,
|
|
2986
|
-
logo,
|
|
2987
|
-
badge,
|
|
2988
|
-
socialLinks,
|
|
2989
|
-
links = [],
|
|
2990
|
-
menuSections = [],
|
|
2991
|
-
copyright: copyrightProp,
|
|
2992
|
-
credits: creditsProp,
|
|
2993
|
-
variant = "full"
|
|
2994
|
-
}) {
|
|
2995
|
-
const isMobile = useIsMobile();
|
|
2996
|
-
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
2997
|
-
const copyright = copyrightProp || `\xA9 ${currentYear} ${siteName}. All rights reserved.`;
|
|
2998
|
-
const credits = creditsProp || {
|
|
2999
|
-
text: "Built with DjangoCFG",
|
|
3000
|
-
url: "https://djangocfg.com"
|
|
3001
|
-
};
|
|
3002
|
-
if (variant === "simple") {
|
|
3003
|
-
return /* @__PURE__ */ jsx("footer", { className: "bg-background border-t border-border mt-auto", children: /* @__PURE__ */ jsx("div", { className: "w-full px-4 py-4", children: /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: copyright }) }) }) });
|
|
3004
|
-
}
|
|
3005
|
-
if (isMobile) {
|
|
3006
|
-
return /* @__PURE__ */ jsx("footer", { className: "lg:hidden bg-background border-t border-border mt-auto", children: /* @__PURE__ */ jsxs("div", { className: "w-full px-4 py-8", children: [
|
|
3007
|
-
/* @__PURE__ */ jsx(
|
|
3008
|
-
FooterProjectInfo,
|
|
3009
|
-
{
|
|
3010
|
-
siteName,
|
|
3011
|
-
description,
|
|
3012
|
-
logo,
|
|
3013
|
-
socialLinks,
|
|
3014
|
-
variant: "mobile"
|
|
3015
|
-
}
|
|
3016
|
-
),
|
|
3017
|
-
links.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-3 mb-6", children: links.map(
|
|
3018
|
-
(link) => link.external ? /* @__PURE__ */ jsx(
|
|
3019
|
-
"a",
|
|
3020
|
-
{
|
|
3021
|
-
href: link.path,
|
|
3022
|
-
target: "_blank",
|
|
3023
|
-
rel: "noopener noreferrer",
|
|
3024
|
-
className: "text-xs text-muted-foreground hover:text-primary transition-colors",
|
|
3025
|
-
children: link.label
|
|
3026
|
-
},
|
|
3027
|
-
link.path
|
|
3028
|
-
) : /* @__PURE__ */ jsx(
|
|
3029
|
-
Link,
|
|
3030
|
-
{
|
|
3031
|
-
href: link.path,
|
|
3032
|
-
className: "text-xs text-muted-foreground hover:text-primary transition-colors",
|
|
3033
|
-
children: link.label
|
|
3034
|
-
},
|
|
3035
|
-
link.path
|
|
3036
|
-
)
|
|
3037
|
-
) }),
|
|
3038
|
-
/* @__PURE__ */ jsx(
|
|
3039
|
-
FooterBottom,
|
|
3040
|
-
{
|
|
3041
|
-
copyright,
|
|
3042
|
-
credits,
|
|
3043
|
-
variant: "mobile"
|
|
3044
|
-
}
|
|
3045
|
-
)
|
|
3046
|
-
] }) });
|
|
3047
|
-
}
|
|
3048
|
-
return /* @__PURE__ */ jsx("footer", { className: "max-lg:hidden bg-background border-t border-border mt-auto", children: /* @__PURE__ */ jsxs("div", { className: "w-full px-4 sm:px-6 lg:px-8 py-12", children: [
|
|
3049
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-8 lg:gap-12", children: [
|
|
3050
|
-
/* @__PURE__ */ jsx(
|
|
3051
|
-
FooterProjectInfo,
|
|
3052
|
-
{
|
|
3053
|
-
siteName,
|
|
3054
|
-
description,
|
|
3055
|
-
logo,
|
|
3056
|
-
badge,
|
|
3057
|
-
socialLinks,
|
|
3058
|
-
variant: "desktop"
|
|
3059
|
-
}
|
|
3060
|
-
),
|
|
3061
|
-
/* @__PURE__ */ jsx(FooterMenuSections, { menuSections })
|
|
3062
|
-
] }),
|
|
3063
|
-
/* @__PURE__ */ jsx(
|
|
3064
|
-
FooterBottom,
|
|
3065
|
-
{
|
|
3066
|
-
copyright,
|
|
3067
|
-
credits,
|
|
3068
|
-
links,
|
|
3069
|
-
variant: "desktop"
|
|
3070
|
-
}
|
|
3071
|
-
)
|
|
3072
|
-
] }) });
|
|
3073
|
-
}
|
|
3074
|
-
__name(PublicFooter, "PublicFooter");
|
|
3075
|
-
function PublicLayout({
|
|
3076
|
-
children,
|
|
3077
|
-
logo,
|
|
3078
|
-
siteName = "App",
|
|
3079
|
-
navigation = [],
|
|
3080
|
-
userMenu
|
|
3081
|
-
}) {
|
|
3082
|
-
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
3083
|
-
const pathname = usePathname();
|
|
3084
|
-
useEffect(() => {
|
|
3085
|
-
setMobileMenuOpen(false);
|
|
3086
|
-
}, [pathname]);
|
|
3087
|
-
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen flex flex-col", children: [
|
|
3088
|
-
/* @__PURE__ */ jsx(
|
|
3089
|
-
PublicNavigation,
|
|
3090
|
-
{
|
|
3091
|
-
logo,
|
|
3092
|
-
siteName,
|
|
3093
|
-
navigation,
|
|
3094
|
-
userMenu,
|
|
3095
|
-
onMobileMenuClick: () => setMobileMenuOpen(true)
|
|
3096
|
-
}
|
|
3097
|
-
),
|
|
3098
|
-
/* @__PURE__ */ jsx(
|
|
3099
|
-
PublicMobileDrawer,
|
|
3100
|
-
{
|
|
3101
|
-
isOpen: mobileMenuOpen,
|
|
3102
|
-
onClose: () => setMobileMenuOpen(false),
|
|
3103
|
-
logo,
|
|
3104
|
-
siteName,
|
|
3105
|
-
navigation,
|
|
3106
|
-
userMenu
|
|
3107
|
-
}
|
|
3108
|
-
),
|
|
3109
|
-
/* @__PURE__ */ jsx("main", { className: "flex-1", children })
|
|
3110
|
-
] });
|
|
3111
|
-
}
|
|
3112
|
-
__name(PublicLayout, "PublicLayout");
|
|
3113
|
-
function PrivateSidebar({ sidebar }) {
|
|
3114
|
-
const pathname = usePathname();
|
|
3115
|
-
const { state, isMobile } = useSidebar();
|
|
3116
|
-
const homeHref = sidebar.homeHref || "/";
|
|
3117
|
-
const isActive = /* @__PURE__ */ __name((href) => {
|
|
3118
|
-
const matches = pathname === href || pathname.startsWith(href + "/");
|
|
3119
|
-
if (!matches) return false;
|
|
3120
|
-
return !sidebar.items.some(
|
|
3121
|
-
(otherItem) => otherItem.href !== href && otherItem.href.startsWith(href + "/") && (pathname === otherItem.href || pathname.startsWith(otherItem.href + "/"))
|
|
3122
|
-
);
|
|
3123
|
-
}, "isActive");
|
|
3124
|
-
return /* @__PURE__ */ jsxs(Sidebar, { collapsible: "icon", children: [
|
|
3125
|
-
/* @__PURE__ */ jsx(SidebarHeader, { children: /* @__PURE__ */ jsx(
|
|
3126
|
-
"div",
|
|
3127
|
-
{
|
|
3128
|
-
className: "flex items-center gap-3",
|
|
3129
|
-
style: state === "collapsed" ? {
|
|
3130
|
-
paddingLeft: "7px",
|
|
3131
|
-
paddingTop: "0.5rem",
|
|
3132
|
-
paddingBottom: "0.5rem",
|
|
3133
|
-
transition: "padding 200ms ease-in-out"
|
|
3134
|
-
} : {
|
|
3135
|
-
padding: "0.5rem",
|
|
3136
|
-
transition: "padding 200ms ease-in-out"
|
|
3137
|
-
},
|
|
3138
|
-
children: /* @__PURE__ */ jsx(Link, { href: homeHref, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3139
|
-
/* @__PURE__ */ jsx(
|
|
3140
|
-
"div",
|
|
3141
|
-
{
|
|
3142
|
-
className: cn(
|
|
3143
|
-
"bg-primary rounded-sm flex items-center justify-center flex-shrink-0",
|
|
3144
|
-
isMobile ? "h-10 w-10" : "h-8 w-8"
|
|
3145
|
-
),
|
|
3146
|
-
children: /* @__PURE__ */ jsx("span", { className: "text-primary-foreground font-bold text-sm", children: "D" })
|
|
3147
|
-
}
|
|
3148
|
-
),
|
|
3149
|
-
state !== "collapsed" && /* @__PURE__ */ jsx(
|
|
3150
|
-
"span",
|
|
3151
|
-
{
|
|
3152
|
-
className: cn(
|
|
3153
|
-
"font-semibold text-foreground truncate",
|
|
3154
|
-
isMobile && "text-base"
|
|
3155
|
-
),
|
|
3156
|
-
style: { whiteSpace: "nowrap" },
|
|
3157
|
-
children: "Dashboard"
|
|
3158
|
-
}
|
|
3159
|
-
)
|
|
3160
|
-
] }) })
|
|
3161
|
-
}
|
|
3162
|
-
) }),
|
|
3163
|
-
/* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsx(SidebarGroup, { children: /* @__PURE__ */ jsx(SidebarGroupContent, { children: /* @__PURE__ */ jsx(SidebarMenu, { children: sidebar.items.map((item) => {
|
|
3164
|
-
const active = isActive(item.href);
|
|
3165
|
-
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(
|
|
3166
|
-
SidebarMenuButton,
|
|
3167
|
-
{
|
|
3168
|
-
asChild: true,
|
|
3169
|
-
isActive: active,
|
|
3170
|
-
tooltip: item.label,
|
|
3171
|
-
size: isMobile ? "lg" : "default",
|
|
3172
|
-
children: /* @__PURE__ */ jsxs(Link, { href: item.href, children: [
|
|
3173
|
-
item.icon && /* @__PURE__ */ jsx(
|
|
3174
|
-
LucideIcon,
|
|
3175
|
-
{
|
|
3176
|
-
icon: typeof item.icon === "string" ? item.icon : item.icon,
|
|
3177
|
-
className: isMobile ? "h-5 w-5" : "h-4 w-4"
|
|
3178
|
-
}
|
|
3179
|
-
),
|
|
3180
|
-
/* @__PURE__ */ jsx("span", { className: isMobile ? "text-base" : "", children: item.label }),
|
|
3181
|
-
item.badge && /* @__PURE__ */ jsx(SidebarMenuBadge, { children: item.badge })
|
|
3182
|
-
] })
|
|
3183
|
-
}
|
|
3184
|
-
) }, item.href);
|
|
3185
|
-
}) }) }) }) })
|
|
3186
|
-
] });
|
|
3187
|
-
}
|
|
3188
|
-
__name(PrivateSidebar, "PrivateSidebar");
|
|
3189
|
-
function PrivateHeader({ header }) {
|
|
3190
|
-
const { user, logout } = useAuth();
|
|
3191
|
-
return /* @__PURE__ */ jsxs(
|
|
3192
|
-
"header",
|
|
3193
|
-
{
|
|
3194
|
-
className: "sticky top-0 z-10 flex items-center justify-between px-4 shrink-0 bg-background border-b border-border",
|
|
3195
|
-
style: { height: "64px", minHeight: "64px" },
|
|
3196
|
-
children: [
|
|
3197
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
3198
|
-
/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }),
|
|
3199
|
-
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mr-2 h-4" }),
|
|
3200
|
-
header?.title && /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold text-foreground", children: header.title })
|
|
3201
|
-
] }),
|
|
3202
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3203
|
-
/* @__PURE__ */ jsx(ThemeToggle, {}),
|
|
3204
|
-
/* @__PURE__ */ jsx(
|
|
3205
|
-
UserMenu,
|
|
3206
|
-
{
|
|
3207
|
-
variant: "desktop",
|
|
3208
|
-
groups: header?.groups,
|
|
3209
|
-
authPath: header?.authPath
|
|
3210
|
-
}
|
|
3211
|
-
)
|
|
3212
|
-
] })
|
|
3213
|
-
]
|
|
3214
|
-
}
|
|
3215
|
-
);
|
|
3216
|
-
}
|
|
3217
|
-
__name(PrivateHeader, "PrivateHeader");
|
|
3218
|
-
function PrivateContent({
|
|
3219
|
-
children,
|
|
3220
|
-
padding = "default"
|
|
3221
|
-
}) {
|
|
3222
|
-
return /* @__PURE__ */ jsx(
|
|
3223
|
-
"main",
|
|
3224
|
-
{
|
|
3225
|
-
className: cn(
|
|
3226
|
-
"flex-1 overflow-y-auto",
|
|
3227
|
-
padding === "default" && "p-4 sm:p-6 lg:p-8"
|
|
3228
|
-
),
|
|
3229
|
-
children
|
|
3230
|
-
}
|
|
3231
|
-
);
|
|
3232
|
-
}
|
|
3233
|
-
__name(PrivateContent, "PrivateContent");
|
|
3234
|
-
function PrivateLayout({
|
|
3235
|
-
children,
|
|
3236
|
-
sidebar,
|
|
3237
|
-
header,
|
|
3238
|
-
contentPadding = "default"
|
|
3239
|
-
}) {
|
|
3240
|
-
const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
|
|
3241
|
-
const router = useRouter();
|
|
3242
|
-
const [isRedirecting, setIsRedirecting] = useState(false);
|
|
3243
|
-
useEffect(() => {
|
|
3244
|
-
if (!isLoading && !isAuthenticated && !isRedirecting) {
|
|
3245
|
-
const currentUrl = window.location.pathname + window.location.search;
|
|
3246
|
-
saveRedirectUrl(currentUrl);
|
|
3247
|
-
setIsRedirecting(true);
|
|
3248
|
-
router.push(header?.authPath || "/auth");
|
|
3249
|
-
}
|
|
3250
|
-
}, [isAuthenticated, isLoading, isRedirecting, router, saveRedirectUrl, header?.authPath]);
|
|
3251
|
-
if (isLoading || isRedirecting || !isAuthenticated) {
|
|
3252
|
-
return /* @__PURE__ */ jsx(
|
|
3253
|
-
Preloader,
|
|
3254
|
-
{
|
|
3255
|
-
variant: "fullscreen",
|
|
3256
|
-
text: isRedirecting ? "Redirecting to login..." : "Authenticating...",
|
|
3257
|
-
size: "lg",
|
|
3258
|
-
backdrop: true,
|
|
3259
|
-
backdropOpacity: 80
|
|
3260
|
-
}
|
|
3261
|
-
);
|
|
3262
|
-
}
|
|
3263
|
-
return /* @__PURE__ */ jsxs(SidebarProvider, { defaultOpen: true, children: [
|
|
3264
|
-
sidebar && /* @__PURE__ */ jsx(PrivateSidebar, { sidebar }),
|
|
3265
|
-
/* @__PURE__ */ jsxs(SidebarInset, { className: "flex flex-col", children: [
|
|
3266
|
-
(header || isAuthenticated) && /* @__PURE__ */ jsx(PrivateHeader, { header }),
|
|
3267
|
-
/* @__PURE__ */ jsx(PrivateContent, { padding: contentPadding, children })
|
|
3268
|
-
] })
|
|
3269
|
-
] });
|
|
3270
|
-
}
|
|
3271
|
-
__name(PrivateLayout, "PrivateLayout");
|
|
3272
|
-
var AuthFormContext = createContext(void 0);
|
|
3273
|
-
var AuthFormProvider = /* @__PURE__ */ __name(({
|
|
3274
|
-
children,
|
|
3275
|
-
sourceUrl: sourceUrlProp,
|
|
3276
|
-
supportUrl,
|
|
3277
|
-
termsUrl,
|
|
3278
|
-
privacyUrl,
|
|
3279
|
-
enablePhoneAuth = false,
|
|
3280
|
-
enableGithubAuth = false,
|
|
3281
|
-
enable2FASetup = true,
|
|
3282
|
-
logoUrl,
|
|
3283
|
-
redirectUrl,
|
|
3284
|
-
onIdentifierSuccess,
|
|
3285
|
-
onOTPSuccess,
|
|
3286
|
-
onError
|
|
3287
|
-
}) => {
|
|
3288
|
-
const sourceUrl = sourceUrlProp || (typeof window !== "undefined" ? window.location.origin : "");
|
|
3289
|
-
const requireTermsAcceptance = Boolean(termsUrl || privacyUrl);
|
|
3290
|
-
const authForm = useAuthForm({
|
|
3291
|
-
onIdentifierSuccess,
|
|
3292
|
-
onOTPSuccess,
|
|
3293
|
-
onError,
|
|
3294
|
-
sourceUrl,
|
|
3295
|
-
redirectUrl,
|
|
3296
|
-
requireTermsAcceptance,
|
|
3297
|
-
enable2FASetup
|
|
3298
|
-
});
|
|
3299
|
-
const value = {
|
|
3300
|
-
...authForm,
|
|
3301
|
-
// UI-specific configuration
|
|
3302
|
-
sourceUrl,
|
|
3303
|
-
supportUrl,
|
|
3304
|
-
termsUrl,
|
|
3305
|
-
privacyUrl,
|
|
3306
|
-
enablePhoneAuth,
|
|
3307
|
-
enableGithubAuth,
|
|
3308
|
-
enable2FASetup,
|
|
3309
|
-
logoUrl,
|
|
3310
|
-
redirectUrl
|
|
3311
|
-
};
|
|
3312
|
-
return /* @__PURE__ */ jsx(AuthFormContext.Provider, { value, children });
|
|
3313
|
-
}, "AuthFormProvider");
|
|
3314
|
-
var useAuthFormContext = /* @__PURE__ */ __name(() => {
|
|
3315
|
-
const context = useContext(AuthFormContext);
|
|
3316
|
-
if (context === void 0) {
|
|
3317
|
-
throw new Error("useAuthFormContext must be used within an AuthFormProvider");
|
|
3318
|
-
}
|
|
3319
|
-
return context;
|
|
3320
|
-
}, "useAuthFormContext");
|
|
3321
|
-
var AuthHelp = /* @__PURE__ */ __name(({
|
|
3322
|
-
className = "",
|
|
3323
|
-
variant = "default"
|
|
3324
|
-
}) => {
|
|
3325
|
-
const { supportUrl, channel } = useAuthFormContext();
|
|
3326
|
-
const getChannelIcon = /* @__PURE__ */ __name(() => {
|
|
3327
|
-
return channel === "phone" ? /* @__PURE__ */ jsx(MessageCircle, { className: "w-4 h-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(Mail, { className: "w-4 h-4 text-muted-foreground" });
|
|
3328
|
-
}, "getChannelIcon");
|
|
3329
|
-
const getHelpText = /* @__PURE__ */ __name(() => {
|
|
3330
|
-
return channel === "phone" ? "Check WhatsApp/SMS" : "Check spam folder";
|
|
3331
|
-
}, "getHelpText");
|
|
3332
|
-
const getDetailedHelp = /* @__PURE__ */ __name(() => {
|
|
3333
|
-
if (channel === "phone") {
|
|
3334
|
-
return {
|
|
3335
|
-
title: "Didn't receive the code?",
|
|
3336
|
-
tips: [
|
|
3337
|
-
"\u2022 Check your WhatsApp messages",
|
|
3338
|
-
"\u2022 Look for SMS messages",
|
|
3339
|
-
"\u2022 Ensure you have signal/internet",
|
|
3340
|
-
"\u2022 Wait a few minutes for delivery"
|
|
3341
|
-
]
|
|
3342
|
-
};
|
|
3343
|
-
} else {
|
|
3344
|
-
return {
|
|
3345
|
-
title: "Didn't receive the email?",
|
|
3346
|
-
tips: [
|
|
3347
|
-
"\u2022 Check your spam or junk folder",
|
|
3348
|
-
"\u2022 Make sure you entered the correct email address",
|
|
3349
|
-
"\u2022 Wait a few minutes for the email to arrive"
|
|
3350
|
-
]
|
|
3351
|
-
};
|
|
3352
|
-
}
|
|
3353
|
-
}, "getDetailedHelp");
|
|
3354
|
-
if (variant === "compact") {
|
|
3355
|
-
return /* @__PURE__ */ jsxs(
|
|
3356
|
-
"div",
|
|
3357
|
-
{
|
|
3358
|
-
className: `flex items-center justify-between p-3 bg-muted/30 rounded-sm border border-border ${className}`,
|
|
3359
|
-
children: [
|
|
3360
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3361
|
-
getChannelIcon(),
|
|
3362
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: getHelpText() })
|
|
3363
|
-
] }),
|
|
3364
|
-
supportUrl && /* @__PURE__ */ jsx(
|
|
3365
|
-
Button,
|
|
3366
|
-
{
|
|
3367
|
-
asChild: true,
|
|
3368
|
-
variant: "ghost",
|
|
3369
|
-
size: "sm",
|
|
3370
|
-
className: "text-xs",
|
|
3371
|
-
children: /* @__PURE__ */ jsxs("a", { href: supportUrl, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-1", children: [
|
|
3372
|
-
/* @__PURE__ */ jsx(HelpCircle, { className: "w-3 h-3" }),
|
|
3373
|
-
"Need help?"
|
|
3374
|
-
] })
|
|
3375
|
-
}
|
|
3376
|
-
)
|
|
3377
|
-
]
|
|
3378
|
-
}
|
|
3379
|
-
);
|
|
3380
|
-
}
|
|
3381
|
-
const helpData = getDetailedHelp();
|
|
3382
|
-
return /* @__PURE__ */ jsxs(
|
|
3383
|
-
"div",
|
|
3384
|
-
{
|
|
3385
|
-
className: `flex flex-col gap-3 p-3 bg-muted/30 rounded-sm border border-border ${className}`,
|
|
3386
|
-
children: [
|
|
3387
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
3388
|
-
getChannelIcon(),
|
|
3389
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3390
|
-
/* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-foreground", children: helpData.title }),
|
|
3391
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5 text-xs text-muted-foreground", children: helpData.tips.map((tip, index) => /* @__PURE__ */ jsx("p", { children: tip }, index)) })
|
|
3392
|
-
] })
|
|
3393
|
-
] }),
|
|
3394
|
-
supportUrl && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 border-t border-border", children: [
|
|
3395
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Still having trouble?" }),
|
|
3396
|
-
/* @__PURE__ */ jsx(
|
|
3397
|
-
Button,
|
|
3398
|
-
{
|
|
3399
|
-
asChild: true,
|
|
3400
|
-
variant: "ghost",
|
|
3401
|
-
size: "sm",
|
|
3402
|
-
className: "text-xs h-7 px-2",
|
|
3403
|
-
children: /* @__PURE__ */ jsxs("a", { href: supportUrl, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-1", children: [
|
|
3404
|
-
/* @__PURE__ */ jsx(HelpCircle, { className: "w-3 h-3" }),
|
|
3405
|
-
"Get Help"
|
|
3406
|
-
] })
|
|
3407
|
-
}
|
|
3408
|
-
)
|
|
3409
|
-
] })
|
|
3410
|
-
]
|
|
3411
|
-
}
|
|
3412
|
-
);
|
|
3413
|
-
}, "AuthHelp");
|
|
3414
|
-
var OAuthProviders = /* @__PURE__ */ __name(() => {
|
|
3415
|
-
const { enableGithubAuth, sourceUrl, setError } = useAuthFormContext();
|
|
3416
|
-
const { isLoading, startGithubAuth } = useGithubAuth({
|
|
3417
|
-
sourceUrl,
|
|
3418
|
-
onError: setError
|
|
3419
|
-
});
|
|
3420
|
-
if (!enableGithubAuth) {
|
|
3421
|
-
return null;
|
|
3422
|
-
}
|
|
3423
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3424
|
-
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
3425
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t border-border" }) }),
|
|
3426
|
-
/* @__PURE__ */ jsx("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsx("span", { className: "bg-card px-2 text-muted-foreground", children: "Or continue with" }) })
|
|
3427
|
-
] }),
|
|
3428
|
-
/* @__PURE__ */ jsxs(
|
|
3429
|
-
Button,
|
|
3430
|
-
{
|
|
3431
|
-
type: "button",
|
|
3432
|
-
variant: "outline",
|
|
3433
|
-
size: "lg",
|
|
3434
|
-
className: "w-full",
|
|
3435
|
-
onClick: startGithubAuth,
|
|
3436
|
-
loading: isLoading,
|
|
3437
|
-
children: [
|
|
3438
|
-
/* @__PURE__ */ jsx(Github, { className: "w-5 h-5" }),
|
|
3439
|
-
"Continue with GitHub"
|
|
3440
|
-
]
|
|
3441
|
-
}
|
|
3442
|
-
)
|
|
3443
|
-
] });
|
|
3444
|
-
}, "OAuthProviders");
|
|
3445
|
-
var OAuthCallback = /* @__PURE__ */ __name(({
|
|
3446
|
-
onSuccess,
|
|
3447
|
-
onError,
|
|
3448
|
-
redirectUrl
|
|
3449
|
-
}) => {
|
|
3450
|
-
const searchParams = useSearchParams();
|
|
3451
|
-
const { setStep, setTwoFactorSessionId, setShouldPrompt2FA } = useAuthFormContext();
|
|
3452
|
-
const [status, setStatus] = useState(null);
|
|
3453
|
-
const [errorMessage, setErrorMessage] = useState(null);
|
|
3454
|
-
const provider = searchParams.get("provider");
|
|
3455
|
-
const code = searchParams.get("code");
|
|
3456
|
-
const state = searchParams.get("state");
|
|
3457
|
-
const error = searchParams.get("error");
|
|
3458
|
-
const errorDescription = searchParams.get("error_description");
|
|
3459
|
-
const {
|
|
3460
|
-
handleGithubCallback,
|
|
3461
|
-
isLoading,
|
|
3462
|
-
error: githubError
|
|
3463
|
-
} = useGithubAuth({
|
|
3464
|
-
onSuccess: /* @__PURE__ */ __name((user, isNewUser) => {
|
|
3465
|
-
setStep("success");
|
|
3466
|
-
onSuccess?.(user, isNewUser, "github");
|
|
3467
|
-
}, "onSuccess"),
|
|
3468
|
-
onError: /* @__PURE__ */ __name((err) => {
|
|
3469
|
-
setStatus("error");
|
|
3470
|
-
setErrorMessage(err);
|
|
3471
|
-
onError?.(err);
|
|
3472
|
-
}, "onError"),
|
|
3473
|
-
onRequires2FA: /* @__PURE__ */ __name((sessionId, shouldPrompt) => {
|
|
3474
|
-
setTwoFactorSessionId(sessionId);
|
|
3475
|
-
setShouldPrompt2FA(shouldPrompt);
|
|
3476
|
-
setStep("2fa");
|
|
3477
|
-
}, "onRequires2FA"),
|
|
3478
|
-
redirectUrl,
|
|
3479
|
-
skipRedirect: true
|
|
3480
|
-
// We handle navigation via success screen
|
|
3481
|
-
});
|
|
3482
|
-
useEffect(() => {
|
|
3483
|
-
if (!provider || !code || !state) {
|
|
3484
|
-
return;
|
|
3485
|
-
}
|
|
3486
|
-
if (error) {
|
|
3487
|
-
setStatus("error");
|
|
3488
|
-
setErrorMessage(errorDescription || error);
|
|
3489
|
-
onError?.(errorDescription || error);
|
|
3490
|
-
return;
|
|
3491
|
-
}
|
|
3492
|
-
const processCallback = /* @__PURE__ */ __name(async () => {
|
|
3493
|
-
setStatus("processing");
|
|
3494
|
-
if (provider === "github") {
|
|
3495
|
-
await handleGithubCallback(code, state);
|
|
3496
|
-
} else {
|
|
3497
|
-
setStatus("error");
|
|
3498
|
-
setErrorMessage(`Unsupported OAuth provider: ${provider}`);
|
|
3499
|
-
onError?.(`Unsupported OAuth provider: ${provider}`);
|
|
3500
|
-
}
|
|
3501
|
-
}, "processCallback");
|
|
3502
|
-
processCallback();
|
|
3503
|
-
}, [provider, code, state, error]);
|
|
3504
|
-
if (!provider || !code && !error) {
|
|
3505
|
-
return null;
|
|
3506
|
-
}
|
|
3507
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center", children: /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md mx-4 shadow-lg", children: [
|
|
3508
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "text-center", children: [
|
|
3509
|
-
status === "processing" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3510
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(Loader2, { className: "w-6 h-6 text-primary animate-spin" }) }),
|
|
3511
|
-
/* @__PURE__ */ jsx(CardTitle, { children: "Signing you in..." }),
|
|
3512
|
-
/* @__PURE__ */ jsxs(CardDescription, { children: [
|
|
3513
|
-
"Please wait while we complete your ",
|
|
3514
|
-
provider,
|
|
3515
|
-
" authentication."
|
|
3516
|
-
] })
|
|
3517
|
-
] }),
|
|
3518
|
-
status === "error" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3519
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-destructive/10 rounded-full flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-6 h-6 text-destructive" }) }),
|
|
3520
|
-
/* @__PURE__ */ jsx(CardTitle, { children: "Authentication Failed" }),
|
|
3521
|
-
/* @__PURE__ */ jsx(CardDescription, { className: "text-destructive", children: errorMessage || githubError || "An error occurred during authentication." })
|
|
3522
|
-
] })
|
|
3523
|
-
] }),
|
|
3524
|
-
status === "error" && /* @__PURE__ */ jsx(CardContent$1, { className: "text-center", children: /* @__PURE__ */ jsx(
|
|
3525
|
-
"a",
|
|
3526
|
-
{
|
|
3527
|
-
href: "/auth",
|
|
3528
|
-
className: "text-primary hover:underline text-sm",
|
|
3529
|
-
children: "Try again"
|
|
3530
|
-
}
|
|
3531
|
-
) })
|
|
3532
|
-
] }) });
|
|
3533
|
-
}, "OAuthCallback");
|
|
3534
|
-
var IdentifierForm = /* @__PURE__ */ __name(() => {
|
|
3535
|
-
const {
|
|
3536
|
-
identifier,
|
|
3537
|
-
channel,
|
|
3538
|
-
isLoading,
|
|
3539
|
-
acceptedTerms,
|
|
3540
|
-
termsUrl,
|
|
3541
|
-
privacyUrl,
|
|
3542
|
-
enablePhoneAuth,
|
|
3543
|
-
setIdentifier,
|
|
3544
|
-
setChannel,
|
|
3545
|
-
setAcceptedTerms,
|
|
3546
|
-
handleIdentifierSubmit,
|
|
3547
|
-
detectChannelFromIdentifier,
|
|
3548
|
-
validateIdentifier,
|
|
3549
|
-
error
|
|
3550
|
-
} = useAuthFormContext();
|
|
3551
|
-
const [localChannel, setLocalChannel] = useState(channel);
|
|
3552
|
-
useEffect(() => {
|
|
3553
|
-
setLocalChannel(channel);
|
|
3554
|
-
}, [channel]);
|
|
3555
|
-
useEffect(() => {
|
|
3556
|
-
if (!enablePhoneAuth && localChannel === "phone") {
|
|
3557
|
-
setLocalChannel("email");
|
|
3558
|
-
setChannel("email");
|
|
3559
|
-
if (identifier && detectChannelFromIdentifier(identifier) === "phone") {
|
|
3560
|
-
setIdentifier("");
|
|
3561
|
-
}
|
|
3562
|
-
}
|
|
3563
|
-
}, [
|
|
3564
|
-
enablePhoneAuth,
|
|
3565
|
-
localChannel,
|
|
3566
|
-
identifier,
|
|
3567
|
-
setChannel,
|
|
3568
|
-
setIdentifier,
|
|
3569
|
-
detectChannelFromIdentifier
|
|
3570
|
-
]);
|
|
3571
|
-
const handleIdentifierChange = /* @__PURE__ */ __name((value) => {
|
|
3572
|
-
setIdentifier(value);
|
|
3573
|
-
const detectedChannel = detectChannelFromIdentifier(value);
|
|
3574
|
-
if (detectedChannel && detectedChannel !== localChannel) {
|
|
3575
|
-
if (detectedChannel === "phone" && !enablePhoneAuth) {
|
|
3576
|
-
return;
|
|
3577
|
-
}
|
|
3578
|
-
setLocalChannel(detectedChannel);
|
|
3579
|
-
setChannel(detectedChannel);
|
|
3580
|
-
}
|
|
3581
|
-
}, "handleIdentifierChange");
|
|
3582
|
-
const handleChannelChange = /* @__PURE__ */ __name((newChannel) => {
|
|
3583
|
-
if (newChannel === "phone" && !enablePhoneAuth) {
|
|
3584
|
-
return;
|
|
3585
|
-
}
|
|
3586
|
-
setLocalChannel(newChannel);
|
|
3587
|
-
setChannel(newChannel);
|
|
3588
|
-
if (identifier && !validateIdentifier(identifier, newChannel)) {
|
|
3589
|
-
setIdentifier("");
|
|
3590
|
-
}
|
|
3591
|
-
}, "handleChannelChange");
|
|
3592
|
-
const getChannelDescription = /* @__PURE__ */ __name(() => {
|
|
3593
|
-
return localChannel === "phone" ? "Enter your phone number to receive a verification code via SMS" : "Enter your email address to receive a verification code";
|
|
3594
|
-
}, "getChannelDescription");
|
|
3595
|
-
const hasAnyLinks = Boolean(termsUrl || privacyUrl);
|
|
3596
|
-
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md mx-auto shadow-lg border border-border bg-card/50 backdrop-blur-sm", children: [
|
|
3597
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "text-center pb-6", children: [
|
|
3598
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(User, { className: "w-6 h-6 text-primary" }) }),
|
|
3599
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children: "Sign In" }),
|
|
3600
|
-
/* @__PURE__ */ jsx(CardDescription, { className: "text-muted-foreground", children: getChannelDescription() })
|
|
3601
|
-
] }),
|
|
3602
|
-
/* @__PURE__ */ jsxs(CardContent$1, { className: "space-y-6", children: [
|
|
3603
|
-
enablePhoneAuth ? /* @__PURE__ */ jsxs(
|
|
3604
|
-
Tabs,
|
|
3605
|
-
{
|
|
3606
|
-
value: localChannel,
|
|
3607
|
-
onValueChange: (value) => handleChannelChange(value),
|
|
3608
|
-
children: [
|
|
3609
|
-
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [
|
|
3610
|
-
/* @__PURE__ */ jsxs(TabsTrigger, { value: "email", className: "flex items-center gap-2", children: [
|
|
3611
|
-
/* @__PURE__ */ jsx(Mail, { className: "w-4 h-4" }),
|
|
3612
|
-
"Email"
|
|
3613
|
-
] }),
|
|
3614
|
-
/* @__PURE__ */ jsxs(TabsTrigger, { value: "phone", className: "flex items-center gap-2", children: [
|
|
3615
|
-
/* @__PURE__ */ jsx(Phone, { className: "w-4 h-4" }),
|
|
3616
|
-
"Phone"
|
|
3617
|
-
] })
|
|
3618
|
-
] }),
|
|
3619
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleIdentifierSubmit, className: "space-y-6 mt-6", children: [
|
|
3620
|
-
/* @__PURE__ */ jsxs(TabsContent, { value: "email", className: "space-y-3 mt-0", children: [
|
|
3621
|
-
/* @__PURE__ */ jsxs(
|
|
3622
|
-
Label,
|
|
3623
|
-
{
|
|
3624
|
-
htmlFor: "identifier",
|
|
3625
|
-
className: "text-sm font-medium text-foreground flex items-center gap-2",
|
|
3626
|
-
children: [
|
|
3627
|
-
/* @__PURE__ */ jsx(Mail, { className: "w-4 h-4" }),
|
|
3628
|
-
"Email Address"
|
|
3629
|
-
]
|
|
3630
|
-
}
|
|
3631
|
-
),
|
|
3632
|
-
/* @__PURE__ */ jsx(
|
|
3633
|
-
Input,
|
|
3634
|
-
{
|
|
3635
|
-
id: "identifier",
|
|
3636
|
-
type: "email",
|
|
3637
|
-
placeholder: "Enter your email address",
|
|
3638
|
-
value: identifier,
|
|
3639
|
-
onChange: (e) => handleIdentifierChange(e.target.value),
|
|
3640
|
-
disabled: isLoading,
|
|
3641
|
-
required: true,
|
|
3642
|
-
className: "h-11 text-base"
|
|
3643
|
-
}
|
|
3644
|
-
)
|
|
3645
|
-
] }),
|
|
3646
|
-
/* @__PURE__ */ jsxs(TabsContent, { value: "phone", className: "space-y-3 mt-0", children: [
|
|
3647
|
-
/* @__PURE__ */ jsxs(
|
|
3648
|
-
Label,
|
|
3649
|
-
{
|
|
3650
|
-
htmlFor: "phone-identifier",
|
|
3651
|
-
className: "text-sm font-medium text-foreground flex items-center gap-2",
|
|
3652
|
-
children: [
|
|
3653
|
-
/* @__PURE__ */ jsx(Phone, { className: "w-4 h-4" }),
|
|
3654
|
-
"Phone Number"
|
|
3655
|
-
]
|
|
3656
|
-
}
|
|
3657
|
-
),
|
|
3658
|
-
/* @__PURE__ */ jsx(
|
|
3659
|
-
PhoneInput,
|
|
3660
|
-
{
|
|
3661
|
-
value: identifier,
|
|
3662
|
-
onChange: (value) => handleIdentifierChange(value || ""),
|
|
3663
|
-
disabled: isLoading,
|
|
3664
|
-
placeholder: "Enter your phone number",
|
|
3665
|
-
defaultCountry: "US",
|
|
3666
|
-
className: "h-11 text-base"
|
|
3667
|
-
}
|
|
3668
|
-
)
|
|
3669
|
-
] }),
|
|
3670
|
-
hasAnyLinks && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
3671
|
-
/* @__PURE__ */ jsx(
|
|
3672
|
-
Checkbox,
|
|
3673
|
-
{
|
|
3674
|
-
id: "terms",
|
|
3675
|
-
checked: acceptedTerms,
|
|
3676
|
-
onCheckedChange: setAcceptedTerms,
|
|
3677
|
-
disabled: isLoading,
|
|
3678
|
-
className: "mt-1"
|
|
3679
|
-
}
|
|
3680
|
-
),
|
|
3681
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground leading-5", children: /* @__PURE__ */ jsxs(Label, { htmlFor: "terms", className: "cursor-pointer", children: [
|
|
3682
|
-
"I agree to the",
|
|
3683
|
-
" ",
|
|
3684
|
-
termsUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3685
|
-
/* @__PURE__ */ jsx(
|
|
3686
|
-
"a",
|
|
3687
|
-
{
|
|
3688
|
-
href: termsUrl,
|
|
3689
|
-
target: "_blank",
|
|
3690
|
-
rel: "noopener noreferrer",
|
|
3691
|
-
className: "text-primary hover:underline font-medium",
|
|
3692
|
-
children: "Terms of Service"
|
|
3693
|
-
}
|
|
3694
|
-
),
|
|
3695
|
-
privacyUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3696
|
-
" ",
|
|
3697
|
-
"and",
|
|
3698
|
-
" "
|
|
3699
|
-
] })
|
|
3700
|
-
] }),
|
|
3701
|
-
privacyUrl && /* @__PURE__ */ jsx(
|
|
3702
|
-
"a",
|
|
3703
|
-
{
|
|
3704
|
-
href: privacyUrl,
|
|
3705
|
-
target: "_blank",
|
|
3706
|
-
rel: "noopener noreferrer",
|
|
3707
|
-
className: "text-primary hover:underline font-medium",
|
|
3708
|
-
children: "Privacy Policy"
|
|
3709
|
-
}
|
|
3710
|
-
)
|
|
3711
|
-
] }) })
|
|
3712
|
-
] }),
|
|
3713
|
-
error && /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive bg-destructive/10 p-3 rounded-md border border-destructive/20", children: error }),
|
|
3714
|
-
/* @__PURE__ */ jsxs(
|
|
3715
|
-
Button,
|
|
3716
|
-
{
|
|
3717
|
-
type: "submit",
|
|
3718
|
-
size: "lg",
|
|
3719
|
-
className: "w-full",
|
|
3720
|
-
disabled: !identifier || hasAnyLinks && !acceptedTerms,
|
|
3721
|
-
loading: isLoading,
|
|
3722
|
-
children: [
|
|
3723
|
-
/* @__PURE__ */ jsx(Send, { className: "w-4 h-4" }),
|
|
3724
|
-
"Send verification code"
|
|
3725
|
-
]
|
|
3726
|
-
}
|
|
3727
|
-
)
|
|
3728
|
-
] })
|
|
3729
|
-
]
|
|
3730
|
-
}
|
|
3731
|
-
) : /* @__PURE__ */ jsxs("form", { onSubmit: handleIdentifierSubmit, className: "space-y-6 mt-6", children: [
|
|
3732
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
3733
|
-
/* @__PURE__ */ jsxs(
|
|
3734
|
-
Label,
|
|
3735
|
-
{
|
|
3736
|
-
htmlFor: "email-only",
|
|
3737
|
-
className: "text-sm font-medium text-foreground flex items-center gap-2",
|
|
3738
|
-
children: [
|
|
3739
|
-
/* @__PURE__ */ jsx(Mail, { className: "w-4 h-4" }),
|
|
3740
|
-
"Email Address"
|
|
3741
|
-
]
|
|
3742
|
-
}
|
|
3743
|
-
),
|
|
3744
|
-
/* @__PURE__ */ jsx(
|
|
3745
|
-
Input,
|
|
3746
|
-
{
|
|
3747
|
-
id: "email-only",
|
|
3748
|
-
type: "email",
|
|
3749
|
-
placeholder: "Enter your email address",
|
|
3750
|
-
value: identifier,
|
|
3751
|
-
onChange: (e) => handleIdentifierChange(e.target.value),
|
|
3752
|
-
disabled: isLoading,
|
|
3753
|
-
required: true,
|
|
3754
|
-
className: "h-11 text-base"
|
|
3755
|
-
}
|
|
3756
|
-
)
|
|
3757
|
-
] }),
|
|
3758
|
-
hasAnyLinks && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
3759
|
-
/* @__PURE__ */ jsx(
|
|
3760
|
-
Checkbox,
|
|
3761
|
-
{
|
|
3762
|
-
id: "terms-email",
|
|
3763
|
-
checked: acceptedTerms,
|
|
3764
|
-
onCheckedChange: setAcceptedTerms,
|
|
3765
|
-
disabled: isLoading,
|
|
3766
|
-
className: "mt-1"
|
|
3767
|
-
}
|
|
3768
|
-
),
|
|
3769
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground leading-5", children: /* @__PURE__ */ jsxs(Label, { htmlFor: "terms-email", className: "cursor-pointer", children: [
|
|
3770
|
-
"I agree to the",
|
|
3771
|
-
" ",
|
|
3772
|
-
termsUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3773
|
-
/* @__PURE__ */ jsx(
|
|
3774
|
-
"a",
|
|
3775
|
-
{
|
|
3776
|
-
href: termsUrl,
|
|
3777
|
-
target: "_blank",
|
|
3778
|
-
rel: "noopener noreferrer",
|
|
3779
|
-
className: "text-primary hover:underline font-medium",
|
|
3780
|
-
children: "Terms of Service"
|
|
3781
|
-
}
|
|
3782
|
-
),
|
|
3783
|
-
privacyUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3784
|
-
" ",
|
|
3785
|
-
"and",
|
|
3786
|
-
" "
|
|
3787
|
-
] })
|
|
3788
|
-
] }),
|
|
3789
|
-
privacyUrl && /* @__PURE__ */ jsx(
|
|
3790
|
-
"a",
|
|
3791
|
-
{
|
|
3792
|
-
href: privacyUrl,
|
|
3793
|
-
target: "_blank",
|
|
3794
|
-
rel: "noopener noreferrer",
|
|
3795
|
-
className: "text-primary hover:underline font-medium",
|
|
3796
|
-
children: "Privacy Policy"
|
|
3797
|
-
}
|
|
3798
|
-
)
|
|
3799
|
-
] }) })
|
|
3800
|
-
] }),
|
|
3801
|
-
error && /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive bg-destructive/10 p-3 rounded-md border border-destructive/20", children: error }),
|
|
3802
|
-
/* @__PURE__ */ jsxs(
|
|
3803
|
-
Button,
|
|
3804
|
-
{
|
|
3805
|
-
type: "submit",
|
|
3806
|
-
size: "lg",
|
|
3807
|
-
className: "w-full",
|
|
3808
|
-
disabled: !identifier || hasAnyLinks && !acceptedTerms,
|
|
3809
|
-
loading: isLoading,
|
|
3810
|
-
children: [
|
|
3811
|
-
/* @__PURE__ */ jsx(Send, { className: "w-4 h-4" }),
|
|
3812
|
-
"Send verification code"
|
|
3813
|
-
]
|
|
3814
|
-
}
|
|
3815
|
-
)
|
|
3816
|
-
] }),
|
|
3817
|
-
/* @__PURE__ */ jsx(OAuthProviders, {}),
|
|
3818
|
-
/* @__PURE__ */ jsx(AuthHelp, {})
|
|
3819
|
-
] })
|
|
3820
|
-
] });
|
|
3821
|
-
}, "IdentifierForm");
|
|
3822
|
-
process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
3823
|
-
var logger = createConsola({
|
|
3824
|
-
level: 4
|
|
3825
|
-
// dev: debug, production: errors only
|
|
3826
|
-
}).withTag("layouts");
|
|
3827
|
-
logger.withTag("auth");
|
|
3828
|
-
logger.withTag("chat");
|
|
3829
|
-
logger.withTag("support");
|
|
3830
|
-
logger.withTag("payments");
|
|
3831
|
-
var profileLogger = logger.withTag("profile");
|
|
3832
|
-
logger.withTag("dashboard");
|
|
3833
|
-
var OTPForm = /* @__PURE__ */ __name(() => {
|
|
3834
|
-
const {
|
|
3835
|
-
identifier,
|
|
3836
|
-
channel,
|
|
3837
|
-
otp,
|
|
3838
|
-
isLoading,
|
|
3839
|
-
error,
|
|
3840
|
-
supportUrl,
|
|
3841
|
-
setOtp,
|
|
3842
|
-
handleOTPSubmit,
|
|
3843
|
-
handleResendOTP,
|
|
3844
|
-
handleBackToIdentifier,
|
|
3845
|
-
isAutoSubmittingFromUrl
|
|
3846
|
-
} = useAuthFormContext();
|
|
3847
|
-
const isAutoSubmittingRef = useRef(false);
|
|
3848
|
-
const handleOTPComplete = useCallback((completedValue) => {
|
|
3849
|
-
if (isAutoSubmittingRef.current || isLoading || isAutoSubmittingFromUrl.current) return;
|
|
3850
|
-
if (completedValue.length === 6) {
|
|
3851
|
-
isAutoSubmittingRef.current = true;
|
|
3852
|
-
const fakeEvent = {
|
|
3853
|
-
preventDefault: /* @__PURE__ */ __name(() => {
|
|
3854
|
-
}, "preventDefault")
|
|
3855
|
-
};
|
|
3856
|
-
setTimeout(async () => {
|
|
3857
|
-
try {
|
|
3858
|
-
await handleOTPSubmit(fakeEvent);
|
|
3859
|
-
} finally {
|
|
3860
|
-
isAutoSubmittingRef.current = false;
|
|
3861
|
-
}
|
|
3862
|
-
}, 100);
|
|
3863
|
-
}
|
|
3864
|
-
}, [handleOTPSubmit, isLoading, isAutoSubmittingFromUrl]);
|
|
3865
|
-
const getChannelIcon = /* @__PURE__ */ __name(() => {
|
|
3866
|
-
return channel === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(MessageCircle, { className: "w-5 h-5 text-primary" }) }) : /* @__PURE__ */ jsx(Mail, { className: "w-5 h-5 text-primary" });
|
|
3867
|
-
}, "getChannelIcon");
|
|
3868
|
-
const getChannelTitle = /* @__PURE__ */ __name(() => {
|
|
3869
|
-
return channel === "phone" ? "Verify Your Phone" : "Verify Your Email";
|
|
3870
|
-
}, "getChannelTitle");
|
|
3871
|
-
const getChannelDescription = /* @__PURE__ */ __name(() => {
|
|
3872
|
-
const channelName = channel === "phone" ? "phone number" : "email address";
|
|
3873
|
-
const method = channel === "phone" ? "WhatsApp/SMS" : "email";
|
|
3874
|
-
return `We've sent a 6-digit verification code to your ${channelName} via ${method}`;
|
|
3875
|
-
}, "getChannelDescription");
|
|
3876
|
-
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md mx-auto shadow-lg border border-border bg-card/50 backdrop-blur-sm", children: [
|
|
3877
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "text-center pb-6", children: [
|
|
3878
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4", children: getChannelIcon() }),
|
|
3879
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children: getChannelTitle() }),
|
|
3880
|
-
/* @__PURE__ */ jsxs(CardDescription, { className: "text-muted-foreground", children: [
|
|
3881
|
-
getChannelDescription(),
|
|
3882
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
3883
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: identifier })
|
|
3884
|
-
] })
|
|
3885
|
-
] }),
|
|
3886
|
-
/* @__PURE__ */ jsxs(CardContent$1, { className: "space-y-6", children: [
|
|
3887
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleOTPSubmit, className: "space-y-6", children: [
|
|
3888
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
3889
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground text-center block", children: "Enter verification code" }),
|
|
3890
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
3891
|
-
OTPInput,
|
|
3892
|
-
{
|
|
3893
|
-
length: 6,
|
|
3894
|
-
validationMode: "numeric",
|
|
3895
|
-
pasteBehavior: "clean",
|
|
3896
|
-
value: otp,
|
|
3897
|
-
onChange: setOtp,
|
|
3898
|
-
onComplete: handleOTPComplete,
|
|
3899
|
-
disabled: isLoading,
|
|
3900
|
-
autoFocus: true,
|
|
3901
|
-
autoSubmit: false,
|
|
3902
|
-
size: "lg"
|
|
3903
|
-
}
|
|
3904
|
-
) }),
|
|
3905
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-950/30 p-2 rounded-md border border-amber-200 dark:border-amber-800 text-center", children: "\u{1F527} Dev Mode: Any OTP code works" })
|
|
3906
|
-
] }),
|
|
3907
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3908
|
-
/* @__PURE__ */ jsxs(
|
|
3909
|
-
Button,
|
|
3910
|
-
{
|
|
3911
|
-
type: "submit",
|
|
3912
|
-
size: "lg",
|
|
3913
|
-
className: "w-full",
|
|
3914
|
-
disabled: otp.length < 6,
|
|
3915
|
-
loading: isLoading,
|
|
3916
|
-
children: [
|
|
3917
|
-
/* @__PURE__ */ jsx(ShieldCheck, { className: "w-5 h-5" }),
|
|
3918
|
-
"Verify Code"
|
|
3919
|
-
]
|
|
3920
|
-
}
|
|
3921
|
-
),
|
|
3922
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
3923
|
-
/* @__PURE__ */ jsxs(
|
|
3924
|
-
Button,
|
|
3925
|
-
{
|
|
3926
|
-
type: "button",
|
|
3927
|
-
variant: "outline",
|
|
3928
|
-
onClick: handleBackToIdentifier,
|
|
3929
|
-
disabled: isLoading,
|
|
3930
|
-
className: "flex-1",
|
|
3931
|
-
children: [
|
|
3932
|
-
/* @__PURE__ */ jsx(ArrowLeft, { className: "w-4 h-4" }),
|
|
3933
|
-
"Back"
|
|
3934
|
-
]
|
|
3935
|
-
}
|
|
3936
|
-
),
|
|
3937
|
-
/* @__PURE__ */ jsxs(
|
|
3938
|
-
Button,
|
|
3939
|
-
{
|
|
3940
|
-
type: "button",
|
|
3941
|
-
variant: "outline",
|
|
3942
|
-
onClick: handleResendOTP,
|
|
3943
|
-
disabled: isLoading,
|
|
3944
|
-
className: "flex-1",
|
|
3945
|
-
children: [
|
|
3946
|
-
/* @__PURE__ */ jsx(RotateCw, { className: "w-4 h-4" }),
|
|
3947
|
-
"Resend"
|
|
3948
|
-
]
|
|
3949
|
-
}
|
|
3950
|
-
)
|
|
3951
|
-
] })
|
|
3952
|
-
] })
|
|
3953
|
-
] }),
|
|
3954
|
-
error && /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive bg-destructive/10 p-3 rounded-md border border-destructive/20", children: error }),
|
|
3955
|
-
supportUrl && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(AuthHelp, {}) })
|
|
3956
|
-
] })
|
|
3957
|
-
] });
|
|
3958
|
-
}, "OTPForm");
|
|
3959
|
-
var TwoFactorForm = /* @__PURE__ */ __name(() => {
|
|
3960
|
-
const {
|
|
3961
|
-
twoFactorCode,
|
|
3962
|
-
useBackupCode,
|
|
3963
|
-
error,
|
|
3964
|
-
is2FALoading,
|
|
3965
|
-
twoFactorWarning,
|
|
3966
|
-
setTwoFactorCode,
|
|
3967
|
-
handle2FASubmit,
|
|
3968
|
-
handleUseBackupCode,
|
|
3969
|
-
handleUseTOTP
|
|
3970
|
-
} = useAuthFormContext();
|
|
3971
|
-
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full", children: [
|
|
3972
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "space-y-1 text-center", children: [
|
|
3973
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-2", children: /* @__PURE__ */ jsx(ShieldCheck, { className: "w-6 h-6 text-primary" }) }),
|
|
3974
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-2xl", children: "Two-Factor Authentication" }),
|
|
3975
|
-
/* @__PURE__ */ jsx(CardDescription, { children: useBackupCode ? "Enter one of your backup recovery codes" : "Enter the 6-digit code from your authenticator app" })
|
|
3976
|
-
] }),
|
|
3977
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handle2FASubmit, children: [
|
|
3978
|
-
/* @__PURE__ */ jsxs(CardContent$1, { className: "space-y-4", children: [
|
|
3979
|
-
error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: /* @__PURE__ */ jsx(AlertDescription, { children: error }) }),
|
|
3980
|
-
twoFactorWarning && /* @__PURE__ */ jsx(Alert, { children: /* @__PURE__ */ jsx(AlertDescription, { children: twoFactorWarning }) }),
|
|
3981
|
-
!useBackupCode && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
3982
|
-
OTPInput,
|
|
3983
|
-
{
|
|
3984
|
-
length: 6,
|
|
3985
|
-
validationMode: "numeric",
|
|
3986
|
-
pasteBehavior: "clean",
|
|
3987
|
-
value: twoFactorCode,
|
|
3988
|
-
onChange: setTwoFactorCode,
|
|
3989
|
-
disabled: is2FALoading,
|
|
3990
|
-
autoFocus: true,
|
|
3991
|
-
size: "lg"
|
|
3992
|
-
}
|
|
3993
|
-
) }),
|
|
3994
|
-
useBackupCode && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
3995
|
-
/* @__PURE__ */ jsx(
|
|
3996
|
-
Input,
|
|
3997
|
-
{
|
|
3998
|
-
type: "text",
|
|
3999
|
-
placeholder: "Enter backup code",
|
|
4000
|
-
value: twoFactorCode,
|
|
4001
|
-
onChange: (e) => setTwoFactorCode(e.target.value.toUpperCase()),
|
|
4002
|
-
disabled: is2FALoading,
|
|
4003
|
-
className: "text-center font-mono text-lg tracking-widest",
|
|
4004
|
-
maxLength: 12,
|
|
4005
|
-
autoComplete: "off"
|
|
4006
|
-
}
|
|
4007
|
-
),
|
|
4008
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center", children: "Backup codes are 8 characters, letters and numbers" })
|
|
4009
|
-
] })
|
|
4010
|
-
] }),
|
|
4011
|
-
/* @__PURE__ */ jsxs(CardFooter, { className: "flex flex-col space-y-3", children: [
|
|
4012
|
-
/* @__PURE__ */ jsx(
|
|
4013
|
-
Button,
|
|
4014
|
-
{
|
|
4015
|
-
type: "submit",
|
|
4016
|
-
className: "w-full",
|
|
4017
|
-
disabled: is2FALoading || !useBackupCode && twoFactorCode.length !== 6,
|
|
4018
|
-
children: is2FALoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4019
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
4020
|
-
"Verifying..."
|
|
4021
|
-
] }) : "Verify"
|
|
4022
|
-
}
|
|
4023
|
-
),
|
|
4024
|
-
/* @__PURE__ */ jsxs(
|
|
4025
|
-
Button,
|
|
4026
|
-
{
|
|
4027
|
-
type: "button",
|
|
4028
|
-
variant: "ghost",
|
|
4029
|
-
className: "w-full text-sm",
|
|
4030
|
-
onClick: useBackupCode ? handleUseTOTP : handleUseBackupCode,
|
|
4031
|
-
disabled: is2FALoading,
|
|
4032
|
-
children: [
|
|
4033
|
-
/* @__PURE__ */ jsx(KeyRound, { className: "mr-2 h-4 w-4" }),
|
|
4034
|
-
useBackupCode ? "Use authenticator app instead" : "Can't access your authenticator? Use a backup code"
|
|
4035
|
-
]
|
|
4036
|
-
}
|
|
4037
|
-
)
|
|
4038
|
-
] })
|
|
4039
|
-
] })
|
|
4040
|
-
] });
|
|
4041
|
-
}, "TwoFactorForm");
|
|
4042
|
-
var TwoFactorSetup = /* @__PURE__ */ __name(({
|
|
4043
|
-
onComplete,
|
|
4044
|
-
onSkip,
|
|
4045
|
-
onError,
|
|
4046
|
-
deviceName
|
|
4047
|
-
}) => {
|
|
4048
|
-
const [confirmCode, setConfirmCode] = useState("");
|
|
4049
|
-
const [showSecret, setShowSecret] = useState(false);
|
|
4050
|
-
const [copiedSecret, setCopiedSecret] = useState(false);
|
|
4051
|
-
const [copiedBackupCodes, setCopiedBackupCodes] = useState(false);
|
|
4052
|
-
const {
|
|
4053
|
-
isLoading,
|
|
4054
|
-
error,
|
|
4055
|
-
setupData,
|
|
4056
|
-
backupCodes,
|
|
4057
|
-
backupCodesWarning,
|
|
4058
|
-
setupStep,
|
|
4059
|
-
startSetup,
|
|
4060
|
-
confirmSetup,
|
|
4061
|
-
resetSetup
|
|
4062
|
-
} = useTwoFactorSetup({
|
|
4063
|
-
onComplete,
|
|
4064
|
-
onError
|
|
4065
|
-
});
|
|
4066
|
-
React.useEffect(() => {
|
|
4067
|
-
if (setupStep === "idle") {
|
|
4068
|
-
startSetup(deviceName);
|
|
4069
|
-
}
|
|
4070
|
-
}, [setupStep, startSetup, deviceName]);
|
|
4071
|
-
const handleConfirm = /* @__PURE__ */ __name(async (e) => {
|
|
4072
|
-
e.preventDefault();
|
|
4073
|
-
await confirmSetup(confirmCode);
|
|
4074
|
-
}, "handleConfirm");
|
|
4075
|
-
const copySecret = /* @__PURE__ */ __name(async () => {
|
|
4076
|
-
if (setupData?.secret) {
|
|
4077
|
-
await navigator.clipboard.writeText(setupData.secret);
|
|
4078
|
-
setCopiedSecret(true);
|
|
4079
|
-
setTimeout(() => setCopiedSecret(false), 2e3);
|
|
4080
|
-
}
|
|
4081
|
-
}, "copySecret");
|
|
4082
|
-
const copyBackupCodes = /* @__PURE__ */ __name(async () => {
|
|
4083
|
-
if (backupCodes) {
|
|
4084
|
-
await navigator.clipboard.writeText(backupCodes.join("\n"));
|
|
4085
|
-
setCopiedBackupCodes(true);
|
|
4086
|
-
setTimeout(() => setCopiedBackupCodes(false), 2e3);
|
|
4087
|
-
}
|
|
4088
|
-
}, "copyBackupCodes");
|
|
4089
|
-
if (isLoading && !setupData) {
|
|
4090
|
-
return /* @__PURE__ */ jsx(Card$1, { className: "w-full max-w-md mx-auto", children: /* @__PURE__ */ jsx(CardContent$1, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin text-primary" }) }) });
|
|
4091
|
-
}
|
|
4092
|
-
if (setupStep === "complete" && backupCodes) {
|
|
4093
|
-
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md mx-auto", children: [
|
|
4094
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "space-y-1 text-center", children: [
|
|
4095
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-green-100 dark:bg-green-900/20 rounded-full flex items-center justify-center mb-2", children: /* @__PURE__ */ jsx(CheckCircle, { className: "w-6 h-6 text-green-600 dark:text-green-400" }) }),
|
|
4096
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-2xl", children: "2FA Enabled!" }),
|
|
4097
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "Save these backup codes in a secure place" })
|
|
4098
|
-
] }),
|
|
4099
|
-
/* @__PURE__ */ jsxs(CardContent$1, { className: "space-y-4", children: [
|
|
4100
|
-
backupCodesWarning && /* @__PURE__ */ jsx(Alert, { children: /* @__PURE__ */ jsx(AlertDescription, { children: backupCodesWarning }) }),
|
|
4101
|
-
/* @__PURE__ */ jsx("div", { className: "bg-muted rounded-lg p-4", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 font-mono text-sm", children: backupCodes.map((code, index) => /* @__PURE__ */ jsx("div", { className: "text-center py-1", children: code }, index)) }) }),
|
|
4102
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center", children: "Each code can only be used once. Store them securely." })
|
|
4103
|
-
] }),
|
|
4104
|
-
/* @__PURE__ */ jsxs(CardFooter, { className: "flex flex-col space-y-3", children: [
|
|
4105
|
-
/* @__PURE__ */ jsxs(
|
|
4106
|
-
Button,
|
|
4107
|
-
{
|
|
4108
|
-
type: "button",
|
|
4109
|
-
variant: "outline",
|
|
4110
|
-
className: "w-full",
|
|
4111
|
-
onClick: copyBackupCodes,
|
|
4112
|
-
children: [
|
|
4113
|
-
/* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }),
|
|
4114
|
-
copiedBackupCodes ? "Copied!" : "Copy all codes"
|
|
4115
|
-
]
|
|
4116
|
-
}
|
|
4117
|
-
),
|
|
4118
|
-
/* @__PURE__ */ jsx(
|
|
4119
|
-
Button,
|
|
4120
|
-
{
|
|
4121
|
-
type: "button",
|
|
4122
|
-
className: "w-full",
|
|
4123
|
-
onClick: () => onComplete?.(backupCodes),
|
|
4124
|
-
children: "I've saved my backup codes"
|
|
4125
|
-
}
|
|
4126
|
-
)
|
|
4127
|
-
] })
|
|
4128
|
-
] });
|
|
4129
|
-
}
|
|
4130
|
-
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md mx-auto", children: [
|
|
4131
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "space-y-1 text-center", children: [
|
|
4132
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-2", children: /* @__PURE__ */ jsx(ShieldCheck, { className: "w-6 h-6 text-primary" }) }),
|
|
4133
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-2xl", children: "Set Up 2FA" }),
|
|
4134
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "Scan this QR code with your authenticator app" })
|
|
4135
|
-
] }),
|
|
4136
|
-
/* @__PURE__ */ jsxs("form", { onSubmit: handleConfirm, children: [
|
|
4137
|
-
/* @__PURE__ */ jsxs(CardContent$1, { className: "space-y-6", children: [
|
|
4138
|
-
error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: /* @__PURE__ */ jsx(AlertDescription, { children: error }) }),
|
|
4139
|
-
setupData && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx("div", { className: "bg-white p-4 rounded-lg", children: /* @__PURE__ */ jsx(
|
|
4140
|
-
QRCodeSVG,
|
|
4141
|
-
{
|
|
4142
|
-
value: setupData.provisioningUri,
|
|
4143
|
-
size: 200,
|
|
4144
|
-
level: "M",
|
|
4145
|
-
marginSize: 0
|
|
4146
|
-
}
|
|
4147
|
-
) }) }),
|
|
4148
|
-
setupData && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4149
|
-
/* @__PURE__ */ jsxs(
|
|
4150
|
-
Button,
|
|
4151
|
-
{
|
|
4152
|
-
type: "button",
|
|
4153
|
-
variant: "ghost",
|
|
4154
|
-
size: "sm",
|
|
4155
|
-
className: "w-full text-xs",
|
|
4156
|
-
onClick: () => setShowSecret(!showSecret),
|
|
4157
|
-
children: [
|
|
4158
|
-
showSecret ? /* @__PURE__ */ jsx(EyeOff, { className: "mr-2 h-3 w-3" }) : /* @__PURE__ */ jsx(Eye, { className: "mr-2 h-3 w-3" }),
|
|
4159
|
-
showSecret ? "Hide" : "Show",
|
|
4160
|
-
" manual entry code"
|
|
4161
|
-
]
|
|
4162
|
-
}
|
|
4163
|
-
),
|
|
4164
|
-
showSecret && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 bg-muted rounded-lg p-3", children: [
|
|
4165
|
-
/* @__PURE__ */ jsx("code", { className: "flex-1 text-xs font-mono break-all", children: setupData.secret }),
|
|
4166
|
-
/* @__PURE__ */ jsx(
|
|
4167
|
-
Button,
|
|
4168
|
-
{
|
|
4169
|
-
type: "button",
|
|
4170
|
-
variant: "ghost",
|
|
4171
|
-
size: "sm",
|
|
4172
|
-
onClick: copySecret,
|
|
4173
|
-
children: /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
|
|
4174
|
-
}
|
|
4175
|
-
)
|
|
4176
|
-
] })
|
|
4177
|
-
] }),
|
|
4178
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4179
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-center text-muted-foreground", children: "Enter the 6-digit code from your app to confirm" }),
|
|
4180
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
4181
|
-
OTPInput,
|
|
4182
|
-
{
|
|
4183
|
-
length: 6,
|
|
4184
|
-
validationMode: "numeric",
|
|
4185
|
-
pasteBehavior: "clean",
|
|
4186
|
-
value: confirmCode,
|
|
4187
|
-
onChange: setConfirmCode,
|
|
4188
|
-
disabled: isLoading,
|
|
4189
|
-
autoFocus: true,
|
|
4190
|
-
size: "lg"
|
|
4191
|
-
}
|
|
4192
|
-
) })
|
|
4193
|
-
] })
|
|
4194
|
-
] }),
|
|
4195
|
-
/* @__PURE__ */ jsxs(CardFooter, { className: "flex flex-col space-y-3", children: [
|
|
4196
|
-
/* @__PURE__ */ jsx(
|
|
4197
|
-
Button,
|
|
4198
|
-
{
|
|
4199
|
-
type: "submit",
|
|
4200
|
-
className: "w-full",
|
|
4201
|
-
disabled: isLoading || confirmCode.length !== 6,
|
|
4202
|
-
children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4203
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
4204
|
-
"Verifying..."
|
|
4205
|
-
] }) : "Confirm & Enable 2FA"
|
|
4206
|
-
}
|
|
4207
|
-
),
|
|
4208
|
-
onSkip && /* @__PURE__ */ jsx(
|
|
4209
|
-
Button,
|
|
4210
|
-
{
|
|
4211
|
-
type: "button",
|
|
4212
|
-
variant: "ghost",
|
|
4213
|
-
className: "w-full",
|
|
4214
|
-
onClick: onSkip,
|
|
4215
|
-
disabled: isLoading,
|
|
4216
|
-
children: "Skip for now"
|
|
4217
|
-
}
|
|
4218
|
-
)
|
|
4219
|
-
] })
|
|
4220
|
-
] })
|
|
4221
|
-
] });
|
|
4222
|
-
}, "TwoFactorSetup");
|
|
4223
|
-
var AuthSuccess = /* @__PURE__ */ __name(({ className, redirectDelay = 1500 }) => {
|
|
4224
|
-
const { logoUrl, redirectUrl } = useAuthFormContext();
|
|
4225
|
-
const router = useCfgRouter();
|
|
4226
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
4227
|
-
useEffect(() => {
|
|
4228
|
-
const animTimer = setTimeout(() => setIsVisible(true), 50);
|
|
4229
|
-
const redirectTimer = setTimeout(() => {
|
|
4230
|
-
const finalUrl = redirectUrl || "/dashboard";
|
|
4231
|
-
router.hardPush(finalUrl);
|
|
4232
|
-
}, redirectDelay);
|
|
4233
|
-
return () => {
|
|
4234
|
-
clearTimeout(animTimer);
|
|
4235
|
-
clearTimeout(redirectTimer);
|
|
4236
|
-
};
|
|
4237
|
-
}, [redirectUrl, redirectDelay, router]);
|
|
4238
|
-
if (!logoUrl) {
|
|
4239
|
-
return /* @__PURE__ */ jsx("div", { className: `fixed inset-0 flex items-center justify-center bg-background z-50 ${className || ""}`, children: /* @__PURE__ */ jsx(
|
|
4240
|
-
"div",
|
|
4241
|
-
{
|
|
4242
|
-
className: `transition-all duration-700 ease-out ${isVisible ? "opacity-100 scale-100" : "opacity-0 scale-95"}`,
|
|
4243
|
-
children: /* @__PURE__ */ jsx("div", { className: "w-24 h-24 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
4244
|
-
"svg",
|
|
4245
|
-
{
|
|
4246
|
-
className: "w-12 h-12 text-green-600 dark:text-green-400",
|
|
4247
|
-
fill: "none",
|
|
4248
|
-
stroke: "currentColor",
|
|
4249
|
-
viewBox: "0 0 24 24",
|
|
4250
|
-
children: /* @__PURE__ */ jsx(
|
|
4251
|
-
"path",
|
|
4252
|
-
{
|
|
4253
|
-
strokeLinecap: "round",
|
|
4254
|
-
strokeLinejoin: "round",
|
|
4255
|
-
strokeWidth: 2,
|
|
4256
|
-
d: "M5 13l4 4L19 7"
|
|
4257
|
-
}
|
|
4258
|
-
)
|
|
4259
|
-
}
|
|
4260
|
-
) })
|
|
4261
|
-
}
|
|
4262
|
-
) });
|
|
4263
|
-
}
|
|
4264
|
-
return /* @__PURE__ */ jsx("div", { className: `fixed inset-0 flex items-center justify-center bg-background z-50 ${className || ""}`, children: /* @__PURE__ */ jsx(
|
|
4265
|
-
"div",
|
|
4266
|
-
{
|
|
4267
|
-
className: `transition-all duration-700 ease-out ${isVisible ? "opacity-100 scale-100" : "opacity-0 scale-90"}`,
|
|
4268
|
-
children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4269
|
-
/* @__PURE__ */ jsx(
|
|
4270
|
-
"div",
|
|
4271
|
-
{
|
|
4272
|
-
className: `absolute inset-0 blur-3xl transition-opacity duration-1000 ${isVisible ? "opacity-20" : "opacity-0"}`,
|
|
4273
|
-
style: {
|
|
4274
|
-
background: "radial-gradient(circle, currentColor 0%, transparent 70%)"
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
),
|
|
4278
|
-
/* @__PURE__ */ jsx(
|
|
4279
|
-
"img",
|
|
4280
|
-
{
|
|
4281
|
-
src: logoUrl,
|
|
4282
|
-
alt: "Success",
|
|
4283
|
-
className: "relative w-32 h-32 sm:w-40 sm:h-40 md:w-48 md:h-48 object-contain"
|
|
4284
|
-
}
|
|
4285
|
-
)
|
|
4286
|
-
] })
|
|
4287
|
-
}
|
|
4288
|
-
) });
|
|
4289
|
-
}, "AuthSuccess");
|
|
4290
|
-
var AuthLayout = /* @__PURE__ */ __name((props) => {
|
|
4291
|
-
const { enableGithubAuth, redirectUrl = "/dashboard", onOAuthSuccess, onError, className } = props;
|
|
4292
|
-
return /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsxs(AuthFormProvider, { ...props, children: [
|
|
4293
|
-
/* @__PURE__ */ jsx(AuthSuccessOverlay, {}),
|
|
4294
|
-
/* @__PURE__ */ jsxs(
|
|
4295
|
-
"div",
|
|
4296
|
-
{
|
|
4297
|
-
className: `min-h-screen flex flex-col items-center justify-center bg-background py-6 px-4 sm:py-12 sm:px-6 lg:px-8 ${className || ""}`,
|
|
4298
|
-
children: [
|
|
4299
|
-
enableGithubAuth && /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
4300
|
-
OAuthCallback,
|
|
4301
|
-
{
|
|
4302
|
-
redirectUrl,
|
|
4303
|
-
onSuccess: onOAuthSuccess ? (user, isNewUser) => onOAuthSuccess(user, isNewUser, "github") : void 0,
|
|
4304
|
-
onError
|
|
4305
|
-
}
|
|
4306
|
-
) }),
|
|
4307
|
-
/* @__PURE__ */ jsxs("div", { className: "w-full sm:max-w-md space-y-8", children: [
|
|
4308
|
-
props.children,
|
|
4309
|
-
/* @__PURE__ */ jsx(AuthContent, {})
|
|
4310
|
-
] })
|
|
4311
|
-
]
|
|
4312
|
-
}
|
|
4313
|
-
)
|
|
4314
|
-
] }) });
|
|
4315
|
-
}, "AuthLayout");
|
|
4316
|
-
var AuthContent = /* @__PURE__ */ __name(() => {
|
|
4317
|
-
const { step, setStep } = useAuthFormContext();
|
|
4318
|
-
switch (step) {
|
|
4319
|
-
case "identifier":
|
|
4320
|
-
return /* @__PURE__ */ jsx(IdentifierForm, {});
|
|
4321
|
-
case "otp":
|
|
4322
|
-
return /* @__PURE__ */ jsx(OTPForm, {});
|
|
4323
|
-
case "2fa":
|
|
4324
|
-
return /* @__PURE__ */ jsx(TwoFactorForm, {});
|
|
4325
|
-
case "2fa-setup":
|
|
4326
|
-
return /* @__PURE__ */ jsx(
|
|
4327
|
-
TwoFactorSetup,
|
|
4328
|
-
{
|
|
4329
|
-
onComplete: () => setStep("success"),
|
|
4330
|
-
onSkip: () => setStep("success")
|
|
4331
|
-
}
|
|
4332
|
-
);
|
|
4333
|
-
case "success":
|
|
4334
|
-
return null;
|
|
4335
|
-
default:
|
|
4336
|
-
return /* @__PURE__ */ jsx(IdentifierForm, {});
|
|
4337
|
-
}
|
|
4338
|
-
}, "AuthContent");
|
|
4339
|
-
var AuthSuccessOverlay = /* @__PURE__ */ __name(() => {
|
|
4340
|
-
const { step } = useAuthFormContext();
|
|
4341
|
-
if (step !== "success") {
|
|
4342
|
-
return null;
|
|
4343
|
-
}
|
|
4344
|
-
return /* @__PURE__ */ jsx(AuthSuccess, {});
|
|
4345
|
-
}, "AuthSuccessOverlay");
|
|
4346
|
-
function AdminLayout(props) {
|
|
4347
|
-
return /* @__PURE__ */ jsx(PrivateLayout, { ...props });
|
|
4348
|
-
}
|
|
4349
|
-
__name(AdminLayout, "AdminLayout");
|
|
4350
|
-
var SettingsGroup = /* @__PURE__ */ __name(({ children, className }) => /* @__PURE__ */ jsx("div", { className: cn("bg-card rounded-xl overflow-hidden divide-y divide-border", className), children }), "SettingsGroup");
|
|
4351
|
-
var SettingsGroupHeader = /* @__PURE__ */ __name(({ children }) => /* @__PURE__ */ jsx("div", { className: "px-4 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider", children }), "SettingsGroupHeader");
|
|
4352
|
-
var SettingsRow = /* @__PURE__ */ __name(({
|
|
4353
|
-
icon,
|
|
4354
|
-
label,
|
|
4355
|
-
value,
|
|
4356
|
-
onClick,
|
|
4357
|
-
destructive,
|
|
4358
|
-
disabled,
|
|
4359
|
-
showChevron = !!onClick,
|
|
4360
|
-
className
|
|
4361
|
-
}) => {
|
|
4362
|
-
const Wrapper = onClick ? "button" : "div";
|
|
4363
|
-
return /* @__PURE__ */ jsxs(
|
|
4364
|
-
Wrapper,
|
|
4365
|
-
{
|
|
4366
|
-
onClick: disabled ? void 0 : onClick,
|
|
4367
|
-
disabled,
|
|
4368
|
-
className: cn(
|
|
4369
|
-
"w-full flex items-center gap-3 px-4 py-3 text-left transition-colors",
|
|
4370
|
-
onClick && !disabled && "hover:bg-muted/50 active:bg-muted cursor-pointer",
|
|
4371
|
-
disabled && "opacity-50 cursor-not-allowed",
|
|
4372
|
-
destructive && "text-destructive",
|
|
4373
|
-
className
|
|
4374
|
-
),
|
|
4375
|
-
children: [
|
|
4376
|
-
icon && /* @__PURE__ */ jsx("span", { className: cn("shrink-0", destructive ? "text-destructive" : "text-muted-foreground"), children: icon }),
|
|
4377
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 font-medium", children: label }),
|
|
4378
|
-
value && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm truncate max-w-[180px]", children: value }),
|
|
4379
|
-
showChevron && /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4 text-muted-foreground/50 shrink-0" })
|
|
4380
|
-
]
|
|
4381
|
-
}
|
|
4382
|
-
);
|
|
4383
|
-
}, "SettingsRow");
|
|
4384
|
-
var fieldLabels = {
|
|
4385
|
-
first_name: "First Name",
|
|
4386
|
-
last_name: "Last Name",
|
|
4387
|
-
company: "Company",
|
|
4388
|
-
position: "Position",
|
|
4389
|
-
phone: "Phone"
|
|
4390
|
-
};
|
|
4391
|
-
var EditFieldDialog = /* @__PURE__ */ __name(({ open, onOpenChange, field, currentValue, onSave, isSaving }) => {
|
|
4392
|
-
const [value, setValue] = useState(currentValue);
|
|
4393
|
-
useEffect(() => {
|
|
4394
|
-
setValue(currentValue);
|
|
4395
|
-
}, [currentValue, open]);
|
|
4396
|
-
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
4397
|
-
if (field) {
|
|
4398
|
-
await onSave(field, value);
|
|
4399
|
-
}
|
|
4400
|
-
}, "handleSave");
|
|
4401
|
-
if (!field) return null;
|
|
4402
|
-
return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent$1, { className: "sm:max-w-md", children: [
|
|
4403
|
-
/* @__PURE__ */ jsx(DialogHeader$1, { children: /* @__PURE__ */ jsx(DialogTitle$1, { children: fieldLabels[field] }) }),
|
|
4404
|
-
/* @__PURE__ */ jsx("div", { className: "py-4", children: /* @__PURE__ */ jsx(
|
|
4405
|
-
Input,
|
|
4406
|
-
{
|
|
4407
|
-
value,
|
|
4408
|
-
onChange: (e) => setValue(e.target.value),
|
|
4409
|
-
placeholder: `Enter ${fieldLabels[field].toLowerCase()}`,
|
|
4410
|
-
autoFocus: true,
|
|
4411
|
-
onKeyDown: (e) => e.key === "Enter" && handleSave()
|
|
4412
|
-
}
|
|
4413
|
-
) }),
|
|
4414
|
-
/* @__PURE__ */ jsxs(DialogFooter$1, { children: [
|
|
4415
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), disabled: isSaving, children: "Cancel" }),
|
|
4416
|
-
/* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: isSaving, children: isSaving ? "Saving..." : "Save" })
|
|
4417
|
-
] })
|
|
4418
|
-
] }) });
|
|
4419
|
-
}, "EditFieldDialog");
|
|
4420
|
-
var ProfileContent = /* @__PURE__ */ __name(({
|
|
4421
|
-
onUnauthenticated,
|
|
4422
|
-
title = "Profile",
|
|
4423
|
-
enable2FA = false,
|
|
4424
|
-
enableDeleteAccount = true
|
|
4425
|
-
}) => {
|
|
4426
|
-
const { user, isLoading, logout, uploadAvatar, updateProfile } = useAuth();
|
|
4427
|
-
const [editingField, setEditingField] = useState(null);
|
|
4428
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
4429
|
-
const [isUploading, setIsUploading] = useState(false);
|
|
4430
|
-
const [show2FASetup, setShow2FASetup] = useState(false);
|
|
4431
|
-
const { has2FAEnabled, fetchStatus: fetch2FAStatus } = useTwoFactorStatus();
|
|
4432
|
-
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
4433
|
-
const form = useForm({
|
|
4434
|
-
resolver: zodResolver(PatchedUserProfileUpdateRequestSchema),
|
|
4435
|
-
defaultValues: {
|
|
4436
|
-
first_name: "",
|
|
4437
|
-
last_name: "",
|
|
4438
|
-
company: "",
|
|
4439
|
-
position: "",
|
|
4440
|
-
phone: ""
|
|
4441
|
-
}
|
|
4442
|
-
});
|
|
4443
|
-
useEffect(() => {
|
|
4444
|
-
if (user) {
|
|
4445
|
-
form.reset({
|
|
4446
|
-
first_name: user.first_name || "",
|
|
4447
|
-
last_name: user.last_name || "",
|
|
4448
|
-
company: user.company || "",
|
|
4449
|
-
position: user.position || "",
|
|
4450
|
-
phone: user.phone || ""
|
|
4451
|
-
});
|
|
4452
|
-
}
|
|
4453
|
-
}, [user, form]);
|
|
4454
|
-
useEffect(() => {
|
|
4455
|
-
if (enable2FA) {
|
|
4456
|
-
fetch2FAStatus();
|
|
4457
|
-
}
|
|
4458
|
-
}, [enable2FA, fetch2FAStatus]);
|
|
4459
|
-
useEffect(() => {
|
|
4460
|
-
if (onUnauthenticated && !user && !isLoading) {
|
|
4461
|
-
onUnauthenticated();
|
|
4462
|
-
}
|
|
4463
|
-
}, [onUnauthenticated, user, isLoading]);
|
|
4464
|
-
const getInitials = /* @__PURE__ */ __name((name) => {
|
|
4465
|
-
if (!name) return "U";
|
|
4466
|
-
return name.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
|
|
4467
|
-
}, "getInitials");
|
|
4468
|
-
const handleAvatarChange = /* @__PURE__ */ __name(async (event) => {
|
|
4469
|
-
const file = event.target.files?.[0];
|
|
4470
|
-
if (!file) return;
|
|
4471
|
-
if (!file.type.startsWith("image/")) {
|
|
4472
|
-
toast.error("Please select an image file");
|
|
4473
|
-
return;
|
|
4474
|
-
}
|
|
4475
|
-
if (file.size > 5 * 1024 * 1024) {
|
|
4476
|
-
toast.error("File size must be less than 5MB");
|
|
4477
|
-
return;
|
|
4478
|
-
}
|
|
4479
|
-
setIsUploading(true);
|
|
4480
|
-
try {
|
|
4481
|
-
await uploadAvatar(file);
|
|
4482
|
-
toast.success("Avatar updated");
|
|
4483
|
-
} catch (error) {
|
|
4484
|
-
toast.error("Failed to upload avatar");
|
|
4485
|
-
profileLogger.error("Avatar upload error:", error);
|
|
4486
|
-
} finally {
|
|
4487
|
-
setIsUploading(false);
|
|
4488
|
-
}
|
|
4489
|
-
}, "handleAvatarChange");
|
|
4490
|
-
const handleFieldSave = /* @__PURE__ */ __name(async (field, value) => {
|
|
4491
|
-
setIsSaving(true);
|
|
4492
|
-
try {
|
|
4493
|
-
await updateProfile({ [field]: value });
|
|
4494
|
-
toast.success("Profile updated");
|
|
4495
|
-
setEditingField(null);
|
|
4496
|
-
} catch (error) {
|
|
4497
|
-
profileLogger.error("Profile update error:", error);
|
|
4498
|
-
toast.error(error?.response?.data?.[field]?.[0] || "Failed to update");
|
|
4499
|
-
} finally {
|
|
4500
|
-
setIsSaving(false);
|
|
4501
|
-
}
|
|
4502
|
-
}, "handleFieldSave");
|
|
4503
|
-
const handleLogout = /* @__PURE__ */ __name(() => {
|
|
4504
|
-
logout();
|
|
4505
|
-
}, "handleLogout");
|
|
4506
|
-
if (isLoading) {
|
|
4507
|
-
return /* @__PURE__ */ jsx(
|
|
4508
|
-
Preloader,
|
|
4509
|
-
{
|
|
4510
|
-
variant: "fullscreen",
|
|
4511
|
-
text: "Loading...",
|
|
4512
|
-
size: "lg",
|
|
4513
|
-
backdrop: true,
|
|
4514
|
-
backdropOpacity: 80
|
|
4515
|
-
}
|
|
4516
|
-
);
|
|
4517
|
-
}
|
|
4518
|
-
if (!user) {
|
|
4519
|
-
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
4520
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mb-4", children: "Not Authenticated" }),
|
|
4521
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Please log in to view your profile." })
|
|
4522
|
-
] }) });
|
|
4523
|
-
}
|
|
4524
|
-
if (show2FASetup) {
|
|
4525
|
-
return /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-8 max-w-lg", children: /* @__PURE__ */ jsx(
|
|
4526
|
-
TwoFactorSetup,
|
|
4527
|
-
{
|
|
4528
|
-
onComplete: () => {
|
|
4529
|
-
setShow2FASetup(false);
|
|
4530
|
-
fetch2FAStatus();
|
|
4531
|
-
},
|
|
4532
|
-
onSkip: () => setShow2FASetup(false)
|
|
4533
|
-
}
|
|
4534
|
-
) });
|
|
4535
|
-
}
|
|
4536
|
-
return /* @__PURE__ */ jsxs("div", { className: "container mx-auto px-4 py-8 max-w-lg", children: [
|
|
4537
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-center mb-8", children: title }),
|
|
4538
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center mb-8", children: [
|
|
4539
|
-
/* @__PURE__ */ jsxs("div", { className: "relative group mb-3", children: [
|
|
4540
|
-
/* @__PURE__ */ jsx(Avatar, { className: "w-24 h-24 text-3xl", children: user.avatar ? /* @__PURE__ */ jsx("img", { src: user.avatar, alt: "Avatar", className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary text-primary-foreground", children: getInitials(user.display_username || user.email || "") }) }),
|
|
4541
|
-
/* @__PURE__ */ jsxs("label", { className: "absolute inset-0 rounded-full bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer", children: [
|
|
4542
|
-
isUploading ? /* @__PURE__ */ jsx("div", { className: "w-6 h-6 border-2 border-white border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsx(Camera, { className: "w-6 h-6 text-white" }),
|
|
4543
|
-
/* @__PURE__ */ jsx(
|
|
4544
|
-
"input",
|
|
4545
|
-
{
|
|
4546
|
-
type: "file",
|
|
4547
|
-
accept: "image/*",
|
|
4548
|
-
onChange: handleAvatarChange,
|
|
4549
|
-
className: "hidden",
|
|
4550
|
-
disabled: isUploading
|
|
4551
|
-
}
|
|
4552
|
-
)
|
|
4553
|
-
] })
|
|
4554
|
-
] }),
|
|
4555
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold", children: user.display_username || user.email }),
|
|
4556
|
-
user.date_joined && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
4557
|
-
"Member since ",
|
|
4558
|
-
moment.utc(user.date_joined).local().format("MMMM YYYY")
|
|
4559
|
-
] })
|
|
4560
|
-
] }),
|
|
4561
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
4562
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4563
|
-
/* @__PURE__ */ jsx(SettingsGroupHeader, { children: "Account" }),
|
|
4564
|
-
/* @__PURE__ */ jsx(SettingsGroup, { children: /* @__PURE__ */ jsx(
|
|
4565
|
-
SettingsRow,
|
|
4566
|
-
{
|
|
4567
|
-
icon: /* @__PURE__ */ jsx(Mail, { className: "w-5 h-5" }),
|
|
4568
|
-
label: "Email",
|
|
4569
|
-
value: user.email,
|
|
4570
|
-
showChevron: false
|
|
4571
|
-
}
|
|
4572
|
-
) })
|
|
4573
|
-
] }),
|
|
4574
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4575
|
-
/* @__PURE__ */ jsx(SettingsGroupHeader, { children: "Personal Information" }),
|
|
4576
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { children: [
|
|
4577
|
-
/* @__PURE__ */ jsx(
|
|
4578
|
-
SettingsRow,
|
|
4579
|
-
{
|
|
4580
|
-
icon: /* @__PURE__ */ jsx(User, { className: "w-5 h-5" }),
|
|
4581
|
-
label: "First Name",
|
|
4582
|
-
value: user.first_name || "Not set",
|
|
4583
|
-
onClick: () => setEditingField("first_name")
|
|
4584
|
-
}
|
|
4585
|
-
),
|
|
4586
|
-
/* @__PURE__ */ jsx(
|
|
4587
|
-
SettingsRow,
|
|
4588
|
-
{
|
|
4589
|
-
icon: /* @__PURE__ */ jsx(User, { className: "w-5 h-5" }),
|
|
4590
|
-
label: "Last Name",
|
|
4591
|
-
value: user.last_name || "Not set",
|
|
4592
|
-
onClick: () => setEditingField("last_name")
|
|
4593
|
-
}
|
|
4594
|
-
),
|
|
4595
|
-
/* @__PURE__ */ jsx(
|
|
4596
|
-
SettingsRow,
|
|
4597
|
-
{
|
|
4598
|
-
icon: /* @__PURE__ */ jsx(Phone, { className: "w-5 h-5" }),
|
|
4599
|
-
label: "Phone",
|
|
4600
|
-
value: user.phone || "Not set",
|
|
4601
|
-
onClick: () => setEditingField("phone")
|
|
4602
|
-
}
|
|
4603
|
-
)
|
|
4604
|
-
] })
|
|
4605
|
-
] }),
|
|
4606
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4607
|
-
/* @__PURE__ */ jsx(SettingsGroupHeader, { children: "Work" }),
|
|
4608
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { children: [
|
|
4609
|
-
/* @__PURE__ */ jsx(
|
|
4610
|
-
SettingsRow,
|
|
4611
|
-
{
|
|
4612
|
-
icon: /* @__PURE__ */ jsx(Building2, { className: "w-5 h-5" }),
|
|
4613
|
-
label: "Company",
|
|
4614
|
-
value: user.company || "Not set",
|
|
4615
|
-
onClick: () => setEditingField("company")
|
|
4616
|
-
}
|
|
4617
|
-
),
|
|
4618
|
-
/* @__PURE__ */ jsx(
|
|
4619
|
-
SettingsRow,
|
|
4620
|
-
{
|
|
4621
|
-
icon: /* @__PURE__ */ jsx(Briefcase, { className: "w-5 h-5" }),
|
|
4622
|
-
label: "Position",
|
|
4623
|
-
value: user.position || "Not set",
|
|
4624
|
-
onClick: () => setEditingField("position")
|
|
4625
|
-
}
|
|
4626
|
-
)
|
|
4627
|
-
] })
|
|
4628
|
-
] }),
|
|
4629
|
-
enable2FA && /* @__PURE__ */ jsxs("div", { children: [
|
|
4630
|
-
/* @__PURE__ */ jsx(SettingsGroupHeader, { children: "Security" }),
|
|
4631
|
-
/* @__PURE__ */ jsx(SettingsGroup, { children: /* @__PURE__ */ jsx(
|
|
4632
|
-
SettingsRow,
|
|
4633
|
-
{
|
|
4634
|
-
icon: has2FAEnabled ? /* @__PURE__ */ jsx(ShieldCheck, { className: "w-5 h-5 text-green-500" }) : /* @__PURE__ */ jsx(Shield, { className: "w-5 h-5" }),
|
|
4635
|
-
label: "Two-Factor Authentication",
|
|
4636
|
-
value: has2FAEnabled ? "On" : "Off",
|
|
4637
|
-
onClick: () => !has2FAEnabled && setShow2FASetup(true),
|
|
4638
|
-
showChevron: !has2FAEnabled
|
|
4639
|
-
}
|
|
4640
|
-
) })
|
|
4641
|
-
] }),
|
|
4642
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4643
|
-
/* @__PURE__ */ jsx(SettingsGroupHeader, { children: "Actions" }),
|
|
4644
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { children: [
|
|
4645
|
-
/* @__PURE__ */ jsx(
|
|
4646
|
-
SettingsRow,
|
|
4647
|
-
{
|
|
4648
|
-
icon: /* @__PURE__ */ jsx(LogOut, { className: "w-5 h-5" }),
|
|
4649
|
-
label: "Sign Out",
|
|
4650
|
-
onClick: handleLogout
|
|
4651
|
-
}
|
|
4652
|
-
),
|
|
4653
|
-
enableDeleteAccount && /* @__PURE__ */ jsx(
|
|
4654
|
-
SettingsRow,
|
|
4655
|
-
{
|
|
4656
|
-
icon: /* @__PURE__ */ jsx(Trash2, { className: "w-5 h-5" }),
|
|
4657
|
-
label: "Delete Account",
|
|
4658
|
-
onClick: () => setShowDeleteConfirm(true),
|
|
4659
|
-
destructive: true
|
|
4660
|
-
}
|
|
4661
|
-
)
|
|
4662
|
-
] })
|
|
4663
|
-
] })
|
|
4664
|
-
] }),
|
|
4665
|
-
/* @__PURE__ */ jsx(
|
|
4666
|
-
EditFieldDialog,
|
|
4667
|
-
{
|
|
4668
|
-
open: editingField !== null,
|
|
4669
|
-
onOpenChange: (open) => !open && setEditingField(null),
|
|
4670
|
-
field: editingField,
|
|
4671
|
-
currentValue: editingField ? user[editingField] || "" : "",
|
|
4672
|
-
onSave: handleFieldSave,
|
|
4673
|
-
isSaving
|
|
4674
|
-
}
|
|
4675
|
-
),
|
|
4676
|
-
/* @__PURE__ */ jsx(
|
|
4677
|
-
DeleteAccountDialog,
|
|
4678
|
-
{
|
|
4679
|
-
open: showDeleteConfirm,
|
|
4680
|
-
onOpenChange: setShowDeleteConfirm
|
|
4681
|
-
}
|
|
4682
|
-
)
|
|
4683
|
-
] });
|
|
4684
|
-
}, "ProfileContent");
|
|
4685
|
-
var CONFIRMATION_TEXT = "DELETE";
|
|
4686
|
-
var DeleteAccountDialog = /* @__PURE__ */ __name(({ open, onOpenChange }) => {
|
|
4687
|
-
const [confirmationInput, setConfirmationInput] = useState("");
|
|
4688
|
-
const { logout } = useAuth();
|
|
4689
|
-
const { isLoading, error, deleteAccount, clearError } = useDeleteAccount();
|
|
4690
|
-
useEffect(() => {
|
|
4691
|
-
if (open) {
|
|
4692
|
-
setConfirmationInput("");
|
|
4693
|
-
clearError();
|
|
4694
|
-
}
|
|
4695
|
-
}, [open, clearError]);
|
|
4696
|
-
const handleDelete = /* @__PURE__ */ __name(async () => {
|
|
4697
|
-
const result = await deleteAccount();
|
|
4698
|
-
if (result.success) {
|
|
4699
|
-
onOpenChange(false);
|
|
4700
|
-
await logout({ skipConfirm: true });
|
|
4701
|
-
}
|
|
4702
|
-
}, "handleDelete");
|
|
4703
|
-
const isValid = confirmationInput.toUpperCase() === CONFIRMATION_TEXT;
|
|
4704
|
-
return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent$1, { children: [
|
|
4705
|
-
/* @__PURE__ */ jsxs(DialogHeader$1, { children: [
|
|
4706
|
-
/* @__PURE__ */ jsx(DialogTitle$1, { className: "text-destructive", children: "Delete Account" }),
|
|
4707
|
-
/* @__PURE__ */ jsx(DialogDescription$1, { children: "This action cannot be undone. Your account will be permanently deleted." })
|
|
4708
|
-
] }),
|
|
4709
|
-
/* @__PURE__ */ jsxs("div", { className: "py-4 space-y-4", children: [
|
|
4710
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
4711
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4712
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
4713
|
-
"Type ",
|
|
4714
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono font-bold", children: CONFIRMATION_TEXT }),
|
|
4715
|
-
" to confirm:"
|
|
4716
|
-
] }),
|
|
4717
|
-
/* @__PURE__ */ jsx(
|
|
4718
|
-
Input,
|
|
4719
|
-
{
|
|
4720
|
-
value: confirmationInput,
|
|
4721
|
-
onChange: (e) => setConfirmationInput(e.target.value),
|
|
4722
|
-
placeholder: CONFIRMATION_TEXT,
|
|
4723
|
-
disabled: isLoading,
|
|
4724
|
-
autoComplete: "off"
|
|
4725
|
-
}
|
|
4726
|
-
)
|
|
4727
|
-
] })
|
|
4728
|
-
] }),
|
|
4729
|
-
/* @__PURE__ */ jsxs(DialogFooter$1, { children: [
|
|
4730
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), disabled: isLoading, children: "Cancel" }),
|
|
4731
|
-
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: handleDelete, disabled: isLoading || !isValid, children: isLoading ? "Deleting..." : "Delete Account" })
|
|
4732
|
-
] })
|
|
4733
|
-
] }) });
|
|
4734
|
-
}, "DeleteAccountDialog");
|
|
4735
|
-
var ProfileLayout = /* @__PURE__ */ __name((props) => {
|
|
4736
|
-
return /* @__PURE__ */ jsx(ProfileContent, { ...props });
|
|
4737
|
-
}, "ProfileLayout");
|
|
4738
|
-
|
|
4739
|
-
export { AdminLayout, AppLayout, AuthFormProvider, AuthHelp, AuthLayout, BaseApp, DjangoCFGLogo, FooterBottom, FooterMenuSections, FooterProjectInfo, FooterSocialLinksComponent, IdentifierForm, OAuthCallback, OAuthProviders, OTPForm, PrivateLayout, ProfileLayout, PublicFooter, PublicLayout, UserMenu, useAuthFormContext };
|
|
4740
|
-
//# sourceMappingURL=layouts.mjs.map
|
|
4741
|
-
//# sourceMappingURL=layouts.mjs.map
|