diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-multishift.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-multishift.spec.ts index bc001ec6..0bd68259 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-multishift.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-multishift.spec.ts @@ -370,22 +370,21 @@ test.describe('Dashboard — multi-shift (3-5) round-trip regression guard', () }); /** - * Phase 4 — second-precision DISPLAY: when a row's site has - * `useOneMinuteIntervals = true` AND the planning has a precise - * `start1StartedAt` stamp (e.g. 07:03:53), the plannings-table cell must - * render the stamp at HH:mm:ss instead of the legacy HH:mm. + * Plannings-table display contract: the cell always renders the actual + * shift stamp as `HH:mm`, even when `AssignedSite.UseOneMinuteIntervals` + * is on. Seconds are never shown regardless of the flag. * - * Server-side seeding `AssignedSite.UseOneMinuteIntervals = true` plus - * a planning with `Start1StartedAt = 2026-05-15 07:03:53` requires DB - * fixture work the CI playwright shard doesn't yet wire up (the tests - * here log in as admin and rely on the default seed). Captured here as - * a TODO so the assertion shape survives any future fixture work; the - * Phase 4 jest unit test on `formatStamp(...)` covers the contract for - * the merge-blocking path. + * Server-side seeding of `AssignedSite.UseOneMinuteIntervals = true` + * plus a planning row with `Start1StartedAt = 2026-05-15 07:03:53` + * requires DB fixture work the CI playwright shard doesn't yet wire up + * (the tests here log in as admin and rely on the default seed). + * Captured here as a TODO so the assertion shape survives any future + * fixture work; the jest unit test on `formatStamp(...)` covers the + * format-helper contract for the merge-blocking path. */ - test.skip('plannings-table renders HH:mm:ss for actual stamp when site flag is on', async ({ page }) => { - // TODO(phase 4 fixture): seed AssignedSite.UseOneMinuteIntervals = true - // for the worker referenced by #cell3_0 AND a PlanRegistration row with + test.skip('plannings-table renders HH:mm for actual stamp when site flag is on', async ({ page }) => { + // TODO(fixture): seed AssignedSite.UseOneMinuteIntervals = true for the + // worker referenced by #cell3_0 AND a PlanRegistration row with // Start1StartedAt = '2026-05-15T07:03:53Z' on a date that lands inside // the dashboard's default visible range. // @@ -400,12 +399,12 @@ test.describe('Dashboard — multi-shift (3-5) round-trip regression guard', () // // // The first-shift actual line is rendered with id firstShiftActual{rowIdx}_{colField}. // const firstShiftActual = page.locator('[id^="firstShiftActual"]').first(); - // await expect(firstShiftActual).toContainText('07:03:53'); - // // Negative guard — the legacy 5-min path would render '07:00' / '07:05' instead. - // await expect(firstShiftActual).not.toContainText(/07:0[05]\s/); + // await expect(firstShiftActual).toContainText('07:03'); + // // Negative guard — seconds are never displayed even when the flag is on. + // await expect(firstShiftActual).not.toContainText('07:03:53'); // // Until the fixture lands the unit test - // `formatStamp (Phase 4) — uses HH:mm:ss format when row.useOneMinuteIntervals is true` + // `formatStamp — uses HH:mm format when row.useOneMinuteIntervals is true` // covers the format-helper contract (eform-client/src/app/plugins/modules/time-planning-pn/ // components/plannings/time-plannings-table/time-plannings-table.component.spec.ts). }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts index b946aa85..36ddcf41 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts @@ -376,9 +376,10 @@ describe('TimePlanningsTableComponent', () => { }); // ------------------------------------------------------------------ - // Phase 4 — second-precision display when UseOneMinuteIntervals is on + // formatStamp / getStopTimeDisplayWithSeconds — always HH:mm + // (`useOneMinuteIntervals` retained on the row but no longer affects display) // ------------------------------------------------------------------ - describe('formatStamp (Phase 4)', () => { + describe('formatStamp', () => { it('returns empty string when value is falsy', () => { expect(component.formatStamp({ useOneMinuteIntervals: true }, null)).toBe(''); expect(component.formatStamp({ useOneMinuteIntervals: true }, undefined as any)).toBe(''); @@ -397,16 +398,16 @@ describe('TimePlanningsTableComponent', () => { expect(result).toBe('07:03'); }); - it("uses HH:mm:ss format when row.useOneMinuteIntervals is true", () => { + it("uses HH:mm format when row.useOneMinuteIntervals is true", () => { const transformSpy = jest .spyOn(component['datePipe'], 'transform') - .mockReturnValue('07:03:53'); + .mockReturnValue('07:03'); const result = component.formatStamp( { useOneMinuteIntervals: true }, '2026-05-15T07:03:53Z', ); - expect(transformSpy).toHaveBeenCalledWith('2026-05-15T07:03:53Z', 'HH:mm:ss', 'UTC'); - expect(result).toBe('07:03:53'); + expect(transformSpy).toHaveBeenCalledWith('2026-05-15T07:03:53Z', 'HH:mm', 'UTC'); + expect(result).toBe('07:03'); }); it("falls back to HH:mm when row is null/undefined (defensive)", () => { @@ -419,7 +420,7 @@ describe('TimePlanningsTableComponent', () => { }); }); - describe('getStopTimeDisplayWithSeconds (Phase 4)', () => { + describe('getStopTimeDisplayWithSeconds', () => { it('returns empty string when either timestamp is falsy', () => { expect( component.getStopTimeDisplayWithSeconds({ useOneMinuteIntervals: true }, null, '2026-05-15T10:00:00Z'), @@ -452,21 +453,22 @@ describe('TimePlanningsTableComponent', () => { expect(result).toBe('15:30'); }); - it("uses HH:mm:ss format when flag on", () => { + it("uses HH:mm format when flag on", () => { const transformSpy = jest .spyOn(component['datePipe'], 'transform') - .mockReturnValue('15:30:11'); + .mockReturnValue('15:30'); const result = component.getStopTimeDisplayWithSeconds( { useOneMinuteIntervals: true }, '2026-05-15T07:00:00Z', '2026-05-15T15:30:11Z', ); - expect(transformSpy).toHaveBeenCalledWith('2026-05-15T15:30:11Z', 'HH:mm:ss', 'UTC'); - expect(result).toBe('15:30:11'); + expect(transformSpy).toHaveBeenCalledWith('2026-05-15T15:30:11Z', 'HH:mm', 'UTC'); + expect(result).toBe('15:30'); }); }); - describe('convertHoursToTimeWithSeconds (Phase 4)', () => { + // Dormant helper — production display no longer uses seconds. + describe('convertHoursToTimeWithSeconds', () => { it('formats whole-hour values with seconds suffix', () => { expect(component.convertHoursToTimeWithSeconds(8)).toBe('08:00:00'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts index df7d582f..1a41f9c1 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts @@ -291,24 +291,22 @@ export class TimePlanningsTableComponent implements OnInit, OnChanges, AfterView } /** - * Phase 4 second-precision sibling of the inline `datePipe.transform(..., 'HH:mm', 'UTC')` - * calls used in the plannings table. When the row's site has - * useOneMinuteIntervals on, the actual stamp cells render at - * HH:mm:ss; otherwise falls through to the legacy HH:mm - * format so flag-off rows are byte-identical to before. + * Formats an actual shift timestamp for display in the plannings table. + * Always renders as `HH:mm`; seconds are never displayed regardless of + * the row's `useOneMinuteIntervals` flag. The `row` parameter is kept + * for call-site stability. */ formatStamp(row: any, value: string | null | undefined): string { if (!value) { return ''; } - const fmt = row?.useOneMinuteIntervals ? 'HH:mm:ss' : 'HH:mm'; - return this.datePipe.transform(value, fmt, 'UTC') ?? ''; + return this.datePipe.transform(value, 'HH:mm', 'UTC') ?? ''; } /** - * Phase 4 second-precision sibling of {@link getStopTimeDisplay}. Same - * cross-day "24:00" guard as the legacy method, but emits seconds in the - * normal case when the row's site has the flag on. + * Stop-time display sibling of {@link getStopTimeDisplay} with the same + * cross-day "24:00" guard. Always renders as `HH:mm`; the `WithSeconds` + * suffix is a historical name — seconds are not emitted. */ getStopTimeDisplayWithSeconds(row: any, startedAt: string | null, stoppedAt: string | null): string { if (!startedAt || !stoppedAt) { @@ -323,8 +321,7 @@ export class TimePlanningsTableComponent implements OnInit, OnChanges, AfterView ) { return '24:00'; } - const fmt = row?.useOneMinuteIntervals ? 'HH:mm:ss' : 'HH:mm'; - return this.datePipe.transform(stoppedAt, fmt, 'UTC') ?? ''; + return this.datePipe.transform(stoppedAt, 'HH:mm', 'UTC') ?? ''; } onFirstColumnClick(row: any): void { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/models/plannings/time-planning.model.ts b/eform-client/src/app/plugins/modules/time-planning-pn/models/plannings/time-planning.model.ts index b99d0b57..5c6faa7d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/models/plannings/time-planning.model.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/models/plannings/time-planning.model.ts @@ -16,10 +16,11 @@ export class TimePlanningModel { deviceManufacturer: string; softwareVersionIsValid: boolean; /** - * Phase 4: mirror of the row's assigned-site UseOneMinuteIntervals flag. - * When true, the plannings table renders actual stamps at HH:mm:ss instead - * of HH:mm. Default false preserves byte-identical legacy behavior for - * rows whose site has the flag off. + * Mirror of the row's assigned-site UseOneMinuteIntervals flag. The flag now + * controls only (a) the wire-precision of stored start/stop timestamps and + * (b) the minutesGap input granularity in the workday-entity dialog (1-min + * vs 5-min). Displayed timestamps in the plannings table always show + * HH:mm regardless of the flag. */ useOneMinuteIntervals: boolean; }