mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat: add onVesselSelected callback to ConfigDrivenSection for handling vessel selection
This commit is contained in:
@@ -25,15 +25,23 @@ interface Props {
|
||||
errors?: Record<string, string>;
|
||||
/** The applicant's registered vessels, for the vessel-picker field. */
|
||||
vessels?: Vessel[];
|
||||
/**
|
||||
* Fired when the vessel picker's value resolves to a known vessel. Vessel
|
||||
* Information and Current Ownership are separate form sections, so this
|
||||
* component (one instance per section) can only fill its own fields —
|
||||
* the parent uses this to fill the rest via the same `VESSEL_FIELD_FILLERS`.
|
||||
*/
|
||||
onVesselSelected?: (vessel: Vessel) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a Vessel's own fields onto whichever sibling fields the backend put
|
||||
* in the same section — same dual key/label matching the nationality prefill
|
||||
* in LicenseApplicationPage uses, so this doesn't depend on exact field-key
|
||||
* naming in the seeded formSchema.
|
||||
* Maps a Vessel's own fields onto whichever fields the backend put in the
|
||||
* license form — same dual key/label matching the nationality prefill in
|
||||
* LicenseApplicationPage uses, so this doesn't depend on exact field-key
|
||||
* naming in the seeded formSchema. Exported so the parent page can apply it
|
||||
* across every section, not just the one the vessel picker lives in.
|
||||
*/
|
||||
const VESSEL_FIELD_FILLERS: {
|
||||
export const VESSEL_FIELD_FILLERS: {
|
||||
matches: (label: string, key: string) => boolean;
|
||||
value: (vessel: Vessel) => unknown;
|
||||
}[] = [
|
||||
@@ -53,9 +61,12 @@ const VESSEL_FIELD_FILLERS: {
|
||||
{ matches: (l, k) => k === 'enginePowerKw' || l.includes('engine power'), value: (v) => v.enginePowerKw },
|
||||
{ matches: (l, k) => k === 'numberOfEngines' || l.includes('number of engines'), value: (v) => v.numberOfEngines },
|
||||
{ matches: (l, k) => k === 'hullMaterial' || l.includes('hull material'), value: (v) => v.hullMaterial },
|
||||
// Current Ownership: only the "current" owner, never "new owner" —
|
||||
// that's who the vessel is being transferred to, not on file anywhere.
|
||||
{ matches: (l, k) => k === 'currentOwnerName' || k === 'currentOwner' || l.includes('current owner'), value: (v) => v.ownerName },
|
||||
];
|
||||
|
||||
function fillFromVessel(
|
||||
export function fillFromVessel(
|
||||
vessel: Vessel,
|
||||
fields: FormFieldConfig[],
|
||||
onChange: (key: string, value: unknown) => void,
|
||||
@@ -82,6 +93,7 @@ export function ConfigDrivenSection({
|
||||
disabled,
|
||||
errors = {},
|
||||
vessels = [],
|
||||
onVesselSelected,
|
||||
}: Props) {
|
||||
const fields = [...(section.fields ?? [])].sort(
|
||||
(a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0),
|
||||
@@ -134,7 +146,7 @@ export function ConfigDrivenSection({
|
||||
// onChange={(v) => {
|
||||
// onChange(field.key, v);
|
||||
// const vessel = vessels.find((x) => x.id === v);
|
||||
// if (vessel) fillFromVessel(vessel, fields, onChange);
|
||||
// if (vessel) { fillFromVessel(vessel, fields, onChange); onVesselSelected?.(vessel); }
|
||||
// }}
|
||||
// searchable
|
||||
// clearable={!field.required}
|
||||
@@ -147,7 +159,10 @@ export function ConfigDrivenSection({
|
||||
const v = e.currentTarget.value;
|
||||
onChange(field.key, v);
|
||||
const vessel = vessels.find((x) => x.id === v);
|
||||
if (vessel) fillFromVessel(vessel, fields, onChange);
|
||||
if (vessel) {
|
||||
fillFromVessel(vessel, fields, onChange);
|
||||
onVesselSelected?.(vessel);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : field.type === 'SELECT' ? (
|
||||
|
||||
@@ -50,10 +50,11 @@ import {
|
||||
type FieldErrors,
|
||||
type FormFieldConfig,
|
||||
type ValidationIssue,
|
||||
type Vessel,
|
||||
} from '@ema-platform/api';
|
||||
import { getCountryCode, getCountryName, ModalFooter } from '@ema-platform/ui';
|
||||
import { useCurrentProfile } from '@ema-platform/auth';
|
||||
import { ConfigDrivenSection } from '../components/ConfigDrivenSection';
|
||||
import { ConfigDrivenSection, fillFromVessel } from '../components/ConfigDrivenSection';
|
||||
import { DocumentSlots } from '../components/DocumentSlots';
|
||||
import { StaffEvidence } from '../components/StaffEvidence';
|
||||
|
||||
@@ -210,6 +211,20 @@ export function LicenseApplicationPage() {
|
||||
|
||||
const readOnly = !['DRAFT', 'RESUBMIT_REQUIRED'].includes(application.status);
|
||||
|
||||
// Vessel Information and Current Ownership are separate form sections, so
|
||||
// ConfigDrivenSection (one instance per section) can't fill both itself —
|
||||
// it reports the pick up here and this fans it out across every section.
|
||||
function handleVesselSelected(vessel: Vessel) {
|
||||
for (const section of config?.licenseType.formSchema.sections ?? []) {
|
||||
fillFromVessel(vessel, section.fields, (key, value) => {
|
||||
setDraft((prev) => ({
|
||||
...prev,
|
||||
[section.key]: { ...(prev[section.key] ?? {}), [key]: value },
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSection(sectionKey: string) {
|
||||
// During an adjustment round only flagged sections are editable, so don't
|
||||
// even attempt a write the server would reject.
|
||||
@@ -459,6 +474,7 @@ export function LicenseApplicationPage() {
|
||||
section={section}
|
||||
values={draft[section.key] ?? {}}
|
||||
formData={draft}
|
||||
onVesselSelected={handleVesselSelected}
|
||||
errors={fieldErrors}
|
||||
vessels={vessels}
|
||||
disabled={readOnly || locked}
|
||||
@@ -589,6 +605,7 @@ export function LicenseApplicationPage() {
|
||||
</Text>
|
||||
<ConfigDrivenSection
|
||||
section={section}
|
||||
onVesselSelected={handleVesselSelected}
|
||||
values={draft[section.key] ?? {}}
|
||||
formData={draft}
|
||||
errors={fieldErrors}
|
||||
|
||||
Reference in New Issue
Block a user