'use client'; import { useState, useRef } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { ArrowLeft, Camera, X } from 'lucide-react'; import { trpc } from '@/lib/trpc/client'; // Resize to max 1600px on longest side and compress to JPEG q=0.8. function compressImage(file: File): Promise { return new Promise((resolve, reject) => { const img = new Image(); const url = URL.createObjectURL(file); img.onload = () => { URL.revokeObjectURL(url); const MAX = 1600; let { width, height } = img; if (width > MAX || height > MAX) { if (width >= height) { height = Math.round((height * MAX) / width); width = MAX; } else { width = Math.round((width * MAX) / height); height = MAX; } } const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); if (!ctx) return reject(new Error('Canvas context unavailable')); ctx.drawImage(img, 0, 0, width, height); canvas.toBlob( (blob) => (blob ? resolve(blob) : reject(new Error('Canvas toBlob failed'))), 'image/jpeg', 0.8, ); }; img.onerror = () => reject(new Error('Image load failed')); img.src = url; }); } export default function NewRequestPage() { const router = useRouter(); const fileRef = useRef(null); const [workstationId, setWorkstationId] = useState(''); const [description, setDescription] = useState(''); const [photoBlob, setPhotoBlob] = useState(null); const [photoPreview, setPhotoPreview] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const { data: workstations = [], isLoading: wsLoading } = trpc.workstation.list.useQuery(); const signUpload = trpc.storage.signPhotoUpload.useMutation(); const createRequest = trpc.maintenanceRequest.create.useMutation(); async function handlePhotoChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; try { const compressed = await compressImage(file); if (photoPreview) URL.revokeObjectURL(photoPreview); const preview = URL.createObjectURL(compressed); setPhotoBlob(compressed); setPhotoPreview(preview); } catch { setError('Não foi possível processar a foto. Tenta de novo.'); } } function removePhoto() { if (photoPreview) URL.revokeObjectURL(photoPreview); setPhotoBlob(null); setPhotoPreview(null); if (fileRef.current) fileRef.current.value = ''; } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!workstationId || description.trim().length < 3) return; setSubmitting(true); setError(null); try { const clientRequestId = crypto.randomUUID(); let photoKey: string | undefined; // 1. Upload photo if present if (photoBlob) { const { uploadUrl, photoKey: key } = await signUpload.mutateAsync({ contentType: 'image/jpeg', byteSize: photoBlob.size, }); const res = await fetch(uploadUrl, { method: 'PUT', body: photoBlob, headers: { 'Content-Type': 'image/jpeg' }, }); if (!res.ok) throw new Error('Falha no upload da foto'); photoKey = key; } // 2. Create request await createRequest.mutateAsync({ workstationId, description: description.trim(), photoKey, clientRequestId, }); // 3. Confirm router.push(`/maintenance/sent?cid=${clientRequestId}`); } catch (err) { setError(err instanceof Error ? err.message : 'Erro ao submeter pedido. Tenta de novo.'); setSubmitting(false); } } const descLen = description.length; const canSubmit = workstationId !== '' && descLen >= 3 && descLen <= 1000 && !submitting; return (
{/* Header */}

Novo pedido de manutenção

{/* Posto */}
{/* Foto */}
Foto (opcional) {photoPreview ? (
{/* eslint-disable-next-line @next/next/no-img-element */} Pré-visualização
) : ( )}
{/* Descrição */}