feat(admin-pause): exact-minute pause display + edit under UseOneMinuteIntervals=true#1568
Merged
renemadsen merged 1 commit intostablefrom May 8, 2026
Merged
Conversation
…teIntervals=true Admin web UI now shows and persists exact-minute pause durations for sites with UseOneMinuteIntervals=true. Fully additive — flag-off code paths and existing pause1Id sentinel encoding are bit-identical. Changes: - DTO: 5 new int? Pause1ExactMinutes-Pause5ExactMinutes (request-only, no schema migration). Mirrored in planning-pr-day.model.ts. - Angular workday-entity-dialog: 5 new pauseNExact ternary vars next to existing pauseNId reads (line 161 untouched), FormControl value swap pauseNId → pauseNExact (the ternary defaults to pauseNId when flag-off, preserving bit-identity), additive write block after each pauseNId assignment (line 1321 untouched). New helpers computeExactPauseMinutes, getPauseTimestampPairs. - C# TimePlanningPlanningService.Update: additive top-level branch gated on UseOneMinuteIntervals && pauseNExactMinutes.HasValue. Loop dispatches to ApplyExactMinutePause(planning, shift, minutes). UseDetailedPauseEditing is completely ignored per user directive. - ApplyExactMinutePause anchors on existing Pause*StartedAt if present, else shift midpoint; computes Pause*StoppedAt = anchor + minutes; clears sub-slot timestamps (pause10-19, pause100-102 for shift 1; analogous for shift 2) so AggregatePauseMinutes does not double-count. exactMinutes=0 short-circuits via ClearPauseTimestamps so admin can clear pauses. - ClearPauseTimestamps: shared helper for sub-slot null-out, called from both zero-clear and non-zero branches as single source of truth. Customer 855: a 12-second worker pause that previously displayed as "00:15" in admin (legacy 5-min-tick formula off pause1Id=4) will now display the actual minute count when admin opens the day. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support in the admin workday edit dialog to display and persist pause durations in exact minutes when a site has UseOneMinuteIntervals=true, while keeping the legacy 5-minute Pause*Id behavior unchanged when the flag is off.
Changes:
- Added request-only
Pause1ExactMinutes–Pause5ExactMinutesfields to the planning day DTOs (C# + Angular model). - Updated the Angular workday dialog to derive pause duration from pause timestamp pairs (including sub-slot pauses) and to submit exact-minute values under the flag.
- Updated
TimePlanningPlanningService.Updateto translate submitted exact-minute pauses into canonicalPause*StartedAt/Pause*StoppedAttimestamps and clear sub-slot pause intervals to avoid double counting.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/Services/TimePlanningPlanningService/TimePlanningPlanningService.cs | Applies exact-minute pause edits on UseOneMinuteIntervals, adds helpers to write canonical pause stamps and clear sub-slot intervals. |
| eFormAPI/Plugins/TimePlanning.Pn/TimePlanning.Pn/Infrastructure/Models/Planning/TimePlanningPlanningPrDayModel.cs | Adds request-only Pause*ExactMinutes properties to the update model. |
| eform-client/src/app/plugins/modules/time-planning-pn/models/plannings/planning-pr-day.model.ts | Adds optional pause*ExactMinutes fields to the Angular model. |
| eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.ts | Computes exact-minute pause display from timestamp pairs and submits exact-minute pause values when the flag is on. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
1400
to
+1403
| this.data.planningPrDayModels.pause1Id = this.convertTimeToMinutes(a1?.pause, true) === 0 ? null : this.convertTimeToMinutes(a1?.pause, true); | ||
| if (this.useOneMinuteIntervals) { | ||
| this.data.planningPrDayModels.pause1ExactMinutes = this.convertTimeToMinutes(a1?.pause, false); | ||
| } |
| // eslint-disable-next-line max-len | ||
| this.data.planningPrDayModels.pause2Id = this.convertTimeToMinutes(a2?.pause, true) === 0 ? null : this.convertTimeToMinutes(a2?.pause, true); | ||
| if (this.useOneMinuteIntervals) { | ||
| this.data.planningPrDayModels.pause2ExactMinutes = this.convertTimeToMinutes(a2?.pause, false); |
Comment on lines
1420
to
+1423
| this.data.planningPrDayModels.pause3Id = this.convertTimeToMinutes(a3?.pause, true) === 0 ? null : this.convertTimeToMinutes(a3?.pause, true); | ||
| if (this.useOneMinuteIntervals) { | ||
| this.data.planningPrDayModels.pause3ExactMinutes = this.convertTimeToMinutes(a3?.pause, false); | ||
| } |
| // eslint-disable-next-line max-len | ||
| this.data.planningPrDayModels.pause4Id = this.convertTimeToMinutes(a4?.pause, true) === 0 ? null : this.convertTimeToMinutes(a4?.pause, true); | ||
| if (this.useOneMinuteIntervals) { | ||
| this.data.planningPrDayModels.pause4ExactMinutes = this.convertTimeToMinutes(a4?.pause, false); |
| // eslint-disable-next-line max-len | ||
| this.data.planningPrDayModels.pause5Id = this.convertTimeToMinutes(a5?.pause, true) === 0 ? null : this.convertTimeToMinutes(a5?.pause, true); | ||
| if (this.useOneMinuteIntervals) { | ||
| this.data.planningPrDayModels.pause5ExactMinutes = this.convertTimeToMinutes(a5?.pause, false); |
Comment on lines
+1054
to
+1059
| private computeExactPauseMinutes(shift: number): number { | ||
| let totalSeconds = 0; | ||
| for (const [start, stop] of this.getPauseTimestampPairs(shift)) { | ||
| if (start && stop) { | ||
| totalSeconds += (new Date(stop).getTime() - new Date(start).getTime()) / 1000; | ||
| } |
Comment on lines
+971
to
+982
| (1, model.Pause1ExactMinutes), | ||
| (2, model.Pause2ExactMinutes), | ||
| (3, model.Pause3ExactMinutes), | ||
| (4, model.Pause4ExactMinutes), | ||
| (5, model.Pause5ExactMinutes), | ||
| }; | ||
| foreach (var (shift, minutes) in exactPauses) | ||
| { | ||
| if (minutes.HasValue) | ||
| { | ||
| ApplyExactMinutePause(planning, shift, minutes.Value); | ||
| } |
Comment on lines
+967
to
+984
| if (assignedSite.UseOneMinuteIntervals) | ||
| { | ||
| var exactPauses = new[] | ||
| { | ||
| (1, model.Pause1ExactMinutes), | ||
| (2, model.Pause2ExactMinutes), | ||
| (3, model.Pause3ExactMinutes), | ||
| (4, model.Pause4ExactMinutes), | ||
| (5, model.Pause5ExactMinutes), | ||
| }; | ||
| foreach (var (shift, minutes) in exactPauses) | ||
| { | ||
| if (minutes.HasValue) | ||
| { | ||
| ApplyExactMinutePause(planning, shift, minutes.Value); | ||
| } | ||
| } | ||
| } |
Comment on lines
+1907
to
+1914
| private void ApplyExactMinutePause(PlanRegistration planning, int shift, int exactMinutes) | ||
| { | ||
| if (exactMinutes == 0) | ||
| { | ||
| ClearPauseTimestamps(planning, shift); | ||
| return; | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Admin web UI now shows and persists exact-minute pause durations for sites with
UseOneMinuteIntervals=true. Fully additive — flag-off code paths and existingpause1Idsentinel encoding stay bit-identical.Closes the customer 855 case where a 12-second worker pause displayed as "00:15" in the admin edit dialog (legacy 5-min-tick formula off
pause1Id=4).Changes
DTO
TimePlanningPlanningPrDayModel: 5 newint? Pause1ExactMinutes-Pause5ExactMinutesproperties (request-only, no schema migration).planning-pr-day.model.ts: matching optional fields.Angular
workday-entity-dialog.component.tspauseNExactternary vars next to existingpauseNIdreads. Existing line 161 untouched.pauseNId→pauseNExact(the ternary defaults topauseNIdwhen flag-off, preserving bit-identity).pauseNIdassignment (line 1321 untouched). When flag-on, sendspauseNExactMinutes = convertTimeToMinutes(value, false)(raw minutes, no sentinel).computeExactPauseMinutes(shift)andgetPauseTimestampPairs(shift).C#
TimePlanningPlanningService.UpdateUseOneMinuteIntervals && pauseNExactMinutes.HasValue. Loop dispatches toApplyExactMinutePause(planning, shift, minutes).UseDetailedPauseEditingcompletely ignored per business directive.ApplyExactMinutePausehelperPause*StartedAtif present, else shift midpoint.Pause*StoppedAt = anchor + minutes.ClearPauseTimestamps(planning, shift)first to wipe sub-slot pairs (pause10-19, pause100-102 for shift 1; pause20-29, pause200-202 for shift 2) —AggregatePauseMinutesonly sees the canonical slot.exactMinutes == 0short-circuits viaClearPauseTimestampsso admin can fully clear pauses (zero-clearing edge case fixed).Constraints honored
pause1Idsentinel-encoded read at line 161 untouched.pause1Idsentinel-encoded write at line 1321 untouched.eform-timeplanning-baseedits.UseDetailedPauseEditingnot entangled — new branch is at top level outside it.Test plan
UseOneMinuteIntervals=truewhere a worker took a 3-minute pause: dialog shows "00:03" (not "00:00" or "00:05").Pause1StoppedAt = Pause1StartedAt + 7 min; sub-slot pause10-pause102 timestamps are cleared. AggregatePauseMinutes returns 7.pause1Idalso cleared by existing line 1321 path.UseOneMinuteIntervals=false: dialog continues to display 5-min-tick decoded value; round-trip is bit-identical tostableHEAD.🤖 Generated with Claude Code