Page 2 · Update Progress — dynamic form (v1.3)
v1.3: every number on the screen is now derived from one seed — the overview totals were hardcoded and contradicted the form beside them. Abutments A1/A2 added to the pile chain. The whole sequence is enforced and each component says which locations are blocked and why.
← Brief 1. Inventory Form 3. 2D Diagram

Update Progress — what changes for a cable-stayed bridge

The screen does not change. No new page, no new route, no new blade. The existing Update Component Progress form is already config-driven — ProgressFormConfigService::getProgressType() maps every component to one of seven types, and the form builds itself from that. Four new components join the dropdown; three of them reuse a progress type that already exists. Only Stay Cable needs a new one.

How the form already decides what to show

One match on progress type picks the field builder. Everything below it — quantity, date, remarks, photo upload — is appended by buildCommonTailFields() and is identical for every component.

$fields = match ($progressType) {
    'girder_step'   => $this->buildGirderStepFields(…),   // Girder — 3 steps
    'item_based'    => $this->buildItemBasedFields(…),    // Pile, Cross Girder
    'height_based'  => $this->buildHeightBasedFields(…),  // Pier Wall, Abut Wall
    'location_only' => $this->buildLocationOnlyFields(…), // Pile Cap, Pier Cap
    'span_based'    => $this->buildCrossGirderFields(…),  // Deck Slab, Crash Barrier
    'date_curing'   => $this->buildDateCuringFields(…),   // Pedestal
    'miscellaneous' => $this->buildMiscellaneousFields(…),
    'cable_stage'   => $this->buildCableStageFields(…),   // ← the only new branch
    default         => [],                               // quantity_only
};
Pylon (Lower) 158

→ height_based. Identical to Pier Wall: cast in lifts, entered as metres. Zero new form code.

Pylon Cross Beam 160

→ location_only. Same as Pier Cap — pick the location, it's done. Zero new form code.

Pylon (Upper / Mast) 159

→ height_based again, with the cross beam as its prerequisite instead of the pile cap.

Stay Cable 161

→ cable_stage — new, but a copy of girder_step with four stages instead of three.

Live · Update Component Progress
Bridge across River Mindhola — 4 Lane
SHOW
Dashboard› Bridge Monitoring› Bridge Overview› Update Component Progress
Select Component and Date
Choose a component and date to update its progress.
RESOLVED PROGRESS TYPE
Component Progress Overview
A real-time overview of the selected component.
Today's Progress Entry

What getFormConfig() returns for the selected component

This is the exact payload the mobile app and the web blade both build from. It updates as you change the dropdown above.


  

The construction sequence — already enforced, and extended the same way

Every builder filters its own location list, so a component simply does not appear at a location until its predecessor is finished. There is no separate Foundation component — piles are the foundation (Pile = 1 in config/constant/component.php), and the chain starts there. The pylon chain uses the same mechanism; it is just longer, and it runs per leg.

// isCapPrerequisiteMet() — the two rules every other step copies
'pile'       → count(completed piles at loc) >= pile_pier_no
'wall_height' → sum(completed heights at loc) >= pier_height
ORDINARY PIER — P1, P6, P7, P8
1Pile — item_basedthe foundation · no prerequisite
2Pile Cap — location_onlyneeds: every pile at this location
3Pier Wall — height_basedneeds: pile cap
4Pier Cap — location_onlyneeds: wall at full height
5Pedestal — date_curingneeds: both end caps
6Girder — girder_stepneeds: pedestal · then cast → stress → launch, with curing between
7Deck Slab — span_basedneeds: girders launched

Four steps. Unchanged by this CR.

PYLON — P2, P3, P4, P5 · PER LEG
1Pile — item_basedthe foundation · no prerequisite
2Pile Cap — location_onlyneeds: every pile at this location
3Pylon (Lower) — height_basedneeds: pile cap · tracked per leg
4Pylon Cross Beam — location_onlyneeds: both legs at full lower height — it spans them
5Pylon (Upper / Mast) — height_basedneeds: cross beam · tracked per leg
6Stay Cable — cable_stageneeds: upper pylon · then anchorage → install → tension → stress

Six steps, and steps 3–6 run once per leg — pl_3 and pr_3 are separate locations with separate heights, exactly as portal-pier legs already are.

One height field, two legs — on purpose. A portal pier stores pier_height_l and pier_height_r because its legs can differ. A pylon’s legs are the same height, so the inventory form captures a single pylon_lower_height / pylon_upper_height and both legs track progress against that one target. Do not add per-leg height fields — the split lives in the location key (pl_n / pr_n), not in the form.
The one thing that is genuinely new. On a girder bridge the chain is strictly linear — you finish the substructure, then the superstructure. At a pylon, cable stressing is interleaved with deck construction: stress cable n, cast the next deck segment, stress cable n+1. The existing validator is one linear chain per bridge. Scope this as a separate rule set for pylon locations, not a patch to the shared chain — otherwise every live girder bridge in the system is affected.

Developer checklist for this screen

FileChangeSize
config/constant/component.phpAdd IDs 158–161 (+162 Deck Segment if segmental)Trivial
ProgressFormConfigService::getProgressType()158/159 → $heightBased; 160 → $locationOnly; 161 → new $cableStageTrivial
ProgressFormConfigServicebuildCableStageFields() — copy buildGirderStepFields(), 4 stages, leg-keyed locationsSmall
buildHeightBasedFields()Accept pylon locations (pl_n/pr_n) and swap the prerequisite per componentSmall
BridgeComponentProgressNew cable_stage column, mirroring girder_stepSmall
Sequence validatorSeparate interleaved rule set for pylon locations — do not extend the shared linear chainThe real work
progressCreate/*.blade.phpOne new pier-type branch. The four existing files are untouched.Medium

Mobile needs the same four components in its picker, but no new screen — it renders whatever getFormConfig() returns, which is the point of the config-driven design.

Page 2 of 3 · ← Back to the brief