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.
Files changed (74) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/GUIDE.md +230 -0
  3. package/dist/cli.js +1 -0
  4. package/dist/index.js +14 -4
  5. package/dist/templates/auth.js +10 -0
  6. package/dist/templates/index.js +30 -1
  7. package/dist/templates/placeholders.js +0 -3
  8. package/dist/templates/rsc-auth.d.ts +12 -0
  9. package/dist/templates/rsc-auth.js +208 -0
  10. package/dist/templates/rsc.js +40 -1
  11. package/dist/templates/shared/css-generator.d.ts +26 -0
  12. package/dist/templates/shared/css-generator.js +553 -0
  13. package/dist/templates/shared/index.d.ts +3 -0
  14. package/dist/templates/shared/index.js +3 -0
  15. package/dist/templates/shared/rsc-styles.d.ts +54 -0
  16. package/dist/templates/shared/rsc-styles.js +68 -0
  17. package/dist/templates/shared/theme.d.ts +133 -0
  18. package/dist/templates/shared/theme.js +141 -0
  19. package/dist/templates/spa.js +10 -0
  20. package/dist/templates/trpc.js +10 -0
  21. package/dist/templates/types.d.ts +2 -1
  22. package/dist/templates/types.js +6 -0
  23. package/package.json +6 -3
  24. package/src/templates/source/api/config/database.ts +13 -32
  25. package/src/templates/source/api/docker-compose.yml +21 -0
  26. package/src/templates/source/root/CLAUDE.auth.md +6 -0
  27. package/src/templates/source/root/CLAUDE.default.md +6 -0
  28. package/src/templates/source/rsc/CLAUDE.md +56 -2
  29. package/src/templates/source/rsc/app/actions/posts.ts +1 -1
  30. package/src/templates/source/rsc/app/actions/users.ts +111 -20
  31. package/src/templates/source/rsc/app/layouts/dashboard.tsx +21 -16
  32. package/src/templates/source/rsc/app/layouts/marketing.tsx +34 -0
  33. package/src/templates/source/rsc/app/layouts/minimal-content.tsx +21 -0
  34. package/src/templates/source/rsc/app/layouts/minimal.tsx +86 -5
  35. package/src/templates/source/rsc/app/layouts/root.tsx +148 -44
  36. package/src/templates/source/rsc/docker-compose.yml +21 -0
  37. package/src/templates/source/rsc/package.json +3 -3
  38. package/src/templates/source/rsc/src/api/database.ts +13 -32
  39. package/src/templates/source/rsc/src/api/handler.ts +1 -1
  40. package/src/templates/source/rsc/src/entry.client.tsx +65 -18
  41. package/src/templates/source/rsc-auth/CLAUDE.md +230 -0
  42. package/src/templates/source/rsc-auth/app/actions/auth.ts +112 -0
  43. package/src/templates/source/rsc-auth/app/actions/users.ts +289 -0
  44. package/src/templates/source/rsc-auth/app/layouts/dashboard.tsx +132 -0
  45. package/src/templates/source/rsc-auth/app/layouts/marketing.tsx +59 -0
  46. package/src/templates/source/rsc-auth/app/layouts/minimal-content.tsx +21 -0
  47. package/src/templates/source/rsc-auth/app/layouts/minimal.tsx +111 -0
  48. package/src/templates/source/rsc-auth/app/layouts/root.tsx +355 -0
  49. package/src/templates/source/rsc-auth/app/pages/_not-found.tsx +15 -0
  50. package/src/templates/source/rsc-auth/app/pages/auth/login.tsx +198 -0
  51. package/src/templates/source/rsc-auth/app/pages/auth/register.tsx +225 -0
  52. package/src/templates/source/rsc-auth/app/pages/dashboard/index.tsx +267 -0
  53. package/src/templates/source/rsc-auth/app/pages/index.tsx +83 -0
  54. package/src/templates/source/rsc-auth/app/pages/users.tsx +47 -0
  55. package/src/templates/source/rsc-auth/app.config.ts +12 -0
  56. package/src/templates/source/rsc-auth/docker-compose.yml +21 -0
  57. package/src/templates/source/rsc-auth/env.example +11 -0
  58. package/src/templates/source/rsc-auth/gitignore +34 -0
  59. package/src/templates/source/rsc-auth/package.json +44 -0
  60. package/src/templates/source/rsc-auth/prisma/schema.prisma +23 -0
  61. package/src/templates/source/rsc-auth/prisma.config.ts +22 -0
  62. package/src/templates/source/rsc-auth/public/favicon.svg +4 -0
  63. package/src/templates/source/rsc-auth/src/api/database.ts +129 -0
  64. package/src/templates/source/rsc-auth/src/api/handler.ts +85 -0
  65. package/src/templates/source/rsc-auth/src/api/procedures/auth.ts +262 -0
  66. package/src/templates/source/rsc-auth/src/api/procedures/health.ts +48 -0
  67. package/src/templates/source/rsc-auth/src/api/procedures/users.ts +87 -0
  68. package/src/templates/source/rsc-auth/src/api/schemas/auth.ts +79 -0
  69. package/src/templates/source/rsc-auth/src/api/schemas/user.ts +38 -0
  70. package/src/templates/source/rsc-auth/src/api/utils/auth.ts +157 -0
  71. package/src/templates/source/rsc-auth/src/entry.client.tsx +63 -0
  72. package/src/templates/source/rsc-auth/src/entry.server.tsx +262 -0
  73. package/src/templates/source/rsc-auth/tsconfig.json +24 -0
  74. package/src/templates/source/shared/scripts/check-client-imports.sh +75 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Login Page
