From feee86301037b2cbfd9ff2bbd162a2c044f6b76a Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 23 Jul 2026 15:55:49 +0000 Subject: [PATCH] feat(ara-projects): unroll the permission detail instead of popping it (#413) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sub-row's wrapper is a one-row grid whose track animates 0fr → 1fr over 160ms, so the detail unrolls to whatever height its own content needs — no measuring, no ref, and no effect to start it: mounting runs the animation. Rows below slide down with it rather than jumping. motion-reduce:animate-none respects a reduced-motion preference. Adds the `disclosure-open` keyframe to tailwind.config.js — namespaced and additive, affecting nothing else. tailwindcss-animate is installed but not registered in `plugins`, so the animate-in/slide-in-from-* utilities the shadcn components reference generate no CSS; enabling that plugin would change animation behaviour across every dialog and tooltip in the app, which is not this ticket's call to make. Collapsing is still immediate — React unmounts the row, so there is no exit animation without holding it mounted through one. --- .../ContractorPermissionsTable.tsx | 126 ++++++++++-------- tailwind.config.js | 15 +++ 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/src/app/projects/[projectId]/setup/contractors/ContractorPermissionsTable.tsx b/src/app/projects/[projectId]/setup/contractors/ContractorPermissionsTable.tsx index 36dd3021..d7bca2b0 100644 --- a/src/app/projects/[projectId]/setup/contractors/ContractorPermissionsTable.tsx +++ b/src/app/projects/[projectId]/setup/contractors/ContractorPermissionsTable.tsx @@ -142,7 +142,8 @@ export function ContractorPermissionsTable({ ? { kind: "blocked", reason } : { kind: "needs-confirmation", reason }; } - if (!res.ok) throw new Error(body.error ?? "Couldn't remove the contractor"); + if (!res.ok) + throw new Error(body.error ?? "Couldn't remove the contractor"); return { kind: "removed" }; }, onSuccess: (outcome, { contractor }) => { @@ -321,7 +322,9 @@ export function ContractorPermissionsTable({
- {contractor.organisationName.charAt(0).toUpperCase()} + {contractor.organisationName + .charAt(0) + .toUpperCase()} {contractor.organisationName} @@ -391,59 +394,71 @@ export function ContractorPermissionsTable({ data-testid={`contractor-permissions-${contractor.organisationId}`} > {/* Full-bleed: the detail table *is* the expanded - space, spanning every column of the row above. */} + space, spanning every column of the row above. + + The wrapper is a one-row grid whose track animates + 0fr → 1fr, so the detail unrolls to whatever height + its own content needs — no measuring, and no effect + to run the animation: mounting starts it. */} - - - - - {PERMISSION_COLUMNS.map((column) => ( - - ))} - - - - {contractor.assignments.map((assignment) => ( - - - {PERMISSION_COLUMNS.map((column) => ( - +
+
+
- Workstream - - {column.header} -
- - {assignment.workstreamName} - - {assignment.workOrders > 0 && ( - - {assignment.workOrders}{" "} - {assignment.workOrders === 1 - ? "work order" - : "work orders"} - - )} - - -
+ + + + {PERMISSION_COLUMNS.map((column) => ( + + ))} + + + + {contractor.assignments.map((assignment) => ( + + + {PERMISSION_COLUMNS.map((column) => ( + + ))} + ))} - - ))} - -
+ Workstream + + {column.header} +
+ + {assignment.workstreamName} + + {assignment.workOrders > 0 && ( + + {assignment.workOrders}{" "} + {assignment.workOrders === 1 + ? "work order" + : "work orders"} + + )} + + +
+ + +
+ )} @@ -467,7 +482,10 @@ export function ContractorPermissionsTable({ Work orders can only be imported once every workstream has both a stage ladder and a contractor.

-