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
- {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}