feat: add saved views — database table, CRUD API, migration
- New views table (id, name, filters jsonb, sort_key, is_public, creator_id) - GET/POST/PATCH/DELETE /views endpoints - Register views router in server Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
84
src/routes/views.ts
Normal file
84
src/routes/views.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Hono } from 'hono';
|
||||
import { HTTPException } from 'hono/http-exception';
|
||||
import { asc, eq } from 'drizzle-orm';
|
||||
import type { Db } from '../db/index.ts';
|
||||
import { views } from '../db/schema.ts';
|
||||
|
||||
export function createViewsRouter(db: Db): Hono {
|
||||
const router = new Hono();
|
||||
|
||||
router.get('/', async (c) => {
|
||||
const result = await db.query.views.findMany({
|
||||
orderBy: asc(views.name),
|
||||
});
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
router.post('/', async (c) => {
|
||||
const body = await c.req.json();
|
||||
const name = String(body.name ?? '').trim();
|
||||
|
||||
if (!name) {
|
||||
throw new HTTPException(400, { message: 'name is required' });
|
||||
}
|
||||
|
||||
const [view] = await db.insert(views).values({
|
||||
name,
|
||||
filters: body.filters ?? [],
|
||||
sort_key: body.sort_key ?? 'updated',
|
||||
columns: body.columns ?? [],
|
||||
is_public: body.is_public ?? false,
|
||||
creator_id: body.creator_id || null,
|
||||
}).returning();
|
||||
|
||||
if (!view) {
|
||||
throw new HTTPException(500, { message: 'Failed to create view' });
|
||||
}
|
||||
|
||||
return c.json(view, 201);
|
||||
});
|
||||
|
||||
router.patch('/:id', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const body = await c.req.json();
|
||||
|
||||
const existing = await db.query.views.findFirst({
|
||||
where: eq(views.id, id),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw new HTTPException(404, { message: 'View not found' });
|
||||
}
|
||||
|
||||
const updateData: Partial<typeof views.$inferInsert> = {};
|
||||
if (body.name !== undefined) updateData.name = String(body.name).trim();
|
||||
if (body.filters !== undefined) updateData.filters = body.filters;
|
||||
if (body.sort_key !== undefined) updateData.sort_key = body.sort_key;
|
||||
if (body.columns !== undefined) updateData.columns = body.columns;
|
||||
if (body.is_public !== undefined) updateData.is_public = body.is_public;
|
||||
|
||||
const [updated] = await db.update(views)
|
||||
.set(updateData)
|
||||
.where(eq(views.id, id))
|
||||
.returning();
|
||||
|
||||
return c.json(updated);
|
||||
});
|
||||
|
||||
router.delete('/:id', async (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
const existing = await db.query.views.findFirst({
|
||||
where: eq(views.id, id),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw new HTTPException(404, { message: 'View not found' });
|
||||
}
|
||||
|
||||
await db.delete(views).where(eq(views.id, id));
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
Reference in New Issue
Block a user