@basicbenframework/core 0.1.0 → 0.1.5

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 (75) hide show
  1. package/.github/workflows/publish.yml +4 -5
  2. package/LICENSE +9 -0
  3. package/README.md +5 -5
  4. package/create-basicben-app/index.js +11 -9
  5. package/create-basicben-app/package.json +4 -4
  6. package/create-basicben-app/template/README.md +2 -2
  7. package/create-basicben-app/template/gitignore +31 -0
  8. package/create-basicben-app/template/src/client/pages/GettingStarted.jsx +2 -2
  9. package/create-basicben-app/template/src/client/pages/Home.jsx +2 -2
  10. package/package.json +11 -11
  11. package/scripts/publish.sh +125 -0
  12. package/my-test-app/.env.example +0 -24
  13. package/my-test-app/README.md +0 -59
  14. package/my-test-app/basicben.config.js +0 -33
  15. package/my-test-app/database.sqlite-shm +0 -0
  16. package/my-test-app/database.sqlite-wal +0 -0
  17. package/my-test-app/index.html +0 -54
  18. package/my-test-app/migrations/001_create_users.js +0 -15
  19. package/my-test-app/migrations/002_create_posts.js +0 -18
  20. package/my-test-app/package-lock.json +0 -2160
  21. package/my-test-app/package.json +0 -29
  22. package/my-test-app/public/.gitkeep +0 -0
  23. package/my-test-app/seeds/01_users.js +0 -29
  24. package/my-test-app/seeds/02_posts.js +0 -43
  25. package/my-test-app/src/client/components/Alert.jsx +0 -11
  26. package/my-test-app/src/client/components/Avatar.jsx +0 -11
  27. package/my-test-app/src/client/components/BackLink.jsx +0 -10
  28. package/my-test-app/src/client/components/Button.jsx +0 -19
  29. package/my-test-app/src/client/components/Card.jsx +0 -10
  30. package/my-test-app/src/client/components/Empty.jsx +0 -6
  31. package/my-test-app/src/client/components/Input.jsx +0 -12
  32. package/my-test-app/src/client/components/Loading.jsx +0 -6
  33. package/my-test-app/src/client/components/Logo.jsx +0 -40
  34. package/my-test-app/src/client/components/Nav/DarkModeToggle.jsx +0 -23
  35. package/my-test-app/src/client/components/Nav/DesktopNav.jsx +0 -32
  36. package/my-test-app/src/client/components/Nav/MobileNav.jsx +0 -107
  37. package/my-test-app/src/client/components/NavLink.jsx +0 -10
  38. package/my-test-app/src/client/components/PageHeader.jsx +0 -8
  39. package/my-test-app/src/client/components/PostCard.jsx +0 -19
  40. package/my-test-app/src/client/components/Textarea.jsx +0 -12
  41. package/my-test-app/src/client/components/ThemeContext.jsx +0 -5
  42. package/my-test-app/src/client/contexts/AppContext.jsx +0 -13
  43. package/my-test-app/src/client/contexts/ToastContext.jsx +0 -94
  44. package/my-test-app/src/client/layouts/AppLayout.jsx +0 -60
  45. package/my-test-app/src/client/layouts/AuthLayout.jsx +0 -33
  46. package/my-test-app/src/client/layouts/DocsLayout.jsx +0 -60
  47. package/my-test-app/src/client/layouts/RootLayout.jsx +0 -25
  48. package/my-test-app/src/client/pages/Auth.jsx +0 -55
  49. package/my-test-app/src/client/pages/Authentication.jsx +0 -236
  50. package/my-test-app/src/client/pages/Database.jsx +0 -426
  51. package/my-test-app/src/client/pages/Feed.jsx +0 -34
  52. package/my-test-app/src/client/pages/FeedPost.jsx +0 -37
  53. package/my-test-app/src/client/pages/GettingStarted.jsx +0 -136
  54. package/my-test-app/src/client/pages/Home.jsx +0 -206
  55. package/my-test-app/src/client/pages/PostForm.jsx +0 -69
  56. package/my-test-app/src/client/pages/Posts.jsx +0 -59
  57. package/my-test-app/src/client/pages/Profile.jsx +0 -68
  58. package/my-test-app/src/client/pages/Routing.jsx +0 -207
  59. package/my-test-app/src/client/pages/Testing.jsx +0 -251
  60. package/my-test-app/src/client/pages/Validation.jsx +0 -210
  61. package/my-test-app/src/controllers/AuthController.js +0 -81
  62. package/my-test-app/src/controllers/HomeController.js +0 -17
  63. package/my-test-app/src/controllers/PostController.js +0 -86
  64. package/my-test-app/src/controllers/ProfileController.js +0 -66
  65. package/my-test-app/src/helpers/api.js +0 -24
  66. package/my-test-app/src/main.jsx +0 -9
  67. package/my-test-app/src/middleware/auth.js +0 -16
  68. package/my-test-app/src/models/Post.js +0 -63
  69. package/my-test-app/src/models/User.js +0 -42
  70. package/my-test-app/src/routes/App.jsx +0 -38
  71. package/my-test-app/src/routes/api/auth.js +0 -7
  72. package/my-test-app/src/routes/api/posts.js +0 -15
  73. package/my-test-app/src/routes/api/profile.js +0 -8
  74. package/my-test-app/src/server/index.js +0 -16
  75. package/my-test-app/vite.config.js +0 -18
