diff --git a/web/src/app/_components/comments/AuditEventItem.tsx b/web/src/app/_components/comments/AuditEventItem.tsx index 052549f..bcc2d00 100644 --- a/web/src/app/_components/comments/AuditEventItem.tsx +++ b/web/src/app/_components/comments/AuditEventItem.tsx @@ -20,7 +20,12 @@ export default function AuditEventItem({ event, validatorUser }: AuditEventItemP const { thumbnailUrl, isLoading } = useUserThumbnail(isValidator ? undefined : event.User, '150x150'); return ( - + void; validatorUser: number; userId: number | null; + currentStatus?: number; } export default function CommentsAndAuditSection({ @@ -25,6 +38,7 @@ export default function CommentsAndAuditSection({ handleCommentSubmit, validatorUser, userId, + currentStatus, }: CommentsAndAuditSectionProps) { const [activeTab, setActiveTab] = useState(0); @@ -32,6 +46,16 @@ export default function CommentsAndAuditSection({ setActiveTab(newValue); }; + // Check if there's validator feedback for changes requested status + // Show badge if status is ChangesRequested and there are validator events + const hasValidatorFeedback = currentStatus === 1 && auditEvents.some(event => + event.User === validatorUser && + ( + event.EventType === AuditEventType.Error || + event.EventType === AuditEventType.CheckList + ) + ); + return ( @@ -41,7 +65,24 @@ export default function CommentsAndAuditSection({ aria-label="comments and audit tabs" > - + + Audit Events + {hasValidatorFeedback && ( + + )} + + } + /> diff --git a/web/src/app/_components/review/ReviewItem.tsx b/web/src/app/_components/review/ReviewItem.tsx index 19e1ee4..b4d3e2a 100644 --- a/web/src/app/_components/review/ReviewItem.tsx +++ b/web/src/app/_components/review/ReviewItem.tsx @@ -18,11 +18,13 @@ type ReviewItemType = SubmissionInfo | MapfixInfo; interface ReviewItemProps { item: ReviewItemType; handleCopyValue: (value: string) => void; + currentUserId?: number; } export function ReviewItem({ item, - handleCopyValue + handleCopyValue, + currentUserId }: ReviewItemProps) { // Type guard to check if item is valid if (!item) return null; @@ -105,6 +107,8 @@ export function ReviewItem({ diff --git a/web/src/app/_components/review/WorkflowStepper.tsx b/web/src/app/_components/review/WorkflowStepper.tsx index 7c2da15..a1d442d 100644 --- a/web/src/app/_components/review/WorkflowStepper.tsx +++ b/web/src/app/_components/review/WorkflowStepper.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { Stepper, Step, StepLabel, Box, StepConnector, stepConnectorClasses, StepIconProps, styled, keyframes } from '@mui/material'; +import { Stepper, Step, StepLabel, Box, StepConnector, stepConnectorClasses, StepIconProps, styled, keyframes, Typography, Paper } from '@mui/material'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import CancelIcon from '@mui/icons-material/Cancel'; import PendingIcon from '@mui/icons-material/Pending'; import WarningIcon from '@mui/icons-material/Warning'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import { Status } from '@/app/ts/Status'; const pulse = keyframes` @@ -18,6 +19,8 @@ const pulse = keyframes` interface WorkflowStepperProps { currentStatus: number; type: 'submission' | 'mapfix'; + submitterId?: number; + currentUserId?: number; } // Define the workflow steps @@ -164,19 +167,49 @@ const CustomStepIcon = (props: StepIconProps & { isRejected?: boolean; isChanges ); }; -const WorkflowStepper: React.FC = ({ currentStatus, type }) => { +const WorkflowStepper: React.FC = ({ currentStatus, type, submitterId, currentUserId }) => { const workflow = type === 'mapfix' ? mapfixWorkflow : submissionWorkflow; // Check if rejected or released const isRejected = currentStatus === Status.Rejected; const isReleased = currentStatus === Status.Release || currentStatus === Status.Releasing; const isChangesRequested = currentStatus === Status.ChangesRequested; + const isUnderConstruction = currentStatus === Status.UnderConstruction; // Find the active step const activeStep = workflow.findIndex(step => step.statuses.includes(currentStatus) ); + // Determine nudge message + const getNudgeContent = () => { + if (isUnderConstruction) { + return { + icon: InfoOutlinedIcon, + title: 'Not Yet Submitted', + message: 'Your submission has been created but has not been submitted. Click "Submit" to submit it.', + color: '#2196f3', + bgColor: 'rgba(33, 150, 243, 0.08)' + }; + } + if (isChangesRequested) { + return { + icon: WarningIcon, + title: 'Changes Requested', + message: 'Review comments and audit events, make modifications, and submit again.', + color: '#ff9800', + bgColor: 'rgba(255, 152, 0, 0.08)' + }; + } + return null; + }; + + const nudge = getNudgeContent(); + + // Only show nudge if current user is the submitter + const isSubmitter = submitterId !== undefined && currentUserId !== undefined && submitterId === currentUserId; + const shouldShowNudge = nudge && isSubmitter; + // If rejected, show all steps as incomplete with error state if (isRejected) { return ( @@ -245,6 +278,36 @@ const WorkflowStepper: React.FC = ({ currentStatus, type } ); })} + + {/* Action Nudge */} + {shouldShowNudge && ( + + + + + + + {nudge.title} + + + {nudge.message} + + + + )} ); }; diff --git a/web/src/app/mapfixes/[mapfixId]/page.tsx b/web/src/app/mapfixes/[mapfixId]/page.tsx index 4923c4e..b656b5d 100644 --- a/web/src/app/mapfixes/[mapfixId]/page.tsx +++ b/web/src/app/mapfixes/[mapfixId]/page.tsx @@ -365,6 +365,7 @@ export default function MapfixDetailsPage() { {/* Comments Section */} @@ -375,6 +376,7 @@ export default function MapfixDetailsPage() { handleCommentSubmit={handleCommentSubmit} validatorUser={validatorUser} userId={user} + currentStatus={mapfix.StatusID} /> diff --git a/web/src/app/submissions/[submissionId]/page.tsx b/web/src/app/submissions/[submissionId]/page.tsx index 84c6c76..e8fc923 100644 --- a/web/src/app/submissions/[submissionId]/page.tsx +++ b/web/src/app/submissions/[submissionId]/page.tsx @@ -267,6 +267,7 @@ export default function SubmissionDetailsPage() { {/* Comments Section */} @@ -277,6 +278,7 @@ export default function SubmissionDetailsPage() { handleCommentSubmit={handleCommentSubmit} validatorUser={validatorUser} userId={user} + currentStatus={submission.StatusID} />