/** * Ticket creation screen for the web workspace (ticket #86) — replaces the list * in the column (no inline permanent form, no desktop popup), per carnet #86. * * Short form: Titre (required) / Priorité / Sprint / Description (optional). * On success the caller opens the created ticket's detail so the user can * complete carnet/assignments/links right away. */ import { useState } from "react"; import type { CreateTicketInput } from "@/ports"; import type { Sprint, TicketPriority } from "@/domain"; import { Button, Input, Panel, cn } from "@/shared"; import { TICKET_PRIORITIES } from "@/features/tickets"; import { webPriorityLabel } from "./webTicketLabels"; const selectClass = cn( "h-9 rounded-md bg-raised px-3 text-sm text-content", "border border-border outline-none transition-colors", "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50", ); const textareaClass = cn( "w-full rounded-md bg-raised p-3 text-sm text-content", "border border-border outline-none transition-colors", "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50", ); export interface WebTicketCreateProps { sprints: Sprint[]; busy: boolean; error: string | null; onCancel: () => void; onCreate: (input: CreateTicketInput, sprintId: string | null) => Promise; } export function WebTicketCreate({ sprints, busy, error, onCancel, onCreate }: WebTicketCreateProps) { const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [priority, setPriority] = useState("medium"); const [sprintId, setSprintId] = useState(""); async function submit(e: React.FormEvent) { e.preventDefault(); const trimmed = title.trim(); if (!trimmed) return; await onCreate( { title: trimmed, priority, ...(description.trim() ? { description: description.trim() } : {}), }, sprintId || null, ); } return ( Annuler } > {error && (

{error}

)}