@@ -1,426 +0,0 @@
1
- import { useTheme } from '../components/ThemeContext'
2
- import { Card } from '../components/Card'
3
- import { PageHeader } from '../components/PageHeader'
4
- import { AppLayout } from '../layouts/AppLayout'
5
- import { DocsLayout } from '../layouts/DocsLayout'
6
-
7
- export function Database() {
8
- const { t } = useTheme()
9
-
10
- const CodeBlock = ({ children, title }) => (
11
- <div className="mt-4">
12
- {title && <div className={`text-xs font-medium mb-2 ${t.muted}`}>{title}</div>}
13
- <div className={`rounded-lg p-4 font-mono text-sm ${t.card} border ${t.border} overflow-x-auto`}>
14
- <pre className={t.text}>{children}</pre>
15
- </div>
16
- </div>
17
- )
18
-
19
- return (
20
- <div>
21
- <PageHeader
22
- title="Database"
23
- subtitle="Adapters, migrations, seeding, and queries"
24
- />
25
-
26
- <div className="space-y-6">
27
- {/* Database Adapters */}
28
- <Card>
29
- <h2 className="text-lg font-semibold mb-2">Database Adapters</h2>
30
- <p className={`text-sm ${t.muted} mb-4`}>
31
- BasicBen supports multiple databases. Configure your adapter in <code>basicben.config.js</code>.
32
- </p>
33
-
34
- <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 mb-4">
35
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
36
- <div className="font-semibold text-sm">SQLite</div>
37
- <p className={`text-xs mt-1 ${t.muted}`}>Local file database (default)</p>
38
- </div>
39
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
40
- <div className="font-semibold text-sm">PostgreSQL</div>
41
- <p className={`text-xs mt-1 ${t.muted}`}>Traditional Postgres</p>
42
- </div>
43
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
44
- <div className="font-semibold text-sm">Turso</div>
45
- <p className={`text-xs mt-1 ${t.muted}`}>Edge SQLite (libSQL)</p>
46
- </div>
47
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
48
- <div className="font-semibold text-sm">PlanetScale</div>
49
- <p className={`text-xs mt-1 ${t.muted}`}>Serverless MySQL</p>
50
- </div>
51
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
52
- <div className="font-semibold text-sm">Neon</div>
53
- <p className={`text-xs mt-1 ${t.muted}`}>Serverless Postgres</p>
54
- </div>
55
- </div>
56
-
57
- <CodeBlock title="basicben.config.js - SQLite (default)">
58
- {`export default {
59
- db: {
60
- driver: 'sqlite',
61
- url: './database.sqlite'
62
- }
63
- }`}
64
- </CodeBlock>
65
-
66
- <CodeBlock title="basicben.config.js - Turso">
67
- {`export default {
68
- db: {
69
- driver: 'turso',
70
- url: process.env.TURSO_URL,
71
- authToken: process.env.TURSO_AUTH_TOKEN
72
- }
73
- }`}
74
- </CodeBlock>
75
-
76
- <CodeBlock title="basicben.config.js - PlanetScale">
77
- {`export default {
78
- db: {
79
- driver: 'planetscale',
80
- url: process.env.DATABASE_URL
81
- }
82
- }`}
83
- </CodeBlock>
84
-
85
- <CodeBlock title="basicben.config.js - Neon">
86
- {`export default {
87
- db: {
88
- driver: 'neon',
89
- url: process.env.DATABASE_URL
90
- }
91
- }`}
92
- </CodeBlock>
93
-
94
- <CodeBlock title="Install the driver you need">
95
- {`# SQLite (default)
96
- npm install better-sqlite3
97
-
98
- # PostgreSQL
99
- npm install pg
100
-
101
- # Turso
102
- npm install @libsql/client
103
-
104
- # PlanetScale
105
- npm install @planetscale/database
106
-
107
- # Neon
108
- npm install @neondatabase/serverless`}
109
- </CodeBlock>
110
- </Card>
111
-
112
- {/* Migrations */}
113
- <Card>
114
- <h2 className="text-lg font-semibold mb-2">Migrations</h2>
115
- <p className={`text-sm ${t.muted} mb-4`}>
116
- Migrations let you version control your database schema. Each migration has an <code>up</code> function to apply changes and a <code>down</code> function to reverse them.
117
- </p>
118
-
119
- <CodeBlock title="Generate a migration">
120
- {`npx basicben make:migration create_posts`}
121
- </CodeBlock>
122
-
123
- <CodeBlock title="migrations/001_create_posts.js">
124
- {`export const up = (db) => {
125
- db.exec(\`
126
- CREATE TABLE posts (
127
- id INTEGER PRIMARY KEY AUTOINCREMENT,
128
- user_id INTEGER NOT NULL,
129
- title TEXT NOT NULL,
130
- content TEXT,
131
- published INTEGER DEFAULT 0,
132
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
133
- FOREIGN KEY (user_id) REFERENCES users(id)
134
- )
135
- \`)
136
- }
137
-
138
- export const down = (db) => {
139
- db.exec('DROP TABLE posts')
140
- }`}
141
- </CodeBlock>
142
-
143
- <div className="mt-4 grid gap-3 sm:grid-cols-2">
144
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
145
- <code className="text-sm font-semibold">npx basicben migrate</code>
146
- <p className={`text-xs mt-1 ${t.muted}`}>Run pending migrations</p>
147
- </div>
148
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
149
- <code className="text-sm font-semibold">npx basicben migrate:rollback</code>
150
- <p className={`text-xs mt-1 ${t.muted}`}>Undo last batch</p>
151
- </div>
152
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
153
- <code className="text-sm font-semibold">npx basicben migrate:fresh</code>
154
- <p className={`text-xs mt-1 ${t.muted}`}>Drop all & re-run</p>
155
- </div>
156
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
157
- <code className="text-sm font-semibold">npx basicben migrate:status</code>
158
- <p className={`text-xs mt-1 ${t.muted}`}>Show migration status</p>
159
- </div>
160
- </div>
161
- </Card>
162
-
163
- {/* Seeding */}
164
- <Card>
165
- <h2 className="text-lg font-semibold mb-2">Seeding</h2>
166
- <p className={`text-sm ${t.muted} mb-4`}>
167
- Seeds populate your database with initial or test data. Create seed files in the <code>seeds/</code> directory. Seeds run in alphabetical order.
168
- </p>
169
-
170
- <CodeBlock title="Generate a seed file">
171
- {`npx basicben make:seed users
172
- # Creates: seeds/users.js`}
173
- </CodeBlock>
174
-
175
- <CodeBlock title="seeds/01_users.js">
176
- {`import { db } from 'basicben'
177
- import { hashPassword } from 'basicben/auth'
178
-
179
- export async function seed() {
180
- const password = await hashPassword('password123')
181
-
182
- await (await db.table('users')).insert({
183
- name: 'Admin User',
184
- email: 'admin@example.com',
185
- password
186
- })
187
-
188
- await (await db.table('users')).insert({
189
- name: 'Test User',
190
- email: 'test@example.com',
191
- password
192
- })
193
-
194
- console.log('Seeded 2 users')
195
- }`}
196
- </CodeBlock>
197
-
198
- <CodeBlock title="seeds/02_posts.js">
199
- {`import { db } from 'basicben'
200
-
201
- export async function seed() {
202
- const user = await (await db.table('users')).first()
203
-
204
- await (await db.table('posts')).insert({
205
- user_id: user.id,
206
- title: 'Welcome to BasicBen',
207
- content: 'Your first blog post!',
208
- published: 1
209
- })
210
-
211
- console.log('Seeded posts')
212
- }`}
213
- </CodeBlock>
214
-
215
- <div className="mt-4 grid gap-3 sm:grid-cols-2">
216
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
217
- <code className="text-sm font-semibold">npx basicben seed</code>
218
- <p className={`text-xs mt-1 ${t.muted}`}>Run all seeds</p>
219
- </div>
220
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
221
- <code className="text-sm font-semibold">npx basicben seed users</code>
222
- <p className={`text-xs mt-1 ${t.muted}`}>Run specific seed</p>
223
- </div>
224
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
225
- <code className="text-sm font-semibold">npx basicben make:seed</code>
226
- <p className={`text-xs mt-1 ${t.muted}`}>Generate seed file</p>
227
- </div>
228
- <div className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
229
- <code className="text-sm font-semibold">npx basicben db:seed</code>
230
- <p className={`text-xs mt-1 ${t.muted}`}>Alias for seed</p>
231
- </div>
232
- </div>
233
- </Card>
234
-
235
- {/* Query Builder */}
236
- <Card>
237
- <h2 className="text-lg font-semibold mb-2">Query Builder</h2>
238
- <p className={`text-sm ${t.muted} mb-4`}>
239
- The query builder provides a fluent API for database queries with built-in SQL injection protection.
240
- </p>
241
-
242
- <CodeBlock title="Basic queries">
243
- {`import { db } from 'basicben'
244
-
245
- // Get all users
246
- const users = await (await db.table('users')).get()
247
-
248
- // Find by ID
249
- const user = await (await db.table('users')).find(1)
250
-
251
- // Filter with where
252
- const admins = await (await db.table('users'))
253
- .where('is_admin', true)
254
- .get()
255
-
256
- // Chain multiple conditions
257
- const results = await (await db.table('posts'))
258
- .where('published', true)
259
- .where('user_id', userId)
260
- .orderBy('created_at', 'DESC')
261
- .limit(10)
262
- .get()`}
263
- </CodeBlock>
264
-
265
- <CodeBlock title="Insert & Update">
266
- {`// Insert a record
267
- const result = await (await db.table('posts'))
268
- .insert({
269
- title: 'My Post',
270
- content: 'Hello world',
271
- user_id: 1
272
- })
273
-
274
- console.log(result.lastInsertRowid) // New ID
275
-
276
- // Update records
277
- await (await db.table('posts'))
278
- .where('id', postId)
279
- .update({ title: 'Updated Title' })
280
-
281
- // Delete records
282
- await (await db.table('posts'))
283
- .where('id', postId)
284
- .delete()`}
285
- </CodeBlock>
286
-
287
- <CodeBlock title="Aggregates & Pagination">
288
- {`// Count records
289
- const count = await (await db.table('posts'))
290
- .where('published', true)
291
- .count()
292
-
293
- // Check existence
294
- const exists = await (await db.table('users'))
295
- .where('email', 'test@example.com')
296
- .exists()
297
-
298
- // Paginate results
299
- const page = await (await db.table('posts'))
300
- .orderBy('created_at', 'DESC')
301
- .paginate(1, 15)
302
-
303
- // Returns: { data, total, page, perPage, totalPages }`}
304
- </CodeBlock>
305
- </Card>
306
-
307
- {/* Mass Assignment Protection */}
308
- <Card>
309
- <h2 className="text-lg font-semibold mb-2">Mass Assignment Protection</h2>
310
- <p className={`text-sm ${t.muted} mb-4`}>
311
- Prevent users from setting fields they shouldn't (like <code>is_admin</code>) by using <code>only()</code> or <code>except()</code>.
312
- </p>
313
-
314
- <CodeBlock title="Whitelist with only()">
315
- {`// Only allow these fields to be set
316
- await (await db.table('users'))
317
- .only('name', 'email', 'bio')
318
- .insert(req.body)
319
-
320
- // Extra fields in req.body are ignored
321
- // { name: 'Bob', email: 'bob@test.com', is_admin: true }
322
- // is_admin is silently dropped`}
323
- </CodeBlock>
324
-
325
- <CodeBlock title="Blacklist with except()">
326
- {`// Block specific fields
327
- await (await db.table('users'))
328
- .except('id', 'is_admin', 'created_at')
329
- .where('id', userId)
330
- .update(req.body)`}
331
- </CodeBlock>
332
-
333
- <CodeBlock title="In your models">
334
- {`// src/models/User.js
335
- export const User = {
336
- fillable: ['name', 'email', 'bio'],
337
-
338
- create: async (data) => {
339
- return (await db.table('users'))
340
- .only(...User.fillable)
341
- .insert(data)
342
- },
343
-
344
- update: async (id, data) => {
345
- return (await db.table('users'))
346
- .only(...User.fillable)
347
- .where('id', id)
348
- .update(data)
349
- }
350
- }`}
351
- </CodeBlock>
352
- </Card>
353
-
354
- {/* Database Validation */}
355
- <Card>
356
- <h2 className="text-lg font-semibold mb-2">Database Validation</h2>
357
- <p className={`text-sm ${t.muted} mb-4`}>
358
- Validate data against your database with <code>unique</code> and <code>exists</code> rules.
359
- </p>
360
-
361
- <CodeBlock title="Unique validation">
362
- {`import { validate, rules } from 'basicben/validation'
363
-
364
- // Check email is unique in users table
365
- const result = await validate(req.body, {
366
- email: [rules.required, rules.email, rules.unique('users')]
367
- })
368
-
369
- // With custom column name
370
- slug: [rules.unique('categories', 'slug')]
371
-
372
- // Exclude current record (for updates)
373
- email: [rules.unique('users', 'email', userId)]`}
374
- </CodeBlock>
375
-
376
- <CodeBlock title="Exists validation">
377
- {`// Check foreign key exists
378
- const result = await validate(req.body, {
379
- user_id: [rules.required, rules.exists('users')],
380
- category_id: [rules.required, rules.exists('categories')]
381
- })
382
-
383
- // With custom column
384
- category: [rules.exists('categories', 'slug')]`}
385
- </CodeBlock>
386
- </Card>
387
-
388
- {/* Raw Queries */}
389
- <Card>
390
- <h2 className="text-lg font-semibold mb-2">Raw Queries</h2>
391
- <p className={`text-sm ${t.muted} mb-4`}>
392
- For complex queries, you can use raw SQL with parameterized queries.
393
- </p>
394
-
395
- <CodeBlock title="Using db directly">
396
- {`import { db } from 'basicben'
397
-
398
- // Parameterized queries (safe from SQL injection)
399
- const posts = await db.all(
400
- 'SELECT * FROM posts WHERE user_id = ? AND published = ?',
401
- [userId, true]
402
- )
403
-
404
- // Single row
405
- const user = await db.get(
406
- 'SELECT * FROM users WHERE email = ?',
407
- [email]
408
- )
409
-
410
- // Insert/Update/Delete
411
- const result = await db.run(
412
- 'INSERT INTO posts (title, user_id) VALUES (?, ?)',
413
- [title, userId]
414
- )
415
-
416
- // Transactions
417
- await db.transaction(async (tx) => {
418
- await tx.run('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, fromId])
419
- await tx.run('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, toId])
420
- })`}
421
- </CodeBlock>
422
- </Card>
423
- </div>
424
- </div>
425
- )
426
- }
@@ -1,34 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
- import { useNavigate } from '@basicbenframework/core/client'
3
- import { PageHeader } from '../components/PageHeader'
4
- import { PostCard } from '../components/PostCard'
5
- import { Loading } from '../components/Loading'
6
- import { Empty } from '../components/Empty'
7
- import { api } from '../../helpers/api'
8
-
9
- export function Feed() {
10
- const navigate = useNavigate()
11
- const [posts, setPosts] = useState([])
12
- const [loading, setLoading] = useState(true)
13
-
14
- useEffect(() => {
15
- api('/api/feed').then(data => setPosts(data.posts)).finally(() => setLoading(false))
16
- }, [])
17
-
18
- if (loading) return <Loading />
19
-
20
- return (
21
- <div>
22
- <PageHeader title="Feed" />
23
- {posts.length === 0 ? (
24
- <Empty>No posts yet</Empty>
25
- ) : (
26
- <div className="space-y-4">
27
- {posts.map(post => (
28
- <PostCard key={post.id} post={post} onClick={() => navigate(`/feed/${post.id}`)} showAuthor />
29
- ))}
30
- </div>
31
- )}
32
- </div>
33
- )
34
- }
@@ -1,37 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
- import { useNavigate, useParams } from '@basicbenframework/core/client'
3
- import { useTheme } from '../components/ThemeContext'
4
- import { Card } from '../components/Card'
5
- import { BackLink } from '../components/BackLink'
6
- import { Loading } from '../components/Loading'
7
- import { api } from '../../helpers/api'
8
-
9
- export function FeedPost() {
10
- const navigate = useNavigate()
11
- const params = useParams()
12
- const postId = params.id
13
- const { t } = useTheme()
14
- const [post, setPost] = useState(null)
15
- const [loading, setLoading] = useState(true)
16
-
17
- useEffect(() => {
18
- api(`/api/feed/${postId}`)
19
- .then(data => setPost(data.post))
20
- .catch(() => navigate('/feed'))
21
- .finally(() => setLoading(false))
22
- }, [postId])
23
-
24
- if (loading) return <Loading />
25
- if (!post) return null
26
-
27
- return (
28
- <div>
29
- <BackLink onClick={() => navigate('/feed')}>Back to feed</BackLink>
30
- <Card className="p-6">
31
- <h1 className="text-2xl font-bold mb-2">{post.title}</h1>
32
- <p className={`text-sm ${t.subtle} mb-6`}>By {post.author_name} &bull; {new Date(post.created_at).toLocaleDateString()}</p>
33
- <p className="whitespace-pre-wrap">{post.content}</p>
34
- </Card>
35
- </div>
36
- )
37
- }
@@ -1,136 +0,0 @@
1
- import { useTheme } from '../components/ThemeContext'
2
- import { Card } from '../components/Card'
3
- import { PageHeader } from '../components/PageHeader'
4
- import { AppLayout } from '../layouts/AppLayout'
5
- import { DocsLayout } from '../layouts/DocsLayout'
6
-
7
- export function GettingStarted() {
8
- const { t } = useTheme()
9
-
10
- const devCommands = [
11
- { cmd: 'npm run dev', desc: 'Start development server' },
12
- { cmd: 'npm run build', desc: 'Build for production' },
13
- { cmd: 'npm run build -- --static', desc: 'Build client only (static hosts)' },
14
- { cmd: 'npm run start', desc: 'Run production server' },
15
- { cmd: 'npm run test', desc: 'Run tests with Vitest' },
16
- ]
17
-
18
- const makeCommands = [
19
- { cmd: 'npm run make:controller', desc: 'Generate a controller' },
20
- { cmd: 'npm run make:model', desc: 'Generate a model' },
21
- { cmd: 'npm run make:route', desc: 'Generate a route file' },
22
- { cmd: 'npm run make:migration', desc: 'Generate a migration' },
23
- { cmd: 'npm run make:middleware', desc: 'Generate middleware' },
24
- { cmd: 'npm run make:seed', desc: 'Generate a seeder' },
25
- ]
26
-
27
- const dbCommands = [
28
- { cmd: 'npm run migrate', desc: 'Run pending migrations' },
29
- { cmd: 'npm run migrate:rollback', desc: 'Roll back last batch' },
30
- { cmd: 'npm run migrate:fresh', desc: 'Drop all and re-run' },
31
- { cmd: 'npm run migrate:status', desc: 'Show migration status' },
32
- { cmd: 'npm run db:seed', desc: 'Run database seeders' },
33
- ]
34
-
35
- return (
36
- <div>
37
- <PageHeader title="Getting Started" />
38
-
39
- <div className="space-y-6">
40
- <Card>
41
- <h2 className="text-lg font-semibold mb-4">Quick Start</h2>
42
- <div className={`rounded-lg p-4 font-mono text-sm ${t.card} border ${t.border} overflow-x-auto`}>
43
- <div className={t.muted}># Create a new project</div>
44
- <div>npx create-basicben-app my-app</div>
45
- <div className="mt-2" />
46
- <div className={t.muted}># Navigate to the project</div>
47
- <div>cd my-app</div>
48
- <div className="mt-2" />
49
- <div className={t.muted}># Start the development server</div>
50
- <div>npm run dev</div>
51
- </div>
52
- </Card>
53
-
54
- <Card>
55
- <h2 className="text-lg font-semibold mb-4">Project Structure</h2>
56
- <div className={`rounded-lg p-4 font-mono text-sm ${t.card} border ${t.border} overflow-x-auto`}>
57
- <pre className={t.text}>{`my-app/
58
- ├── src/
59
- │ ├── client/ # React frontend
60
- │ │ ├── components/ # Reusable components
61
- │ │ └── pages/ # Page components
62
- │ ├── routes/ # API route files
63
- │ ├── controllers/ # Business logic
64
- │ ├── models/ # Database models
65
- │ └── middleware/ # Route middleware
66
- ├── migrations/ # Database migrations
67
- ├── public/ # Static assets
68
- └── basicben.config.js # Framework config`}</pre>
69
- </div>
70
- </Card>
71
-
72
- <Card>
73
- <h2 className="text-lg font-semibold mb-4">CLI Commands</h2>
74
-
75
- <h3 className={`text-sm font-medium mb-2 ${t.muted}`}>Development</h3>
76
- <div className="grid gap-2 sm:grid-cols-2 mb-4">
77
- {devCommands.map(({ cmd, desc }) => (
78
- <div key={cmd} className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
79
- <code className="text-sm font-semibold">{cmd}</code>
80
- <p className={`text-xs mt-1 ${t.muted}`}>{desc}</p>
81
- </div>
82
- ))}
83
- </div>
84
-
85
- <h3 className={`text-sm font-medium mb-2 ${t.muted}`}>Scaffolding</h3>
86
- <div className="grid gap-2 sm:grid-cols-2 mb-4">
87
- {makeCommands.map(({ cmd, desc }) => (
88
- <div key={cmd} className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
89
- <code className="text-sm font-semibold">{cmd}</code>
90
- <p className={`text-xs mt-1 ${t.muted}`}>{desc}</p>
91
- </div>
92
- ))}
93
- </div>
94
-
95
- <h3 className={`text-sm font-medium mb-2 ${t.muted}`}>Database</h3>
96
- <div className="grid gap-2 sm:grid-cols-2">
97
- {dbCommands.map(({ cmd, desc }) => (
98
- <div key={cmd} className={`rounded-lg p-3 ${t.card} border ${t.border}`}>
99
- <code className="text-sm font-semibold">{cmd}</code>
100
- <p className={`text-xs mt-1 ${t.muted}`}>{desc}</p>
101
- </div>
102
- ))}
103
- </div>
104
- </Card>
105
-
106
- <Card>
107
- <h2 className="text-lg font-semibold mb-4">Resources</h2>
108
- <div className="flex flex-wrap gap-3">
109
- <a
110
- href="https://github.com/BasicBenFramework/basicben-framework"
111
- target="_blank"
112
- rel="noopener noreferrer"
113
- className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg ${t.btnSecondary} transition text-sm`}
114
- >
115
- <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
116
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
117
- </svg>
118
- GitHub
119
- </a>
120
- <a
121
- href="https://basicben.com"
122
- target="_blank"
123
- rel="noopener noreferrer"
124
- className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg ${t.btnSecondary} transition text-sm`}
125
- >
126
- <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
127
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
128
- </svg>
129
- Documentation
130
- </a>
131
- </div>
132
- </Card>
133
- </div>
134
- </div>
135
- )
136
- }