From 405ddb326c3bb48dbc90ded40846babf43f4c752 Mon Sep 17 00:00:00 2001 From: MarconLP <13001502+MarconLP@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:50:17 +0100 Subject: [PATCH 1/3] feat: allow editing additional perf feedback --- .../migration.sql | 2 + prisma/schema.prisma | 1 + src/components/PerformanceProgramPanel.tsx | 139 +++++++++++++++--- src/routes/employee.$employeeId.tsx | 34 +++++ 4 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 prisma/migrations/20260227000000_add_updated_at_to_performance_program_feedback/migration.sql diff --git a/prisma/migrations/20260227000000_add_updated_at_to_performance_program_feedback/migration.sql b/prisma/migrations/20260227000000_add_updated_at_to_performance_program_feedback/migration.sql new file mode 100644 index 00000000..fd6ac49d --- /dev/null +++ b/prisma/migrations/20260227000000_add_updated_at_to_performance_program_feedback/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "PerformanceProgramFeedback" ADD COLUMN "updatedAt" TIMESTAMP(3); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8eb2a888..b42784e0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -323,6 +323,7 @@ model PerformanceProgramFeedback { givenBy User @relation("FeedbackGivenBy", fields: [givenByUserId], references: [id]) files File[] createdAt DateTime @default(now()) + updatedAt DateTime? } model CommissionBonus { diff --git a/src/components/PerformanceProgramPanel.tsx b/src/components/PerformanceProgramPanel.tsx index 9aeb081d..d46cbd07 100644 --- a/src/components/PerformanceProgramPanel.tsx +++ b/src/components/PerformanceProgramPanel.tsx @@ -1,6 +1,13 @@ import { memo, useState } from 'react' -import { Eye, File as FileIcon, MessageSquare, X } from 'lucide-react' +import { + Check, + Eye, + File as FileIcon, + MessageSquare, + X, +} from 'lucide-react' import { Button } from '@/components/ui/button' +import { Textarea } from '@/components/ui/textarea' import { Tooltip, TooltipContent, @@ -11,9 +18,11 @@ import { createToast } from 'vercel-toast' import { useServerFn } from '@tanstack/react-start' import { resolvePerformanceProgram, + updateProgramFeedback, getProofFileUrl, deleteProofFile, } from '@/routes/employee.$employeeId' +import { useSession } from '@/lib/auth-client' import { PerformanceProgramChecklistItem } from './PerformanceProgramChecklistItem' import { InlineProofImage, isImageFile } from './InlineProofImage' import { FeedbackInput } from './FeedbackInput' @@ -121,8 +130,17 @@ export function PerformanceProgramPanel({ reportingChain = [], }: PerformanceProgramPanelProps) { const [isResolving, setIsResolving] = useState(false) + const [editingFeedbackId, setEditingFeedbackId] = useState( + null, + ) + const [editingText, setEditingText] = useState('') + const [isSavingEdit, setIsSavingEdit] = useState(false) + + const { data: session } = useSession() + const currentUserId = session?.user?.id const resolveProgram = useServerFn(resolvePerformanceProgram) + const editFeedback = useServerFn(updateProgramFeedback) const getFileUrl = useServerFn(getProofFileUrl) const deleteFile = useServerFn(deleteProofFile) @@ -188,6 +206,41 @@ export function PerformanceProgramPanel({ } } + const handleStartEdit = (feedbackId: string, currentText: string) => { + setEditingFeedbackId(feedbackId) + setEditingText(currentText) + } + + const handleCancelEdit = () => { + setEditingFeedbackId(null) + setEditingText('') + } + + const handleSaveEdit = async () => { + if (!editingFeedbackId || !editingText.trim()) return + + setIsSavingEdit(true) + try { + await editFeedback({ + data: { + feedbackId: editingFeedbackId, + feedback: editingText.trim(), + }, + }) + createToast('Feedback updated', { timeout: 3000 }) + setEditingFeedbackId(null) + setEditingText('') + onUpdate() + } catch (error) { + createToast( + error instanceof Error ? error.message : 'Failed to update feedback', + { timeout: 3000 }, + ) + } finally { + setIsSavingEdit(false) + } + } + if (!program) { return null } @@ -289,10 +342,14 @@ export function PerformanceProgramPanel({ const nonImageFiles = feedback.files.filter( (f) => !isImageFile(f.fileName, f.mimeType), ) + const canEdit = + program.status === 'ACTIVE' && + feedback.givenBy.id === currentUserId + const isEditing = editingFeedbackId === feedback.id return (
@@ -302,24 +359,70 @@ export function PerformanceProgramPanel({ {new Date(feedback.createdAt).toLocaleDateString()} - {nonImageFiles.length > 0 && ( -
- {nonImageFiles.map((file) => ( - - ))} -
+ {feedback.updatedAt && ( + + (edited) + )} +
+ {nonImageFiles.map((file) => ( + + ))} + {canEdit && !isEditing && ( + + )} +
- {feedback.feedback && ( -

- {feedback.feedback} -

+ {isEditing ? ( +
+