From 2de2aa65fe18d30fbde380a2baeedbce0ff83a7c Mon Sep 17 00:00:00 2001 From: Enes1998 <104234112+Enes1998@users.noreply.github.com> Date: Tue, 26 May 2026 19:56:01 +0200 Subject: [PATCH 01/12] fix(fe): close FE-2/FE-5 gaps + delete remaining app/* duplicates - Delete 18 more stale files left by main->develop merge: app/admin/** (full tree), app/help/, app/profile/[id]/, and 6 orphan error.tsx/loading.tsx whose page.tsx was already removed. Canonical versions remain in app/(protected)/. - FE-2 (useReducer): refactor ProficienciesStep's three related useState calls (selectedSkills, isExiting, isGoingBack) into a single useReducer with a discriminated-union action type. - FE-5 (Server Actions): add submitQuickFeedback Server Action in app/(protected)/help/actions.ts, wired into the Help page via useActionState. Demonstrates the "use server" boundary and revalidatePath; logs feedback server-side. Lint: 0 errors. Type-check: 0 errors. Dev server boots clean. Co-Authored-By: Claude Opus 4.7 --- .../client/app/(protected)/help/actions.ts | 36 ++ frontend/client/app/(protected)/help/page.tsx | 44 ++- .../client/app/admin/analytics/loading.tsx | 29 -- frontend/client/app/admin/analytics/page.tsx | 236 ------------- frontend/client/app/admin/audit/page.tsx | 206 ------------ frontend/client/app/admin/error.tsx | 32 -- frontend/client/app/admin/flags/page.tsx | 212 ------------ frontend/client/app/admin/jobs/page.tsx | 140 -------- frontend/client/app/admin/layout.tsx | 60 ---- frontend/client/app/admin/page.tsx | 281 ---------------- frontend/client/app/admin/projects/page.tsx | 192 ----------- frontend/client/app/admin/support/page.tsx | 310 ------------------ frontend/client/app/discover/error.tsx | 32 -- frontend/client/app/discover/loading.tsx | 32 -- frontend/client/app/help/page.tsx | 181 ---------- frontend/client/app/messages/error.tsx | 32 -- frontend/client/app/messages/loading.tsx | 28 -- frontend/client/app/profile/[id]/page.tsx | 94 ------ frontend/client/app/projects/error.tsx | 32 -- frontend/client/app/projects/loading.tsx | 24 -- .../onboarding/ProficienciesStep.tsx | 45 ++- 21 files changed, 116 insertions(+), 2162 deletions(-) create mode 100644 frontend/client/app/(protected)/help/actions.ts delete mode 100644 frontend/client/app/admin/analytics/loading.tsx delete mode 100644 frontend/client/app/admin/analytics/page.tsx delete mode 100644 frontend/client/app/admin/audit/page.tsx delete mode 100644 frontend/client/app/admin/error.tsx delete mode 100644 frontend/client/app/admin/flags/page.tsx delete mode 100644 frontend/client/app/admin/jobs/page.tsx delete mode 100644 frontend/client/app/admin/layout.tsx delete mode 100644 frontend/client/app/admin/page.tsx delete mode 100644 frontend/client/app/admin/projects/page.tsx delete mode 100644 frontend/client/app/admin/support/page.tsx delete mode 100644 frontend/client/app/discover/error.tsx delete mode 100644 frontend/client/app/discover/loading.tsx delete mode 100644 frontend/client/app/help/page.tsx delete mode 100644 frontend/client/app/messages/error.tsx delete mode 100644 frontend/client/app/messages/loading.tsx delete mode 100644 frontend/client/app/profile/[id]/page.tsx delete mode 100644 frontend/client/app/projects/error.tsx delete mode 100644 frontend/client/app/projects/loading.tsx diff --git a/frontend/client/app/(protected)/help/actions.ts b/frontend/client/app/(protected)/help/actions.ts new file mode 100644 index 0000000..8976567 --- /dev/null +++ b/frontend/client/app/(protected)/help/actions.ts @@ -0,0 +1,36 @@ +"use server"; + +import { revalidatePath } from "next/cache"; + +export type QuickFeedbackResult = + | { ok: true } + | { ok: false; error: string }; + +const MIN_LEN = 5; +const MAX_LEN = 1000; + +export async function submitQuickFeedback( + _prev: QuickFeedbackResult | null, + formData: FormData, +): Promise { + const message = formData.get("message")?.toString().trim() ?? ""; + + if (message.length < MIN_LEN) { + return { ok: false, error: `Message must be at least ${MIN_LEN} characters.` }; + } + if (message.length > MAX_LEN) { + return { ok: false, error: `Message must be under ${MAX_LEN} characters.` }; + } + + console.info( + JSON.stringify({ + event: "quick_feedback", + at: new Date().toISOString(), + length: message.length, + preview: message.slice(0, 120), + }), + ); + + revalidatePath("/help"); + return { ok: true }; +} diff --git a/frontend/client/app/(protected)/help/page.tsx b/frontend/client/app/(protected)/help/page.tsx index 1e68486..b6468b6 100644 --- a/frontend/client/app/(protected)/help/page.tsx +++ b/frontend/client/app/(protected)/help/page.tsx @@ -1,10 +1,11 @@ "use client"; -import { useState } from "react"; +import { useActionState, useState } from "react"; import { motion } from "framer-motion"; import { NavigationHeader } from "@/components/ui/NavigationHeader"; import { useCreateSupportTicket, useMySupportTickets } from "@/hooks/useAdmin"; import type { SupportTicketPriority, SupportTicketStatus } from "@/types/admin"; +import { submitQuickFeedback } from "./actions"; const PRIORITY_OPTIONS: SupportTicketPriority[] = ["Low", "Normal", "High", "Urgent"]; @@ -32,6 +33,10 @@ export default function HelpPage() { const [submitted, setSubmitted] = useState(false); const { data: mine, isLoading } = useMySupportTickets(); const create = useCreateSupportTicket(); + const [feedbackState, feedbackAction, feedbackPending] = useActionState( + submitQuickFeedback, + null, + ); const canSubmit = subject.trim().length > 0 && body.trim().length > 0 && !create.isPending; @@ -139,6 +144,43 @@ export default function HelpPage() { +
+

+ Quick anonymous feedback +

+

+ Server Action — runs on the Next.js server, no client API call. +

+
+