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;
}