create-velox-app 0.4.6 → 0.4.7
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/dist/templates/placeholders.d.ts +4 -0
- package/dist/templates/placeholders.d.ts.map +1 -1
- package/dist/templates/placeholders.js +17 -2
- package/dist/templates/placeholders.js.map +1 -1
- package/dist/templates/shared/web-base.d.ts +3 -2
- package/dist/templates/shared/web-base.d.ts.map +1 -1
- package/dist/templates/shared/web-base.js +10 -6
- package/dist/templates/shared/web-base.js.map +1 -1
- package/package.json +2 -2
- package/src/templates/source/api/index.auth.ts +4 -0
- package/src/templates/source/api/index.default.ts +4 -0
- package/src/templates/source/api/package.auth.json +1 -0
- package/src/templates/source/api/package.default.json +1 -0
- package/src/templates/source/api/procedures/auth.ts +4 -4
- package/src/templates/source/api/tsconfig.json +3 -2
- package/src/templates/source/web/App.module.css +48 -0
- package/src/templates/source/web/main.tsx +31 -12
- package/src/templates/source/web/package.json +9 -8
- package/src/templates/source/web/routes/__root.tsx +22 -1
- package/src/templates/source/web/routes/index.auth.tsx +17 -61
- package/src/templates/source/web/routes/index.default.tsx +7 -21
- package/src/templates/source/web/routes/users.tsx +70 -0
- package/src/templates/source/web/tsconfig.json +2 -1
|
@@ -58,6 +58,10 @@ export declare const CONDITIONALS: {
|
|
|
58
58
|
readonly AUTH_END: "/* @endif auth */";
|
|
59
59
|
readonly DEFAULT_START: "/* @if default */";
|
|
60
60
|
readonly DEFAULT_END: "/* @endif default */";
|
|
61
|
+
readonly JSX_AUTH_START: "{/* @if auth */}";
|
|
62
|
+
readonly JSX_AUTH_END: "{/* @endif auth */}";
|
|
63
|
+
readonly JSX_DEFAULT_START: "{/* @if default */}";
|
|
64
|
+
readonly JSX_DEFAULT_END: "{/* @endif default */}";
|
|
61
65
|
};
|
|
62
66
|
/**
|
|
63
67
|
* Process conditional blocks in template content.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/templates/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,kDAAkD;;IAElD,+CAA+C;;IAE/C,wCAAwC;;IAExC,wDAAwD;;IAExD,sCAAsC;;IAEtC,0CAA0C;;CAElC,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,cAK5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,cAKzB,CAAC;AAaF;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAgBjF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,cAAc,GACrB,MAAM,CAGR;AAaD;;;GAGG;AACH,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/templates/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,kDAAkD;;IAElD,+CAA+C;;IAE/C,wCAAwC;;IAExC,wDAAwD;;IAExD,sCAAsC;;IAEtC,0CAA0C;;CAElC,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,cAK5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,cAKzB,CAAC;AAaF;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAgBjF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,cAAc,GACrB,MAAM,CAGR;AAaD;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CAUf,CAAC;AA0BX;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,GACnC,MAAM,CA8BR"}
|
|
@@ -106,11 +106,20 @@ export const CONDITIONALS = {
|
|
|
106
106
|
AUTH_END: '/* @endif auth */',
|
|
107
107
|
DEFAULT_START: '/* @if default */',
|
|
108
108
|
DEFAULT_END: '/* @endif default */',
|
|
109
|
+
// JSX-style conditionals (wrapped in braces)
|
|
110
|
+
JSX_AUTH_START: '{/* @if auth */}',
|
|
111
|
+
JSX_AUTH_END: '{/* @endif auth */}',
|
|
112
|
+
JSX_DEFAULT_START: '{/* @if default */}',
|
|
113
|
+
JSX_DEFAULT_END: '{/* @endif default */}',
|
|
109
114
|
};
|
|
110
115
|
/** Pre-compiled regex for auth conditional blocks (performance optimization) */
|
|
111
116
|
const AUTH_BLOCK_PATTERN = new RegExp(`${escapeRegex(CONDITIONALS.AUTH_START)}[\\s\\S]*?${escapeRegex(CONDITIONALS.AUTH_END)}`, 'g');
|
|
112
117
|
/** Pre-compiled regex for default conditional blocks (performance optimization) */
|
|
113
118
|
const DEFAULT_BLOCK_PATTERN = new RegExp(`${escapeRegex(CONDITIONALS.DEFAULT_START)}[\\s\\S]*?${escapeRegex(CONDITIONALS.DEFAULT_END)}`, 'g');
|
|
119
|
+
/** Pre-compiled regex for JSX auth conditional blocks */
|
|
120
|
+
const JSX_AUTH_BLOCK_PATTERN = new RegExp(`${escapeRegex(CONDITIONALS.JSX_AUTH_START)}[\\s\\S]*?${escapeRegex(CONDITIONALS.JSX_AUTH_END)}`, 'g');
|
|
121
|
+
/** Pre-compiled regex for JSX default conditional blocks */
|
|
122
|
+
const JSX_DEFAULT_BLOCK_PATTERN = new RegExp(`${escapeRegex(CONDITIONALS.JSX_DEFAULT_START)}[\\s\\S]*?${escapeRegex(CONDITIONALS.JSX_DEFAULT_END)}`, 'g');
|
|
114
123
|
/**
|
|
115
124
|
* Process conditional blocks in template content.
|
|
116
125
|
*
|
|
@@ -120,25 +129,31 @@ const DEFAULT_BLOCK_PATTERN = new RegExp(`${escapeRegex(CONDITIONALS.DEFAULT_STA
|
|
|
120
129
|
*/
|
|
121
130
|
export function processConditionals(content, template) {
|
|
122
131
|
let result = content;
|
|
123
|
-
// Process auth conditionals
|
|
132
|
+
// Process auth conditionals (both JS and JSX style)
|
|
124
133
|
if (template === 'auth') {
|
|
125
134
|
// Keep auth content but remove markers
|
|
126
135
|
result = result.replaceAll(CONDITIONALS.AUTH_START, '');
|
|
127
136
|
result = result.replaceAll(CONDITIONALS.AUTH_END, '');
|
|
137
|
+
result = result.replaceAll(CONDITIONALS.JSX_AUTH_START, '');
|
|
138
|
+
result = result.replaceAll(CONDITIONALS.JSX_AUTH_END, '');
|
|
128
139
|
}
|
|
129
140
|
else {
|
|
130
141
|
// Remove entire auth blocks
|
|
131
142
|
result = result.replace(AUTH_BLOCK_PATTERN, '');
|
|
143
|
+
result = result.replace(JSX_AUTH_BLOCK_PATTERN, '');
|
|
132
144
|
}
|
|
133
|
-
// Process default conditionals
|
|
145
|
+
// Process default conditionals (both JS and JSX style)
|
|
134
146
|
if (template === 'default') {
|
|
135
147
|
// Keep default content but remove markers
|
|
136
148
|
result = result.replaceAll(CONDITIONALS.DEFAULT_START, '');
|
|
137
149
|
result = result.replaceAll(CONDITIONALS.DEFAULT_END, '');
|
|
150
|
+
result = result.replaceAll(CONDITIONALS.JSX_DEFAULT_START, '');
|
|
151
|
+
result = result.replaceAll(CONDITIONALS.JSX_DEFAULT_END, '');
|
|
138
152
|
}
|
|
139
153
|
else {
|
|
140
154
|
// Remove entire default blocks
|
|
141
155
|
result = result.replace(DEFAULT_BLOCK_PATTERN, '');
|
|
156
|
+
result = result.replace(JSX_DEFAULT_BLOCK_PATTERN, '');
|
|
142
157
|
}
|
|
143
158
|
return result;
|
|
144
159
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../src/templates/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,kDAAkD;IAClD,YAAY,EAAE,kBAAkB;IAChC,+CAA+C;IAC/C,eAAe,EAAE,qBAAqB;IACtC,wCAAwC;IACxC,eAAe,EAAE,qBAAqB;IACtC,wDAAwD;IACxD,OAAO,EAAE,aAAa;IACtB,sCAAsC;IACtC,QAAQ,EAAE,cAAc;IACxB,0CAA0C;IAC1C,QAAQ,EAAE,cAAc;CAChB,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,WAAW,EAAE,EAAE;IACf,cAAc,EAAE,MAAM;IACtB,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,QAAQ;CACnB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,WAAW,EAAE,EAAE;IACf,cAAc,EAAE,MAAM;IACtB,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,QAAQ;CACnB,CAAC;AAEF,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;GAEG;AACH,SAAS,aAAa,CAAC,cAAgD;IACrE,OAAO,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,MAAsB;IACvE,MAAM,YAAY,GAA2B;QAC3C,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,WAAW;QAC/C,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,cAAc;QACrD,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,eAAe;QAC/C,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC;QAC5D,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM;QAC/B,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM;KAChC,CAAC;IAEF,IAAI,MAAM,GAAG,OAAO,CAAC;IACrB,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAChE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAgC,EAChC,MAAsB;IAEtB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACpD,OAAO,iBAAiB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,UAAU,EAAE,gBAAgB;IAC5B,QAAQ,EAAE,mBAAmB;IAC7B,aAAa,EAAE,mBAAmB;IAClC,WAAW,EAAE,sBAAsB;
|
|
1
|
+
{"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../src/templates/placeholders.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,10 +9,11 @@ export declare function generateWebTsConfig(): string;
|
|
|
9
9
|
export declare function generateViteConfig(config: TemplateConfig): string;
|
|
10
10
|
export declare function generateWebIndexHtml(config: TemplateConfig): string;
|
|
11
11
|
export declare function generateFavicon(): string;
|
|
12
|
-
export declare function generateMainTsx(): string;
|
|
13
|
-
export declare function generateRootRoute(): string;
|
|
12
|
+
export declare function generateMainTsx(config: TemplateConfig): string;
|
|
13
|
+
export declare function generateRootRoute(config: TemplateConfig): string;
|
|
14
14
|
export declare function generateDefaultIndexRoute(): string;
|
|
15
15
|
export declare function generateAuthIndexRoute(): string;
|
|
16
16
|
export declare function generateAboutRoute(): string;
|
|
17
|
+
export declare function generateUsersRoute(config: TemplateConfig): string;
|
|
17
18
|
export declare function generateWebBaseFiles(config: TemplateConfig, isAuthTemplate: boolean): TemplateFile[];
|
|
18
19
|
//# sourceMappingURL=web-base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-base.d.ts","sourceRoot":"","sources":["../../../src/templates/shared/web-base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhE,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"web-base.d.ts","sourceRoot":"","sources":["../../../src/templates/shared/web-base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhE,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEhE;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEjE;AAMD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,OAAO,GACtB,YAAY,EAAE,CAqBhB"}
|
|
@@ -23,11 +23,11 @@ export function generateWebIndexHtml(config) {
|
|
|
23
23
|
export function generateFavicon() {
|
|
24
24
|
return compileTemplate('web/favicon.svg', DEFAULT_CONFIG);
|
|
25
25
|
}
|
|
26
|
-
export function generateMainTsx() {
|
|
27
|
-
return compileTemplate('web/main.tsx',
|
|
26
|
+
export function generateMainTsx(config) {
|
|
27
|
+
return compileTemplate('web/main.tsx', config);
|
|
28
28
|
}
|
|
29
|
-
export function generateRootRoute() {
|
|
30
|
-
return compileTemplate('web/routes/__root.tsx',
|
|
29
|
+
export function generateRootRoute(config) {
|
|
30
|
+
return compileTemplate('web/routes/__root.tsx', config);
|
|
31
31
|
}
|
|
32
32
|
export function generateDefaultIndexRoute() {
|
|
33
33
|
return compileTemplate('web/routes/index.default.tsx', DEFAULT_CONFIG);
|
|
@@ -38,6 +38,9 @@ export function generateAuthIndexRoute() {
|
|
|
38
38
|
export function generateAboutRoute() {
|
|
39
39
|
return compileTemplate('web/routes/about.tsx', DEFAULT_CONFIG);
|
|
40
40
|
}
|
|
41
|
+
export function generateUsersRoute(config) {
|
|
42
|
+
return compileTemplate('web/routes/users.tsx', config);
|
|
43
|
+
}
|
|
41
44
|
// ============================================================================
|
|
42
45
|
// Generate All Web Base Files
|
|
43
46
|
// ============================================================================
|
|
@@ -50,13 +53,14 @@ export function generateWebBaseFiles(config, isAuthTemplate) {
|
|
|
50
53
|
{ path: 'apps/web/index.html', content: generateWebIndexHtml(config) },
|
|
51
54
|
{ path: 'apps/web/public/favicon.svg', content: generateFavicon() },
|
|
52
55
|
// Entry point
|
|
53
|
-
{ path: 'apps/web/src/main.tsx', content: generateMainTsx() },
|
|
56
|
+
{ path: 'apps/web/src/main.tsx', content: generateMainTsx(config) },
|
|
54
57
|
// Routes
|
|
55
|
-
{ path: 'apps/web/src/routes/__root.tsx', content: generateRootRoute() },
|
|
58
|
+
{ path: 'apps/web/src/routes/__root.tsx', content: generateRootRoute(config) },
|
|
56
59
|
{
|
|
57
60
|
path: 'apps/web/src/routes/index.tsx',
|
|
58
61
|
content: isAuthTemplate ? generateAuthIndexRoute() : generateDefaultIndexRoute(),
|
|
59
62
|
},
|
|
63
|
+
{ path: 'apps/web/src/routes/users.tsx', content: generateUsersRoute(config) },
|
|
60
64
|
{ path: 'apps/web/src/routes/about.tsx', content: generateAboutRoute() },
|
|
61
65
|
];
|
|
62
66
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-base.js","sourceRoot":"","sources":["../../../src/templates/shared/web-base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGjE,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,MAAM,UAAU,sBAAsB;IACpC,OAAO,eAAe,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,eAAe,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,eAAe,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,eAAe,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,eAAe,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,eAAe;
|
|
1
|
+
{"version":3,"file":"web-base.js","sourceRoot":"","sources":["../../../src/templates/shared/web-base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGjE,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,MAAM,UAAU,sBAAsB;IACpC,OAAO,eAAe,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,eAAe,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,eAAe,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,eAAe,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,eAAe,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,OAAO,eAAe,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,OAAO,eAAe,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,eAAe,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,eAAe,CAAC,2BAA2B,EAAE,WAAW,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,eAAe,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,eAAe,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,+EAA+E;AAC/E,8BAA8B;AAC9B,+EAA+E;AAE/E,MAAM,UAAU,oBAAoB,CAClC,MAAsB,EACtB,cAAuB;IAEvB,OAAO;QACL,eAAe;QACf,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,sBAAsB,EAAE,EAAE;QACpE,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE;QAClE,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE;QACxE,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE;QACtE,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE;QAEnE,cAAc;QACd,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE;QAEnE,SAAS;QACT,EAAE,IAAI,EAAE,gCAAgC,EAAE,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE;QAC9E;YACE,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,yBAAyB,EAAE;SACjF;QACD,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE;QAC9E,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE;KACzE,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-velox-app",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Project scaffolder for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"picocolors": "1.1.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/node": "
|
|
28
|
+
"@types/node": "25.0.0",
|
|
29
29
|
"typescript": "5.9.3"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
@@ -12,6 +12,10 @@ import { authProcedures } from './procedures/auth.js';
|
|
|
12
12
|
import { healthProcedures } from './procedures/health.js';
|
|
13
13
|
import { userProcedures } from './procedures/users.js';
|
|
14
14
|
|
|
15
|
+
// Router type for frontend type safety
|
|
16
|
+
const router = { auth: authProcedures, health: healthProcedures, users: userProcedures };
|
|
17
|
+
export type AppRouter = typeof router;
|
|
18
|
+
|
|
15
19
|
const app = await veloxApp({
|
|
16
20
|
port: config.port,
|
|
17
21
|
host: config.host,
|
|
@@ -10,6 +10,10 @@ import { prisma } from './config/database.js';
|
|
|
10
10
|
import { healthProcedures } from './procedures/health.js';
|
|
11
11
|
import { userProcedures } from './procedures/users.js';
|
|
12
12
|
|
|
13
|
+
// Router type for frontend type safety
|
|
14
|
+
const router = { health: healthProcedures, users: userProcedures };
|
|
15
|
+
export type AppRouter = typeof router;
|
|
16
|
+
|
|
13
17
|
const app = await veloxApp({
|
|
14
18
|
port: config.port,
|
|
15
19
|
host: config.host,
|
|
@@ -127,7 +127,7 @@ const DUMMY_HASH = '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.uy7dPSSXB5G6Uy
|
|
|
127
127
|
// ============================================================================
|
|
128
128
|
|
|
129
129
|
export const authProcedures = defineProcedures('auth', {
|
|
130
|
-
|
|
130
|
+
createAccount: procedure()
|
|
131
131
|
.rest({ method: 'POST', path: '/auth/register' })
|
|
132
132
|
.use(rateLimiter.register())
|
|
133
133
|
.input(RegisterInput)
|
|
@@ -165,7 +165,7 @@ export const authProcedures = defineProcedures('auth', {
|
|
|
165
165
|
});
|
|
166
166
|
}),
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
createSession: procedure()
|
|
169
169
|
.rest({ method: 'POST', path: '/auth/login' })
|
|
170
170
|
.use(
|
|
171
171
|
rateLimiter.login((ctx) => {
|
|
@@ -198,7 +198,7 @@ export const authProcedures = defineProcedures('auth', {
|
|
|
198
198
|
});
|
|
199
199
|
}),
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
createRefresh: procedure()
|
|
202
202
|
.rest({ method: 'POST', path: '/auth/refresh' })
|
|
203
203
|
.use(rateLimiter.refresh())
|
|
204
204
|
.input(RefreshInput)
|
|
@@ -247,7 +247,7 @@ export const authProcedures = defineProcedures('auth', {
|
|
|
247
247
|
}
|
|
248
248
|
}),
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
deleteSession: procedure()
|
|
251
251
|
.rest({ method: 'POST', path: '/auth/logout' })
|
|
252
252
|
.guard(authenticated)
|
|
253
253
|
.output(LogoutResponse)
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
"rootDir": "./src",
|
|
6
6
|
"outDir": "./dist",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"composite": true,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true
|
|
9
10
|
},
|
|
10
11
|
"include": ["src/**/*"],
|
|
11
12
|
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
@@ -247,6 +247,54 @@
|
|
|
247
247
|
margin-top: 0.5rem;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/* Table */
|
|
251
|
+
.tableContainer {
|
|
252
|
+
background: #111;
|
|
253
|
+
border: 1px solid #222;
|
|
254
|
+
border-radius: 12px;
|
|
255
|
+
overflow: hidden;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.table {
|
|
259
|
+
width: 100%;
|
|
260
|
+
border-collapse: collapse;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.table th,
|
|
264
|
+
.table td {
|
|
265
|
+
padding: 1rem 1.5rem;
|
|
266
|
+
text-align: left;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.table th {
|
|
270
|
+
background: #0a0a0a;
|
|
271
|
+
color: #888;
|
|
272
|
+
font-weight: 600;
|
|
273
|
+
font-size: 0.85rem;
|
|
274
|
+
text-transform: uppercase;
|
|
275
|
+
letter-spacing: 0.05em;
|
|
276
|
+
border-bottom: 1px solid #222;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.table td {
|
|
280
|
+
color: #fff;
|
|
281
|
+
border-bottom: 1px solid #1a1a1a;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.table tbody tr:hover {
|
|
285
|
+
background: #1a1a1a;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.table tbody tr:last-child td {
|
|
289
|
+
border-bottom: none;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.emptyState {
|
|
293
|
+
text-align: center;
|
|
294
|
+
color: #666;
|
|
295
|
+
padding: 2rem !important;
|
|
296
|
+
}
|
|
297
|
+
|
|
250
298
|
/* Responsive */
|
|
251
299
|
@media (max-width: 768px) {
|
|
252
300
|
.nav {
|
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { StrictMode } from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { RouterProvider, createRouter } from '@tanstack/react-router';
|
|
4
|
-
import {
|
|
4
|
+
import { VeloxProvider } from '@veloxts/client/react';
|
|
5
5
|
import { routeTree } from './routeTree.gen';
|
|
6
6
|
import './styles/global.css';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
defaultOptions: {
|
|
11
|
-
queries: {
|
|
12
|
-
staleTime: 1000 * 60, // 1 minute
|
|
13
|
-
retry: 1,
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
});
|
|
8
|
+
// Import router type from API for full type safety
|
|
9
|
+
import type { AppRouter } from '../../api/src/index.js';
|
|
17
10
|
|
|
18
11
|
// Create router with route tree
|
|
19
12
|
const router = createRouter({ routeTree });
|
|
@@ -25,14 +18,40 @@ declare module '@tanstack/react-router' {
|
|
|
25
18
|
}
|
|
26
19
|
}
|
|
27
20
|
|
|
21
|
+
/* @if auth */
|
|
22
|
+
// Dynamic headers for auth - fetches token on each request
|
|
23
|
+
const getAuthHeaders = () => {
|
|
24
|
+
const token = localStorage.getItem('token');
|
|
25
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Route mappings for auth procedures with custom .rest() endpoints
|
|
29
|
+
const routes = {
|
|
30
|
+
auth: {
|
|
31
|
+
createAccount: '/auth/register',
|
|
32
|
+
createSession: '/auth/login',
|
|
33
|
+
createRefresh: '/auth/refresh',
|
|
34
|
+
deleteSession: '/auth/logout',
|
|
35
|
+
getMe: '/auth/me',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
/* @endif auth */
|
|
39
|
+
|
|
28
40
|
// Render application
|
|
29
41
|
const rootElement = document.getElementById('root');
|
|
30
42
|
if (!rootElement) throw new Error('Root element not found');
|
|
31
43
|
|
|
32
44
|
createRoot(rootElement).render(
|
|
33
45
|
<StrictMode>
|
|
34
|
-
|
|
46
|
+
{/* @if default */}
|
|
47
|
+
<VeloxProvider<AppRouter> config={{ baseUrl: '/api' }}>
|
|
48
|
+
<RouterProvider router={router} />
|
|
49
|
+
</VeloxProvider>
|
|
50
|
+
{/* @endif default */}
|
|
51
|
+
{/* @if auth */}
|
|
52
|
+
<VeloxProvider<AppRouter> config={{ baseUrl: '/api', headers: getAuthHeaders, routes }}>
|
|
35
53
|
<RouterProvider router={router} />
|
|
36
|
-
</
|
|
54
|
+
</VeloxProvider>
|
|
55
|
+
{/* @endif auth */}
|
|
37
56
|
</StrictMode>
|
|
38
57
|
);
|
|
@@ -10,17 +10,18 @@
|
|
|
10
10
|
"type-check": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"react": "
|
|
14
|
-
"react-
|
|
15
|
-
"@
|
|
16
|
-
"
|
|
13
|
+
"@tanstack/react-query": "5.90.12",
|
|
14
|
+
"@tanstack/react-router": "1.140.5",
|
|
15
|
+
"@veloxts/client": "__VELOXTS_VERSION__",
|
|
16
|
+
"react": "19.2.1",
|
|
17
|
+
"react-dom": "19.2.1"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"@types/react": "19.
|
|
20
|
-
"@types/react-dom": "19.
|
|
20
|
+
"@types/react": "19.2.7",
|
|
21
|
+
"@types/react-dom": "19.2.3",
|
|
21
22
|
"@vitejs/plugin-react": "5.1.2",
|
|
22
|
-
"@tanstack/router-plugin": "1.140.
|
|
23
|
-
"vite": "
|
|
23
|
+
"@tanstack/router-plugin": "1.140.5",
|
|
24
|
+
"vite": "7.2.7",
|
|
24
25
|
"typescript": "5.9.3"
|
|
25
26
|
}
|
|
26
27
|
}
|
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
import { createRootRoute, Outlet, Link } from '@tanstack/react-router';
|
|
2
2
|
import styles from '@/App.module.css';
|
|
3
|
+
/* @if auth */
|
|
4
|
+
import { useQuery } from '@veloxts/client/react';
|
|
5
|
+
import type { AppRouter } from '../../../api/src/index.js';
|
|
6
|
+
/* @endif auth */
|
|
3
7
|
|
|
4
8
|
export const Route = createRootRoute({
|
|
5
9
|
component: RootLayout,
|
|
6
10
|
});
|
|
7
11
|
|
|
8
12
|
function RootLayout() {
|
|
13
|
+
/* @if auth */
|
|
14
|
+
const { data: user } = useQuery<AppRouter, 'auth', 'getMe'>('auth', 'getMe', {}, { retry: false });
|
|
15
|
+
const isAuthenticated = !!user;
|
|
16
|
+
/* @endif auth */
|
|
17
|
+
|
|
9
18
|
return (
|
|
10
19
|
<div className={styles.app}>
|
|
11
20
|
<nav className={styles.nav}>
|
|
12
21
|
<div className={styles.navBrand}>
|
|
13
22
|
<Link to="/" className={styles.logo}>
|
|
14
|
-
Velox
|
|
23
|
+
Velox TS
|
|
15
24
|
</Link>
|
|
16
25
|
</div>
|
|
17
26
|
<div className={styles.navLinks}>
|
|
18
27
|
<Link to="/" className={styles.navLink} activeProps={{ className: styles.navLinkActive }}>
|
|
19
28
|
Home
|
|
20
29
|
</Link>
|
|
30
|
+
{/* @if default */}
|
|
31
|
+
<Link to="/users" className={styles.navLink} activeProps={{ className: styles.navLinkActive }}>
|
|
32
|
+
Users
|
|
33
|
+
</Link>
|
|
34
|
+
{/* @endif default */}
|
|
35
|
+
{/* @if auth */}
|
|
36
|
+
{isAuthenticated && (
|
|
37
|
+
<Link to="/users" className={styles.navLink} activeProps={{ className: styles.navLinkActive }}>
|
|
38
|
+
Users
|
|
39
|
+
</Link>
|
|
40
|
+
)}
|
|
41
|
+
{/* @endif auth */}
|
|
21
42
|
<Link to="/about" className={styles.navLink} activeProps={{ className: styles.navLinkActive }}>
|
|
22
43
|
About
|
|
23
44
|
</Link>
|
|
@@ -1,49 +1,9 @@
|
|
|
1
1
|
import { createFileRoute } from '@tanstack/react-router';
|
|
2
|
-
import { useQuery, useMutation, useQueryClient } from '@
|
|
2
|
+
import { useQuery, useMutation, useQueryClient } from '@veloxts/client/react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import type { AppRouter } from '../../../api/src/index.js';
|
|
4
5
|
import styles from '@/App.module.css';
|
|
5
6
|
|
|
6
|
-
// API helpers
|
|
7
|
-
const api = {
|
|
8
|
-
get: async <T,>(path: string): Promise<T> => {
|
|
9
|
-
const token = localStorage.getItem('accessToken');
|
|
10
|
-
const res = await fetch(`/api${path}`, {
|
|
11
|
-
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
12
|
-
});
|
|
13
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
14
|
-
return res.json();
|
|
15
|
-
},
|
|
16
|
-
post: async <T,>(path: string, data: unknown): Promise<T> => {
|
|
17
|
-
const token = localStorage.getItem('accessToken');
|
|
18
|
-
const res = await fetch(`/api${path}`, {
|
|
19
|
-
method: 'POST',
|
|
20
|
-
headers: {
|
|
21
|
-
'Content-Type': 'application/json',
|
|
22
|
-
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
23
|
-
},
|
|
24
|
-
body: JSON.stringify(data),
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok) {
|
|
27
|
-
const error = await res.json().catch(() => ({}));
|
|
28
|
-
throw new Error(error.message || `HTTP ${res.status}`);
|
|
29
|
-
}
|
|
30
|
-
return res.json();
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
interface User {
|
|
35
|
-
id: string;
|
|
36
|
-
name: string;
|
|
37
|
-
email: string;
|
|
38
|
-
roles?: string[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface AuthResponse {
|
|
42
|
-
user: User;
|
|
43
|
-
accessToken: string;
|
|
44
|
-
refreshToken: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
7
|
export const Route = createFileRoute('/')({
|
|
48
8
|
component: HomePage,
|
|
49
9
|
});
|
|
@@ -57,19 +17,18 @@ function HomePage() {
|
|
|
57
17
|
const [error, setError] = useState('');
|
|
58
18
|
|
|
59
19
|
// Check if user is logged in
|
|
60
|
-
const { data: user, isLoading } = useQuery(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
20
|
+
const { data: user, isLoading } = useQuery<AppRouter, 'auth', 'getMe'>(
|
|
21
|
+
'auth',
|
|
22
|
+
'getMe',
|
|
23
|
+
{},
|
|
24
|
+
{ retry: false }
|
|
25
|
+
);
|
|
65
26
|
|
|
66
|
-
const login = useMutation({
|
|
67
|
-
mutationFn: (data: { email: string; password: string }) =>
|
|
68
|
-
api.post<AuthResponse>('/auth/login', data),
|
|
27
|
+
const login = useMutation<AppRouter, 'auth', 'createSession'>('auth', 'createSession', {
|
|
69
28
|
onSuccess: (data) => {
|
|
70
|
-
localStorage.setItem('
|
|
29
|
+
localStorage.setItem('token', data.accessToken);
|
|
71
30
|
localStorage.setItem('refreshToken', data.refreshToken);
|
|
72
|
-
queryClient.invalidateQueries({ queryKey: ['
|
|
31
|
+
queryClient.invalidateQueries({ queryKey: ['auth', 'getMe'] });
|
|
73
32
|
setError('');
|
|
74
33
|
},
|
|
75
34
|
onError: (err) => {
|
|
@@ -77,13 +36,11 @@ function HomePage() {
|
|
|
77
36
|
},
|
|
78
37
|
});
|
|
79
38
|
|
|
80
|
-
const register = useMutation({
|
|
81
|
-
mutationFn: (data: { name: string; email: string; password: string }) =>
|
|
82
|
-
api.post<AuthResponse>('/auth/register', data),
|
|
39
|
+
const register = useMutation<AppRouter, 'auth', 'createAccount'>('auth', 'createAccount', {
|
|
83
40
|
onSuccess: (data) => {
|
|
84
|
-
localStorage.setItem('
|
|
41
|
+
localStorage.setItem('token', data.accessToken);
|
|
85
42
|
localStorage.setItem('refreshToken', data.refreshToken);
|
|
86
|
-
queryClient.invalidateQueries({ queryKey: ['
|
|
43
|
+
queryClient.invalidateQueries({ queryKey: ['auth', 'getMe'] });
|
|
87
44
|
setError('');
|
|
88
45
|
},
|
|
89
46
|
onError: (err) => {
|
|
@@ -91,12 +48,11 @@ function HomePage() {
|
|
|
91
48
|
},
|
|
92
49
|
});
|
|
93
50
|
|
|
94
|
-
const logout = useMutation({
|
|
95
|
-
mutationFn: () => api.post('/auth/logout', {}),
|
|
51
|
+
const logout = useMutation<AppRouter, 'auth', 'deleteSession'>('auth', 'deleteSession', {
|
|
96
52
|
onSuccess: () => {
|
|
97
|
-
localStorage.removeItem('
|
|
53
|
+
localStorage.removeItem('token');
|
|
98
54
|
localStorage.removeItem('refreshToken');
|
|
99
|
-
queryClient.setQueryData(['
|
|
55
|
+
queryClient.setQueryData(['auth', 'getMe'], null);
|
|
100
56
|
},
|
|
101
57
|
});
|
|
102
58
|
|
|
@@ -1,32 +1,18 @@
|
|
|
1
1
|
import { createFileRoute } from '@tanstack/react-router';
|
|
2
|
-
import { useQuery } from '@
|
|
2
|
+
import { useQuery } from '@veloxts/client/react';
|
|
3
|
+
import type { AppRouter } from '../../../api/src/index.js';
|
|
3
4
|
import styles from '@/App.module.css';
|
|
4
5
|
|
|
5
|
-
// API helper
|
|
6
|
-
const api = {
|
|
7
|
-
get: async <T,>(path: string): Promise<T> => {
|
|
8
|
-
const res = await fetch(`/api${path}`);
|
|
9
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
10
|
-
return res.json();
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
|
|
14
6
|
export const Route = createFileRoute('/')({
|
|
15
7
|
component: HomePage,
|
|
16
8
|
});
|
|
17
9
|
|
|
18
|
-
interface HealthResponse {
|
|
19
|
-
status: string;
|
|
20
|
-
version: string;
|
|
21
|
-
timestamp: string;
|
|
22
|
-
uptime: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
10
|
function HomePage() {
|
|
26
|
-
const { data: health, isLoading, error } = useQuery(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
const { data: health, isLoading, error } = useQuery<AppRouter, 'health', 'check'>(
|
|
12
|
+
'health',
|
|
13
|
+
'check',
|
|
14
|
+
{}
|
|
15
|
+
);
|
|
30
16
|
|
|
31
17
|
return (
|
|
32
18
|
<div className={styles.container}>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Users Page - Demonstrates type-safe data fetching with VeloxTS hooks
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createFileRoute } from '@tanstack/react-router';
|
|
6
|
+
import { useQuery } from '@veloxts/client/react';
|
|
7
|
+
import type { AppRouter } from '../../../api/src/index.js';
|
|
8
|
+
import styles from '@/App.module.css';
|
|
9
|
+
|
|
10
|
+
export const Route = createFileRoute('/users')({
|
|
11
|
+
component: UsersPage,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function UsersPage() {
|
|
15
|
+
const { data, isLoading, error } = useQuery<AppRouter, 'users', 'listUsers'>(
|
|
16
|
+
'users',
|
|
17
|
+
'listUsers',
|
|
18
|
+
{}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className={styles.container}>
|
|
23
|
+
<div className={styles.hero}>
|
|
24
|
+
<h1 className={styles.title}>Users</h1>
|
|
25
|
+
<p className={styles.subtitle}>
|
|
26
|
+
Type-safe data fetching with VeloxTS hooks
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
{isLoading ? (
|
|
31
|
+
<p className={styles.loading}>Loading users...</p>
|
|
32
|
+
) : error ? (
|
|
33
|
+
<p className={styles.error}>Error: {error.message}</p>
|
|
34
|
+
) : (
|
|
35
|
+
<div className={styles.tableContainer}>
|
|
36
|
+
<table className={styles.table}>
|
|
37
|
+
<thead>
|
|
38
|
+
<tr>
|
|
39
|
+
<th>Name</th>
|
|
40
|
+
<th>Email</th>
|
|
41
|
+
<th>Created</th>
|
|
42
|
+
</tr>
|
|
43
|
+
</thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
{data?.data.map((user) => (
|
|
46
|
+
<tr key={user.id}>
|
|
47
|
+
<td>{user.name}</td>
|
|
48
|
+
<td>{user.email}</td>
|
|
49
|
+
<td>{new Date(user.createdAt).toLocaleDateString()}</td>
|
|
50
|
+
</tr>
|
|
51
|
+
))}
|
|
52
|
+
{data?.data.length === 0 && (
|
|
53
|
+
<tr>
|
|
54
|
+
<td colSpan={3} className={styles.emptyState}>
|
|
55
|
+
No users found. Create one via the API!
|
|
56
|
+
</td>
|
|
57
|
+
</tr>
|
|
58
|
+
)}
|
|
59
|
+
</tbody>
|
|
60
|
+
</table>
|
|
61
|
+
{data && (
|
|
62
|
+
<p className={styles.meta}>
|
|
63
|
+
Page {data.meta.page} - {data.meta.total} total users
|
|
64
|
+
</p>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|