subcontrcator: post a status update section- removed photo required option
This commit is contained in:
@@ -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
|
||||
<SectionHeading
|
||||
icon={RefreshCcw}
|
||||
title="Update Task Status"
|
||||
hint="Move this task through the workflow. A note and at least one photo are required for every status change."
|
||||
hint="Move this task through the workflow. Add a note describing the change, and attach photos if you have them."
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 sm:gap-3 mt-4">
|
||||
@@ -520,7 +537,10 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<RequiredLabel ok={photoOk}>Photo upload <span className="text-zinc-400 font-normal normal-case">— {copy.photoHint}</span></RequiredLabel>
|
||||
<label className="text-xs font-bold uppercase tracking-wider text-zinc-600 dark:text-zinc-300 mb-1.5 flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400" />
|
||||
<span>Photo upload <span className="text-zinc-400 font-normal normal-case">— {copy.photoHint}</span></span>
|
||||
</label>
|
||||
<PhotoPicker
|
||||
photos={statusPhotos}
|
||||
onChange={setStatusPhotos}
|
||||
@@ -532,11 +552,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
|
||||
{!canConfirm && !isSubmittingStatus && (
|
||||
<div className="flex items-start gap-2 text-[11px] text-amber-700 dark:text-amber-400">
|
||||
<AlertTriangle size={12} className="mt-0.5 shrink-0" />
|
||||
<span>
|
||||
{(!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.'}
|
||||
</span>
|
||||
<span>Add a note to enable the status update.</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -564,8 +580,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Continuous progress updates while In Progress or On Hold */}
|
||||
{!pendingStatus && canPostUpdates && !disabled && (
|
||||
{!pendingStatus && canPostUpdates && (
|
||||
<div className="mt-4 p-4 rounded-xl bg-zinc-50 dark:bg-white/[0.03] border border-zinc-200 dark:border-white/10 space-y-4">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider ${updateBadgeCls}`}>
|
||||
@@ -573,11 +588,7 @@ const StatusManagementCard = ({ task, statuses, disabled, onChange, onPostProgre
|
||||
</span>
|
||||
<h4 className="text-sm font-bold text-zinc-900 dark:text-white">Post a progress update</h4>
|
||||
</div>
|
||||
<p className="text-[12px] text-zinc-500">
|
||||
{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.'}
|
||||
</p>
|
||||
<p className="text-[12px] text-zinc-500">{progressCopy}</p>
|
||||
|
||||
<div>
|
||||
<RequiredLabel ok={progressNoteOk}>Progress note</RequiredLabel>
|
||||
@@ -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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<RequiredLabel ok={progressPhotoOk}>Photos</RequiredLabel>
|
||||
<label className="text-xs font-bold uppercase tracking-wider text-zinc-600 dark:text-zinc-300 mb-1.5 flex items-center gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400" />
|
||||
<span>Photos</span>
|
||||
</label>
|
||||
<PhotoPicker
|
||||
photos={progressPhotos}
|
||||
onChange={setProgressPhotos}
|
||||
disabled={isSubmittingProgress}
|
||||
ctaLabel={task.status === 'On Hold' ? 'Tap to upload supporting photo(s)' : 'Tap to upload progress photo(s)'}
|
||||
ctaLabel="Tap to upload photo(s)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user