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.
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
};
→ height_based. Identical to Pier Wall: cast in lifts, entered as metres. Zero new form code.
→ location_only. Same as Pier Cap — pick the location, it's done. Zero new form code.
→ height_based again, with the cross beam as its prerequisite instead of the pile cap.
→ cable_stage — new, but a copy of girder_step with four stages instead of three.
getFormConfig() returns for the selected componentThis is the exact payload the mobile app and the web blade both build from. It updates as you change the dropdown above.
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.
Four steps. Unchanged by this CR.
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.
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.
| File | Change | Size |
|---|---|---|
| config/constant/component.php | Add IDs 158–161 (+162 Deck Segment if segmental) | Trivial |
| ProgressFormConfigService::getProgressType() | 158/159 → $heightBased; 160 → $locationOnly; 161 → new $cableStage | Trivial |
| ProgressFormConfigService | buildCableStageFields() — copy buildGirderStepFields(), 4 stages, leg-keyed locations | Small |
| buildHeightBasedFields() | Accept pylon locations (pl_n/pr_n) and swap the prerequisite per component | Small |
| BridgeComponentProgress | New cable_stage column, mirroring girder_step | Small |
| Sequence validator | Separate interleaved rule set for pylon locations — do not extend the shared linear chain | The real work |
| progressCreate/*.blade.php | One 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.