create-nexu 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +603 -0
- package/package.json +41 -0
- package/templates/default/.changeset/config.json +11 -0
- package/templates/default/.eslintignore +13 -0
- package/templates/default/.eslintrc.js +66 -0
- package/templates/default/.github/actions/quality/action.yml +53 -0
- package/templates/default/.github/dependabot.yml +51 -0
- package/templates/default/.github/workflows/deploy-dev.yml +83 -0
- package/templates/default/.github/workflows/deploy-prod.yml +83 -0
- package/templates/default/.github/workflows/deploy-rec.yml +83 -0
- package/templates/default/.husky/commit-msg +1 -0
- package/templates/default/.husky/pre-commit +1 -0
- package/templates/default/.prettierignore +7 -0
- package/templates/default/.prettierrc +19 -0
- package/templates/default/.vscode/extensions.json +14 -0
- package/templates/default/.vscode/settings.json +36 -0
- package/templates/default/apps/.gitkeep +0 -0
- package/templates/default/commitlint.config.js +26 -0
- package/templates/default/docker/docker-compose.dev.yml +49 -0
- package/templates/default/docker/docker-compose.prod.yml +64 -0
- package/templates/default/docker/docker-compose.yml +5 -0
- package/templates/default/package.json +56 -0
- package/templates/default/packages/cache/package.json +26 -0
- package/templates/default/packages/cache/src/index.ts +137 -0
- package/templates/default/packages/cache/tsconfig.json +9 -0
- package/templates/default/packages/cache/tsup.config.ts +9 -0
- package/templates/default/packages/config/eslint/index.js +20 -0
- package/templates/default/packages/config/package.json +9 -0
- package/templates/default/packages/config/typescript/base.json +26 -0
- package/templates/default/packages/constants/package.json +26 -0
- package/templates/default/packages/constants/src/index.ts +121 -0
- package/templates/default/packages/constants/tsconfig.json +9 -0
- package/templates/default/packages/constants/tsup.config.ts +9 -0
- package/templates/default/packages/logger/package.json +27 -0
- package/templates/default/packages/logger/src/index.ts +197 -0
- package/templates/default/packages/logger/tsconfig.json +11 -0
- package/templates/default/packages/logger/tsup.config.ts +9 -0
- package/templates/default/packages/result/package.json +26 -0
- package/templates/default/packages/result/src/index.ts +142 -0
- package/templates/default/packages/result/tsconfig.json +9 -0
- package/templates/default/packages/result/tsup.config.ts +9 -0
- package/templates/default/packages/types/package.json +26 -0
- package/templates/default/packages/types/src/index.ts +78 -0
- package/templates/default/packages/types/tsconfig.json +9 -0
- package/templates/default/packages/types/tsup.config.ts +10 -0
- package/templates/default/packages/ui/package.json +38 -0
- package/templates/default/packages/ui/src/components/Button.tsx +65 -0
- package/templates/default/packages/ui/src/components/Card.tsx +90 -0
- package/templates/default/packages/ui/src/components/Input.tsx +51 -0
- package/templates/default/packages/ui/src/index.ts +15 -0
- package/templates/default/packages/ui/tsconfig.json +11 -0
- package/templates/default/packages/ui/tsup.config.ts +11 -0
- package/templates/default/packages/utils/package.json +30 -0
- package/templates/default/packages/utils/src/index.test.ts +130 -0
- package/templates/default/packages/utils/src/index.ts +154 -0
- package/templates/default/packages/utils/tsconfig.json +10 -0
- package/templates/default/packages/utils/tsup.config.ts +10 -0
- package/templates/default/pnpm-workspace.yaml +3 -0
- package/templates/default/scripts/deploy.sh +25 -0
- package/templates/default/scripts/generate-app.sh +166 -0
- package/templates/default/scripts/publish-cli.sh +54 -0
- package/templates/default/scripts/setup.sh +70 -0
- package/templates/default/services/.env.example +16 -0
- package/templates/default/services/docker-compose.yml +207 -0
- package/templates/default/services/grafana/provisioning/dashboards/dashboards.yml +11 -0
- package/templates/default/services/grafana/provisioning/datasources/datasources.yml +9 -0
- package/templates/default/services/postgres/init/.gitkeep +2 -0
- package/templates/default/services/prometheus/prometheus.yml +13 -0
- package/templates/default/tsconfig.json +27 -0
- package/templates/default/turbo.json +40 -0
- package/templates/default/vitest.config.ts +15 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/result",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"lint": "eslint src/",
|
|
19
|
+
"lint:fix": "eslint src/ --fix",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"typescript": "^5.4.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Result type - Either success with data or failure with error
|
|
2
|
+
export type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };
|
|
3
|
+
|
|
4
|
+
// Create success result
|
|
5
|
+
export function ok<T>(data: T): Result<T, never> {
|
|
6
|
+
return { ok: true, data };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Create failure result
|
|
10
|
+
export function err<E = Error>(error: E): Result<never, E> {
|
|
11
|
+
return { ok: false, error };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Try/catch wrapper for sync functions
|
|
15
|
+
export function tryCatch<T, E = Error>(fn: () => T, onError?: (e: unknown) => E): Result<T, E> {
|
|
16
|
+
try {
|
|
17
|
+
return ok(fn());
|
|
18
|
+
} catch (e) {
|
|
19
|
+
const error = onError ? onError(e) : e instanceof Error ? e : new Error(String(e));
|
|
20
|
+
return err(error as E);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Try/catch wrapper for async functions
|
|
25
|
+
export async function tryCatchAsync<T, E = Error>(
|
|
26
|
+
fn: () => Promise<T>,
|
|
27
|
+
onError?: (e: unknown) => E
|
|
28
|
+
): Promise<Result<T, E>> {
|
|
29
|
+
try {
|
|
30
|
+
const data = await fn();
|
|
31
|
+
return ok(data);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
const error = onError ? onError(e) : e instanceof Error ? e : new Error(String(e));
|
|
34
|
+
return err(error as E);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Wrap a function to return Result
|
|
39
|
+
export function wrap<T, Args extends unknown[], E = Error>(
|
|
40
|
+
fn: (...args: Args) => T,
|
|
41
|
+
onError?: (e: unknown) => E
|
|
42
|
+
): (...args: Args) => Result<T, E> {
|
|
43
|
+
return (...args: Args) => tryCatch(() => fn(...args), onError);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Wrap an async function to return Result
|
|
47
|
+
export function wrapAsync<T, Args extends unknown[], E = Error>(
|
|
48
|
+
fn: (...args: Args) => Promise<T>,
|
|
49
|
+
onError?: (e: unknown) => E
|
|
50
|
+
): (...args: Args) => Promise<Result<T, E>> {
|
|
51
|
+
return (...args: Args) => tryCatchAsync(() => fn(...args), onError);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Type guards
|
|
55
|
+
export function isOk<T, E>(result: Result<T, E>): result is { ok: true; data: T } {
|
|
56
|
+
return result.ok === true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function isErr<T, E>(result: Result<T, E>): result is { ok: false; error: E } {
|
|
60
|
+
return result.ok === false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Unwrap - get data or throw
|
|
64
|
+
export function unwrap<T, E>(result: Result<T, E>): T {
|
|
65
|
+
if (result.ok) return result.data;
|
|
66
|
+
throw result.error;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Unwrap with default value
|
|
70
|
+
export function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {
|
|
71
|
+
return result.ok ? result.data : defaultValue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Unwrap error or throw
|
|
75
|
+
export function unwrapErr<T, E>(result: Result<T, E>): E {
|
|
76
|
+
if (!result.ok) return result.error;
|
|
77
|
+
throw new Error('Called unwrapErr on Ok result');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Map data if ok
|
|
81
|
+
export function map<T, U, E>(result: Result<T, E>, fn: (data: T) => U): Result<U, E> {
|
|
82
|
+
return result.ok ? ok(fn(result.data)) : result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Map error if err
|
|
86
|
+
export function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> {
|
|
87
|
+
return result.ok ? result : err(fn(result.error));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// FlatMap / chain
|
|
91
|
+
export function flatMap<T, U, E>(
|
|
92
|
+
result: Result<T, E>,
|
|
93
|
+
fn: (data: T) => Result<U, E>
|
|
94
|
+
): Result<U, E> {
|
|
95
|
+
return result.ok ? fn(result.data) : result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Async flatMap
|
|
99
|
+
export async function flatMapAsync<T, U, E>(
|
|
100
|
+
result: Result<T, E>,
|
|
101
|
+
fn: (data: T) => Promise<Result<U, E>>
|
|
102
|
+
): Promise<Result<U, E>> {
|
|
103
|
+
return result.ok ? fn(result.data) : result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Match pattern
|
|
107
|
+
export function match<T, E, U>(
|
|
108
|
+
result: Result<T, E>,
|
|
109
|
+
handlers: { ok: (data: T) => U; err: (error: E) => U }
|
|
110
|
+
): U {
|
|
111
|
+
return result.ok ? handlers.ok(result.data) : handlers.err(result.error);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Combine multiple results
|
|
115
|
+
export function all<T extends Result<unknown, unknown>[]>(
|
|
116
|
+
results: [...T]
|
|
117
|
+
): Result<
|
|
118
|
+
{ [K in keyof T]: T[K] extends Result<infer U, unknown> ? U : never },
|
|
119
|
+
T[number] extends Result<unknown, infer E> ? E : never
|
|
120
|
+
> {
|
|
121
|
+
const data: unknown[] = [];
|
|
122
|
+
|
|
123
|
+
for (const result of results) {
|
|
124
|
+
if (!result.ok) return result as Result<never, unknown> as never;
|
|
125
|
+
data.push(result.data);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return ok(data) as never;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// fromPromise - convert Promise to Result
|
|
132
|
+
export async function fromPromise<T, E = Error>(
|
|
133
|
+
promise: Promise<T>,
|
|
134
|
+
onError?: (e: unknown) => E
|
|
135
|
+
): Promise<Result<T, E>> {
|
|
136
|
+
return tryCatchAsync(() => promise, onError);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// toPromise - convert Result to Promise
|
|
140
|
+
export function toPromise<T, E>(result: Result<T, E>): Promise<T> {
|
|
141
|
+
return result.ok ? Promise.resolve(result.data) : Promise.reject(result.error);
|
|
142
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/types",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"lint": "eslint src/",
|
|
19
|
+
"lint:fix": "eslint src/ --fix",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"typescript": "^5.4.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// User types
|
|
2
|
+
export interface User {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
avatar?: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CreateUserInput {
|
|
12
|
+
email: string;
|
|
13
|
+
name: string;
|
|
14
|
+
password: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UpdateUserInput {
|
|
18
|
+
email?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
avatar?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// API Response types
|
|
24
|
+
export interface ApiResponse<T> {
|
|
25
|
+
success: boolean;
|
|
26
|
+
data?: T;
|
|
27
|
+
error?: ApiError;
|
|
28
|
+
meta?: ApiMeta;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ApiError {
|
|
32
|
+
code: string;
|
|
33
|
+
message: string;
|
|
34
|
+
details?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ApiMeta {
|
|
38
|
+
page?: number;
|
|
39
|
+
limit?: number;
|
|
40
|
+
total?: number;
|
|
41
|
+
totalPages?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Pagination types
|
|
45
|
+
export interface PaginationParams {
|
|
46
|
+
page?: number;
|
|
47
|
+
limit?: number;
|
|
48
|
+
sortBy?: string;
|
|
49
|
+
sortOrder?: 'asc' | 'desc';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PaginatedResponse<T> {
|
|
53
|
+
items: T[];
|
|
54
|
+
meta: Required<ApiMeta>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Auth types
|
|
58
|
+
export interface AuthTokens {
|
|
59
|
+
accessToken: string;
|
|
60
|
+
refreshToken: string;
|
|
61
|
+
expiresIn: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface LoginInput {
|
|
65
|
+
email: string;
|
|
66
|
+
password: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RegisterInput extends CreateUserInput {
|
|
70
|
+
confirmPassword: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Utility types
|
|
74
|
+
export type Nullable<T> = T | null;
|
|
75
|
+
export type Optional<T> = T | undefined;
|
|
76
|
+
export type DeepPartial<T> = {
|
|
77
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
78
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/ui",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./components/*": {
|
|
15
|
+
"import": "./dist/components/*.mjs",
|
|
16
|
+
"require": "./dist/components/*.js",
|
|
17
|
+
"types": "./dist/components/*.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"lint": "eslint src/",
|
|
24
|
+
"lint:fix": "eslint src/ --fix",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.2.0",
|
|
29
|
+
"@types/react-dom": "^18.2.0",
|
|
30
|
+
"react": "^18.2.0",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.4.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": "^18.2.0",
|
|
36
|
+
"react-dom": "^18.2.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
5
|
+
size?: 'sm' | 'md' | 'lg';
|
|
6
|
+
isLoading?: boolean;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const variantStyles = {
|
|
11
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
|
|
12
|
+
secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',
|
|
13
|
+
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-500',
|
|
14
|
+
ghost: 'text-gray-600 hover:bg-gray-100 focus:ring-gray-500',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const sizeStyles = {
|
|
18
|
+
sm: 'px-3 py-1.5 text-sm',
|
|
19
|
+
md: 'px-4 py-2 text-base',
|
|
20
|
+
lg: 'px-6 py-3 text-lg',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Button: React.FC<ButtonProps> = ({
|
|
24
|
+
variant = 'primary',
|
|
25
|
+
size = 'md',
|
|
26
|
+
isLoading = false,
|
|
27
|
+
disabled,
|
|
28
|
+
className = '',
|
|
29
|
+
children,
|
|
30
|
+
...props
|
|
31
|
+
}) => {
|
|
32
|
+
return (
|
|
33
|
+
<button
|
|
34
|
+
className={`
|
|
35
|
+
inline-flex items-center justify-center rounded-lg font-medium
|
|
36
|
+
transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2
|
|
37
|
+
disabled:cursor-not-allowed disabled:opacity-50
|
|
38
|
+
${variantStyles[variant]}
|
|
39
|
+
${sizeStyles[size]}
|
|
40
|
+
${className}
|
|
41
|
+
`}
|
|
42
|
+
disabled={disabled || isLoading}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
{isLoading && (
|
|
46
|
+
<svg className="-ml-1 mr-2 h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
47
|
+
<circle
|
|
48
|
+
className="opacity-25"
|
|
49
|
+
cx="12"
|
|
50
|
+
cy="12"
|
|
51
|
+
r="10"
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
strokeWidth="4"
|
|
54
|
+
/>
|
|
55
|
+
<path
|
|
56
|
+
className="opacity-75"
|
|
57
|
+
fill="currentColor"
|
|
58
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
59
|
+
/>
|
|
60
|
+
</svg>
|
|
61
|
+
)}
|
|
62
|
+
{children}
|
|
63
|
+
</button>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
variant?: 'default' | 'bordered' | 'elevated';
|
|
5
|
+
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const variantStyles = {
|
|
10
|
+
default: 'bg-white',
|
|
11
|
+
bordered: 'bg-white border border-gray-200',
|
|
12
|
+
elevated: 'bg-white shadow-lg',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const paddingStyles = {
|
|
16
|
+
none: '',
|
|
17
|
+
sm: 'p-3',
|
|
18
|
+
md: 'p-4',
|
|
19
|
+
lg: 'p-6',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const Card: React.FC<CardProps> = ({
|
|
23
|
+
variant = 'bordered',
|
|
24
|
+
padding = 'md',
|
|
25
|
+
className = '',
|
|
26
|
+
children,
|
|
27
|
+
...props
|
|
28
|
+
}) => {
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={`
|
|
32
|
+
rounded-lg
|
|
33
|
+
${variantStyles[variant]}
|
|
34
|
+
${paddingStyles[padding]}
|
|
35
|
+
${className}
|
|
36
|
+
`}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
45
|
+
children: React.ReactNode;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const CardHeader: React.FC<CardHeaderProps> = ({ className = '', children, ...props }) => {
|
|
49
|
+
return (
|
|
50
|
+
<div className={`mb-4 ${className}`} {...props}>
|
|
51
|
+
{children}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
57
|
+
children: React.ReactNode;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const CardTitle: React.FC<CardTitleProps> = ({ className = '', children, ...props }) => {
|
|
61
|
+
return (
|
|
62
|
+
<h3 className={`text-lg font-semibold text-gray-900 ${className}`} {...props}>
|
|
63
|
+
{children}
|
|
64
|
+
</h3>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export interface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
69
|
+
children: React.ReactNode;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const CardContent: React.FC<CardContentProps> = ({ className = '', children, ...props }) => {
|
|
73
|
+
return (
|
|
74
|
+
<div className={`text-gray-600 ${className}`} {...props}>
|
|
75
|
+
{children}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
81
|
+
children: React.ReactNode;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const CardFooter: React.FC<CardFooterProps> = ({ className = '', children, ...props }) => {
|
|
85
|
+
return (
|
|
86
|
+
<div className={`mt-4 border-t border-gray-200 pt-4 ${className}`} {...props}>
|
|
87
|
+
{children}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
+
label?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
helperText?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
10
|
+
({ label, error, helperText, className = '', id, ...props }, ref) => {
|
|
11
|
+
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-');
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="w-full">
|
|
15
|
+
{label && (
|
|
16
|
+
<label htmlFor={inputId} className="mb-1 block text-sm font-medium text-gray-700">
|
|
17
|
+
{label}
|
|
18
|
+
</label>
|
|
19
|
+
)}
|
|
20
|
+
<input
|
|
21
|
+
ref={ref}
|
|
22
|
+
id={inputId}
|
|
23
|
+
className={`
|
|
24
|
+
w-full rounded-lg border px-3 py-2 shadow-sm
|
|
25
|
+
focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500
|
|
26
|
+
disabled:cursor-not-allowed disabled:bg-gray-100
|
|
27
|
+
${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'border-gray-300'}
|
|
28
|
+
${className}
|
|
29
|
+
`}
|
|
30
|
+
aria-invalid={error ? 'true' : 'false'}
|
|
31
|
+
aria-describedby={
|
|
32
|
+
error ? `${inputId}-error` : helperText ? `${inputId}-helper` : undefined
|
|
33
|
+
}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
{error && (
|
|
37
|
+
<p id={`${inputId}-error`} className="mt-1 text-sm text-red-600">
|
|
38
|
+
{error}
|
|
39
|
+
</p>
|
|
40
|
+
)}
|
|
41
|
+
{helperText && !error && (
|
|
42
|
+
<p id={`${inputId}-helper`} className="mt-1 text-sm text-gray-500">
|
|
43
|
+
{helperText}
|
|
44
|
+
</p>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
Input.displayName = 'Input';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
export { Button } from './components/Button';
|
|
3
|
+
export type { ButtonProps } from './components/Button';
|
|
4
|
+
|
|
5
|
+
export { Input } from './components/Input';
|
|
6
|
+
export type { InputProps } from './components/Input';
|
|
7
|
+
|
|
8
|
+
export { Card, CardHeader, CardTitle, CardContent, CardFooter } from './components/Card';
|
|
9
|
+
export type {
|
|
10
|
+
CardProps,
|
|
11
|
+
CardHeaderProps,
|
|
12
|
+
CardTitleProps,
|
|
13
|
+
CardContentProps,
|
|
14
|
+
CardFooterProps,
|
|
15
|
+
} from './components/Card';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/utils",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"lint": "eslint src/",
|
|
19
|
+
"lint:fix": "eslint src/ --fix",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:coverage": "vitest run --coverage"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.4.0",
|
|
28
|
+
"vitest": "^2.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|