From 0edcb7769f034c3f312a1baa40c8f5695d3ffdf6 Mon Sep 17 00:00:00 2001 From: Jury018 Date: Wed, 25 Feb 2026 14:03:59 +0800 Subject: [PATCH 1/2] feat: add Ticket Support page with form and Coming Soon modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add /home/ticket-support route to paths.config.ts - Add Ticket Support link in sidebar (Codevs section, after Org Chart) - Create ticket-support SVG icon for sidebar - Create server page component with Codev Overflow-style header - Create client-side TicketSupportForm with fields: Full Name, Role Position, Project, Assigned Team, Type, Priority, Ticket Title, Ticket Details - Project dropdown includes 'None' option for manual team entry - Type dropdown includes 'Other' with free-text input - Ticket Details has 1000 character limit - Create non-dismissable Coming Soon modal (absolute positioned, sidebar accessible) - Frontend only — no backend connections - Dark/light mode support throughout --- .../_components/ComingSoonModal.tsx | 51 +++ .../_components/TicketSupportForm.tsx | 308 ++++++++++++++++++ .../app/home/ticket-support/page.tsx | 101 ++++++ apps/codebility/config/paths.config.ts | 2 + apps/codebility/constants/sidebar.ts | 12 +- .../assets/svgs/icon-ticket-support.svg | 5 + apps/codebility/public/assets/svgs/index.js | 3 +- 7 files changed, 478 insertions(+), 4 deletions(-) create mode 100644 apps/codebility/app/home/ticket-support/_components/ComingSoonModal.tsx create mode 100644 apps/codebility/app/home/ticket-support/_components/TicketSupportForm.tsx create mode 100644 apps/codebility/app/home/ticket-support/page.tsx create mode 100644 apps/codebility/public/assets/svgs/icon-ticket-support.svg diff --git a/apps/codebility/app/home/ticket-support/_components/ComingSoonModal.tsx b/apps/codebility/app/home/ticket-support/_components/ComingSoonModal.tsx new file mode 100644 index 000000000..2aeca5245 --- /dev/null +++ b/apps/codebility/app/home/ticket-support/_components/ComingSoonModal.tsx @@ -0,0 +1,51 @@ +"use client"; + +export default function ComingSoonModal() { + return ( +
+ {/* Backdrop with blur */} +
+ + {/* Modal Content */} +
+ {/* Decorative glow */} +
+ + {/* Icon */} +
+ + + +
+ + {/* Title */} +

+ COMING SOON +

+ + {/* Subtitle */} +

+ We're working hard to bring you this feature. Stay tuned for + updates! +

+ + {/* Animated progress dots */} +
+ + + +
+
+
+ ); +} diff --git a/apps/codebility/app/home/ticket-support/_components/TicketSupportForm.tsx b/apps/codebility/app/home/ticket-support/_components/TicketSupportForm.tsx new file mode 100644 index 000000000..3ad733f84 --- /dev/null +++ b/apps/codebility/app/home/ticket-support/_components/TicketSupportForm.tsx @@ -0,0 +1,308 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface ProjectData { + id: string; + name: string; + team_leader_name: string | null; +} + +interface TicketSupportFormProps { + projects: ProjectData[]; +} + +const ROLE_POSITIONS = [ + "Frontend Developer", + "Backend Developer", + "UI/UX Designer", + "Mobile Developer", + "QA Tester", + "Project Manager", + "Full Stack Developer", +]; + +const TICKET_TYPES = [ + "Bug Report", + "Feature Request", + "Access Issue", + "UI/UX Issue", + "Performance Issue", + "Account Issue", + "Other", +]; + +const PRIORITY_LEVELS = ["critical", "high", "medium", "low"]; + +const TICKET_DETAILS_MAX_CHARS = 1000; + +export default function TicketSupportForm({ + projects, +}: TicketSupportFormProps) { + const [fullName, setFullName] = useState(""); + const [rolePosition, setRolePosition] = useState(""); + const [selectedProject, setSelectedProject] = useState(""); + const [assignedTeam, setAssignedTeam] = useState(""); + const [ticketType, setTicketType] = useState(""); + const [otherType, setOtherType] = useState(""); + const [priority, setPriority] = useState(""); + const [ticketTitle, setTicketTitle] = useState(""); + const [ticketDetails, setTicketDetails] = useState(""); + const handleProjectChange = (projectId: string) => { + setSelectedProject(projectId); + if (projectId === "none") { + setAssignedTeam(""); + return; + } + const project = projects.find((p) => p.id === projectId); + if (project?.team_leader_name) { + setAssignedTeam(project.team_leader_name); + } else { + setAssignedTeam(""); + } + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + // Frontend-only — no backend submission + alert("Ticket submission is coming soon!"); + }; + + return ( +
+ {/* Inquiree Info Section */} +
+
+
+

+ Inquiree Info +

+
+
+ +
+ {/* Full Name */} +
+ + setFullName(e.target.value)} + placeholder="Enter your full name" + className="bg-light-900 dark:bg-dark-200 dark:text-light-900 focus:border-customBlue-500 w-full rounded-lg border border-gray-300 p-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-customBlue-100 dark:border-zinc-700 dark:placeholder:text-gray-500" + required + /> +
+ + {/* Role Position */} +
+ + +
+ + {/* Project */} +
+ + +
+ + {/* Assigned Team */} +
+ + setAssignedTeam(e.target.value)} + placeholder={selectedProject === "none" ? "Enter team or lead name" : "Auto-filled from project"} + className={`bg-light-900 dark:bg-dark-200 dark:text-light-900 w-full rounded-lg border border-gray-300 p-2 text-sm placeholder:text-gray-400 dark:border-zinc-700 dark:placeholder:text-gray-500 ${selectedProject !== "none" ? "cursor-not-allowed" : "focus:border-customBlue-500 focus:outline-none focus:ring-1 focus:ring-customBlue-100" + }`} + /> +
+
+
+ + {/* How can we help you? Section */} +
+
+
+

+ How can we help you? +

+
+
+ +
+ {/* Type */} +
+ + +
+ + {/* Priority */} +
+ + +
+
+ + {/* Other Type input (shown when "Other" is selected) */} + {ticketType === "Other" && ( +
+ + setOtherType(e.target.value)} + placeholder="Describe your inquiry type" + className="bg-light-900 dark:bg-dark-200 dark:text-light-900 focus:border-customBlue-500 w-full rounded-lg border border-gray-300 p-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-customBlue-100 dark:border-zinc-700 dark:placeholder:text-gray-500" + required + /> +
+ )} + + {/* Ticket Title (Optional) */} +
+ + setTicketTitle(e.target.value)} + placeholder="Brief summary of your issue" + className="bg-light-900 dark:bg-dark-200 dark:text-light-900 focus:border-customBlue-500 w-full rounded-lg border border-gray-300 p-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-customBlue-100 dark:border-zinc-700 dark:placeholder:text-gray-500" + /> +
+ + {/* Ticket Details */} +
+
+ + TICKET_DETAILS_MAX_CHARS * 0.9 + ? "font-semibold text-red-500" + : "text-gray-400 dark:text-gray-500" + }`} + > + {ticketDetails.length}/{TICKET_DETAILS_MAX_CHARS} + +
+