3
+ *
4
+ * Client component for authentication using server actions.
5
+ * Tokens are stored in httpOnly cookies automatically by the server action.
6
+ */
7
+ 'use client';
8
+
9
+ import { useState } from 'react';
10
+
11
+ import { login } from '@/app/actions/auth';
12
+
13
+ export default function LoginPage() {
14
+ const [email, setEmail] = useState('');
15
+ const [password, setPassword] = useState('');
16
+ const [error, setError] = useState('');
17
+ const [loading, setLoading] = useState(false);
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault();
21
+ setError('');
22
+ setLoading(true);
23
+
24
+ try {
25
+ // Call server action - tokens are stored in httpOnly cookies automatically
26
+ const result = await login({ email, password });
27
+
28
+ if (!result.success) {
29
+ throw new Error(result.error.message);
30
+ }
31
+
32
+ // Redirect to dashboard (cookies are already set by server action)
33
+ window.location.href = '/dashboard';
34
+ } catch (err) {
35
+ setError(err instanceof Error ? err.message : 'Login failed');
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+
41
+ return (
42
+ <div className="auth-container">
43
+ <style>{`
44
+ .auth-container {
45
+ max-width: 450px;
46
+ margin: 0 auto;
47
+ }
48
+
49
+ .auth-card {
50
+ background: #111;
51
+ padding: 2rem;
52
+ border-radius: 8px;
53
+ border: 1px solid #222;
54
+ }
55
+
56
+ .auth-card h1 {
57
+ font-size: 1.5rem;
58
+ font-weight: 700;
59
+ margin-bottom: 1.5rem;
60
+ text-align: center;
61
+ color: #ededed;
62
+ }
63
+
64
+ .auth-form {
65
+ margin-top: 1.5rem;
66
+ }
67
+
68
+ .form-group {
69
+ margin-bottom: 1.25rem;
70
+ }
71
+
72
+ .form-label {
73
+ display: block;
74
+ margin-bottom: 0.5rem;
75
+ color: #ededed;
76
+ font-weight: 500;
77
+ font-size: 0.875rem;
78
+ }
79
+
80
+ .form-input {
81
+ width: 100%;
82
+ padding: 0.75rem 1rem;
83
+ border-radius: 4px;
84
+ border: 1px solid #222;
85
+ background: #0a0a0a;
86
+ color: #ededed;
87
+ font-size: 0.875rem;
88
+ transition: border-color 0.2s, background 0.2s;
89
+ }
90
+
91
+ .form-input:focus {
92
+ outline: none;
93
+ border-color: #00d9ff;
94
+ background: #111;
95
+ }
96
+
97
+ .form-input:hover {
98
+ border-color: #333;
99
+ }
100
+
101
+ .btn {
102
+ width: 100%;
103
+ padding: 0.75rem 1.25rem;
104
+ border: none;
105
+ border-radius: 4px;
106
+ cursor: pointer;
107
+ font-weight: 500;
108
+ font-size: 0.875rem;
109
+ transition: background 0.2s, opacity 0.2s;
110
+ }
111
+
112
+ .btn-primary {
113
+ background: #00d9ff;
114
+ color: #000;
115
+ }
116
+
117
+ .btn-primary:hover:not(:disabled) {
118
+ background: rgba(0, 217, 255, 0.8);
119
+ }
120
+
121
+ .btn-primary:disabled {
122
+ opacity: 0.6;
123
+ cursor: not-allowed;
124
+ }
125
+
126
+ .error-message {
127
+ padding: 0.75rem;
128
+ background: #2a1111;
129
+ color: #ff6666;
130
+ border-radius: 4px;
131
+ margin-bottom: 1rem;
132
+ border: 1px solid #ff4444;
133
+ font-size: 0.875rem;
134
+ }
135
+
136
+ .auth-footer {
137
+ margin-top: 1.5rem;
138
+ text-align: center;
139
+ color: #888;
140
+ font-size: 0.875rem;
141
+ }
142
+
143
+ .auth-footer a {
144
+ color: #00d9ff;
145
+ font-weight: 500;
146
+ }
147
+
148
+ .auth-footer a:hover {
149
+ opacity: 0.8;
150
+ }
151
+ `}</style>
152
+
153
+ <div className="auth-card">
154
+ <h1>Login</h1>
155
+
156
+ <form onSubmit={handleSubmit} className="auth-form">
157
+ {error && <div className="error-message">{error}</div>}
158
+
159
+ <div className="form-group">
160
+ <label htmlFor="email" className="form-label">
161
+ Email
162
+ </label>
163
+ <input
164
+ id="email"
165
+ type="email"
166
+ value={email}
167
+ onChange={(e) => setEmail(e.target.value)}
168
+ required
169
+ className="form-input"
170
+ />
171
+ </div>
172
+
173
+ <div className="form-group">
174
+ <label htmlFor="password" className="form-label">
175
+ Password
176
+ </label>
177
+ <input
178
+ id="password"
179
+ type="password"
180
+ value={password}
181
+ onChange={(e) => setPassword(e.target.value)}
182
+ required
183
+ className="form-input"
184
+ />
185
+ </div>
186
+
187
+ <button type="submit" disabled={loading} className="btn btn-primary">
188
+ {loading ? 'Signing in...' : 'Sign In'}
189
+ </button>
190
+ </form>
191
+
192
+ <p className="auth-footer">
193
+ Don't have an account? <a href="/auth/register">Register</a>
194
+ </p>
195
+ </div>
196
+ </div>
197
+ );
198
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Registration Page
3
+ *
4
+ * Client component for user registration using server actions.
5
+ * Tokens are stored in httpOnly cookies automatically by the server action.
6
+ */
7
+ 'use client';
8
+
9
+ import { useState } from 'react';
10
+
11
+ import { register } from '@/app/actions/auth';
12
+
13
+ export default function RegisterPage() {
14
+ const [name, setName] = useState('');
15
+ const [email, setEmail] = useState('');
16
+ const [password, setPassword] = useState('');
17
+ const [error, setError] = useState('');
18
+ const [loading, setLoading] = useState(false);
19
+
20
+ const handleSubmit = async (e: React.FormEvent) => {
21
+ e.preventDefault();
22
+ setError('');
23
+ setLoading(true);
24
+
25
+ try {
26
+ // Call server action - tokens are stored in httpOnly cookies automatically
27
+ const result = await register({ name, email, password });
28
+
29
+ if (!result.success) {
30
+ throw new Error(result.error.message);
31
+ }
32
+
33
+ // Redirect to dashboard (cookies are already set by server action)
34
+ window.location.href = '/dashboard';
35
+ } catch (err) {
36
+ setError(err instanceof Error ? err.message : 'Registration failed');
37
+ } finally {
38
+ setLoading(false);
39
+ }
40
+ };
41
+
42
+ return (
43
+ <div className="auth-container">
44
+ <style>{`
45
+ .auth-container {
46
+ max-width: 450px;
47
+ margin: 0 auto;
48
+ }
49
+
50
+ .auth-card {
51
+ background: #111;
52
+ padding: 2rem;
53
+ border-radius: 8px;
54
+ border: 1px solid #222;
55
+ }
56
+
57
+ .auth-card h1 {
58
+ font-size: 1.5rem;
59
+ font-weight: 700;
60
+ margin-bottom: 1.5rem;
61
+ text-align: center;
62
+ color: #ededed;
63
+ }
64
+
65
+ .auth-form {
66
+ margin-top: 1.5rem;
67
+ }
68
+
69
+ .form-group {
70
+ margin-bottom: 1.25rem;
71
+ }
72
+
73
+ .form-label {
74
+ display: block;
75
+ margin-bottom: 0.5rem;
76
+ color: #ededed;
77
+ font-weight: 500;
78
+ font-size: 0.875rem;
79
+ }
80
+
81
+ .form-input {
82
+ width: 100%;
83
+ padding: 0.75rem 1rem;
84
+ border-radius: 4px;
85
+ border: 1px solid #222;
86
+ background: #0a0a0a;
87
+ color: #ededed;
88
+ font-size: 0.875rem;
89
+ transition: border-color 0.2s, background 0.2s;
90
+ }
91
+
92
+ .form-input:focus {
93
+ outline: none;
94
+ border-color: #00d9ff;
95
+ background: #111;
96
+ }
97
+
98
+ .form-input:hover {
99
+ border-color: #333;
100
+ }
101
+
102
+ .form-hint {
103
+ display: block;
104
+ color: #888;
105
+ font-size: 0.75rem;
106
+ margin-top: 0.5rem;
107
+ }
108
+
109
+ .btn {
110
+ width: 100%;
111
+ padding: 0.75rem 1.25rem;
112
+ border: none;
113
+ border-radius: 4px;
114
+ cursor: pointer;
115
+ font-weight: 500;
116
+ font-size: 0.875rem;
117
+ transition: background 0.2s, opacity 0.2s;
118
+ }
119
+
120
+ .btn-primary {
121
+ background: #00d9ff;
122
+ color: #000;
123
+ }
124
+
125
+ .btn-primary:hover:not(:disabled) {
126
+ background: rgba(0, 217, 255, 0.8);
127
+ }
128
+
129
+ .btn-primary:disabled {
130
+ opacity: 0.6;
131
+ cursor: not-allowed;
132
+ }
133
+
134
+ .error-message {
135
+ padding: 0.75rem;
136
+ background: #2a1111;
137
+ color: #ff6666;
138
+ border-radius: 4px;
139
+ margin-bottom: 1rem;
140
+ border: 1px solid #ff4444;
141
+ font-size: 0.875rem;
142
+ }
143
+
144
+ .auth-footer {
145
+ margin-top: 1.5rem;
146
+ text-align: center;
147
+ color: #888;
148
+ font-size: 0.875rem;
149
+ }
150
+
151
+ .auth-footer a {
152
+ color: #00d9ff;
153
+ font-weight: 500;
154
+ }
155
+
156
+ .auth-footer a:hover {
157
+ opacity: 0.8;
158
+ }
159
+ `}</style>
160
+
161
+ <div className="auth-card">
162
+ <h1>Create Account</h1>
163
+
164
+ <form onSubmit={handleSubmit} className="auth-form">
165
+ {error && <div className="error-message">{error}</div>}
166
+
167
+ <div className="form-group">
168
+ <label htmlFor="name" className="form-label">
169
+ Name
170
+ </label>
171
+ <input
172
+ id="name"
173
+ type="text"
174
+ value={name}
175
+ onChange={(e) => setName(e.target.value)}
176
+ required
177
+ minLength={2}
178
+ className="form-input"
179
+ />
180
+ </div>
181
+
182
+ <div className="form-group">
183
+ <label htmlFor="email" className="form-label">
184
+ Email
185
+ </label>
186
+ <input
187
+ id="email"
188
+ type="email"
189
+ value={email}
190
+ onChange={(e) => setEmail(e.target.value)}
191
+ required
192
+ className="form-input"
193
+ />
194
+ </div>
195
+
196
+ <div className="form-group">
197
+ <label htmlFor="password" className="form-label">
198
+ Password
199
+ </label>
200
+ <input
201
+ id="password"
202
+ type="password"
203
+ value={password}
204
+ onChange={(e) => setPassword(e.target.value)}
205
+ required
206
+ minLength={12}
207
+ className="form-input"
208
+ />
209
+ <small className="form-hint">
210
+ At least 12 characters with uppercase, lowercase, and number
211
+ </small>
212
+ </div>
213
+
214
+ <button type="submit" disabled={loading} className="btn btn-primary">
215
+ {loading ? 'Creating account...' : 'Create Account'}
216
+ </button>
217
+ </form>
218
+
219
+ <p className="auth-footer">
220
+ Already have an account? <a href="/auth/login">Login</a>
221
+ </p>
222
+ </div>
223
+ </div>
224
+ );
225
+ }
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Dashboard Page
3
+ *
4
+ * Protected page that requires authentication.
5
+ * Uses httpOnly cookie-based authentication (tokens stored in cookies by server actions).
6
+ */
7
+ 'use client';
8
+
9
+ import { useEffect, useState } from 'react';
10
+
11
+ import { logout } from '@/app/actions/auth';
12
+
13
+ interface User {
14
+ id: string;
15
+ name: string;
16
+ email: string;
17
+ roles: string[];
18
+ }
19
+
20
+ export default function DashboardPage() {
21
+ const [user, setUser] = useState<User | null>(null);
22
+ const [loading, setLoading] = useState(true);
23
+ const [error, setError] = useState('');
24
+
25
+ useEffect(() => {
26
+ const fetchUser = async () => {
27
+ try {
28
+ // Cookies are sent automatically with credentials: 'include'
29
+ const response = await fetch('/api/auth/me', {
30
+ credentials: 'include',
31
+ });
32
+
33
+ if (!response.ok) {
34
+ // Token invalid, expired, or not present - redirect to login
35
+ window.location.href = '/auth/login';
36
+ return;
37
+ }
38
+
39
+ const userData = await response.json();
40
+ setUser(userData);
41
+ } catch {
42
+ setError('Failed to load user data');
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ };
47
+
48
+ fetchUser();
49
+ }, []);
50
+
51
+ const handleLogout = async () => {
52
+ try {
53
+ // Call server action to clear auth cookies
54
+ await logout();
55
+ } catch {
56
+ // Ignore errors - we're redirecting anyway
57
+ }
58
+
59
+ // Redirect to home
60
+ window.location.href = '/';
61
+ };
62
+
63
+ if (loading) {
64
+ return (
65
+ <div className="dashboard-loading">
66
+ <style>{`
67
+ .dashboard-loading {
68
+ padding: 2rem;
69
+ text-align: center;
70
+ color: #888;
71
+ }
72
+ `}</style>
73
+ Loading...
74
+ </div>
75
+ );
76
+ }
77
+
78
+ if (error) {
79
+ return (
80
+ <div className="dashboard-error">
81
+ <style>{`
82
+ .dashboard-error {
83
+ padding: 2rem;
84
+ text-align: center;
85
+ color: #ff6666;
86
+ }
87
+ `}</style>
88
+ {error}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ if (!user) {
94
+ return null;
95
+ }
96
+
97
+ return (
98
+ <div className="dashboard">
99
+ <style>{`
100
+ .dashboard {
101
+ max-width: 800px;
102
+ margin: 0 auto;
103
+ }
104
+
105
+ .dashboard-header {
106
+ display: flex;
107
+ justify-content: space-between;
108
+ align-items: center;
109
+ margin-bottom: 2rem;
110
+ }
111
+
112
+ .dashboard-header h1 {
113
+ font-size: 1.75rem;
114
+ font-weight: 700;
115
+ color: #ededed;
116
+ margin: 0;
117
+ }
118
+
119
+ .btn-logout {
120
+ padding: 0.5rem 1rem;
121
+ background: transparent;
122
+ color: #ff6666;
123
+ border: 1px solid #ff6666;
124
+ border-radius: 4px;
125
+ cursor: pointer;
126
+ font-weight: 500;
127
+ font-size: 0.875rem;
128
+ transition: background 0.2s, color 0.2s;
129
+ }
130
+
131
+ .btn-logout:hover {
132
+ background: #ff6666;
133
+ color: #000;
134
+ }
135
+
136
+ .user-card {
137
+ padding: 1.5rem;
138
+ background: #111;
139
+ border: 1px solid #222;
140
+ border-radius: 8px;
141
+ margin-bottom: 1.5rem;
142
+ }
143
+
144
+ .user-card h2 {
145
+ font-size: 1.25rem;
146
+ font-weight: 600;
147
+ color: #ededed;
148
+ margin: 0 0 1rem 0;
149
+ }
150
+
151
+ .user-info {
152
+ color: #888;
153
+ font-size: 0.875rem;
154
+ line-height: 1.8;
155
+ }
156
+
157
+ .user-info strong {
158
+ color: #ededed;
159
+ }
160
+
161
+ .actions-card {
162
+ padding: 1.5rem;
163
+ background: #0d1a26;
164
+ border: 1px solid #1a3a5c;
165
+ border-radius: 8px;
166
+ margin-bottom: 1.5rem;
167
+ }
168
+
169
+ .actions-card h3 {
170
+ font-size: 1rem;
171
+ font-weight: 600;
172
+ color: #00d9ff;
173
+ margin: 0 0 0.75rem 0;
174
+ }
175
+
176
+ .actions-card p {
177
+ color: #888;
178
+ font-size: 0.875rem;
179
+ margin: 0 0 1rem 0;
180
+ }
181
+
182
+ .actions-list {
183
+ list-style: none;
184
+ padding: 0;
185
+ margin: 0;
186
+ }
187
+
188
+ .actions-list li {
189
+ color: #888;
190
+ font-size: 0.875rem;
191
+ padding: 0.5rem 0;
192
+ border-bottom: 1px solid #1a3a5c;
193
+ }
194
+
195
+ .actions-list li:last-child {
196
+ border-bottom: none;
197
+ }
198
+
199
+ .admin-tag {
200
+ display: inline-block;
201
+ padding: 0.125rem 0.5rem;
202
+ background: #00d9ff;
203
+ color: #000;
204
+ border-radius: 4px;
205
+ font-size: 0.75rem;
206
+ font-weight: 600;
207
+ margin-right: 0.5rem;
208
+ }
209
+
210
+ .back-link {
211
+ display: inline-block;
212
+ color: #00d9ff;
213
+ font-size: 0.875rem;
214
+ text-decoration: none;
215
+ }
216
+
217
+ .back-link:hover {
218
+ opacity: 0.8;
219
+ }
220
+ `}</style>
221
+
222
+ <div className="dashboard-header">
223
+ <h1>Dashboard</h1>
224
+ <button type="button" onClick={handleLogout} className="btn-logout">
225
+ Logout
226
+ </button>
227
+ </div>
228
+
229
+ <div className="user-card">
230
+ <h2>Welcome, {user.name || 'User'}!</h2>
231
+ <div className="user-info">
232
+ <p>
233
+ <strong>Email:</strong> {user.email}
234
+ </p>
235
+ <p>
236
+ <strong>Roles:</strong> {user.roles.join(', ')}
237
+ </p>
238
+ </div>
239
+ </div>
240
+
241
+ <div className="actions-card">
242
+ <h3>Your Actions</h3>
243
+ <p>As an authenticated user, you can:</p>
244
+ <ul className="actions-list">
245
+ <li>View your profile</li>
246
+ <li>Update your settings</li>
247
+ {user.roles.includes('admin') && (
248
+ <>
249
+ <li>
250
+ <span className="admin-tag">Admin</span>
251
+ Manage users
252
+ </li>
253
+ <li>
254
+ <span className="admin-tag">Admin</span>
255
+ View all data
256
+ </li>
257
+ </>
258
+ )}
259
+ </ul>
260
+ </div>
261
+
262
+ <a href="/" className="back-link">
263
+ ← Back to Home
264
+ </a>
265
+ </div>
266
+ );
267
+ }