luxlabs 1.0.1 → 1.0.3
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 +1 -1
- package/templates/interface-boilerplate/app/api/auth/[...all]/route.ts +52 -0
- package/templates/interface-boilerplate/app/auth/callback/page.tsx +22 -31
- package/templates/interface-boilerplate/app/dashboard/page.tsx +41 -214
- package/templates/interface-boilerplate/app/favicon.ico +0 -0
- package/templates/interface-boilerplate/app/globals.css +18 -113
- package/templates/interface-boilerplate/app/layout.tsx +21 -33
- package/templates/interface-boilerplate/app/page.tsx +37 -116
- package/templates/interface-boilerplate/app/settings/page.tsx +71 -0
- package/templates/interface-boilerplate/app/sign-in/page.tsx +19 -0
- package/templates/interface-boilerplate/app/sign-up/page.tsx +19 -0
- package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +144 -0
- package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +236 -0
- package/templates/interface-boilerplate/components/ui/badge.tsx +18 -14
- package/templates/interface-boilerplate/components/ui/button.tsx +29 -24
- package/templates/interface-boilerplate/components/ui/card.tsx +76 -57
- package/templates/interface-boilerplate/components/ui/input.tsx +8 -9
- package/templates/interface-boilerplate/eslint.config.mjs +18 -0
- package/templates/interface-boilerplate/lib/auth-client.ts +18 -0
- package/templates/interface-boilerplate/lib/auth.config.ts +111 -0
- package/templates/interface-boilerplate/lib/lux.ts +8 -0
- package/templates/interface-boilerplate/lib/utils.ts +3 -3
- package/templates/interface-boilerplate/middleware.ts +60 -0
- package/templates/interface-boilerplate/next.config.ts +7 -0
- package/templates/interface-boilerplate/package-lock.json +6855 -0
- package/templates/interface-boilerplate/package.json +18 -37
- package/templates/interface-boilerplate/postcss.config.mjs +7 -0
- package/templates/interface-boilerplate/tsconfig.json +18 -4
- package/templates/interface-boilerplate/.env.example +0 -2
- package/templates/interface-boilerplate/.eslintrc.json +0 -4
- package/templates/interface-boilerplate/app/login/page.tsx +0 -178
- package/templates/interface-boilerplate/app/signup/page.tsx +0 -199
- package/templates/interface-boilerplate/components/AnalyticsProvider.tsx +0 -142
- package/templates/interface-boilerplate/components/AuthGuard.tsx +0 -76
- package/templates/interface-boilerplate/components/ErrorBoundary.tsx +0 -106
- package/templates/interface-boilerplate/components/theme-provider.tsx +0 -9
- package/templates/interface-boilerplate/components/theme-toggle.tsx +0 -39
- package/templates/interface-boilerplate/components/ui/avatar.tsx +0 -46
- package/templates/interface-boilerplate/components/ui/checkbox.tsx +0 -27
- package/templates/interface-boilerplate/components/ui/dialog.tsx +0 -100
- package/templates/interface-boilerplate/components/ui/dropdown-menu.tsx +0 -173
- package/templates/interface-boilerplate/components/ui/index.ts +0 -53
- package/templates/interface-boilerplate/components/ui/label.tsx +0 -20
- package/templates/interface-boilerplate/components/ui/progress.tsx +0 -24
- package/templates/interface-boilerplate/components/ui/select.tsx +0 -149
- package/templates/interface-boilerplate/components/ui/separator.tsx +0 -25
- package/templates/interface-boilerplate/components/ui/skeleton.tsx +0 -12
- package/templates/interface-boilerplate/components/ui/switch.tsx +0 -28
- package/templates/interface-boilerplate/components/ui/tabs.tsx +0 -54
- package/templates/interface-boilerplate/components/ui/textarea.tsx +0 -22
- package/templates/interface-boilerplate/components/ui/tooltip.tsx +0 -29
- package/templates/interface-boilerplate/lib/analytics.ts +0 -182
- package/templates/interface-boilerplate/lib/auth-context.tsx +0 -83
- package/templates/interface-boilerplate/lib/auth.ts +0 -199
- package/templates/interface-boilerplate/lib/callFlow.ts +0 -234
- package/templates/interface-boilerplate/lib/flowTracer.ts +0 -195
- package/templates/interface-boilerplate/lib/hooks/.gitkeep +0 -0
- package/templates/interface-boilerplate/lib/stores/.gitkeep +0 -0
- package/templates/interface-boilerplate/next.config.js +0 -6
- package/templates/interface-boilerplate/postcss.config.js +0 -6
- package/templates/interface-boilerplate/tailwind.config.js +0 -103
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
5
|
-
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
|
|
6
|
-
import { cn } from '@/lib/utils';
|
|
7
|
-
|
|
8
|
-
const Select = SelectPrimitive.Root;
|
|
9
|
-
const SelectGroup = SelectPrimitive.Group;
|
|
10
|
-
const SelectValue = SelectPrimitive.Value;
|
|
11
|
-
|
|
12
|
-
const SelectTrigger = React.forwardRef<
|
|
13
|
-
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
14
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
15
|
-
>(({ className, children, ...props }, ref) => (
|
|
16
|
-
<SelectPrimitive.Trigger
|
|
17
|
-
ref={ref}
|
|
18
|
-
className={cn(
|
|
19
|
-
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
|
20
|
-
className
|
|
21
|
-
)}
|
|
22
|
-
{...props}
|
|
23
|
-
>
|
|
24
|
-
{children}
|
|
25
|
-
<SelectPrimitive.Icon asChild>
|
|
26
|
-
<ChevronDown className="h-4 w-4 opacity-50" />
|
|
27
|
-
</SelectPrimitive.Icon>
|
|
28
|
-
</SelectPrimitive.Trigger>
|
|
29
|
-
));
|
|
30
|
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
31
|
-
|
|
32
|
-
const SelectScrollUpButton = React.forwardRef<
|
|
33
|
-
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
34
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
|
35
|
-
>(({ className, ...props }, ref) => (
|
|
36
|
-
<SelectPrimitive.ScrollUpButton
|
|
37
|
-
ref={ref}
|
|
38
|
-
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
|
39
|
-
{...props}
|
|
40
|
-
>
|
|
41
|
-
<ChevronUp className="h-4 w-4" />
|
|
42
|
-
</SelectPrimitive.ScrollUpButton>
|
|
43
|
-
));
|
|
44
|
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
45
|
-
|
|
46
|
-
const SelectScrollDownButton = React.forwardRef<
|
|
47
|
-
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
48
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
|
49
|
-
>(({ className, ...props }, ref) => (
|
|
50
|
-
<SelectPrimitive.ScrollDownButton
|
|
51
|
-
ref={ref}
|
|
52
|
-
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
|
53
|
-
{...props}
|
|
54
|
-
>
|
|
55
|
-
<ChevronDown className="h-4 w-4" />
|
|
56
|
-
</SelectPrimitive.ScrollDownButton>
|
|
57
|
-
));
|
|
58
|
-
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
59
|
-
|
|
60
|
-
const SelectContent = React.forwardRef<
|
|
61
|
-
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
62
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
63
|
-
>(({ className, children, position = 'popper', ...props }, ref) => (
|
|
64
|
-
<SelectPrimitive.Portal>
|
|
65
|
-
<SelectPrimitive.Content
|
|
66
|
-
ref={ref}
|
|
67
|
-
className={cn(
|
|
68
|
-
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
69
|
-
position === 'popper' &&
|
|
70
|
-
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
71
|
-
className
|
|
72
|
-
)}
|
|
73
|
-
position={position}
|
|
74
|
-
{...props}
|
|
75
|
-
>
|
|
76
|
-
<SelectScrollUpButton />
|
|
77
|
-
<SelectPrimitive.Viewport
|
|
78
|
-
className={cn(
|
|
79
|
-
'p-1',
|
|
80
|
-
position === 'popper' &&
|
|
81
|
-
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
|
82
|
-
)}
|
|
83
|
-
>
|
|
84
|
-
{children}
|
|
85
|
-
</SelectPrimitive.Viewport>
|
|
86
|
-
<SelectScrollDownButton />
|
|
87
|
-
</SelectPrimitive.Content>
|
|
88
|
-
</SelectPrimitive.Portal>
|
|
89
|
-
));
|
|
90
|
-
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
91
|
-
|
|
92
|
-
const SelectLabel = React.forwardRef<
|
|
93
|
-
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
94
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
95
|
-
>(({ className, ...props }, ref) => (
|
|
96
|
-
<SelectPrimitive.Label
|
|
97
|
-
ref={ref}
|
|
98
|
-
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
|
99
|
-
{...props}
|
|
100
|
-
/>
|
|
101
|
-
));
|
|
102
|
-
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
103
|
-
|
|
104
|
-
const SelectItem = React.forwardRef<
|
|
105
|
-
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
106
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
107
|
-
>(({ className, children, ...props }, ref) => (
|
|
108
|
-
<SelectPrimitive.Item
|
|
109
|
-
ref={ref}
|
|
110
|
-
className={cn(
|
|
111
|
-
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
112
|
-
className
|
|
113
|
-
)}
|
|
114
|
-
{...props}
|
|
115
|
-
>
|
|
116
|
-
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
117
|
-
<SelectPrimitive.ItemIndicator>
|
|
118
|
-
<Check className="h-4 w-4" />
|
|
119
|
-
</SelectPrimitive.ItemIndicator>
|
|
120
|
-
</span>
|
|
121
|
-
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
122
|
-
</SelectPrimitive.Item>
|
|
123
|
-
));
|
|
124
|
-
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
125
|
-
|
|
126
|
-
const SelectSeparator = React.forwardRef<
|
|
127
|
-
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
128
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
129
|
-
>(({ className, ...props }, ref) => (
|
|
130
|
-
<SelectPrimitive.Separator
|
|
131
|
-
ref={ref}
|
|
132
|
-
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
|
133
|
-
{...props}
|
|
134
|
-
/>
|
|
135
|
-
));
|
|
136
|
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
137
|
-
|
|
138
|
-
export {
|
|
139
|
-
Select,
|
|
140
|
-
SelectGroup,
|
|
141
|
-
SelectValue,
|
|
142
|
-
SelectTrigger,
|
|
143
|
-
SelectContent,
|
|
144
|
-
SelectLabel,
|
|
145
|
-
SelectItem,
|
|
146
|
-
SelectSeparator,
|
|
147
|
-
SelectScrollUpButton,
|
|
148
|
-
SelectScrollDownButton,
|
|
149
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
|
5
|
-
import { cn } from '@/lib/utils';
|
|
6
|
-
|
|
7
|
-
const Separator = React.forwardRef<
|
|
8
|
-
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
9
|
-
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
10
|
-
>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
|
|
11
|
-
<SeparatorPrimitive.Root
|
|
12
|
-
ref={ref}
|
|
13
|
-
decorative={decorative}
|
|
14
|
-
orientation={orientation}
|
|
15
|
-
className={cn(
|
|
16
|
-
'shrink-0 bg-border',
|
|
17
|
-
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
|
18
|
-
className
|
|
19
|
-
)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
));
|
|
23
|
-
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
24
|
-
|
|
25
|
-
export { Separator };
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { cn } from '@/lib/utils';
|
|
2
|
-
|
|
3
|
-
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
4
|
-
return (
|
|
5
|
-
<div
|
|
6
|
-
className={cn('animate-pulse rounded-md bg-muted', className)}
|
|
7
|
-
{...props}
|
|
8
|
-
/>
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export { Skeleton };
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
5
|
-
import { cn } from '@/lib/utils';
|
|
6
|
-
|
|
7
|
-
const Switch = React.forwardRef<
|
|
8
|
-
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
9
|
-
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
|
10
|
-
>(({ className, ...props }, ref) => (
|
|
11
|
-
<SwitchPrimitives.Root
|
|
12
|
-
className={cn(
|
|
13
|
-
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
|
14
|
-
className
|
|
15
|
-
)}
|
|
16
|
-
{...props}
|
|
17
|
-
ref={ref}
|
|
18
|
-
>
|
|
19
|
-
<SwitchPrimitives.Thumb
|
|
20
|
-
className={cn(
|
|
21
|
-
'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0'
|
|
22
|
-
)}
|
|
23
|
-
/>
|
|
24
|
-
</SwitchPrimitives.Root>
|
|
25
|
-
));
|
|
26
|
-
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
27
|
-
|
|
28
|
-
export { Switch };
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
5
|
-
import { cn } from '@/lib/utils';
|
|
6
|
-
|
|
7
|
-
const Tabs = TabsPrimitive.Root;
|
|
8
|
-
|
|
9
|
-
const TabsList = React.forwardRef<
|
|
10
|
-
React.ElementRef<typeof TabsPrimitive.List>,
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
12
|
-
>(({ className, ...props }, ref) => (
|
|
13
|
-
<TabsPrimitive.List
|
|
14
|
-
ref={ref}
|
|
15
|
-
className={cn(
|
|
16
|
-
'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',
|
|
17
|
-
className
|
|
18
|
-
)}
|
|
19
|
-
{...props}
|
|
20
|
-
/>
|
|
21
|
-
));
|
|
22
|
-
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
23
|
-
|
|
24
|
-
const TabsTrigger = React.forwardRef<
|
|
25
|
-
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
26
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
27
|
-
>(({ className, ...props }, ref) => (
|
|
28
|
-
<TabsPrimitive.Trigger
|
|
29
|
-
ref={ref}
|
|
30
|
-
className={cn(
|
|
31
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
|
|
32
|
-
className
|
|
33
|
-
)}
|
|
34
|
-
{...props}
|
|
35
|
-
/>
|
|
36
|
-
));
|
|
37
|
-
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
38
|
-
|
|
39
|
-
const TabsContent = React.forwardRef<
|
|
40
|
-
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
41
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
42
|
-
>(({ className, ...props }, ref) => (
|
|
43
|
-
<TabsPrimitive.Content
|
|
44
|
-
ref={ref}
|
|
45
|
-
className={cn(
|
|
46
|
-
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
47
|
-
className
|
|
48
|
-
)}
|
|
49
|
-
{...props}
|
|
50
|
-
/>
|
|
51
|
-
));
|
|
52
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
53
|
-
|
|
54
|
-
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { cn } from '@/lib/utils';
|
|
3
|
-
|
|
4
|
-
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
5
|
-
|
|
6
|
-
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
7
|
-
({ className, ...props }, ref) => {
|
|
8
|
-
return (
|
|
9
|
-
<textarea
|
|
10
|
-
className={cn(
|
|
11
|
-
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
12
|
-
className
|
|
13
|
-
)}
|
|
14
|
-
ref={ref}
|
|
15
|
-
{...props}
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
);
|
|
20
|
-
Textarea.displayName = 'Textarea';
|
|
21
|
-
|
|
22
|
-
export { Textarea };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as React from 'react';
|
|
4
|
-
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
5
|
-
import { cn } from '@/lib/utils';
|
|
6
|
-
|
|
7
|
-
const TooltipProvider = TooltipPrimitive.Provider;
|
|
8
|
-
|
|
9
|
-
const Tooltip = TooltipPrimitive.Root;
|
|
10
|
-
|
|
11
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
12
|
-
|
|
13
|
-
const TooltipContent = React.forwardRef<
|
|
14
|
-
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
15
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
16
|
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
17
|
-
<TooltipPrimitive.Content
|
|
18
|
-
ref={ref}
|
|
19
|
-
sideOffset={sideOffset}
|
|
20
|
-
className={cn(
|
|
21
|
-
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
22
|
-
className
|
|
23
|
-
)}
|
|
24
|
-
{...props}
|
|
25
|
-
/>
|
|
26
|
-
));
|
|
27
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
28
|
-
|
|
29
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PostHog Analytics Helper
|
|
3
|
-
*
|
|
4
|
-
* Initializes PostHog for tracking user behavior in production interfaces.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* 1. Wrap your app with <AnalyticsProvider> (in layout.tsx)
|
|
8
|
-
* 2. Use trackEvent() for custom events
|
|
9
|
-
* 3. Use identifyUser() when user logs in
|
|
10
|
-
* 4. Use resetUser() when user logs out
|
|
11
|
-
*
|
|
12
|
-
* A/B Testing (Feature Flags):
|
|
13
|
-
* 1. Create experiments in PostHog dashboard
|
|
14
|
-
* 2. Use useFeatureFlagVariantKey() hook in client components
|
|
15
|
-
* 3. Use getFeatureFlag() for server-side or non-React code
|
|
16
|
-
*
|
|
17
|
-
* @see AnalyticsProvider component for React context setup
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import posthog from 'posthog-js';
|
|
21
|
-
|
|
22
|
-
const POSTHOG_KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY || '';
|
|
23
|
-
const POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com';
|
|
24
|
-
const INTERFACE_ID = process.env.NEXT_PUBLIC_LUX_INTERFACE_ID || '';
|
|
25
|
-
|
|
26
|
-
let initialized = false;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Initialize PostHog analytics
|
|
30
|
-
* NOTE: Prefer using <AnalyticsProvider> instead of calling this directly.
|
|
31
|
-
* This function is kept for backward compatibility and non-React contexts.
|
|
32
|
-
*/
|
|
33
|
-
export function initAnalytics(): void {
|
|
34
|
-
if (typeof window === 'undefined') return;
|
|
35
|
-
if (initialized) return;
|
|
36
|
-
|
|
37
|
-
if (!POSTHOG_KEY) {
|
|
38
|
-
console.warn('[analytics] PostHog not configured - missing POSTHOG_KEY');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Initialize PostHog with standard configuration
|
|
43
|
-
posthog.init(POSTHOG_KEY, {
|
|
44
|
-
api_host: POSTHOG_HOST,
|
|
45
|
-
autocapture: true,
|
|
46
|
-
capture_pageview: true,
|
|
47
|
-
capture_pageleave: true,
|
|
48
|
-
persistence: 'localStorage',
|
|
49
|
-
loaded: (ph) => {
|
|
50
|
-
// Register super properties that will be sent with every event
|
|
51
|
-
if (INTERFACE_ID) {
|
|
52
|
-
ph.register({
|
|
53
|
-
$lux_interface_id: INTERFACE_ID,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
initialized = true;
|
|
60
|
-
console.log('[analytics] PostHog initialized');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Track a custom event
|
|
65
|
-
* @param name - Event name (e.g., 'button_clicked', 'form_submitted')
|
|
66
|
-
* @param properties - Optional event properties
|
|
67
|
-
*/
|
|
68
|
-
export function trackEvent(name: string, properties?: Record<string, any>): void {
|
|
69
|
-
if (!initialized && !posthog.__loaded) return;
|
|
70
|
-
posthog.capture(name, properties);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Identify a user (call when user logs in)
|
|
75
|
-
* @param userId - Unique user identifier
|
|
76
|
-
* @param properties - Optional user properties (email, name, etc.)
|
|
77
|
-
*/
|
|
78
|
-
export function identifyUser(userId: string, properties?: Record<string, any>): void {
|
|
79
|
-
if (!initialized && !posthog.__loaded) return;
|
|
80
|
-
posthog.identify(userId, properties);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Reset user identity (call when user logs out)
|
|
85
|
-
*/
|
|
86
|
-
export function resetUser(): void {
|
|
87
|
-
if (!initialized && !posthog.__loaded) return;
|
|
88
|
-
posthog.reset();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Check if analytics is initialized
|
|
93
|
-
*/
|
|
94
|
-
export function isAnalyticsInitialized(): boolean {
|
|
95
|
-
return initialized || posthog.__loaded;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ============================================================================
|
|
99
|
-
// A/B Testing & Feature Flags
|
|
100
|
-
// ============================================================================
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Get a feature flag value (for non-React contexts or server-side checks)
|
|
104
|
-
*
|
|
105
|
-
* For React components, prefer using the useFeatureFlagVariantKey hook:
|
|
106
|
-
* ```tsx
|
|
107
|
-
* import { useFeatureFlagVariantKey } from 'posthog-js/react';
|
|
108
|
-
* const variant = useFeatureFlagVariantKey('my-experiment');
|
|
109
|
-
* ```
|
|
110
|
-
*
|
|
111
|
-
* @param flagKey - The feature flag key from PostHog
|
|
112
|
-
* @returns The variant key (e.g., 'control', 'test') or undefined if not loaded
|
|
113
|
-
*/
|
|
114
|
-
export function getFeatureFlag(flagKey: string): string | boolean | undefined {
|
|
115
|
-
if (!posthog.__loaded) return undefined;
|
|
116
|
-
return posthog.getFeatureFlag(flagKey);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Check if a feature flag is enabled (boolean flags)
|
|
121
|
-
* @param flagKey - The feature flag key from PostHog
|
|
122
|
-
* @returns true if enabled, false otherwise
|
|
123
|
-
*/
|
|
124
|
-
export function isFeatureEnabled(flagKey: string): boolean {
|
|
125
|
-
if (!posthog.__loaded) return false;
|
|
126
|
-
return posthog.isFeatureEnabled(flagKey) ?? false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Get the payload associated with a feature flag
|
|
131
|
-
* Useful for passing configuration data with experiments
|
|
132
|
-
*
|
|
133
|
-
* @param flagKey - The feature flag key from PostHog
|
|
134
|
-
* @returns The payload object or undefined
|
|
135
|
-
*/
|
|
136
|
-
export function getFeatureFlagPayload(flagKey: string): Record<string, any> | undefined {
|
|
137
|
-
if (!posthog.__loaded) return undefined;
|
|
138
|
-
return posthog.getFeatureFlagPayload(flagKey) as Record<string, any> | undefined;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Manually reload feature flags
|
|
143
|
-
* Useful after user identification or when you need fresh flag values
|
|
144
|
-
*/
|
|
145
|
-
export function reloadFeatureFlags(): void {
|
|
146
|
-
if (!posthog.__loaded) return;
|
|
147
|
-
posthog.reloadFeatureFlags();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Register a callback for when feature flags are loaded
|
|
152
|
-
* @param callback - Function to call when flags are ready
|
|
153
|
-
*/
|
|
154
|
-
export function onFeatureFlagsLoaded(callback: () => void): void {
|
|
155
|
-
if (!posthog.__loaded) return;
|
|
156
|
-
posthog.onFeatureFlags(callback);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Track a conversion event for an A/B test
|
|
161
|
-
* This helps PostHog calculate experiment results
|
|
162
|
-
*
|
|
163
|
-
* @param experimentKey - The experiment/feature flag key
|
|
164
|
-
* @param eventName - The conversion event name (e.g., 'signup_completed')
|
|
165
|
-
* @param properties - Optional additional properties
|
|
166
|
-
*/
|
|
167
|
-
export function trackExperimentConversion(
|
|
168
|
-
experimentKey: string,
|
|
169
|
-
eventName: string,
|
|
170
|
-
properties?: Record<string, any>
|
|
171
|
-
): void {
|
|
172
|
-
if (!posthog.__loaded) return;
|
|
173
|
-
|
|
174
|
-
// Get the current variant for attribution
|
|
175
|
-
const variant = posthog.getFeatureFlag(experimentKey);
|
|
176
|
-
|
|
177
|
-
posthog.capture(eventName, {
|
|
178
|
-
...properties,
|
|
179
|
-
$experiment_key: experimentKey,
|
|
180
|
-
$experiment_variant: variant,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
4
|
-
import { User, AuthSession, getSession, signIn, signUp, signOut, signInWithGoogle } from './auth';
|
|
5
|
-
|
|
6
|
-
interface AuthContextType {
|
|
7
|
-
user: User | null;
|
|
8
|
-
isLoading: boolean;
|
|
9
|
-
isAuthenticated: boolean;
|
|
10
|
-
signIn: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
|
|
11
|
-
signUp: (email: string, password: string, name?: string) => Promise<{ success: boolean; error?: string }>;
|
|
12
|
-
signOut: () => Promise<void>;
|
|
13
|
-
signInWithGoogle: (redirectUri?: string) => void;
|
|
14
|
-
refresh: () => Promise<void>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const AuthContext = createContext<AuthContextType | null>(null);
|
|
18
|
-
|
|
19
|
-
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
20
|
-
const [user, setUser] = useState<User | null>(null);
|
|
21
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
22
|
-
|
|
23
|
-
const refresh = async () => {
|
|
24
|
-
try {
|
|
25
|
-
const session = await getSession();
|
|
26
|
-
setUser(session?.user || null);
|
|
27
|
-
} catch {
|
|
28
|
-
setUser(null);
|
|
29
|
-
} finally {
|
|
30
|
-
setIsLoading(false);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
refresh();
|
|
36
|
-
}, []);
|
|
37
|
-
|
|
38
|
-
const handleSignIn = async (email: string, password: string) => {
|
|
39
|
-
const result = await signIn(email, password);
|
|
40
|
-
if (result.success && result.user) {
|
|
41
|
-
setUser(result.user);
|
|
42
|
-
}
|
|
43
|
-
return result;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const handleSignUp = async (email: string, password: string, name?: string) => {
|
|
47
|
-
const result = await signUp(email, password, name);
|
|
48
|
-
if (result.success && result.user) {
|
|
49
|
-
setUser(result.user);
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const handleSignOut = async () => {
|
|
55
|
-
await signOut();
|
|
56
|
-
setUser(null);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<AuthContext.Provider
|
|
61
|
-
value={{
|
|
62
|
-
user,
|
|
63
|
-
isLoading,
|
|
64
|
-
isAuthenticated: !!user,
|
|
65
|
-
signIn: handleSignIn,
|
|
66
|
-
signUp: handleSignUp,
|
|
67
|
-
signOut: handleSignOut,
|
|
68
|
-
signInWithGoogle,
|
|
69
|
-
refresh,
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
{children}
|
|
73
|
-
</AuthContext.Provider>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function useAuth(): AuthContextType {
|
|
78
|
-
const context = useContext(AuthContext);
|
|
79
|
-
if (!context) {
|
|
80
|
-
throw new Error('useAuth must be used within an AuthProvider');
|
|
81
|
-
}
|
|
82
|
-
return context;
|
|
83
|
-
}
|