'use client' /** * The "Open Ticket" form at the top of . * * Composition only — every leaf is an existing oss-lib primitive: * - `` for subject * - `` for content * - `` + `` for files * - `` for submit * * Submit gating combines: * - subject + content trim non-empty * - no uploads in flight * - not currently submitting (single-flight, parent-owned) * - support system online */ import { useState } from 'react' import { Card } from '../ui/card' import { Input } from '../ui/input' import { Textarea } from '../ui/textarea' import { Button } from '../ui/button' import { ChatAttachmentAddButton, ChatAttachmentChipStrip, } from '../chat/chat-attachment-bar' import { useChatAttachments } from '../chat/hooks/use-chat-attachments' import { TICKET_TEXT_MAX_CHARS } from './types' const COUNTER_VISIBLE_AT = Math.floor(TICKET_TEXT_MAX_CHARS * 0.8) export interface TicketOpenFormProps { /** Wired to `useTicketActions().submitTicket`. Returns true on success * so the form can clear itself. */ onSubmit: (input: { subject: string; content: string; attachments: import('../chat/utils/chat-attachment-markdown').ChatAttachment[] }) => Promise isSubmitting: boolean supportSystemDown: boolean } export function TicketOpenForm({ onSubmit, isSubmitting, supportSystemDown, }: TicketOpenFormProps) { const [subject, setSubject] = useState('') const [content, setContent] = useState('') // Reuse the chat composer's attachment hook directly — same upload // pipeline, same readiness flags. The hook returns `readyAttachments` // (the wire-shape projection) and `hasInflightUploads`. const { attachments, readyAttachments, hasInflightUploads, addFiles, removeAttachment, clear } = useChatAttachments() const trimmedSubject = subject.trim() const trimmedContent = content.trim() const overCap = content.length > TICKET_TEXT_MAX_CHARS const showCounter = content.length >= COUNTER_VISIBLE_AT const canSubmit = !isSubmitting && !supportSystemDown && !hasInflightUploads && trimmedSubject.length > 0 && trimmedContent.length > 0 && !overCap const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!canSubmit) return const ok = await onSubmit({ subject: trimmedSubject, content: trimmedContent, attachments: readyAttachments, }) if (ok) { setSubject('') setContent('') clear() } } return ( Need Support? Can't find what you're looking for? Submit a support ticket below — we'll follow up shortly. {supportSystemDown && ( Support system temporarily unavailable. Please try again shortly. )} Ticket Subject setSubject(e.target.value)} disabled={isSubmitting || supportSystemDown} maxLength={200} /> Your Message setContent(e.target.value)} disabled={isSubmitting || supportSystemDown} rows={5} className="resize-none" /> {showCounter && ( {content.length}/{TICKET_TEXT_MAX_CHARS} )} Open Ticket ) }
Can't find what you're looking for? Submit a support ticket below — we'll follow up shortly.
Support system temporarily unavailable. Please try again shortly.
{content.length}/{TICKET_TEXT_MAX_CHARS}