feat(04-01): multi-project schema migration — projects, settings, FK pivot
- schema.ts: add projects table, settings kv table, slug on clients; migrate 6 FK from client_id to project_id (phases, payments, documents, notes, time_entries, quote_items); update all relations and TypeScript types - admin-queries.ts: fix getAllClientsWithPayments + getClientFullDetail to aggregate through projects; add getAllProjectsWithPayments, getProjectFullDetail, getClientWithProjects, ClientWithProjects type - settings.ts: new file — getSetting, updateSetting, getTargetHourlyRate, SETTINGS_KEYS - Fix all downstream files: actions.ts, quote-actions.ts, new/actions.ts, timer-actions.ts, approve/route.ts, comment/route.ts, TimerCell.tsx, analytics-queries.ts, client-view.ts, seed.ts - DB migration applied to Coolify Postgres (all test data cleared, schema rebuilt) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { db } from "@/db";
|
||||
import { time_entries } from "@/db/schema";
|
||||
import { eq, isNull } from "drizzle-orm";
|
||||
import { time_entries, projects } from "@/db/schema";
|
||||
import { eq, isNull, asc } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export async function startTimer(clientId: string): Promise<{ entryId: string }> {
|
||||
// Stop any currently running session (for any client) before starting a new one
|
||||
export async function startTimer(projectId: string): Promise<{ entryId: string }> {
|
||||
// Stop any currently running session before starting a new one
|
||||
const running = await db
|
||||
.select({ id: time_entries.id })
|
||||
.from(time_entries)
|
||||
@@ -30,11 +30,23 @@ export async function startTimer(clientId: string): Promise<{ entryId: string }>
|
||||
}
|
||||
|
||||
const id = nanoid();
|
||||
await db.insert(time_entries).values({ id, client_id: clientId });
|
||||
await db.insert(time_entries).values({ id, project_id: projectId });
|
||||
revalidatePath("/admin");
|
||||
return { entryId: id };
|
||||
}
|
||||
|
||||
export async function startTimerForClient(clientId: string): Promise<{ entryId: string }> {
|
||||
const projectRows = await db
|
||||
.select({ id: projects.id })
|
||||
.from(projects)
|
||||
.where(eq(projects.client_id, clientId))
|
||||
.orderBy(asc(projects.created_at))
|
||||
.limit(1);
|
||||
const projectId = projectRows[0]?.id;
|
||||
if (!projectId) throw new Error("Nessun progetto trovato per questo cliente");
|
||||
return startTimer(projectId);
|
||||
}
|
||||
|
||||
export async function stopTimer(entryId: string): Promise<void> {
|
||||
const rows = await db
|
||||
.select({ started_at: time_entries.started_at })
|
||||
|
||||
Reference in New Issue
Block a user