From fef69b653c1dcc5dd5208f3a80927b82d2ce7d77 Mon Sep 17 00:00:00 2001 From: grabowski Date: Thu, 16 Apr 2026 16:09:52 +0700 Subject: [PATCH] Add inline rename/edit on project detail page Co-Authored-By: Claude Opus 4.6 (1M context) --- .../projects/[projectId]/+page.server.ts | 43 ++++++++++- .../projects/[projectId]/+page.svelte | 74 +++++++++++++++++-- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.server.ts b/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.server.ts index a9a5329..05a057a 100644 --- a/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.server.ts @@ -1,8 +1,10 @@ -import { error } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; +import { error, fail } from '@sveltejs/kit'; +import type { Actions, PageServerLoad } from './$types'; import { db } from '$lib/server/db/index.js'; import { projects, expenses, users, categories } from '$lib/server/db/schema.js'; import { eq, and, sql } from 'drizzle-orm'; +import { requireCompanyRole } from '$lib/server/authorization.js'; +import { logCompanyEvent } from '$lib/server/audit.js'; export const load: PageServerLoad = async ({ params, parent }) => { await parent(); @@ -48,3 +50,40 @@ export const load: PageServerLoad = async ({ params, parent }) => { return { project, expenses: expenseList, stats }; }; + +export const actions: Actions = { + updateProject: async ({ request, locals, params }) => { + const { user } = await requireCompanyRole(locals, params.companyId, 'manager'); + + const fd = await request.formData(); + const name = fd.get('name')?.toString().trim(); + const description = fd.get('description')?.toString().trim() || null; + + if (!name) return fail(400, { action: 'updateProject', error: 'Project name is required' }); + + const [existing] = await db + .select({ id: projects.id, name: projects.name }) + .from(projects) + .where(and(eq(projects.id, params.projectId), eq(projects.companyId, params.companyId))) + .limit(1); + if (!existing) error(404, 'Project not found'); + + await db + .update(projects) + .set({ name, description, updatedAt: new Date() }) + .where(eq(projects.id, params.projectId)); + + const renamed = existing.name !== name; + await logCompanyEvent( + params.companyId, + user.id, + 'project_updated', + renamed + ? `Project renamed from "${existing.name}" to "${name}"` + : `Project "${name}" updated`, + { projectId: params.projectId, previousName: existing.name, newName: name } + ); + + return { success: true, action: 'updateProject' }; + } +}; diff --git a/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.svelte b/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.svelte index 2fe67c6..02112c4 100644 --- a/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.svelte +++ b/src/routes/(app)/companies/[companyId]/projects/[projectId]/+page.svelte @@ -1,10 +1,14 @@ @@ -12,14 +16,68 @@
-
-
-

{data.project.name}

- {#if data.project.description} -

{data.project.description}

+
+
+ {#if editing && canEdit} +
async ({ result, update }) => { + await update({ reset: false }); + if (result.type === 'success') editing = false; + }} + class="space-y-2" + > + + +
+ + +
+ {#if form?.action === 'updateProject' && form.error} +

{form.error}

+ {/if} +
+ {:else} +
+

{data.project.name}

+ {#if canEdit} + + {/if} +
+ {#if data.project.description} +

{data.project.description}

+ {/if} {/if}
- {#if canAddExpense} + {#if canAddExpense && !editing}