Skip to content

Commit b31c938

Browse files
committed
chore: fixes to recurring end dates
1 parent 7a00d87 commit b31c938

3 files changed

Lines changed: 76 additions & 19 deletions

File tree

libs/form-fields/src/lib/recurrence-field.component.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import {
4242
import { CommonModule } from '@angular/common';
4343
import { MatFormFieldModule } from '@angular/material/form-field';
4444
import { MatSelectModule } from '@angular/material/select';
45-
import { SettingsService } from 'libs/common/src/lib/settings.service';
4645
import { TranslatePipe } from 'libs/components/src/lib/translate.pipe';
4746
import { RecurrenceDetails } from 'libs/events/src/lib/event.interfaces';
4847
import { RecurrenceModalComponent } from './recurrence-modal.component';
@@ -167,7 +166,6 @@ import { RecurrenceModalComponent } from './recurrence-modal.component';
167166
})
168167
export class RecurrenceFieldComponent implements ControlValueAccessor, OnInit {
169168
private _dialog = inject(MatDialog);
170-
private _settings = inject(SettingsService);
171169
private _destroyRef = inject(DestroyRef);
172170
private _controlContainer = inject(ControlContainer, { optional: true });
173171

@@ -280,10 +278,8 @@ export class RecurrenceFieldComponent implements ControlValueAccessor, OnInit {
280278

281279
public setSimple(pattern: string) {
282280
const day_of_week = new Date(this.date()).getDay();
283-
const default_recurrence =
284-
this._settings.get('app.default_recurrence_period') || 180;
285281
const end_date = endOfDay(
286-
addDays(this.date(), default_recurrence),
282+
addDays(this.date(), this.available_days()),
287283
).valueOf();
288284
if (pattern === 'none') {
289285
this.setValue(NO_RECURR);
@@ -377,6 +373,17 @@ export class RecurrenceFieldComponent implements ControlValueAccessor, OnInit {
377373
}
378374
if (current.end_type === 'instances' && current.end_instances) {
379375
updated.end_date = this._computeEndDate(current, date_value);
376+
} else if (
377+
current.end_type === 'date' &&
378+
current.end_date &&
379+
// end_date is always stored as endOfDay(), so this comparison
380+
// is effectively a day-boundary check: reset only when
381+
// the entire end day is before the new booking date.
382+
current.end_date < date_value
383+
) {
384+
updated.end_date = endOfDay(
385+
addDays(date_value, this.available_days()),
386+
).valueOf();
380387
}
381388
if (Object.keys(updated).length) {
382389
this.setValue({ ...current, ...updated });

libs/form-fields/src/lib/recurrence-modal.component.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ import { DateFieldComponent } from './date-field.component';
130130
}}</mat-radio-button>
131131
<a-date-field
132132
formControlName="end_date"
133+
[from]="date"
133134
[to]="end_date"
134135
></a-date-field>
135136
</div>
@@ -230,18 +231,13 @@ export class RecurrenceModalComponent extends AsyncHandler implements OnInit {
230231
week: new FormControl(0),
231232
monthly_type: new FormControl<MonthlyType>('day_of_month'),
232233
end_type: new FormControl<RecurrEndType>('never'),
233-
end_date: new FormControl(addMonths(Date.now(), 3)),
234+
end_date: new FormControl(
235+
endOfDay(addDays(this.date, this.available_days)),
236+
),
234237
end_instances: new FormControl(13),
235238
});
236239

237-
constructor() {
238-
super();
239-
}
240-
241240
public ngOnInit() {
242-
if (this.form.value.end_date > this.end_date) {
243-
this.form.patchValue({ end_date: this.end_date });
244-
}
245241
this.subscription(
246242
'end_type',
247243
this.form.controls.end_type.valueChanges.subscribe((type) =>
@@ -258,6 +254,12 @@ export class RecurrenceModalComponent extends AsyncHandler implements OnInit {
258254
if (!this.form.value.type || this.form.value.type === 'none') {
259255
this.form.patchValue({ type: 'daily' });
260256
}
257+
// Clamp end_date to valid range after loading the saved value
258+
if (this.form.value.end_date < this.date) {
259+
this.form.patchValue({ end_date: this.date });
260+
} else if (this.form.value.end_date > this.end_date) {
261+
this.form.patchValue({ end_date: this.end_date });
262+
}
261263
this._onEndTypeChange(this.form.value.end_type);
262264
if (this.form.value.type === 'monthly' && this.form.value.week) {
263265
const set = this.form.value.weekdays;

libs/form-fields/src/tests/recurrence-field.component.spec.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
import { MockModule, MockProvider } from 'ng-mocks';
2222

2323
import { BookingRecurrence } from '@placeos/common';
24-
import { SettingsService } from 'libs/common/src/lib/settings.service';
2524
import { RecurrenceFieldComponent } from '../lib/recurrence-field.component';
2625

2726
// ---------------------------------------------------------------------------
@@ -52,10 +51,7 @@ describe('RecurrenceFieldComponent – date change regression', () => {
5251
const createComponent = createComponentFactory({
5352
component: RecurrenceFieldComponent,
5453
imports: [MockModule(MatFormFieldModule), MockModule(MatSelectModule)],
55-
providers: [
56-
MockProvider(SettingsService, { get: jest.fn() }),
57-
MockProvider(MatDialog, { open: jest.fn() }),
58-
],
54+
providers: [MockProvider(MatDialog, { open: jest.fn() })],
5955
});
6056

6157
/** Update the date signal input and flush effects. */
@@ -295,8 +291,14 @@ describe('RecurrenceFieldComponent – date change regression', () => {
295291
endOfMonth(addMonths(mar15, 2)).valueOf(),
296292
);
297293
});
294+
});
298295

299-
it('should NOT change end_date when end_type is "date"', () => {
296+
// -----------------------------------------------------------------------
297+
// Date-based end_date is clamped when booking date moves past it
298+
// -----------------------------------------------------------------------
299+
describe('date-based end_date is clamped when booking date moves forward', () => {
300+
it('should NOT change end_date when end_type is "date" and new date is still before end_date', () => {
301+
// end_date is June 30; new booking date Feb 1 is still well before it
300302
const fixed_end = new Date(2024, 5, 30).valueOf();
301303
setDate(new Date(2024, 0, 8).valueOf());
302304
spectator.component.setValue({
@@ -310,8 +312,54 @@ describe('RecurrenceFieldComponent – date change regression', () => {
310312

311313
setDate(new Date(2024, 1, 1).valueOf());
312314

315+
// end_date is still in the future relative to the new booking date
316+
// so it should remain unchanged
313317
expect(spectator.component.value().end_date).toBe(fixed_end);
314318
});
319+
320+
it('should update end_date when booking date moves past the recurrence end_date', () => {
321+
// Set up weekly recurrence ending April 5, 2024
322+
const apr5 = new Date(2024, 3, 5).valueOf();
323+
setDate(new Date(2024, 2, 1).valueOf()); // booking date: March 1
324+
spectator.component.setValue({
325+
_custom: true,
326+
type: 'weekly',
327+
interval: 1,
328+
weekdays: new Set([5 as any]), // Friday
329+
end_type: 'date',
330+
end_date: apr5,
331+
});
332+
333+
// Move booking date to April 10 – now AFTER the end_date of April 5
334+
const apr10 = new Date(2024, 3, 10).valueOf();
335+
setDate(apr10);
336+
337+
// end_date must be >= new booking date; stale April 5 is invalid
338+
expect(spectator.component.value().end_date).toBeGreaterThanOrEqual(
339+
apr10,
340+
);
341+
});
342+
343+
it('should NOT change end_date when booking date moves forward but stays before end_date', () => {
344+
// Set up weekly recurrence ending April 30, 2024
345+
const apr30 = new Date(2024, 3, 30).valueOf();
346+
setDate(new Date(2024, 2, 1).valueOf()); // booking date: March 1
347+
spectator.component.setValue({
348+
_custom: true,
349+
type: 'weekly',
350+
interval: 1,
351+
weekdays: new Set([5 as any]), // Friday
352+
end_type: 'date',
353+
end_date: apr30,
354+
});
355+
356+
// Move booking date to April 10 – still BEFORE the end_date of April 30
357+
const apr10 = new Date(2024, 3, 10).valueOf();
358+
setDate(apr10);
359+
360+
// end_date should remain unchanged because it is still in the future
361+
expect(spectator.component.value().end_date).toBe(apr30);
362+
});
315363
});
316364

317365
// -----------------------------------------------------------------------

0 commit comments

Comments
 (0)