create-velox-app 0.6.31 → 0.6.51
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/CHANGELOG.md +120 -0
- package/GUIDE.md +230 -0
- package/dist/cli.js +1 -0
- package/dist/index.js +14 -4
- package/dist/templates/auth.js +10 -0
- package/dist/templates/index.js +30 -1
- package/dist/templates/placeholders.js +0 -3
- package/dist/templates/rsc-auth.d.ts +12 -0
- package/dist/templates/rsc-auth.js +208 -0
- package/dist/templates/rsc.js +40 -1
- package/dist/templates/shared/css-generator.d.ts +26 -0
- package/dist/templates/shared/css-generator.js +553 -0
- package/dist/templates/shared/index.d.ts +3 -0
- package/dist/templates/shared/index.js +3 -0
- package/dist/templates/shared/rsc-styles.d.ts +54 -0
- package/dist/templates/shared/rsc-styles.js +68 -0
- package/dist/templates/shared/theme.d.ts +133 -0
- package/dist/templates/shared/theme.js +141 -0
- package/dist/templates/spa.js +10 -0
- package/dist/templates/trpc.js +10 -0
- package/dist/templates/types.d.ts +2 -1
- package/dist/templates/types.js +6 -0
- package/package.json +6 -3
- package/src/templates/source/api/config/database.ts +13 -32
- package/src/templates/source/api/docker-compose.yml +21 -0
- package/src/templates/source/root/CLAUDE.auth.md +6 -0
- package/src/templates/source/root/CLAUDE.default.md +6 -0
- package/src/templates/source/rsc/CLAUDE.md +56 -2
- package/src/templates/source/rsc/app/actions/posts.ts +1 -1
- package/src/templates/source/rsc/app/actions/users.ts +111 -20
- package/src/templates/source/rsc/app/layouts/dashboard.tsx +21 -16
- package/src/templates/source/rsc/app/layouts/marketing.tsx +34 -0
- package/src/templates/source/rsc/app/layouts/minimal-content.tsx +21 -0
- package/src/templates/source/rsc/app/layouts/minimal.tsx +86 -5
- package/src/templates/source/rsc/app/layouts/root.tsx +148 -44
- package/src/templates/source/rsc/docker-compose.yml +21 -0
- package/src/templates/source/rsc/package.json +3 -3
- package/src/templates/source/rsc/src/api/database.ts +13 -32
- package/src/templates/source/rsc/src/api/handler.ts +1 -1
- package/src/templates/source/rsc/src/entry.client.tsx +65 -18
- package/src/templates/source/rsc-auth/CLAUDE.md +230 -0
- package/src/templates/source/rsc-auth/app/actions/auth.ts +112 -0
- package/src/templates/source/rsc-auth/app/actions/users.ts +289 -0
- package/src/templates/source/rsc-auth/app/layouts/dashboard.tsx +132 -0
- package/src/templates/source/rsc-auth/app/layouts/marketing.tsx +59 -0
- package/src/templates/source/rsc-auth/app/layouts/minimal-content.tsx +21 -0
- package/src/templates/source/rsc-auth/app/layouts/minimal.tsx +111 -0
- package/src/templates/source/rsc-auth/app/layouts/root.tsx +355 -0
- package/src/templates/source/rsc-auth/app/pages/_not-found.tsx +15 -0
- package/src/templates/source/rsc-auth/app/pages/auth/login.tsx +198 -0
- package/src/templates/source/rsc-auth/app/pages/auth/register.tsx +225 -0
- package/src/templates/source/rsc-auth/app/pages/dashboard/index.tsx +267 -0
- package/src/templates/source/rsc-auth/app/pages/index.tsx +83 -0
- package/src/templates/source/rsc-auth/app/pages/users.tsx +47 -0
- package/src/templates/source/rsc-auth/app.config.ts +12 -0
- package/src/templates/source/rsc-auth/docker-compose.yml +21 -0
- package/src/templates/source/rsc-auth/env.example +11 -0
- package/src/templates/source/rsc-auth/gitignore +34 -0
- package/src/templates/source/rsc-auth/package.json +44 -0
- package/src/templates/source/rsc-auth/prisma/schema.prisma +23 -0
- package/src/templates/source/rsc-auth/prisma.config.ts +22 -0
- package/src/templates/source/rsc-auth/public/favicon.svg +4 -0
- package/src/templates/source/rsc-auth/src/api/database.ts +129 -0
- package/src/templates/source/rsc-auth/src/api/handler.ts +85 -0
- package/src/templates/source/rsc-auth/src/api/procedures/auth.ts +262 -0
- package/src/templates/source/rsc-auth/src/api/procedures/health.ts +48 -0
- package/src/templates/source/rsc-auth/src/api/procedures/users.ts +87 -0
- package/src/templates/source/rsc-auth/src/api/schemas/auth.ts +79 -0
- package/src/templates/source/rsc-auth/src/api/schemas/user.ts +38 -0
- package/src/templates/source/rsc-auth/src/api/utils/auth.ts +157 -0
- package/src/templates/source/rsc-auth/src/entry.client.tsx +63 -0
- package/src/templates/source/rsc-auth/src/entry.server.tsx +262 -0
- package/src/templates/source/rsc-auth/tsconfig.json +24 -0
- package/src/templates/source/shared/scripts/check-client-imports.sh +75 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Layout
|
|
3
|
+
*
|
|
4
|
+
* A nested layout for pages in the (dashboard) route group.
|
|
5
|
+
* Adds a sidebar navigation for dashboard-related pages.
|
|
6
|
+
* This is NOT a full HTML document - it wraps within RootLayout.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
|
|
11
|
+
interface DashboardLayoutProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
params?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function DashboardLayout({ children }: DashboardLayoutProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="dashboard-layout">
|
|
19
|
+
<style>{`
|
|
20
|
+
.dashboard-layout {
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: 2rem;
|
|
23
|
+
min-height: 60vh;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dashboard-sidebar {
|
|
27
|
+
width: 240px;
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
background: #111;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
padding: 1.5rem;
|
|
32
|
+
border: 1px solid #222;
|
|
33
|
+
height: fit-content;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.dashboard-sidebar h3 {
|
|
37
|
+
font-size: 0.75rem;
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
color: #888;
|
|
40
|
+
margin-bottom: 0.75rem;
|
|
41
|
+
padding: 0 0.75rem;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
letter-spacing: 0.05em;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sidebar-nav {
|
|
47
|
+
list-style: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sidebar-nav a {
|
|
51
|
+
display: block;
|
|
52
|
+
padding: 0.75rem;
|
|
53
|
+
color: #ededed;
|
|
54
|
+
text-decoration: none;
|
|
55
|
+
border-radius: 4px;
|
|
56
|
+
transition: background 0.2s;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.sidebar-nav a:hover {
|
|
60
|
+
background: #1a1a1a;
|
|
61
|
+
color: #00d9ff;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.sidebar-nav a.active {
|
|
65
|
+
background: #00d9ff;
|
|
66
|
+
color: #000;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dashboard-content {
|
|
70
|
+
flex: 1;
|
|
71
|
+
background: #111;
|
|
72
|
+
border-radius: 8px;
|
|
73
|
+
padding: 1.5rem;
|
|
74
|
+
border: 1px solid #222;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dashboard-badge {
|
|
78
|
+
display: inline-block;
|
|
79
|
+
background: #00d9ff;
|
|
80
|
+
color: #000;
|
|
81
|
+
font-size: 0.625rem;
|
|
82
|
+
text-transform: uppercase;
|
|
83
|
+
padding: 0.5rem 0.75rem;
|
|
84
|
+
border-radius: 4px;
|
|
85
|
+
margin-bottom: 1rem;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
letter-spacing: 0.05em;
|
|
88
|
+
}
|
|
89
|
+
`}</style>
|
|
90
|
+
|
|
91
|
+
<aside className="dashboard-sidebar">
|
|
92
|
+
<h3>Dashboard</h3>
|
|
93
|
+
<nav>
|
|
94
|
+
<ul className="sidebar-nav">
|
|
95
|
+
<li>
|
|
96
|
+
<a href="/profile">Profile</a>
|
|
97
|
+
</li>
|
|
98
|
+
<li>
|
|
99
|
+
<a href="/settings">Settings</a>
|
|
100
|
+
</li>
|
|
101
|
+
<li>
|
|
102
|
+
<a href="/settings?tab=notifications">Notifications</a>
|
|
103
|
+
</li>
|
|
104
|
+
<li>
|
|
105
|
+
<a href="/settings?tab=security">Security</a>
|
|
106
|
+
</li>
|
|
107
|
+
</ul>
|
|
108
|
+
</nav>
|
|
109
|
+
|
|
110
|
+
<h3 style={{ marginTop: '1rem' }}>Navigation</h3>
|
|
111
|
+
<nav>
|
|
112
|
+
<ul className="sidebar-nav">
|
|
113
|
+
<li>
|
|
114
|
+
<a href="/">Home</a>
|
|
115
|
+
</li>
|
|
116
|
+
<li>
|
|
117
|
+
<a href="/users">Users</a>
|
|
118
|
+
</li>
|
|
119
|
+
<li>
|
|
120
|
+
<a href="/docs/getting-started">Docs</a>
|
|
121
|
+
</li>
|
|
122
|
+
</ul>
|
|
123
|
+
</nav>
|
|
124
|
+
</aside>
|
|
125
|
+
|
|
126
|
+
<div className="dashboard-content">
|
|
127
|
+
<span className="dashboard-badge">Dashboard</span>
|
|
128
|
+
{children}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketing Layout
|
|
3
|
+
*
|
|
4
|
+
* This layout wraps pages in the (marketing) route group.
|
|
5
|
+
* It's automatically applied to all pages in app/pages/(marketing)/*.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
|
|
10
|
+
interface MarketingLayoutProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
params?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function MarketingLayout({ children }: MarketingLayoutProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="marketing-layout">
|
|
18
|
+
<style>{`
|
|
19
|
+
.marketing-layout {
|
|
20
|
+
position: relative;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.marketing-banner {
|
|
24
|
+
background: #111;
|
|
25
|
+
border: 1px solid #222;
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
padding: 1rem 1.5rem;
|
|
28
|
+
margin-bottom: 1.5rem;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: 1rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.marketing-banner .badge {
|
|
35
|
+
display: inline-block;
|
|
36
|
+
background: #00d9ff;
|
|
37
|
+
color: #000;
|
|
38
|
+
font-size: 0.625rem;
|
|
39
|
+
text-transform: uppercase;
|
|
40
|
+
padding: 0.5rem 0.75rem;
|
|
41
|
+
border-radius: 4px;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
letter-spacing: 0.05em;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.marketing-banner span:not(.badge) {
|
|
47
|
+
color: #888;
|
|
48
|
+
font-size: 0.875rem;
|
|
49
|
+
}
|
|
50
|
+
`}</style>
|
|
51
|
+
|
|
52
|
+
<div className="marketing-banner">
|
|
53
|
+
<span className="badge">Marketing</span>
|
|
54
|
+
<span>This page uses the marketing layout</span>
|
|
55
|
+
</div>
|
|
56
|
+
{children}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Content Layout
|
|
3
|
+
*
|
|
4
|
+
* The content portion of the minimal layout, separate from the HTML document shell.
|
|
5
|
+
* This component is shared between server and client for proper hydration.
|
|
6
|
+
*
|
|
7
|
+
* Server renders: <div id="root"><MinimalContent><Page /></MinimalContent></div>
|
|
8
|
+
* Client hydrates: <MinimalContent><Page /></MinimalContent>
|
|
9
|
+
*
|
|
10
|
+
* This ensures the React tree matches on both sides, enabling proper hydration.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ReactNode } from 'react';
|
|
14
|
+
|
|
15
|
+
interface MinimalContentProps {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function MinimalContent({ children }: MinimalContentProps) {
|
|
20
|
+
return <div className="minimal-content">{children}</div>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Layout (Document Shell)
|
|
3
|
+
*
|
|
4
|
+
* A stripped-down layout for pages that need minimal chrome,
|
|
5
|
+
* such as print-friendly pages, embedded widgets, or auth pages.
|
|
6
|
+
*
|
|
7
|
+
* This component provides the HTML document shell (server-only).
|
|
8
|
+
* The actual content wrapper is in MinimalContent (shared with client).
|
|
9
|
+
*
|
|
10
|
+
* Architecture for proper hydration:
|
|
11
|
+
* - Server renders: <MinimalLayout><Page /></MinimalLayout>
|
|
12
|
+
* Which produces: <html>...<div id="root"><MinimalContent><Page /></></div>...</html>
|
|
13
|
+
* - Client hydrates #root with: <MinimalContent><Page /></MinimalContent>
|
|
14
|
+
* This ensures the React tree matches exactly.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
|
|
19
|
+
import MinimalContent from './minimal-content.tsx';
|
|
20
|
+
|
|
21
|
+
interface MinimalLayoutProps {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
params?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function MinimalLayout({ children }: MinimalLayoutProps) {
|
|
27
|
+
return (
|
|
28
|
+
<html lang="en">
|
|
29
|
+
<head>
|
|
30
|
+
<meta charSet="utf-8" />
|
|
31
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
32
|
+
<title>VeloxTS</title>
|
|
33
|
+
<style>{`
|
|
34
|
+
/* Global Reset & Dark Mode Base */
|
|
35
|
+
*,
|
|
36
|
+
*::before,
|
|
37
|
+
*::after {
|
|
38
|
+
box-sizing: border-box;
|
|
39
|
+
margin: 0;
|
|
40
|
+
padding: 0;
|
|
41
|
+
font: inherit;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
html {
|
|
45
|
+
font-size: 16px;
|
|
46
|
+
-webkit-font-smoothing: antialiased;
|
|
47
|
+
-moz-osx-font-smoothing: grayscale;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
body {
|
|
51
|
+
background: #0a0a0a;
|
|
52
|
+
color: #ededed;
|
|
53
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
54
|
+
line-height: 1.6;
|
|
55
|
+
min-height: 100svh;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
h1, h2, h3, h4, h5, h6 {
|
|
59
|
+
text-wrap: balance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
p, li, figcaption {
|
|
63
|
+
text-wrap: pretty;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
a {
|
|
67
|
+
color: #00d9ff;
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
transition: opacity 0.2s;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
a:hover {
|
|
73
|
+
opacity: 0.8;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
code {
|
|
77
|
+
font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Monaco, "Courier New", monospace;
|
|
78
|
+
background: #1a1a1a;
|
|
79
|
+
padding: 0.2em 0.4em;
|
|
80
|
+
border-radius: 4px;
|
|
81
|
+
font-size: 0.9em;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
::selection {
|
|
85
|
+
background: #00d9ff;
|
|
86
|
+
color: #000;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.minimal-body {
|
|
90
|
+
min-height: 100vh;
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
justify-content: center;
|
|
94
|
+
padding: 2rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.minimal-content {
|
|
98
|
+
width: 100%;
|
|
99
|
+
max-width: 450px;
|
|
100
|
+
}
|
|
101
|
+
`}</style>
|
|
102
|
+
</head>
|
|
103
|
+
<body className="minimal-body">
|
|
104
|
+
<div id="root">
|
|
105
|
+
<MinimalContent>{children}</MinimalContent>
|
|
106
|
+
</div>
|
|
107
|
+
<script src="/_build/src/entry.client.tsx" type="module" />
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root Layout
|
|
3
|
+
*
|
|
4
|
+
* The root layout wraps all pages with common UI elements.
|
|
5
|
+
* This is a Server Component that provides the HTML structure.
|
|
6
|
+
*
|
|
7
|
+
* Features auth-aware navigation that shows/hides links based on authentication status.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ReactNode } from 'react';
|
|
11
|
+
|
|
12
|
+
interface RootLayoutProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
params?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function RootLayout({ children }: RootLayoutProps) {
|
|
18
|
+
// Note: Auth state is handled at page level (dashboard redirects if not authenticated)
|
|
19
|
+
// For client-side auth-aware nav, use a client component that checks localStorage
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charSet="utf-8" />
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
26
|
+
<title>VeloxTS App</title>
|
|
27
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
28
|
+
<style>{`
|
|
29
|
+
/* Global Reset & Dark Mode Base */
|
|
30
|
+
*,
|
|
31
|
+
*::before,
|
|
32
|
+
*::after {
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
font: inherit;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
html {
|
|
40
|
+
font-size: 16px;
|
|
41
|
+
-webkit-font-smoothing: antialiased;
|
|
42
|
+
-moz-osx-font-smoothing: grayscale;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
body {
|
|
46
|
+
background: #0a0a0a;
|
|
47
|
+
color: #ededed;
|
|
48
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
49
|
+
line-height: 1.6;
|
|
50
|
+
min-height: 100svh;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
h1, h2, h3, h4, h5, h6 {
|
|
54
|
+
text-wrap: balance;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
p, li, figcaption {
|
|
58
|
+
text-wrap: pretty;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
img, picture, svg, video, canvas {
|
|
62
|
+
max-width: 100%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
a {
|
|
66
|
+
color: #00d9ff;
|
|
67
|
+
text-decoration: none;
|
|
68
|
+
transition: opacity 0.2s;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
a:hover {
|
|
72
|
+
opacity: 0.8;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
code {
|
|
76
|
+
font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Monaco, "Courier New", monospace;
|
|
77
|
+
background: #1a1a1a;
|
|
78
|
+
padding: 0.2em 0.4em;
|
|
79
|
+
border-radius: 4px;
|
|
80
|
+
font-size: 0.9em;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
::selection {
|
|
84
|
+
background: #00d9ff;
|
|
85
|
+
color: #000;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
::-webkit-scrollbar {
|
|
89
|
+
width: 8px;
|
|
90
|
+
height: 8px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
::-webkit-scrollbar-track {
|
|
94
|
+
background: #111;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
::-webkit-scrollbar-thumb {
|
|
98
|
+
background: #333;
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
::-webkit-scrollbar-thumb:hover {
|
|
103
|
+
background: #444;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Layout Structure */
|
|
107
|
+
.layout {
|
|
108
|
+
min-height: 100vh;
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.nav {
|
|
114
|
+
background: #111;
|
|
115
|
+
padding: 1rem 2rem;
|
|
116
|
+
border-bottom: 1px solid #222;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.nav-list {
|
|
120
|
+
display: flex;
|
|
121
|
+
gap: 2rem;
|
|
122
|
+
list-style: none;
|
|
123
|
+
max-width: 1200px;
|
|
124
|
+
margin: 0 auto;
|
|
125
|
+
align-items: center;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.nav-link {
|
|
129
|
+
color: #ededed;
|
|
130
|
+
text-decoration: none;
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
transition: color 0.2s;
|
|
133
|
+
padding: 0.5rem 0.75rem;
|
|
134
|
+
border-radius: 4px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.nav-link:hover {
|
|
138
|
+
color: #00d9ff;
|
|
139
|
+
background: #1a1a1a;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.main {
|
|
143
|
+
flex: 1;
|
|
144
|
+
padding: 2rem;
|
|
145
|
+
max-width: 1200px;
|
|
146
|
+
margin: 0 auto;
|
|
147
|
+
width: 100%;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.footer {
|
|
151
|
+
background: #111;
|
|
152
|
+
color: #888;
|
|
153
|
+
text-align: center;
|
|
154
|
+
padding: 1rem;
|
|
155
|
+
font-size: 0.875rem;
|
|
156
|
+
border-top: 1px solid #222;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Home Page Hero */
|
|
160
|
+
.home-page .hero {
|
|
161
|
+
text-align: center;
|
|
162
|
+
padding: 3rem 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.home-page h1 {
|
|
166
|
+
font-size: 2.5rem;
|
|
167
|
+
margin-bottom: 0.75rem;
|
|
168
|
+
font-weight: 700;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.home-page .tagline {
|
|
172
|
+
color: #888;
|
|
173
|
+
font-size: 1.25rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Stats Cards */
|
|
177
|
+
.stats {
|
|
178
|
+
display: flex;
|
|
179
|
+
gap: 1rem;
|
|
180
|
+
justify-content: center;
|
|
181
|
+
margin: 2rem 0;
|
|
182
|
+
flex-wrap: wrap;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.stat-card {
|
|
186
|
+
background: #111;
|
|
187
|
+
padding: 1.5rem 2rem;
|
|
188
|
+
border-radius: 8px;
|
|
189
|
+
border: 1px solid #222;
|
|
190
|
+
text-align: center;
|
|
191
|
+
min-width: 150px;
|
|
192
|
+
transition: border-color 0.2s;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.stat-card:hover {
|
|
196
|
+
border-color: #00d9ff;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.stat-value {
|
|
200
|
+
display: block;
|
|
201
|
+
font-size: 2rem;
|
|
202
|
+
font-weight: 700;
|
|
203
|
+
color: #00d9ff;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.stat-label {
|
|
207
|
+
color: #888;
|
|
208
|
+
font-size: 0.875rem;
|
|
209
|
+
margin-top: 0.5rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Features Section */
|
|
213
|
+
.features {
|
|
214
|
+
background: #111;
|
|
215
|
+
padding: 2rem;
|
|
216
|
+
border-radius: 8px;
|
|
217
|
+
border: 1px solid #222;
|
|
218
|
+
margin-top: 2rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.features h2 {
|
|
222
|
+
margin-bottom: 1.5rem;
|
|
223
|
+
font-size: 1.5rem;
|
|
224
|
+
font-weight: 600;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.features ul {
|
|
228
|
+
list-style: none;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.features li {
|
|
232
|
+
padding: 0.75rem 0;
|
|
233
|
+
border-bottom: 1px solid #222;
|
|
234
|
+
color: #ededed;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.features li:last-child {
|
|
238
|
+
border-bottom: none;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.cta {
|
|
242
|
+
text-align: center;
|
|
243
|
+
margin-top: 2rem;
|
|
244
|
+
color: #888;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Users Page */
|
|
248
|
+
.users-page h1 {
|
|
249
|
+
margin-bottom: 1.5rem;
|
|
250
|
+
font-size: 2rem;
|
|
251
|
+
font-weight: 700;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.empty-state {
|
|
255
|
+
color: #888;
|
|
256
|
+
padding: 2rem;
|
|
257
|
+
text-align: center;
|
|
258
|
+
background: #111;
|
|
259
|
+
border-radius: 8px;
|
|
260
|
+
border: 1px solid #222;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.user-list {
|
|
264
|
+
list-style: none;
|
|
265
|
+
display: grid;
|
|
266
|
+
gap: 1rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.user-card {
|
|
270
|
+
background: #111;
|
|
271
|
+
padding: 1rem;
|
|
272
|
+
border-radius: 8px;
|
|
273
|
+
border: 1px solid #222;
|
|
274
|
+
display: flex;
|
|
275
|
+
justify-content: space-between;
|
|
276
|
+
align-items: center;
|
|
277
|
+
transition: border-color 0.2s;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.user-card:hover {
|
|
281
|
+
border-color: #00d9ff;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.user-name {
|
|
285
|
+
font-weight: 600;
|
|
286
|
+
color: #ededed;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.user-email {
|
|
290
|
+
color: #888;
|
|
291
|
+
font-size: 0.875rem;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* About Page */
|
|
295
|
+
.about-page h1 {
|
|
296
|
+
font-size: 2rem;
|
|
297
|
+
margin-bottom: 1.5rem;
|
|
298
|
+
font-weight: 700;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.about-page h2 {
|
|
302
|
+
font-size: 1.5rem;
|
|
303
|
+
margin-top: 2rem;
|
|
304
|
+
margin-bottom: 1rem;
|
|
305
|
+
font-weight: 600;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.about-page p {
|
|
309
|
+
color: #888;
|
|
310
|
+
margin-bottom: 1rem;
|
|
311
|
+
line-height: 1.8;
|
|
312
|
+
}
|
|
313
|
+
`}</style>
|
|
314
|
+
</head>
|
|
315
|
+
<body>
|
|
316
|
+
<div className="layout">
|
|
317
|
+
<nav className="nav">
|
|
318
|
+
<ul className="nav-list">
|
|
319
|
+
<li>
|
|
320
|
+
<a href="/" className="nav-link">
|
|
321
|
+
Home
|
|
322
|
+
</a>
|
|
323
|
+
</li>
|
|
324
|
+
<li>
|
|
325
|
+
<a href="/users" className="nav-link">
|
|
326
|
+
Users
|
|
327
|
+
</a>
|
|
328
|
+
</li>
|
|
329
|
+
<li>
|
|
330
|
+
<a href="/dashboard" className="nav-link">
|
|
331
|
+
Dashboard
|
|
332
|
+
</a>
|
|
333
|
+
</li>
|
|
334
|
+
<li>
|
|
335
|
+
<a href="/auth/login" className="nav-link">
|
|
336
|
+
Login
|
|
337
|
+
</a>
|
|
338
|
+
</li>
|
|
339
|
+
<li>
|
|
340
|
+
<a href="/auth/register" className="nav-link">
|
|
341
|
+
Register
|
|
342
|
+
</a>
|
|
343
|
+
</li>
|
|
344
|
+
</ul>
|
|
345
|
+
</nav>
|
|
346
|
+
|
|
347
|
+
<main className="main">{children}</main>
|
|
348
|
+
|
|
349
|
+
<footer className="footer">Built with VeloxTS • React Server Components</footer>
|
|
350
|
+
</div>
|
|
351
|
+
<script src="/_build/src/entry.client.tsx" type="module" />
|
|
352
|
+
</body>
|
|
353
|
+
</html>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 404 Not Found Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export default function NotFoundPage() {
|
|
6
|
+
return (
|
|
7
|
+
<div style={{ padding: '4rem', textAlign: 'center' }}>
|
|
8
|
+
<h1 style={{ fontSize: '4rem', margin: 0 }}>404</h1>
|
|
9
|
+
<p style={{ fontSize: '1.5rem', color: '#666' }}>Page not found</p>
|
|
10
|
+
<a href="/" style={{ color: '#007bff' }}>
|
|
11
|
+
Go home
|
|
12
|
+
</a>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|