rusty-replay 0.0.4
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/.eslintrc.js +10 -0
- package/.vscode/settings.json +3 -0
- package/README.md +92 -0
- package/apps/web/README.md +11 -0
- package/apps/web/api/auth/keys.ts +3 -0
- package/apps/web/api/auth/types.ts +25 -0
- package/apps/web/api/auth/use-query-profile.ts +19 -0
- package/apps/web/api/auth/use-sign-in.ts +24 -0
- package/apps/web/api/axios.ts +122 -0
- package/apps/web/api/error-code.ts +36 -0
- package/apps/web/api/event/keys.ts +14 -0
- package/apps/web/api/event/types.ts +91 -0
- package/apps/web/api/event/use-mutation-event-assignee.ts +103 -0
- package/apps/web/api/event/use-mutation-event-priority.ts +97 -0
- package/apps/web/api/event/use-mutation-event-status.ts +198 -0
- package/apps/web/api/event/use-query-event-detail.ts +25 -0
- package/apps/web/api/event/use-query-event-list.ts +42 -0
- package/apps/web/api/health-check/index.ts +21 -0
- package/apps/web/api/project/keys.ts +4 -0
- package/apps/web/api/project/types.ts +28 -0
- package/apps/web/api/project/use-create-project.ts +30 -0
- package/apps/web/api/project/use-query-project-list.ts +19 -0
- package/apps/web/api/project/use-query-project-users.ts +23 -0
- package/apps/web/api/types.ts +44 -0
- package/apps/web/app/(auth)/layout.tsx +5 -0
- package/apps/web/app/(auth)/sign-in/page.tsx +20 -0
- package/apps/web/app/(auth)/sign-up/page.tsx +5 -0
- package/apps/web/app/(project)/project/[project_id]/issues/[issue_id]/page.tsx +17 -0
- package/apps/web/app/(project)/project/[project_id]/issues/page.tsx +15 -0
- package/apps/web/app/(project)/project/[project_id]/page.tsx +10 -0
- package/apps/web/app/(project)/project/page.tsx +10 -0
- package/apps/web/app/(report)/error-list/page.tsx +7 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +35 -0
- package/apps/web/app/page.tsx +3 -0
- package/apps/web/components/.gitkeep +0 -0
- package/apps/web/components/event-list/event-detail.tsx +242 -0
- package/apps/web/components/event-list/event-list.tsx +376 -0
- package/apps/web/components/event-list/preview.tsx +573 -0
- package/apps/web/components/layouts/default-layout.tsx +59 -0
- package/apps/web/components/login-form.tsx +124 -0
- package/apps/web/components/project/create-project.tsx +130 -0
- package/apps/web/components/project/hooks/use-get-event-params.ts +9 -0
- package/apps/web/components/project/hooks/use-get-project-params.ts +10 -0
- package/apps/web/components/project/project-detail.tsx +240 -0
- package/apps/web/components/project/project-list.tsx +137 -0
- package/apps/web/components/providers.tsx +25 -0
- package/apps/web/components/ui/assignee-dropdown.tsx +176 -0
- package/apps/web/components/ui/event-status-dropdown.tsx +104 -0
- package/apps/web/components/ui/priority-dropdown.tsx +123 -0
- package/apps/web/components/widget/app-sidebar.tsx +225 -0
- package/apps/web/components/widget/nav-main.tsx +73 -0
- package/apps/web/components/widget/nav-projects.tsx +84 -0
- package/apps/web/components/widget/nav-user.tsx +113 -0
- package/apps/web/components.json +20 -0
- package/apps/web/constants/routes.ts +12 -0
- package/apps/web/eslint.config.js +4 -0
- package/apps/web/hooks/use-boolean-state.ts +13 -0
- package/apps/web/lib/.gitkeep +0 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.mjs +6 -0
- package/apps/web/package.json +60 -0
- package/apps/web/postcss.config.mjs +1 -0
- package/apps/web/providers/flag-provider.tsx +35 -0
- package/apps/web/providers/query-client-provider.tsx +17 -0
- package/apps/web/providers/telemetry-provider.tsx +12 -0
- package/apps/web/tsconfig.json +24 -0
- package/apps/web/utils/avatar.ts +26 -0
- package/apps/web/utils/date.ts +26 -0
- package/apps/web/utils/front-end-tracer.ts +119 -0
- package/apps/web/utils/schema/project.schema.ts +12 -0
- package/apps/web/utils/span-processor.ts +36 -0
- package/package.json +21 -0
- package/packages/eslint-config/README.md +3 -0
- package/packages/eslint-config/base.js +32 -0
- package/packages/eslint-config/next.js +51 -0
- package/packages/eslint-config/package.json +25 -0
- package/packages/eslint-config/react-internal.js +41 -0
- package/packages/rusty-replay/README.md +165 -0
- package/packages/rusty-replay/package.json +67 -0
- package/packages/rusty-replay/src/environment.ts +27 -0
- package/packages/rusty-replay/src/error-batcher.ts +75 -0
- package/packages/rusty-replay/src/front-end-tracer.ts +86 -0
- package/packages/rusty-replay/src/handler.ts +37 -0
- package/packages/rusty-replay/src/index.ts +8 -0
- package/packages/rusty-replay/src/recorder.ts +71 -0
- package/packages/rusty-replay/src/reporter.ts +115 -0
- package/packages/rusty-replay/src/utils.ts +13 -0
- package/packages/rusty-replay/tsconfig.build.json +13 -0
- package/packages/rusty-replay/tsconfig.json +27 -0
- package/packages/rusty-replay/tsup.config.ts +39 -0
- package/packages/typescript-config/README.md +3 -0
- package/packages/typescript-config/base.json +20 -0
- package/packages/typescript-config/nextjs.json +13 -0
- package/packages/typescript-config/package.json +9 -0
- package/packages/typescript-config/react-library.json +8 -0
- package/packages/ui/components.json +20 -0
- package/packages/ui/eslint.config.js +4 -0
- package/packages/ui/package.json +60 -0
- package/packages/ui/postcss.config.mjs +6 -0
- package/packages/ui/src/components/.gitkeep +0 -0
- package/packages/ui/src/components/avatar.tsx +53 -0
- package/packages/ui/src/components/badge.tsx +46 -0
- package/packages/ui/src/components/breadcrumb.tsx +109 -0
- package/packages/ui/src/components/button.tsx +59 -0
- package/packages/ui/src/components/calendar.tsx +75 -0
- package/packages/ui/src/components/calendars/date-picker.tsx +43 -0
- package/packages/ui/src/components/calendars/date-range-picker.tsx +79 -0
- package/packages/ui/src/components/card.tsx +92 -0
- package/packages/ui/src/components/checkbox.tsx +32 -0
- package/packages/ui/src/components/collapsible.tsx +33 -0
- package/packages/ui/src/components/dialog.tsx +135 -0
- package/packages/ui/src/components/dialogs/confirmation-modal.tsx +216 -0
- package/packages/ui/src/components/dropdown-menu.tsx +261 -0
- package/packages/ui/src/components/input.tsx +30 -0
- package/packages/ui/src/components/label.tsx +24 -0
- package/packages/ui/src/components/login-form.tsx +68 -0
- package/packages/ui/src/components/mode-switcher.tsx +34 -0
- package/packages/ui/src/components/popover.tsx +48 -0
- package/packages/ui/src/components/scroll-area.tsx +58 -0
- package/packages/ui/src/components/select.tsx +185 -0
- package/packages/ui/src/components/separator.tsx +28 -0
- package/packages/ui/src/components/sheet.tsx +139 -0
- package/packages/ui/src/components/sidebar.tsx +726 -0
- package/packages/ui/src/components/skeleton.tsx +13 -0
- package/packages/ui/src/components/sonner.tsx +25 -0
- package/packages/ui/src/components/table.tsx +116 -0
- package/packages/ui/src/components/tabs.tsx +66 -0
- package/packages/ui/src/components/team-switcher.tsx +91 -0
- package/packages/ui/src/components/textarea.tsx +18 -0
- package/packages/ui/src/components/tooltip.tsx +61 -0
- package/packages/ui/src/hooks/.gitkeep +0 -0
- package/packages/ui/src/hooks/use-meta-color.ts +28 -0
- package/packages/ui/src/hooks/use-mobile.ts +19 -0
- package/packages/ui/src/lib/utils.ts +6 -0
- package/packages/ui/src/styles/globals.css +138 -0
- package/packages/ui/tsconfig.json +13 -0
- package/packages/ui/tsconfig.lint.json +8 -0
- package/pnpm-workspace.yaml +4 -0
- package/tsconfig.json +4 -0
- package/turbo.json +21 -0
@@ -0,0 +1,216 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import * as React from 'react';
|
4
|
+
import { XIcon } from 'lucide-react';
|
5
|
+
|
6
|
+
import { cn } from '@workspace/ui/lib/utils';
|
7
|
+
import { Button } from '../button';
|
8
|
+
import {
|
9
|
+
Dialog,
|
10
|
+
DialogClose,
|
11
|
+
DialogContent,
|
12
|
+
DialogFooter,
|
13
|
+
DialogHeader,
|
14
|
+
DialogTitle,
|
15
|
+
} from '../dialog';
|
16
|
+
|
17
|
+
export interface ConfirmationModalProps {
|
18
|
+
/**
|
19
|
+
* Whether the modal is visible
|
20
|
+
*/
|
21
|
+
visible: boolean;
|
22
|
+
/**
|
23
|
+
* The title of the modal
|
24
|
+
*/
|
25
|
+
title: string | React.ReactNode;
|
26
|
+
/**
|
27
|
+
* The size of the modal
|
28
|
+
*/
|
29
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
30
|
+
/**
|
31
|
+
* The label for the confirm button
|
32
|
+
*/
|
33
|
+
confirmLabel?: string;
|
34
|
+
/**
|
35
|
+
* The label for the confirm button when loading
|
36
|
+
*/
|
37
|
+
confirmLabelLoading?: string;
|
38
|
+
/**
|
39
|
+
* The label for the cancel button
|
40
|
+
*/
|
41
|
+
cancelLabel?: string;
|
42
|
+
/**
|
43
|
+
* Callback when the confirm button is clicked
|
44
|
+
*/
|
45
|
+
onConfirm: () => void;
|
46
|
+
/**
|
47
|
+
* Callback when the modal is closed or cancel button is clicked
|
48
|
+
*/
|
49
|
+
onCancel: () => void;
|
50
|
+
/**
|
51
|
+
* Whether the modal is in a loading state
|
52
|
+
*/
|
53
|
+
loading?: boolean;
|
54
|
+
/**
|
55
|
+
* Whether the confirm button is disabled
|
56
|
+
*/
|
57
|
+
disabled?: boolean;
|
58
|
+
/**
|
59
|
+
* The variant of the confirm button
|
60
|
+
*/
|
61
|
+
variant?: 'default' | 'destructive' | 'warning';
|
62
|
+
/**
|
63
|
+
* Alert configuration
|
64
|
+
*/
|
65
|
+
alert?: {
|
66
|
+
title?: string;
|
67
|
+
description?: string | React.ReactNode;
|
68
|
+
className?: string;
|
69
|
+
};
|
70
|
+
/**
|
71
|
+
* Whether to hide the close button
|
72
|
+
*/
|
73
|
+
hideClose?: boolean;
|
74
|
+
/**
|
75
|
+
* Children to render in the modal body
|
76
|
+
*/
|
77
|
+
children?: React.ReactNode;
|
78
|
+
/**
|
79
|
+
* Additional class names for the modal content
|
80
|
+
*/
|
81
|
+
className?: string;
|
82
|
+
}
|
83
|
+
|
84
|
+
const sizeMap = {
|
85
|
+
sm: 'sm:max-w-sm',
|
86
|
+
md: 'sm:max-w-md',
|
87
|
+
lg: 'sm:max-w-lg',
|
88
|
+
xl: 'sm:max-w-xl',
|
89
|
+
full: 'sm:max-w-[calc(100%-4rem)]',
|
90
|
+
};
|
91
|
+
|
92
|
+
const buttonVariantMap = {
|
93
|
+
default: 'default',
|
94
|
+
destructive: 'destructive',
|
95
|
+
warning: 'warning',
|
96
|
+
};
|
97
|
+
|
98
|
+
const ConfirmationModal = React.forwardRef<
|
99
|
+
React.ElementRef<typeof DialogContent>,
|
100
|
+
ConfirmationModalProps
|
101
|
+
>(
|
102
|
+
(
|
103
|
+
{
|
104
|
+
title,
|
105
|
+
size = 'sm',
|
106
|
+
visible,
|
107
|
+
onCancel,
|
108
|
+
onConfirm,
|
109
|
+
loading = false,
|
110
|
+
cancelLabel = 'Cancel',
|
111
|
+
confirmLabel = 'Submit',
|
112
|
+
confirmLabelLoading,
|
113
|
+
alert,
|
114
|
+
children,
|
115
|
+
variant = 'default',
|
116
|
+
disabled,
|
117
|
+
hideClose = false,
|
118
|
+
className,
|
119
|
+
...props
|
120
|
+
},
|
121
|
+
ref
|
122
|
+
) => {
|
123
|
+
// Handle the button click
|
124
|
+
const handleConfirm: React.MouseEventHandler<HTMLButtonElement> = (e) => {
|
125
|
+
e.preventDefault();
|
126
|
+
e.stopPropagation();
|
127
|
+
onConfirm();
|
128
|
+
};
|
129
|
+
|
130
|
+
return (
|
131
|
+
<Dialog
|
132
|
+
open={visible}
|
133
|
+
onOpenChange={(open) => {
|
134
|
+
if (!open && visible) {
|
135
|
+
onCancel();
|
136
|
+
}
|
137
|
+
}}
|
138
|
+
>
|
139
|
+
<DialogContent
|
140
|
+
ref={ref}
|
141
|
+
className={cn(
|
142
|
+
'p-0 gap-0 block overflow-hidden',
|
143
|
+
sizeMap[size],
|
144
|
+
className
|
145
|
+
)}
|
146
|
+
{...props}
|
147
|
+
>
|
148
|
+
{!hideClose && (
|
149
|
+
<DialogClose className="absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
|
150
|
+
<XIcon className="h-4 w-4" />
|
151
|
+
<span className="sr-only">Close</span>
|
152
|
+
</DialogClose>
|
153
|
+
)}
|
154
|
+
|
155
|
+
<DialogHeader className="border-b p-4">
|
156
|
+
<DialogTitle className="text-lg font-semibold">{title}</DialogTitle>
|
157
|
+
</DialogHeader>
|
158
|
+
|
159
|
+
{alert && (
|
160
|
+
<div
|
161
|
+
className={cn(
|
162
|
+
'p-4 border-b',
|
163
|
+
{
|
164
|
+
'bg-red-50 text-red-900': variant === 'destructive',
|
165
|
+
'bg-yellow-50 text-yellow-900': variant === 'warning',
|
166
|
+
'bg-blue-50 text-blue-900': variant === 'default',
|
167
|
+
},
|
168
|
+
alert.className
|
169
|
+
)}
|
170
|
+
>
|
171
|
+
{alert.title && <div className="font-medium">{alert.title}</div>}
|
172
|
+
{alert.description && (
|
173
|
+
<div className="text-sm mt-1">{alert.description}</div>
|
174
|
+
)}
|
175
|
+
</div>
|
176
|
+
)}
|
177
|
+
|
178
|
+
{children && <div className="p-4">{children}</div>}
|
179
|
+
|
180
|
+
<DialogFooter className="flex justify-end gap-2 border-t p-4">
|
181
|
+
<Button
|
182
|
+
variant="outline"
|
183
|
+
size="sm"
|
184
|
+
onClick={onCancel}
|
185
|
+
disabled={loading}
|
186
|
+
>
|
187
|
+
{cancelLabel}
|
188
|
+
</Button>
|
189
|
+
<Button
|
190
|
+
variant={
|
191
|
+
buttonVariantMap[variant] as
|
192
|
+
| 'default'
|
193
|
+
| 'destructive'
|
194
|
+
| 'secondary'
|
195
|
+
| 'outline'
|
196
|
+
| 'ghost'
|
197
|
+
| 'link'
|
198
|
+
| undefined
|
199
|
+
}
|
200
|
+
size="sm"
|
201
|
+
onClick={handleConfirm}
|
202
|
+
disabled={disabled || loading}
|
203
|
+
className="truncate"
|
204
|
+
>
|
205
|
+
{loading ? confirmLabelLoading || confirmLabel : confirmLabel}
|
206
|
+
</Button>
|
207
|
+
</DialogFooter>
|
208
|
+
</DialogContent>
|
209
|
+
</Dialog>
|
210
|
+
);
|
211
|
+
}
|
212
|
+
);
|
213
|
+
|
214
|
+
ConfirmationModal.displayName = 'ConfirmationModal';
|
215
|
+
|
216
|
+
export { ConfirmationModal };
|
@@ -0,0 +1,261 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import * as React from 'react';
|
4
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
5
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
6
|
+
|
7
|
+
import { cn } from '@workspace/ui/lib/utils';
|
8
|
+
|
9
|
+
function DropdownMenu({
|
10
|
+
...props
|
11
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
12
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
13
|
+
}
|
14
|
+
|
15
|
+
function DropdownMenuPortal({
|
16
|
+
...props
|
17
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
18
|
+
return (
|
19
|
+
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
20
|
+
);
|
21
|
+
}
|
22
|
+
|
23
|
+
function DropdownMenuTrigger({
|
24
|
+
...props
|
25
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
26
|
+
return (
|
27
|
+
<DropdownMenuPrimitive.Trigger
|
28
|
+
data-slot="dropdown-menu-trigger"
|
29
|
+
{...props}
|
30
|
+
/>
|
31
|
+
);
|
32
|
+
}
|
33
|
+
|
34
|
+
function DropdownMenuContent({
|
35
|
+
className,
|
36
|
+
sideOffset = 4,
|
37
|
+
...props
|
38
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
39
|
+
return (
|
40
|
+
<DropdownMenuPrimitive.Portal>
|
41
|
+
<DropdownMenuPrimitive.Content
|
42
|
+
data-slot="dropdown-menu-content"
|
43
|
+
sideOffset={sideOffset}
|
44
|
+
className={cn(
|
45
|
+
'bg-popover text-popover-foreground 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
|
46
|
+
className
|
47
|
+
)}
|
48
|
+
{...props}
|
49
|
+
/>
|
50
|
+
</DropdownMenuPrimitive.Portal>
|
51
|
+
);
|
52
|
+
}
|
53
|
+
|
54
|
+
function DropdownMenuGroup({
|
55
|
+
...props
|
56
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
57
|
+
return (
|
58
|
+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
59
|
+
);
|
60
|
+
}
|
61
|
+
|
62
|
+
function DropdownMenuItem({
|
63
|
+
className,
|
64
|
+
inset,
|
65
|
+
variant = 'default',
|
66
|
+
...props
|
67
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
68
|
+
inset?: boolean;
|
69
|
+
variant?: 'default' | 'destructive';
|
70
|
+
}) {
|
71
|
+
return (
|
72
|
+
<DropdownMenuPrimitive.Item
|
73
|
+
data-slot="dropdown-menu-item"
|
74
|
+
data-inset={inset}
|
75
|
+
data-variant={variant}
|
76
|
+
className={cn(
|
77
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
78
|
+
className
|
79
|
+
)}
|
80
|
+
{...props}
|
81
|
+
/>
|
82
|
+
);
|
83
|
+
}
|
84
|
+
|
85
|
+
function DropdownMenuCheckboxItem({
|
86
|
+
className,
|
87
|
+
children,
|
88
|
+
checked,
|
89
|
+
...props
|
90
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
91
|
+
return (
|
92
|
+
<DropdownMenuPrimitive.CheckboxItem
|
93
|
+
data-slot="dropdown-menu-checkbox-item"
|
94
|
+
className={cn(
|
95
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
96
|
+
className
|
97
|
+
)}
|
98
|
+
checked={checked}
|
99
|
+
{...props}
|
100
|
+
>
|
101
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
102
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
103
|
+
<CheckIcon className="size-4" />
|
104
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
105
|
+
</span>
|
106
|
+
{children}
|
107
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
108
|
+
);
|
109
|
+
}
|
110
|
+
|
111
|
+
function DropdownMenuRadioGroup({
|
112
|
+
...props
|
113
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
114
|
+
return (
|
115
|
+
<DropdownMenuPrimitive.RadioGroup
|
116
|
+
data-slot="dropdown-menu-radio-group"
|
117
|
+
{...props}
|
118
|
+
/>
|
119
|
+
);
|
120
|
+
}
|
121
|
+
|
122
|
+
function DropdownMenuRadioItem({
|
123
|
+
className,
|
124
|
+
children,
|
125
|
+
...props
|
126
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
127
|
+
return (
|
128
|
+
<DropdownMenuPrimitive.RadioItem
|
129
|
+
data-slot="dropdown-menu-radio-item"
|
130
|
+
className={cn(
|
131
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
132
|
+
className
|
133
|
+
)}
|
134
|
+
{...props}
|
135
|
+
>
|
136
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
137
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
138
|
+
<CircleIcon className="size-2 fill-current" />
|
139
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
140
|
+
</span>
|
141
|
+
{children}
|
142
|
+
</DropdownMenuPrimitive.RadioItem>
|
143
|
+
);
|
144
|
+
}
|
145
|
+
|
146
|
+
function DropdownMenuLabel({
|
147
|
+
className,
|
148
|
+
inset,
|
149
|
+
...props
|
150
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
151
|
+
inset?: boolean;
|
152
|
+
}) {
|
153
|
+
return (
|
154
|
+
<DropdownMenuPrimitive.Label
|
155
|
+
data-slot="dropdown-menu-label"
|
156
|
+
data-inset={inset}
|
157
|
+
className={cn(
|
158
|
+
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
|
159
|
+
className
|
160
|
+
)}
|
161
|
+
{...props}
|
162
|
+
/>
|
163
|
+
);
|
164
|
+
}
|
165
|
+
|
166
|
+
function DropdownMenuSeparator({
|
167
|
+
className,
|
168
|
+
...props
|
169
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
170
|
+
return (
|
171
|
+
<DropdownMenuPrimitive.Separator
|
172
|
+
data-slot="dropdown-menu-separator"
|
173
|
+
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
174
|
+
{...props}
|
175
|
+
/>
|
176
|
+
);
|
177
|
+
}
|
178
|
+
|
179
|
+
function DropdownMenuShortcut({
|
180
|
+
className,
|
181
|
+
...props
|
182
|
+
}: React.ComponentProps<'span'>) {
|
183
|
+
return (
|
184
|
+
<span
|
185
|
+
data-slot="dropdown-menu-shortcut"
|
186
|
+
className={cn(
|
187
|
+
'text-muted-foreground ml-auto text-xs tracking-widest',
|
188
|
+
className
|
189
|
+
)}
|
190
|
+
{...props}
|
191
|
+
/>
|
192
|
+
);
|
193
|
+
}
|
194
|
+
|
195
|
+
function DropdownMenuSub({
|
196
|
+
...props
|
197
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
198
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
199
|
+
}
|
200
|
+
|
201
|
+
function DropdownMenuSubTrigger({
|
202
|
+
className,
|
203
|
+
inset,
|
204
|
+
children,
|
205
|
+
...props
|
206
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
207
|
+
inset?: boolean;
|
208
|
+
}) {
|
209
|
+
return (
|
210
|
+
<DropdownMenuPrimitive.SubTrigger
|
211
|
+
data-slot="dropdown-menu-sub-trigger"
|
212
|
+
data-inset={inset}
|
213
|
+
className={cn(
|
214
|
+
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
215
|
+
className
|
216
|
+
)}
|
217
|
+
{...props}
|
218
|
+
>
|
219
|
+
{children}
|
220
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
221
|
+
</DropdownMenuPrimitive.SubTrigger>
|
222
|
+
);
|
223
|
+
}
|
224
|
+
|
225
|
+
function DropdownMenuSubContent({
|
226
|
+
className,
|
227
|
+
...props
|
228
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
229
|
+
return (
|
230
|
+
<DropdownMenuPrimitive.SubContent
|
231
|
+
data-slot="dropdown-menu-sub-content"
|
232
|
+
className={cn(
|
233
|
+
'bg-popover text-popover-foreground 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 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
|
234
|
+
className
|
235
|
+
)}
|
236
|
+
{...props}
|
237
|
+
/>
|
238
|
+
);
|
239
|
+
}
|
240
|
+
|
241
|
+
import { DropdownMenuCheckboxItemProps } from '@radix-ui/react-dropdown-menu';
|
242
|
+
|
243
|
+
export {
|
244
|
+
DropdownMenu,
|
245
|
+
DropdownMenuPortal,
|
246
|
+
DropdownMenuTrigger,
|
247
|
+
DropdownMenuContent,
|
248
|
+
DropdownMenuGroup,
|
249
|
+
DropdownMenuLabel,
|
250
|
+
DropdownMenuItem,
|
251
|
+
DropdownMenuCheckboxItem,
|
252
|
+
DropdownMenuRadioGroup,
|
253
|
+
DropdownMenuRadioItem,
|
254
|
+
DropdownMenuSeparator,
|
255
|
+
DropdownMenuShortcut,
|
256
|
+
DropdownMenuSub,
|
257
|
+
DropdownMenuSubTrigger,
|
258
|
+
DropdownMenuSubContent,
|
259
|
+
};
|
260
|
+
|
261
|
+
export type { DropdownMenuCheckboxItemProps };
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import * as React from 'react';
|
2
|
+
|
3
|
+
import { cn } from '@workspace/ui/lib/utils';
|
4
|
+
|
5
|
+
interface InputProps extends React.ComponentProps<'input'> {
|
6
|
+
error?: string;
|
7
|
+
}
|
8
|
+
|
9
|
+
function Input({ className, type, error, ...props }: InputProps) {
|
10
|
+
return (
|
11
|
+
<div className="w-full">
|
12
|
+
<input
|
13
|
+
type={type}
|
14
|
+
data-slot="input"
|
15
|
+
className={cn(
|
16
|
+
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
17
|
+
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
18
|
+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
19
|
+
error ? 'border-destructive' : '',
|
20
|
+
className
|
21
|
+
)}
|
22
|
+
aria-invalid={error ? 'true' : 'false'}
|
23
|
+
{...props}
|
24
|
+
/>
|
25
|
+
{error && <p className="text-xs text-destructive mt-1">{error}</p>}
|
26
|
+
</div>
|
27
|
+
);
|
28
|
+
}
|
29
|
+
|
30
|
+
export { Input };
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"use client"
|
2
|
+
|
3
|
+
import * as React from "react"
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
5
|
+
|
6
|
+
import { cn } from "@workspace/ui/lib/utils"
|
7
|
+
|
8
|
+
function Label({
|
9
|
+
className,
|
10
|
+
...props
|
11
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
12
|
+
return (
|
13
|
+
<LabelPrimitive.Root
|
14
|
+
data-slot="label"
|
15
|
+
className={cn(
|
16
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
17
|
+
className
|
18
|
+
)}
|
19
|
+
{...props}
|
20
|
+
/>
|
21
|
+
)
|
22
|
+
}
|
23
|
+
|
24
|
+
export { Label }
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { cn } from '@workspace/ui/lib/utils';
|
2
|
+
import { Button } from '@workspace/ui/components/button';
|
3
|
+
import {
|
4
|
+
Card,
|
5
|
+
CardContent,
|
6
|
+
CardDescription,
|
7
|
+
CardHeader,
|
8
|
+
CardTitle,
|
9
|
+
} from '@workspace/ui/components/card';
|
10
|
+
import { Input } from '@workspace/ui/components/input';
|
11
|
+
import { Label } from '@workspace/ui/components/label';
|
12
|
+
|
13
|
+
export function LoginForm({
|
14
|
+
className,
|
15
|
+
...props
|
16
|
+
}: React.ComponentPropsWithoutRef<'div'>) {
|
17
|
+
return (
|
18
|
+
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
19
|
+
<Card>
|
20
|
+
<CardHeader>
|
21
|
+
<CardTitle className="text-2xl">Login</CardTitle>
|
22
|
+
<CardDescription>
|
23
|
+
Enter your email below to login to your account
|
24
|
+
</CardDescription>
|
25
|
+
</CardHeader>
|
26
|
+
<CardContent>
|
27
|
+
<form>
|
28
|
+
<div className="flex flex-col gap-6">
|
29
|
+
<div className="grid gap-2">
|
30
|
+
<Label htmlFor="email">Email</Label>
|
31
|
+
<Input
|
32
|
+
id="email"
|
33
|
+
type="email"
|
34
|
+
placeholder="m@example.com"
|
35
|
+
required
|
36
|
+
/>
|
37
|
+
</div>
|
38
|
+
<div className="grid gap-2">
|
39
|
+
<div className="flex items-center">
|
40
|
+
<Label htmlFor="password">Password</Label>
|
41
|
+
<a
|
42
|
+
href="#"
|
43
|
+
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
|
44
|
+
>
|
45
|
+
Forgot your password?
|
46
|
+
</a>
|
47
|
+
</div>
|
48
|
+
<Input id="password" type="password" required />
|
49
|
+
</div>
|
50
|
+
<Button type="submit" className="w-full">
|
51
|
+
Login
|
52
|
+
</Button>
|
53
|
+
<Button variant="outline" className="w-full">
|
54
|
+
Login with Google
|
55
|
+
</Button>
|
56
|
+
</div>
|
57
|
+
<div className="mt-4 text-center text-sm">
|
58
|
+
Don't have an account?{' '}
|
59
|
+
<a href="#" className="underline underline-offset-4">
|
60
|
+
Sign up
|
61
|
+
</a>
|
62
|
+
</div>
|
63
|
+
</form>
|
64
|
+
</CardContent>
|
65
|
+
</Card>
|
66
|
+
</div>
|
67
|
+
);
|
68
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import * as React from 'react';
|
4
|
+
import { MoonIcon, SunIcon } from 'lucide-react';
|
5
|
+
import { useTheme } from 'next-themes';
|
6
|
+
import { Button } from './button';
|
7
|
+
import { META_THEME_COLORS, useMetaColor } from '../hooks/use-meta-color';
|
8
|
+
|
9
|
+
export function ModeSwitcher() {
|
10
|
+
const { setTheme, resolvedTheme } = useTheme();
|
11
|
+
const { setMetaColor } = useMetaColor();
|
12
|
+
|
13
|
+
const toggleTheme = React.useCallback(() => {
|
14
|
+
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
|
15
|
+
setMetaColor(
|
16
|
+
resolvedTheme === 'dark'
|
17
|
+
? META_THEME_COLORS.light
|
18
|
+
: META_THEME_COLORS.dark
|
19
|
+
);
|
20
|
+
}, [resolvedTheme, setTheme, setMetaColor]);
|
21
|
+
|
22
|
+
return (
|
23
|
+
<Button
|
24
|
+
variant="outline"
|
25
|
+
size="icon"
|
26
|
+
className="group/toggle size-8"
|
27
|
+
onClick={toggleTheme}
|
28
|
+
>
|
29
|
+
<SunIcon className="hidden [html.dark_&]:block" />
|
30
|
+
<MoonIcon className="hidden [html.light_&]:block" />
|
31
|
+
<span className="sr-only">Toggle theme</span>
|
32
|
+
</Button>
|
33
|
+
);
|
34
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"use client"
|
2
|
+
|
3
|
+
import * as React from "react"
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
5
|
+
|
6
|
+
import { cn } from "@workspace/ui/lib/utils"
|
7
|
+
|
8
|
+
function Popover({
|
9
|
+
...props
|
10
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
11
|
+
return <PopoverPrimitive.Root data-slot="popover" {...props} />
|
12
|
+
}
|
13
|
+
|
14
|
+
function PopoverTrigger({
|
15
|
+
...props
|
16
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
|
17
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
|
18
|
+
}
|
19
|
+
|
20
|
+
function PopoverContent({
|
21
|
+
className,
|
22
|
+
align = "center",
|
23
|
+
sideOffset = 4,
|
24
|
+
...props
|
25
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
26
|
+
return (
|
27
|
+
<PopoverPrimitive.Portal>
|
28
|
+
<PopoverPrimitive.Content
|
29
|
+
data-slot="popover-content"
|
30
|
+
align={align}
|
31
|
+
sideOffset={sideOffset}
|
32
|
+
className={cn(
|
33
|
+
"bg-popover text-popover-foreground 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 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
34
|
+
className
|
35
|
+
)}
|
36
|
+
{...props}
|
37
|
+
/>
|
38
|
+
</PopoverPrimitive.Portal>
|
39
|
+
)
|
40
|
+
}
|
41
|
+
|
42
|
+
function PopoverAnchor({
|
43
|
+
...props
|
44
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
45
|
+
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
|
46
|
+
}
|
47
|
+
|
48
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
|