#66: Project detail "Open Demands" summary incorrectly counted COMPLETED demands as open. Fix: add `status !== "COMPLETED"` to the activeDemands filter in /projects/[id]/page.tsx. #59/#67: Project creation and edit had two bugs: 1. Both invalidated `project.list` but the page queries `project.listWithCosts` — the list never refreshed after a save. 2. Success toasts were either absent (ProjectModal) or mounted inside the wizard component that unmounts before the toast finishes. Fix: correct invalidation key to listWithCosts; add optional onSuccess prop to both ProjectWizard and ProjectModal; ProjectsClient wires onSuccess to a persistent SuccessToast rendered outside the modals. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -28,6 +28,7 @@ import { useRowOrder } from "~/hooks/useRowOrder.js";
|
||||
import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js";
|
||||
import { DraggableTableRow } from "~/components/ui/DraggableTableRow.js";
|
||||
import { ShoringBadge } from "~/components/projects/ShoringIndicator.js";
|
||||
import { SuccessToast } from "~/components/ui/SuccessToast.js";
|
||||
|
||||
import { PROJECT_STATUS_BADGE as STATUS_COLORS, ORDER_TYPE_BADGE as ORDER_TYPE_COLORS } from "~/lib/status-styles.js";
|
||||
|
||||
@@ -182,6 +183,7 @@ export function ProjectsClient() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [wizardOpen, setWizardOpen] = useState(false);
|
||||
const [editingProject, setEditingProject] = useState<Project | null>(null);
|
||||
const [successToast, setSuccessToast] = useState<string | null>(null);
|
||||
const [openStatusProjectId, setOpenStatusProjectId] = useState<string | null>(null);
|
||||
const [batchStatusPicker, setBatchStatusPicker] = useState(false);
|
||||
const [confirmBatchStatus, setConfirmBatchStatus] = useState<{ ids: string[]; status: string } | null>(null);
|
||||
@@ -749,10 +751,34 @@ export function ProjectsClient() {
|
||||
/>
|
||||
|
||||
{/* Modal */}
|
||||
{modalOpen && <ProjectModal project={editingProject} onClose={closeModal} />}
|
||||
{modalOpen && (
|
||||
<ProjectModal
|
||||
project={editingProject}
|
||||
onClose={closeModal}
|
||||
onSuccess={(name) =>
|
||||
setSuccessToast(
|
||||
editingProject
|
||||
? `Project "${name}" updated successfully.`
|
||||
: `Project "${name}" created successfully.`,
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Wizard */}
|
||||
<ProjectWizard open={wizardOpen} onClose={() => setWizardOpen(false)} />
|
||||
<ProjectWizard
|
||||
open={wizardOpen}
|
||||
onClose={() => setWizardOpen(false)}
|
||||
onSuccess={(shortCode, name) =>
|
||||
setSuccessToast(`Project "${shortCode} — ${name}" created successfully.`)
|
||||
}
|
||||
/>
|
||||
|
||||
<SuccessToast
|
||||
show={successToast !== null}
|
||||
message={successToast ?? ""}
|
||||
onDone={() => setSuccessToast(null)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default async function ProjectDetailPage({ params }: ProjectDetailPagePro
|
||||
}
|
||||
|
||||
const activeAssignments = project.assignments.filter((assignment) => assignment.status !== "CANCELLED");
|
||||
const activeDemands = project.demands.filter((demand) => demand.status !== "CANCELLED");
|
||||
const activeDemands = project.demands.filter((demand) => demand.status !== "CANCELLED" && demand.status !== "COMPLETED");
|
||||
const requestedSeats = activeDemands.reduce((sum, demand) => sum + demand.requestedHeadcount, 0);
|
||||
const unfilledSeats = activeDemands.reduce((sum, demand) => sum + demand.unfilledHeadcount, 0);
|
||||
|
||||
|
||||
@@ -95,9 +95,10 @@ function projectToForm(project: Project): FormState {
|
||||
interface ProjectModalProps {
|
||||
project?: Project | null;
|
||||
onClose: () => void;
|
||||
onSuccess?: (name: string) => void;
|
||||
}
|
||||
|
||||
export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
export function ProjectModal({ project, onClose, onSuccess }: ProjectModalProps) {
|
||||
const isEdit = !!project;
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
@@ -116,7 +117,8 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
// @ts-ignore TS2589: tRPC infers union type too deeply for CreateProjectSchema with .refine()
|
||||
const createMutation = trpc.project.create.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.project.list.invalidate();
|
||||
await utils.project.listWithCosts.invalidate();
|
||||
onSuccess?.(form.name.trim());
|
||||
onClose();
|
||||
},
|
||||
onError: (err) => {
|
||||
@@ -126,7 +128,8 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
|
||||
const updateMutation = trpc.project.update.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.project.list.invalidate();
|
||||
await utils.project.listWithCosts.invalidate();
|
||||
onSuccess?.(form.name.trim());
|
||||
onClose();
|
||||
},
|
||||
onError: (err) => {
|
||||
|
||||
@@ -1041,9 +1041,10 @@ function Step5({ state, onChange, onSubmit, isSubmitting, submitError }: Step5Pr
|
||||
interface ProjectWizardProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess?: (shortCode: string, name: string) => void;
|
||||
}
|
||||
|
||||
export function ProjectWizard({ open, onClose }: ProjectWizardProps) {
|
||||
export function ProjectWizard({ open, onClose, onSuccess }: ProjectWizardProps) {
|
||||
const utils = trpc.useUtils();
|
||||
const [step, setStep] = useState(0);
|
||||
const [state, setState] = useState<WizardState>(makeDefaultState);
|
||||
@@ -1164,13 +1165,14 @@ export function ProjectWizard({ open, onClose }: ProjectWizardProps) {
|
||||
}
|
||||
}
|
||||
|
||||
await utils.project.list.invalidate();
|
||||
await utils.project.listWithCosts.invalidate();
|
||||
await utils.timeline.getEntries.invalidate();
|
||||
await utils.timeline.getEntriesView.invalidate();
|
||||
setShowConfetti(true);
|
||||
setShowSuccessToast(true);
|
||||
setTimeout(() => {
|
||||
setShowConfetti(false);
|
||||
onSuccess?.(project.shortCode, project.name);
|
||||
handleClose();
|
||||
}, 1200);
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user