diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx index a1886a3..5354fec 100644 --- a/src/data/mockStore.jsx +++ b/src/data/mockStore.jsx @@ -6280,9 +6280,9 @@ export const MockStoreProvider = ({ children }) => { const addSubcontractorTaskActivity = (taskId, { note, photos = [] }, actor) => { const trimmed = note?.trim() || ''; - if (!trimmed || !photos.length) return; + if (!trimmed) return; const now = new Date().toISOString(); - const normalizedPhotos = photos.map((p, i) => ({ + const normalizedPhotos = (photos || []).map((p, i) => ({ id: p.id || `${Date.now()}_${i}_${Math.random().toString(36).slice(2, 7)}`, url: p.url, name: p.name || '', @@ -6298,7 +6298,7 @@ export const MockStoreProvider = ({ children }) => { companyName = t.companyName; assignedByUserId = t.assignedBy; subId = t.subcontractorId; - const entry = { + const activityEntry = { id: `a_${Date.now()}`, type: 'progress_update', status: t.status, @@ -6309,9 +6309,19 @@ export const MockStoreProvider = ({ children }) => { actorRole: actor?.role || 'Subcontractor', at: now, }; + const historyEntry = { + id: `h_${Date.now()}`, + status: t.status, + actorId: actor?.id || t.subcontractorId, + actorName: actor?.name || t.subcontractorName, + comment: trimmed, + at: now, + isProgressUpdate: true, + }; return { ...t, - activities: [...(t.activities || []), entry], + activities: [...(t.activities || []), activityEntry], + statusHistory: [...(t.statusHistory || []), historyEntry], updatedAt: now, }; })); diff --git a/src/pages/subcontractor/SubcontractorTaskDetailPage.jsx b/src/pages/subcontractor/SubcontractorTaskDetailPage.jsx index c32fbfc..c8f7280 100644 --- a/src/pages/subcontractor/SubcontractorTaskDetailPage.jsx +++ b/src/pages/subcontractor/SubcontractorTaskDetailPage.jsx @@ -348,28 +348,28 @@ const TaskHeaderCard = ({ task, overdue, onLogExpense, onLogFee }) => { const STATUS_COPY = { Assigned: { title: 'Acknowledge assignment', - hint: 'Confirm you have received this task and attach a photo of the site or scope of work.', + hint: 'Confirm you have received this task and share any notes about the site or scope of work.', notePlaceholder: 'e.g. Acknowledged — heading to site tomorrow morning.', photoHint: 'Site or arrival photo', accent: 'amber', }, 'In Progress': { title: 'Start work', - hint: 'Add a brief note about what you are starting and one or more "before" photos.', + hint: 'Add a brief note about what you are starting and attach any "before" photos if you have them.', notePlaceholder: 'e.g. On site. Materials staged. Starting south slope ridge cap removal.', photoHint: 'Before / starting photo', accent: 'blue', }, 'On Hold': { title: 'Put task on hold', - hint: 'Tell us why work has stopped, and attach a supporting photo (weather, materials, access, etc.).', + hint: 'Tell us why work has stopped and add any supporting photos (weather, materials, access, etc.) if applicable.', notePlaceholder: 'e.g. Work stopped due to rain. Will resume tomorrow.', photoHint: 'Supporting photo (e.g. rain, blocked access)', accent: 'orange', }, Completed: { title: 'Mark as completed', - hint: 'Add a final completion note and at least one photo showing the finished work.', + hint: 'Add a final completion note and attach any photos of the finished work.', notePlaceholder: 'e.g. House painting completed successfully. Walked through with homeowner.', photoHint: 'Completion / final-look photo(s)', accent: 'emerald', @@ -396,12 +396,10 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre const copy = pendingStatus ? STATUS_COPY[pendingStatus] || STATUS_COPY.Assigned : null; const accentBtnCls = copy ? ACCENT_BTN[copy.accent] : ACCENT_BTN.blue; const noteOk = statusNote.trim().length > 0; - const photoOk = statusPhotos.length > 0; - const canConfirm = noteOk && photoOk && !isSubmittingStatus; + const canConfirm = noteOk && !isSubmittingStatus; const progressNoteOk = progressNote.trim().length > 0; - const progressPhotoOk = progressPhotos.length > 0; - const canPostProgress = progressNoteOk && progressPhotoOk && !isSubmittingProgress; + const canPostProgress = progressNoteOk && !isSubmittingProgress; const handlePick = (s) => { if (disabled) return; @@ -443,15 +441,34 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre }, 350); }; - const canPostUpdates = task.status === 'In Progress' || task.status === 'On Hold'; - const updateAccent = task.status === 'On Hold' ? 'orange' : 'blue'; - const updateBadgeCls = task.status === 'On Hold' - ? 'bg-orange-100 text-orange-700 dark:bg-orange-500/20 dark:text-orange-400' - : 'bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-400'; - const UpdateIcon = task.status === 'On Hold' ? PauseCircle : Play; - const postBtnCls = task.status === 'On Hold' - ? 'bg-orange-600 hover:bg-orange-500 shadow-orange-500/20' - : 'bg-blue-600 hover:bg-blue-500 shadow-blue-500/20'; + const canPostUpdates = ['Assigned', 'In Progress', 'On Hold', 'Completed'].includes(task.status); + const updateStatusCfg = STATUS_CONFIG[task.status] || STATUS_CONFIG.Assigned; + const updateBadgeCls = updateStatusCfg.cls; + const UpdateIcon = updateStatusCfg.icon || Play; + const postBtnCls = (() => { + switch (task.status) { + case 'Assigned': return 'bg-amber-600 hover:bg-amber-500 shadow-amber-500/20'; + case 'On Hold': return 'bg-orange-600 hover:bg-orange-500 shadow-orange-500/20'; + case 'Completed': return 'bg-emerald-600 hover:bg-emerald-500 shadow-emerald-500/20'; + default: return 'bg-blue-600 hover:bg-blue-500 shadow-blue-500/20'; + } + })(); + const progressPlaceholder = (() => { + switch (task.status) { + case 'Assigned': return 'e.g. "Scheduled for Friday, reviewed site plan."'; + case 'On Hold': return 'e.g. "Still waiting on materials — supplier ETA Friday."'; + case 'Completed': return 'e.g. "Follow-up walkthrough done, no issues noted."'; + default: return 'e.g. "First wall painting completed."'; + } + })(); + const progressCopy = (() => { + switch (task.status) { + case 'Assigned': return 'Share early notes about the upcoming work — site prep, schedule changes, anything the admin should know before you start.'; + case 'On Hold': return 'Keep the admin posted while this task is paused — share why it is still on hold or what changed.'; + case 'Completed': return 'Add a follow-up note after completion — punch-list items, homeowner feedback, or anything that came up after sign-off.'; + default: return 'Keep the admin in the loop with ongoing updates while you work.'; + } + })(); const sortedActivities = useMemo(() => { return [...(task.activities || [])].sort((a, b) => new Date(b.at) - new Date(a.at)); @@ -462,7 +479,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
@@ -520,7 +537,10 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
- Photo upload — {copy.photoHint} + - - {(!noteOk && !photoOk) && 'Add a note and at least one photo to enable the status update.'} - {(!noteOk && photoOk) && 'Add a note to enable the status update.'} - {(noteOk && !photoOk) && 'Upload at least one photo to enable the status update.'} - + Add a note to enable the status update.
)} @@ -564,8 +580,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre )} - {/* Continuous progress updates while In Progress or On Hold */} - {!pendingStatus && canPostUpdates && !disabled && ( + {!pendingStatus && canPostUpdates && (
@@ -573,11 +588,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre

Post a progress update

-

- {task.status === 'On Hold' - ? 'Keep the admin posted while this task is paused — share why it is still on hold or what changed. Each update needs a note and at least one photo.' - : 'Keep the admin in the loop with ongoing updates while you work. Each update needs a note and at least one photo.'} -

+

{progressCopy}

Progress note @@ -586,20 +597,21 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre onChange={(e) => setProgressNote(e.target.value)} rows={2} disabled={isSubmittingProgress} - placeholder={task.status === 'On Hold' - ? 'e.g. "Still waiting on materials — supplier ETA Friday."' - : 'e.g. "First wall painting completed."'} + placeholder={progressPlaceholder} className="w-full text-sm rounded-lg bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-white/10 px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500/40 transition-colors disabled:opacity-60" />
- Photos +