diff --git a/src/pages/client/NewRequest.jsx b/src/pages/client/NewRequest.jsx index 1752d51..20dd078 100755 --- a/src/pages/client/NewRequest.jsx +++ b/src/pages/client/NewRequest.jsx @@ -6,6 +6,11 @@ import { serviceTypes } from '../../data/mockData'; import { supabase } from '../../lib/supabase'; import { sendEmail } from '../../lib/email'; import { useAuth } from '../../context/AuthContext'; +import { addDaysToDateOnly, getTodayDateOnlyEST } from '../../lib/dates'; +import { createInitialSubmissionForRequest, createTaskForRequest, findOrCreateProject } from '../../lib/requestSubmission'; + +const defaultRequestDeadline = () => addDaysToDateOnly(getTodayDateOnlyEST(), 3); +const emptyForm = (project = '') => ({ project, serviceType: '', title: '', deadline: defaultRequestDeadline(), description: '', isHot: false }); export default function NewRequest() { const { currentUser } = useAuth(); @@ -17,24 +22,28 @@ export default function NewRequest() { const [submitted, setSubmitted] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); - const [form, setForm] = useState({ project: preselectedProject, serviceType: '', title: '', deadline: '', description: '' }); + const [form, setForm] = useState(() => emptyForm(preselectedProject)); + const [requestKey, setRequestKey] = useState(() => crypto.randomUUID()); const [files, setFiles] = useState([]); const [customProjects, setCustomProjects] = useState([]); const [isTypingProject, setIsTypingProject] = useState(false); const [newProjectName, setNewProjectName] = useState(''); + const companyOptions = currentUser.companies?.length ? currentUser.companies : (currentUser.company ? [currentUser.company] : []); + const [selectedCompanyId, setSelectedCompanyId] = useState(companyOptions[0]?.id || ''); + const selectedCompany = companyOptions.find(company => company.id === selectedCompanyId) || companyOptions[0]; useEffect(() => { async function load() { - if (!currentUser.company?.id) return; + if (!selectedCompanyId) return; const { data: p } = await supabase .from('projects') .select('id, name') - .eq('company_id', currentUser.company.id) + .eq('company_id', selectedCompanyId) .order('created_at', { ascending: false }); setExistingProjects((p || []).map(pr => ({ id: pr.id, name: pr.name }))); } load(); - }, [currentUser.company?.id]); + }, [selectedCompanyId]); const allProjectNames = [ ...existingProjects.map(p => p.name), @@ -65,89 +74,96 @@ export default function NewRequest() { const handleSubmit = async (e) => { e.preventDefault(); - if (!currentUser.company?.id) { + if (saving) return; + if (!selectedCompanyId) { alert('Your account is not yet assigned to a company. Please contact support.'); return; } setSaving(true); setError(null); - // Find existing project by name within this company, or create new one - let projectId; - const existing = existingProjects.find(p => p.name === form.project); - if (existing) { - projectId = existing.id; - } else { - const { data: newProject, error: projectError } = await supabase.from('projects').insert({ - company_id: currentUser.company.id, - name: form.project, - status: 'active', - }).select().single(); - if (projectError || !newProject) { setError('Failed to create project.'); setSaving(false); return; } - projectId = newProject.id; + const projectName = form.project.trim(); + if (!projectName) { + setError('Please select or create a project before submitting this request.'); + setSaving(false); + return; } - if (!projectId) { setSaving(false); return; } + try { + const resolvedProject = await findOrCreateProject(selectedCompanyId, projectName, existingProjects); + if (!existingProjects.some(project => project.id === resolvedProject.id)) { + setExistingProjects(prev => [{ id: resolvedProject.id, name: resolvedProject.name }, ...prev]); + } + const projectId = resolvedProject.id; - // Create task - const { data: task } = await supabase.from('tasks').insert({ - project_id: projectId, - title: form.title.trim() || form.serviceType, - status: 'not_started', - current_version: 0, - }).select().single(); + if (!projectId) { setSaving(false); return; } - if (!task) { setSaving(false); return; } + const { task } = await createTaskForRequest({ + projectId, + title: form.title.trim() || form.serviceType, + requestKey, + }); - // Create submission - const { data: submission } = await supabase.from('submissions').insert({ - task_id: task.id, - version_number: 1, - type: 'initial', - service_type: form.serviceType, - deadline: form.deadline || null, - description: form.description, - submitted_by: currentUser.id, - submitted_by_name: currentUser.name, - }).select().single(); + if (!task) { setSaving(false); return; } - // Upload files - if (submission && files.length > 0) { - for (const file of files) { - const path = `${task.id}/${Date.now()}_${file.name}`; - const { data: uploaded, error: uploadError } = await supabase.storage.from('submissions').upload(path, file); - if (uploadError) { - setError(`Failed to upload "${file.name}": ${uploadError.message}`); - setSaving(false); - return; - } - if (uploaded) { - await supabase.from('submission_files').insert({ - submission_id: submission.id, - name: file.name, - storage_path: path, - size: file.size, - }); + const { submission } = await createInitialSubmissionForRequest({ + taskId: task.id, + requestKey, + isHot: form.isHot, + serviceType: form.serviceType, + deadline: form.deadline, + description: form.description, + submittedBy: currentUser.id, + submittedByName: currentUser.name, + }); + + // Upload files — rollback task on any failure so no orphaned records + if (submission && files.length > 0) { + for (const file of files) { + const path = `${task.id}/${Date.now()}_${file.name}`; + const { data: uploaded, error: uploadError } = await supabase.storage.from('submissions').upload(path, file); + if (uploadError) { + await supabase.from('tasks').delete().eq('id', task.id); + throw new Error(`Failed to upload "${file.name}": ${uploadError.message}`); + } + if (uploaded) { + const { error: fileRecordError } = await supabase.from('submission_files').insert({ + submission_id: submission.id, + name: file.name, + storage_path: path, + size: file.size, + }); + if (fileRecordError) { + await supabase.from('tasks').delete().eq('id', task.id); + throw new Error(`Failed to save file record for "${file.name}": ${fileRecordError.message}`); + } + } } } + + sendEmail('new_request', 'hello@fourgebranding.com', { + clientName: currentUser.name, + clientEmail: currentUser.email, + company: selectedCompany?.name || '', + serviceType: form.serviceType, + projectName, + deadline: form.deadline, + description: form.description, + taskId: task.id, + }).catch((emailError) => { + console.error('New request email failed:', emailError); + }); + + setSaving(false); + setSubmitted(true); + } catch (err) { + console.error('Request submission failed:', err); + setError(err.message || 'Something went wrong. Please try again.'); + setSaving(false); } - - sendEmail('new_request', 'hello@fourgebranding.com', { - clientName: currentUser.name, - clientEmail: currentUser.email, - company: currentUser.company?.name || '', - serviceType: form.serviceType, - projectName: form.project, - deadline: form.deadline, - description: form.description, - taskId: task.id, - }); - - setSaving(false); - setSubmitted(true); }; - if (!currentUser.company?.id) { + if (!companyOptions.length) { return (
@@ -174,7 +190,7 @@ export default function NewRequest() {

-
@@ -194,6 +210,25 @@ export default function NewRequest() {
+ {companyOptions.length > 1 && ( +
+ + +
+ )} +
{isTypingProject ? ( @@ -233,6 +268,17 @@ export default function NewRequest() {
+
+ +
+