startx 0.0.1
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/.editorconfig +20 -0
- package/.prettierignore +24 -0
- package/.prettierrc.js +52 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/apps/core-server/.env.example +24 -0
- package/apps/core-server/Dockerfile +61 -0
- package/apps/core-server/eslint.config.mjs +47 -0
- package/apps/core-server/package.json +73 -0
- package/apps/core-server/src/config/custom-type.ts +54 -0
- package/apps/core-server/src/events/index.ts +37 -0
- package/apps/core-server/src/index.ts +19 -0
- package/apps/core-server/src/middlewares/auth-middleware.ts +50 -0
- package/apps/core-server/src/middlewares/cors-middleware.ts +6 -0
- package/apps/core-server/src/middlewares/error-middleware.ts +23 -0
- package/apps/core-server/src/middlewares/logger-middleware.ts +21 -0
- package/apps/core-server/src/middlewares/notfound-middleware.ts +14 -0
- package/apps/core-server/src/middlewares/serve-static.ts +24 -0
- package/apps/core-server/src/routes/files/router.ts +7 -0
- package/apps/core-server/src/routes/server.ts +36 -0
- package/apps/core-server/tsconfig.json +10 -0
- package/apps/core-server/tsdown.config.ts +14 -0
- package/biome.json +62 -0
- package/configs/eslint-config/package.json +60 -0
- package/configs/eslint-config/plugins.d.ts +1 -0
- package/configs/eslint-config/src/configs/base.ts +237 -0
- package/configs/eslint-config/src/configs/frontend.ts +62 -0
- package/configs/eslint-config/src/configs/node.ts +10 -0
- package/configs/eslint-config/src/plugin.ts +25 -0
- package/configs/eslint-config/src/rules/index.ts +30 -0
- package/configs/eslint-config/src/rules/no-argument-spread.test.ts +47 -0
- package/configs/eslint-config/src/rules/no-argument-spread.ts +96 -0
- package/configs/eslint-config/src/rules/no-dynamic-import-template.ts +32 -0
- package/configs/eslint-config/src/rules/no-internal-package-import.ts +40 -0
- package/configs/eslint-config/src/rules/no-interpolation-in-regular-string.ts +32 -0
- package/configs/eslint-config/src/rules/no-json-parse-json-stringify.test.ts +34 -0
- package/configs/eslint-config/src/rules/no-json-parse-json-stringify.ts +49 -0
- package/configs/eslint-config/src/rules/no-plain-errors.ts +50 -0
- package/configs/eslint-config/src/rules/no-skipped-tests.ts +61 -0
- package/configs/eslint-config/src/rules/no-top-level-relative-imports-in-backend-module.ts +27 -0
- package/configs/eslint-config/src/rules/no-type-unsafe-event-emitter.ts +33 -0
- package/configs/eslint-config/src/rules/no-uncaught-json-parse.test.ts +21 -0
- package/configs/eslint-config/src/rules/no-uncaught-json-parse.ts +45 -0
- package/configs/eslint-config/src/rules/no-untyped-config-class-field.ts +26 -0
- package/configs/eslint-config/src/rules/no-unused-param-catch-clause.ts +33 -0
- package/configs/eslint-config/src/rules/no-useless-catch-throw.test.ts +34 -0
- package/configs/eslint-config/src/rules/no-useless-catch-throw.ts +47 -0
- package/configs/eslint-config/src/utils/json.ts +21 -0
- package/configs/eslint-config/tsconfig.json +8 -0
- package/configs/eslint-config/tsdown.config.ts +11 -0
- package/configs/eslint-config/vitest.config.ts +3 -0
- package/configs/tsdown-config/package.json +14 -0
- package/configs/tsdown-config/src/config/tsdown.base.ts +13 -0
- package/configs/typescript-config/package.json +10 -0
- package/configs/typescript-config/tsconfig.common.json +32 -0
- package/configs/typescript-config/tsconfig.frontend.json +14 -0
- package/configs/typescript-config/tsconfig.node.json +9 -0
- package/configs/vitest-config/package.json +25 -0
- package/configs/vitest-config/src/base.ts +34 -0
- package/configs/vitest-config/src/frontend.ts +15 -0
- package/configs/vitest-config/src/node.ts +5 -0
- package/configs/vitest-config/tsconfig.json +7 -0
- package/package.json +47 -0
- package/packages/@repo/constants/eslint.config.mjs +21 -0
- package/packages/@repo/constants/package.json +19 -0
- package/packages/@repo/constants/src/api.ts +1 -0
- package/packages/@repo/constants/src/index.ts +8 -0
- package/packages/@repo/constants/src/time.ts +23 -0
- package/packages/@repo/constants/tsconfig.json +7 -0
- package/packages/@repo/db/eslint.config.mjs +21 -0
- package/packages/@repo/db/package.json +30 -0
- package/packages/@repo/db/src/functions.ts +122 -0
- package/packages/@repo/db/src/index.ts +20 -0
- package/packages/@repo/db/src/schema/common.ts +49 -0
- package/packages/@repo/db/src/schema/index.ts +1 -0
- package/packages/@repo/db/tsconfig.json +13 -0
- package/packages/@repo/lib/eslint.config.mjs +49 -0
- package/packages/@repo/lib/package.json +57 -0
- package/packages/@repo/lib/src/bucket-module/file-storage.ts +49 -0
- package/packages/@repo/lib/src/bucket-module/s3-storage.ts +114 -0
- package/packages/@repo/lib/src/bucket-module/utils.ts +11 -0
- package/packages/@repo/lib/src/command-module.ts +77 -0
- package/packages/@repo/lib/src/constants.ts +3 -0
- package/packages/@repo/lib/src/cookie-module.ts +42 -0
- package/packages/@repo/lib/src/custom-type.ts +54 -0
- package/packages/@repo/lib/src/env.ts +13 -0
- package/packages/@repo/lib/src/error-handlers-module/index.ts +11 -0
- package/packages/@repo/lib/src/file-system/index.ts +90 -0
- package/packages/@repo/lib/src/hashing-module.ts +9 -0
- package/packages/@repo/lib/src/index.ts +27 -0
- package/packages/@repo/lib/src/logger-module/log-config.ts +16 -0
- package/packages/@repo/lib/src/logger-module/logger.ts +78 -0
- package/packages/@repo/lib/src/logger-module/memory-profiler.ts +65 -0
- package/packages/@repo/lib/src/mail-module/api.ts +0 -0
- package/packages/@repo/lib/src/mail-module/mock.ts +8 -0
- package/packages/@repo/lib/src/mail-module/nodemailer.ts +45 -0
- package/packages/@repo/lib/src/notification-module/index.ts +172 -0
- package/packages/@repo/lib/src/notification-module/push-notification.ts +90 -0
- package/packages/@repo/lib/src/oauth2-client.ts +109 -0
- package/packages/@repo/lib/src/otp-module.ts +98 -0
- package/packages/@repo/lib/src/pagination-module.ts +49 -0
- package/packages/@repo/lib/src/token-module.ts +35 -0
- package/packages/@repo/lib/src/user-session.ts +117 -0
- package/packages/@repo/lib/src/utils.ts +42 -0
- package/packages/@repo/lib/src/validation-module.ts +187 -0
- package/packages/@repo/lib/tsconfig.json +7 -0
- package/packages/@repo/mail/package.json +29 -0
- package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +168 -0
- package/packages/@repo/mail/src/index.ts +13 -0
- package/packages/@repo/mail/tsconfig.build.json +14 -0
- package/packages/@repo/mail/tsconfig.json +13 -0
- package/packages/@repo/mail/tsdown.config.ts +9 -0
- package/packages/@repo/redis/eslint.config.mjs +8 -0
- package/packages/@repo/redis/package.json +31 -0
- package/packages/@repo/redis/src/index.ts +2 -0
- package/packages/@repo/redis/src/lib/redis-client.ts +23 -0
- package/packages/@repo/redis/src/lib/redis-module.ts +3 -0
- package/packages/@repo/redis/tsconfig.json +12 -0
- package/packages/ui/components.json +17 -0
- package/packages/ui/eslint.config.mjs +18 -0
- package/packages/ui/package.json +67 -0
- package/packages/ui/postcss.config.mjs +9 -0
- package/packages/ui/src/components/custom/form-wrapper.tsx +551 -0
- package/packages/ui/src/components/custom/grid-component.tsx +23 -0
- package/packages/ui/src/components/custom/hover-tool.tsx +38 -0
- package/packages/ui/src/components/custom/image-picker.tsx +109 -0
- package/packages/ui/src/components/custom/no-content.tsx +37 -0
- package/packages/ui/src/components/custom/page-container.tsx +24 -0
- package/packages/ui/src/components/custom/page-section.tsx +59 -0
- package/packages/ui/src/components/custom/simple-popover.tsx +29 -0
- package/packages/ui/src/components/custom/switch-component.tsx +20 -0
- package/packages/ui/src/components/custom/theme-provider.tsx +74 -0
- package/packages/ui/src/components/custom/typography.tsx +111 -0
- package/packages/ui/src/components/extensions/carousel.tsx +392 -0
- package/packages/ui/src/components/hooks/event/use-click.tsx +39 -0
- package/packages/ui/src/components/hooks/time/useDebounce.tsx +21 -0
- package/packages/ui/src/components/hooks/time/useInterval.tsx +35 -0
- package/packages/ui/src/components/hooks/time/useTimeout.tsx +19 -0
- package/packages/ui/src/components/hooks/time/useTimer.tsx +51 -0
- package/packages/ui/src/components/hooks/use-media-query.tsx +19 -0
- package/packages/ui/src/components/hooks/use-persistent-storage.tsx +52 -0
- package/packages/ui/src/components/hooks/use-update-effect.tsx +13 -0
- package/packages/ui/src/components/hooks/use-window-dimension.tsx +30 -0
- package/packages/ui/src/components/lib/utils.ts +242 -0
- package/packages/ui/src/components/lucide.tsx +3 -0
- package/packages/ui/src/components/sonner.tsx +1 -0
- package/packages/ui/src/components/ui/alert-dialog.tsx +116 -0
- package/packages/ui/src/components/ui/avatar.tsx +53 -0
- package/packages/ui/src/components/ui/badge.tsx +46 -0
- package/packages/ui/src/components/ui/breadcrumb.tsx +109 -0
- package/packages/ui/src/components/ui/button.tsx +96 -0
- package/packages/ui/src/components/ui/card.tsx +92 -0
- package/packages/ui/src/components/ui/carousel.tsx +243 -0
- package/packages/ui/src/components/ui/checkbox.tsx +32 -0
- package/packages/ui/src/components/ui/command.tsx +155 -0
- package/packages/ui/src/components/ui/dialog.tsx +127 -0
- package/packages/ui/src/components/ui/dropdown-menu.tsx +226 -0
- package/packages/ui/src/components/ui/form.tsx +165 -0
- package/packages/ui/src/components/ui/input-otp.tsx +76 -0
- package/packages/ui/src/components/ui/input.tsx +21 -0
- package/packages/ui/src/components/ui/label.tsx +24 -0
- package/packages/ui/src/components/ui/multiple-select.tsx +510 -0
- package/packages/ui/src/components/ui/popover.tsx +42 -0
- package/packages/ui/src/components/ui/select.tsx +170 -0
- package/packages/ui/src/components/ui/separator.tsx +28 -0
- package/packages/ui/src/components/ui/sheet.tsx +130 -0
- package/packages/ui/src/components/ui/skeleton.tsx +13 -0
- package/packages/ui/src/components/ui/spinner.tsx +16 -0
- package/packages/ui/src/components/ui/switch.tsx +28 -0
- package/packages/ui/src/components/ui/table.tsx +116 -0
- package/packages/ui/src/components/ui/tabs.tsx +54 -0
- package/packages/ui/src/components/ui/textarea.tsx +18 -0
- package/packages/ui/src/components/ui/timeline.tsx +118 -0
- package/packages/ui/src/components/ui/tooltip.tsx +30 -0
- package/packages/ui/src/components/util/n-formattor.ts +22 -0
- package/packages/ui/src/components/util/storage.ts +37 -0
- package/packages/ui/src/globals.css +87 -0
- package/packages/ui/tailwind.config.ts +94 -0
- package/packages/ui/tsconfig.json +12 -0
- package/pnpm-workspace.yaml +43 -0
- package/turbo.json +77 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { type ClassValue, clsx } from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
format,
|
|
5
|
+
isToday,
|
|
6
|
+
isYesterday,
|
|
7
|
+
isThisWeek,
|
|
8
|
+
differenceInDays,
|
|
9
|
+
formatDistance,
|
|
10
|
+
parseISO,
|
|
11
|
+
differenceInYears,
|
|
12
|
+
} from 'date-fns';
|
|
13
|
+
import { twMerge } from 'tailwind-merge';
|
|
14
|
+
|
|
15
|
+
import type { Store } from '../util/storage';
|
|
16
|
+
|
|
17
|
+
export function cn(...inputs: ClassValue[]) {
|
|
18
|
+
return twMerge(clsx(inputs));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// function isFileArray(value: any): value is File[] {
|
|
22
|
+
// return Array.isArray(value) && value.every(item => item instanceof File);
|
|
23
|
+
// }
|
|
24
|
+
|
|
25
|
+
export function getFormData(object: {
|
|
26
|
+
[key: string]: undefined | string | number | boolean | Blob | File | object | File[];
|
|
27
|
+
}): FormData {
|
|
28
|
+
const formData = new FormData();
|
|
29
|
+
Object.entries(object).forEach(([key, value]) => {
|
|
30
|
+
if (value instanceof Blob || value instanceof File) {
|
|
31
|
+
formData.append(key, value);
|
|
32
|
+
} else if (value instanceof FileList || (Array.isArray(value) && value[0] instanceof File)) {
|
|
33
|
+
Array.from(value).forEach((file: File) => {
|
|
34
|
+
formData.append(key, file);
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
if (!value) return;
|
|
38
|
+
formData.append(key, JSON.stringify(value));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return formData;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function switchPath(pathname: string, tab: string) {
|
|
45
|
+
const fragments = pathname.split('/').filter((str) => str);
|
|
46
|
+
fragments[2] = tab;
|
|
47
|
+
const path = `/${fragments.join('/')}`;
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getRelativeDate(date?: string | Date) {
|
|
52
|
+
const now = new Date();
|
|
53
|
+
if (!date) return '';
|
|
54
|
+
if (isToday(date)) {
|
|
55
|
+
return format(date, 'h:mm a');
|
|
56
|
+
} else if (isYesterday(date)) {
|
|
57
|
+
return 'yesterday';
|
|
58
|
+
} else if (isThisWeek(date) && differenceInDays(now, date) <= 7) {
|
|
59
|
+
return format(date, 'EEEE'); // EEEE gives the full name of the day like Sunday, Monday
|
|
60
|
+
} else {
|
|
61
|
+
return format(date, 'dd/MM/yy');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getRelativeDateWithTime(date?: string | Date) {
|
|
66
|
+
const now = new Date();
|
|
67
|
+
if (!date) return '';
|
|
68
|
+
return formatDistance(new Date(date), now, { addSuffix: true });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getRelativeProDate(dateString: string) {
|
|
72
|
+
try {
|
|
73
|
+
const date = parseISO(dateString);
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const yearsDifference = differenceInYears(now, date);
|
|
76
|
+
const dateFormat = yearsDifference >= 1 ? 'dd MMM yyyy, h:mm a' : 'dd MMM, h:mm a';
|
|
77
|
+
const formattedDate = format(date, dateFormat);
|
|
78
|
+
return formattedDate;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Error formatting date:', error);
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function isValidId(str: string) {
|
|
85
|
+
const regex = /^[a-f0-9]{24}$/i;
|
|
86
|
+
return regex.test(str);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function getRelativeDateOnly(date?: string | Date) {
|
|
90
|
+
const now = new Date();
|
|
91
|
+
if (!date) return '';
|
|
92
|
+
if (isToday(date)) {
|
|
93
|
+
return 'today';
|
|
94
|
+
} else if (isYesterday(date)) {
|
|
95
|
+
return 'yesterday';
|
|
96
|
+
} else if (isThisWeek(date) && differenceInDays(now, date) <= 7) {
|
|
97
|
+
return format(date, 'EEEE'); // EEEE gives the full name of the day like Sunday, Monday
|
|
98
|
+
} else {
|
|
99
|
+
return format(date, 'dd/MM/yy');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export type SocialMediaUrlMetaData = {
|
|
103
|
+
platform: 'youtube' | 'x' | 'instagram';
|
|
104
|
+
url: string;
|
|
105
|
+
contentId: string;
|
|
106
|
+
};
|
|
107
|
+
export function getUrlMetaData(urlString: string): SocialMediaUrlMetaData | null {
|
|
108
|
+
const url = new URL(urlString);
|
|
109
|
+
let contentId: string;
|
|
110
|
+
let platform: 'youtube' | 'x' | 'instagram';
|
|
111
|
+
switch (url.origin) {
|
|
112
|
+
case 'https://youtu.be':
|
|
113
|
+
platform = 'youtube';
|
|
114
|
+
contentId = url.pathname.replace('/', '');
|
|
115
|
+
break;
|
|
116
|
+
case 'https://www.youtube.com':
|
|
117
|
+
platform = 'youtube';
|
|
118
|
+
contentId = url.searchParams.get('v') as string;
|
|
119
|
+
break;
|
|
120
|
+
case 'https://x.com':
|
|
121
|
+
platform = 'x';
|
|
122
|
+
contentId = url.pathname
|
|
123
|
+
.split('/')
|
|
124
|
+
.filter((str) => str)
|
|
125
|
+
.at(-1) as string;
|
|
126
|
+
break;
|
|
127
|
+
case 'https://www.instagram.com':
|
|
128
|
+
platform = 'instagram';
|
|
129
|
+
contentId = url.pathname
|
|
130
|
+
.split('/')
|
|
131
|
+
.filter((str) => str)
|
|
132
|
+
.at(-1) as string;
|
|
133
|
+
break;
|
|
134
|
+
case 'https://instagram.com':
|
|
135
|
+
platform = 'instagram';
|
|
136
|
+
contentId = url.pathname
|
|
137
|
+
.split('/')
|
|
138
|
+
.filter((str) => str)
|
|
139
|
+
.at(-1) as string;
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
platform,
|
|
146
|
+
url: urlString,
|
|
147
|
+
contentId,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function isValidUrl(url: string) {
|
|
152
|
+
try {
|
|
153
|
+
const origin = new URL(url).origin;
|
|
154
|
+
return true;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function memoize<T extends (...args: any[]) => any>(fn: T): T {
|
|
161
|
+
const cache: { [key: string]: ReturnType<T> } = {};
|
|
162
|
+
|
|
163
|
+
return ((...args: Parameters<T>): ReturnType<T> => {
|
|
164
|
+
const key = JSON.stringify(args);
|
|
165
|
+
if (cache[key] !== undefined) {
|
|
166
|
+
return cache[key];
|
|
167
|
+
} else {
|
|
168
|
+
const result = fn(...args);
|
|
169
|
+
cache[key] = result;
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
}) as T;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function memoizeAsync<T extends (...args: any[]) => Promise<any>>(fn: T): T {
|
|
176
|
+
const cache: { [key: string]: ReturnType<T> } = {};
|
|
177
|
+
|
|
178
|
+
return (async (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
|
179
|
+
const key = JSON.stringify(args);
|
|
180
|
+
if (cache[key] !== undefined) {
|
|
181
|
+
return await cache[key];
|
|
182
|
+
} else {
|
|
183
|
+
const result = await fn(...args);
|
|
184
|
+
cache[key] = result;
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
}) as T;
|
|
188
|
+
}
|
|
189
|
+
export function memoizePersistentAsync<T extends (...args: any[]) => Promise<any>>(
|
|
190
|
+
fn: T,
|
|
191
|
+
store: Store,
|
|
192
|
+
): T {
|
|
193
|
+
return (async (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
|
194
|
+
const key = JSON.stringify(args);
|
|
195
|
+
const cachedValue = store.get(key);
|
|
196
|
+
if (cachedValue !== null) {
|
|
197
|
+
try {
|
|
198
|
+
return JSON.parse(cachedValue);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Error parsing cached value:', error);
|
|
201
|
+
return await fn(...args);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
const result = await fn(...args);
|
|
205
|
+
store.set(key, JSON.stringify(result));
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
}) as T;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function formatNumberWithKMB(num: number): string {
|
|
212
|
+
const formatter = new Intl.NumberFormat('en-US', {
|
|
213
|
+
notation: 'compact',
|
|
214
|
+
minimumSignificantDigits: 1,
|
|
215
|
+
maximumSignificantDigits: 3,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return formatter.format(num);
|
|
219
|
+
}
|
|
220
|
+
export function currencyToNumber(formattedCurrency: string): number {
|
|
221
|
+
// Remove everything except digits, minus sign, and decimal point
|
|
222
|
+
const numericString = formattedCurrency.replace(/[^0-9.-]+/g, '');
|
|
223
|
+
|
|
224
|
+
// Convert the string back to a number
|
|
225
|
+
const parsedNumber = Number.parseFloat(numericString);
|
|
226
|
+
|
|
227
|
+
// Handle potential NaN if parsing fails
|
|
228
|
+
if (isNaN(parsedNumber)) {
|
|
229
|
+
throw new Error('Invalid currency format');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return parsedNumber;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function createLabels(e: string) {
|
|
236
|
+
return e
|
|
237
|
+
.replaceAll('_', ' ')
|
|
238
|
+
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
239
|
+
.trim();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export { cva, type VariantProps } from 'class-variance-authority';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "sonner";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { buttonVariants } from "@/components/ui/button";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
const AlertDialog = AlertDialogPrimitive.Root;
|
|
9
|
+
|
|
10
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
11
|
+
|
|
12
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
13
|
+
|
|
14
|
+
const AlertDialogOverlay = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
17
|
+
>(({ className, ...props }, ref) => (
|
|
18
|
+
<AlertDialogPrimitive.Overlay
|
|
19
|
+
className={cn(
|
|
20
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
ref={ref}
|
|
25
|
+
/>
|
|
26
|
+
));
|
|
27
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
28
|
+
|
|
29
|
+
const AlertDialogContent = React.forwardRef<
|
|
30
|
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
31
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
|
32
|
+
>(({ className, ...props }, ref) => (
|
|
33
|
+
<AlertDialogPortal>
|
|
34
|
+
<AlertDialogOverlay />
|
|
35
|
+
<AlertDialogPrimitive.Content
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(
|
|
38
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
</AlertDialogPortal>
|
|
44
|
+
));
|
|
45
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
46
|
+
|
|
47
|
+
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
48
|
+
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
|
49
|
+
);
|
|
50
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
51
|
+
|
|
52
|
+
const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
53
|
+
<div
|
|
54
|
+
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
59
|
+
|
|
60
|
+
const AlertDialogTitle = React.forwardRef<
|
|
61
|
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
62
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
63
|
+
>(({ className, ...props }, ref) => (
|
|
64
|
+
<AlertDialogPrimitive.Title
|
|
65
|
+
ref={ref}
|
|
66
|
+
className={cn("text-lg font-semibold", className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
));
|
|
70
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
71
|
+
|
|
72
|
+
const AlertDialogDescription = React.forwardRef<
|
|
73
|
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
74
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
75
|
+
>(({ className, ...props }, ref) => (
|
|
76
|
+
<AlertDialogPrimitive.Description
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
));
|
|
82
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
83
|
+
|
|
84
|
+
const AlertDialogAction = React.forwardRef<
|
|
85
|
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
86
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
|
87
|
+
>(({ className, ...props }, ref) => (
|
|
88
|
+
<AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
|
|
89
|
+
));
|
|
90
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
91
|
+
|
|
92
|
+
const AlertDialogCancel = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
94
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
95
|
+
>(({ className, ...props }, ref) => (
|
|
96
|
+
<AlertDialogPrimitive.Cancel
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
));
|
|
102
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
AlertDialog,
|
|
106
|
+
AlertDialogPortal,
|
|
107
|
+
AlertDialogOverlay,
|
|
108
|
+
AlertDialogTrigger,
|
|
109
|
+
AlertDialogContent,
|
|
110
|
+
AlertDialogHeader,
|
|
111
|
+
AlertDialogFooter,
|
|
112
|
+
AlertDialogTitle,
|
|
113
|
+
AlertDialogDescription,
|
|
114
|
+
AlertDialogAction,
|
|
115
|
+
AlertDialogCancel,
|
|
116
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
|
4
|
+
import * as React from "react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
className={cn(
|
|
16
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function AvatarImage({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
28
|
+
return (
|
|
29
|
+
<AvatarPrimitive.Image
|
|
30
|
+
data-slot="avatar-image"
|
|
31
|
+
className={cn("aspect-square size-full", className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function AvatarFallback({
|
|
38
|
+
className,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
41
|
+
return (
|
|
42
|
+
<AvatarPrimitive.Fallback
|
|
43
|
+
data-slot="avatar-fallback"
|
|
44
|
+
className={cn(
|
|
45
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
14
|
+
secondary:
|
|
15
|
+
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
16
|
+
destructive:
|
|
17
|
+
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70",
|
|
18
|
+
outline:
|
|
19
|
+
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
function Badge({
|
|
29
|
+
className,
|
|
30
|
+
variant,
|
|
31
|
+
asChild = false,
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentProps<"span"> &
|
|
34
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
35
|
+
const Comp = asChild ? Slot : "span";
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Comp
|
|
39
|
+
data-slot="badge"
|
|
40
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
12
|
+
return (
|
|
13
|
+
<ol
|
|
14
|
+
data-slot="breadcrumb-list"
|
|
15
|
+
className={cn(
|
|
16
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
25
|
+
return (
|
|
26
|
+
<li
|
|
27
|
+
data-slot="breadcrumb-item"
|
|
28
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function BreadcrumbLink({
|
|
35
|
+
asChild,
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<"a"> & {
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}) {
|
|
41
|
+
const Comp = asChild ? Slot : "a";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Comp
|
|
45
|
+
data-slot="breadcrumb-link"
|
|
46
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
53
|
+
return (
|
|
54
|
+
<span
|
|
55
|
+
data-slot="breadcrumb-page"
|
|
56
|
+
role="link"
|
|
57
|
+
aria-disabled="true"
|
|
58
|
+
aria-current="page"
|
|
59
|
+
className={cn("text-foreground font-normal", className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function BreadcrumbSeparator({
|
|
66
|
+
children,
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<"li">) {
|
|
70
|
+
return (
|
|
71
|
+
<li
|
|
72
|
+
data-slot="breadcrumb-separator"
|
|
73
|
+
role="presentation"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
{children ?? <ChevronRight />}
|
|
79
|
+
</li>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function BreadcrumbEllipsis({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<"span">) {
|
|
87
|
+
return (
|
|
88
|
+
<span
|
|
89
|
+
data-slot="breadcrumb-ellipsis"
|
|
90
|
+
role="presentation"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<MoreHorizontal className="size-4" />
|
|
96
|
+
<span className="sr-only">More</span>
|
|
97
|
+
</span>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
Breadcrumb,
|
|
103
|
+
BreadcrumbList,
|
|
104
|
+
BreadcrumbItem,
|
|
105
|
+
BreadcrumbLink,
|
|
106
|
+
BreadcrumbPage,
|
|
107
|
+
BreadcrumbSeparator,
|
|
108
|
+
BreadcrumbEllipsis,
|
|
109
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { RiLoader3Line } from "react-icons/ri";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
16
|
+
outline:
|
|
17
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
disabled: "text-gray-500 bg-gray-400 pointed-none",
|
|
23
|
+
minimal: "py-0 px-0 md:py-0 md:p-0 max-h-fit",
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
default: "h-10 px-4 py-2",
|
|
27
|
+
sm: "h-9 rounded-md px-3",
|
|
28
|
+
lg: "h-11 rounded-md px-8",
|
|
29
|
+
icon: "h-10 w-10",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
variant: "default",
|
|
34
|
+
size: "default",
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export interface ButtonProps
|
|
40
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
41
|
+
VariantProps<typeof buttonVariants> {
|
|
42
|
+
asChild?: boolean;
|
|
43
|
+
loading?: boolean;
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
hideChild?: boolean;
|
|
46
|
+
icon?: React.ReactNode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
className,
|
|
53
|
+
variant,
|
|
54
|
+
icon,
|
|
55
|
+
size,
|
|
56
|
+
disabled = false,
|
|
57
|
+
loading = false,
|
|
58
|
+
asChild = false,
|
|
59
|
+
hideChild = false,
|
|
60
|
+
...props
|
|
61
|
+
},
|
|
62
|
+
ref
|
|
63
|
+
) => {
|
|
64
|
+
const Comp = asChild ? Slot : "button";
|
|
65
|
+
return (
|
|
66
|
+
<Comp
|
|
67
|
+
ref={ref}
|
|
68
|
+
{...props}
|
|
69
|
+
disabled={disabled || loading}
|
|
70
|
+
className={`${cn(
|
|
71
|
+
"ease-in-out duration-75 active:scale-95",
|
|
72
|
+
buttonVariants({ variant, size, className }),
|
|
73
|
+
"transition-transform will-change-transform cursor-pointer"
|
|
74
|
+
)} `}
|
|
75
|
+
>
|
|
76
|
+
<RiLoader3Line
|
|
77
|
+
className={`${loading ? "size-6 animate-spin " : "hidden"} ${
|
|
78
|
+
!hideChild && "mr-2"
|
|
79
|
+
} transition-transform `}
|
|
80
|
+
/>
|
|
81
|
+
<span
|
|
82
|
+
className={cn(
|
|
83
|
+
(!icon || (hideChild && loading)) && "hidden",
|
|
84
|
+
props.children && "mr-2"
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{icon}
|
|
88
|
+
</span>
|
|
89
|
+
<>{hideChild ? !loading && props.children : props.children}</>
|
|
90
|
+
</Comp>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
Button.displayName = "Button";
|
|
95
|
+
|
|
96
|
+
export { Button, buttonVariants };
|