import { FastifyInstance } from "fastify" import { stafftimeentries, stafftimenetryconnects } from "../../../db/schema" import { eq, and, gte, lte, desc } from "drizzle-orm" export default async function staffTimeRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // ▶ Neue Zeit starten // ------------------------------------------------------------- server.post("/staff/time", async (req, reply) => { try { const userId = req.user.user_id const tenantId = req.user.tenant_id const body = req.body as any const normalizeDate = (val: any) => { if (!val) return null const d = new Date(val) return isNaN(d.getTime()) ? null : d } const dataToInsert = { tenant_id: tenantId, user_id: body.user_id || userId, type: body.type || "work", description: body.description || null, started_at: normalizeDate(body.started_at), stopped_at: normalizeDate(body.stopped_at), } const [created] = await server.db .insert(stafftimeentries) .values(dataToInsert) .returning() return created } catch (err: any) { console.error(err) return reply.code(400).send({ error: err.message }) } }) // ------------------------------------------------------------- // ▶ Zeit stoppen // ------------------------------------------------------------- server.put<{ Params: { id: string }, Body: { stopped_at: string } }>("/staff/time/:id/stop", async (req, reply) => { try { const { id } = req.params const { stopped_at } = req.body // Normalize timestamp const normalizeDate = (val: any) => { const d = new Date(val) return isNaN(d.getTime()) ? null : d } const stopTime = normalizeDate(stopped_at) if (!stopTime) { return reply.code(400).send({ error: "Invalid stopped_at timestamp" }) } const [updated] = await server.db .update(stafftimeentries) .set({ stopped_at: stopTime, updated_at: new Date(), }) .where(eq(stafftimeentries.id, id)) .returning() if (!updated) { return reply.code(404).send({ error: "Time entry not found" }) } return reply.send(updated) } catch (err: any) { console.error("STOP ERROR:", err) return reply.code(500).send({ error: err.message || "Internal server error" }) } }) // ------------------------------------------------------------- // ▶ Liste aller Zeiten // ------------------------------------------------------------- server.get("/staff/time", async (req, reply) => { try { const { from, to, type, user_id } = req.query as any const { tenant_id, user_id: currentUserId } = req.user let where = and(eq(stafftimeentries.tenant_id, tenant_id)) // Zugriffsbeschränkung if (!req.hasPermission("staff.time.read_all")) { where = and(where, eq(stafftimeentries.user_id, currentUserId)) } else if (user_id) { where = and(where, eq(stafftimeentries.user_id, user_id)) } if (from) where = and(where, gte(stafftimeentries.started_at, from)) if (to) where = and(where, lte(stafftimeentries.started_at, to)) if (type) where = and(where, eq(stafftimeentries.type, type)) const rows = await server.db .select() .from(stafftimeentries) .where(where) .orderBy(desc(stafftimeentries.started_at)) return rows } catch (err) { console.error(err) return reply.code(400).send({ error: (err as Error).message }) } }) // ------------------------------------------------------------- // ▶ Einzelne Zeit (inkl. Connects) // ------------------------------------------------------------- server.get("/staff/time/:id", async (req, reply) => { try { const { id } = req.params as any const rows = await server.db .select() .from(stafftimeentries) .where(eq(stafftimeentries.id, id)) .limit(1) if (!rows.length) return reply.code(404).send({ error: "Not found" }) const entry = rows[0] const connects = await server.db .select() .from(stafftimenetryconnects) .where(eq(stafftimenetryconnects.stafftimeentry, id)) return { ...entry, staff_time_entry_connects: connects } } catch (err) { return reply.code(400).send({ error: (err as Error).message }) } }) // ------------------------------------------------------------- // ▶ Zeit bearbeiten // ------------------------------------------------------------- // ▶ Zeit bearbeiten server.put<{ Params: { id: string }, }>("/staff/time/:id", async (req, reply) => { try { const { id } = req.params const body = req.body // Normalize all timestamp fields const normalizeDate = (val: any) => { if (!val) return null const d = new Date(val) return isNaN(d.getTime()) ? null : d } const updateData: any = { // @ts-ignore ...body, updated_at: new Date(), } // Only convert if present — avoid overriding with null unless sent // @ts-ignore if (body.started_at !== undefined) { // @ts-ignore updateData.started_at = normalizeDate(body.started_at) } // @ts-ignore if (body.stopped_at !== undefined) { // @ts-ignore updateData.stopped_at = normalizeDate(body.stopped_at) } // @ts-ignore if (body.approved_at !== undefined) { // @ts-ignore updateData.approved_at = normalizeDate(body.approved_at) } const [updated] = await server.db .update(stafftimeentries) .set(updateData) .where(eq(stafftimeentries.id, id)) .returning() if (!updated) { return reply.code(404).send({ error: "Time entry not found" }) } return reply.send(updated) } catch (err: any) { console.error("UPDATE ERROR:", err) return reply.code(500).send({ error: err.message || "Internal server error" }) } }) // ------------------------------------------------------------- // ▶ Zeit löschen // ------------------------------------------------------------- server.delete("/staff/time/:id", async (req, reply) => { try { const { id } = req.params as any await server.db .delete(stafftimeentries) .where(eq(stafftimeentries.id, id)) return { success: true } } catch (err) { return reply.code(400).send({ error: (err as Error).message }) } }) }