@tmlmobilidade/dates 20260331.1620.53 → 20260406.1418.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calendar/rules/calculation/matchers.js +5 -5
- package/dist/calendar/rules/formatting/common.d.ts +4 -2
- package/dist/calendar/rules/formatting/common.js +7 -6
- package/dist/calendar/rules/formatting/rule-summary.d.ts +1 -16
- package/dist/calendar/rules/formatting/rule-summary.js +62 -40
- package/dist/calendar/rules/preview/affectedDates.js +5 -0
- package/package.json +1 -1
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
export function manualRuleMatchesContext(rule, ctx) {
|
|
19
19
|
if (rule.event_id) {
|
|
20
20
|
// Event-exception rules: date matching already done by filteredManualRules in computeActiveRules.
|
|
21
|
-
// year_period_ids is cleared for these rules, so skip that check.
|
|
22
21
|
// If weekdays is non-empty, the user narrowed the event to specific days — check it.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
if (rule.weekdays.length > 0 && !rule.weekdays.includes(ctx.weekday))
|
|
23
|
+
return false;
|
|
24
|
+
// If year_period_ids is non-empty, the user narrowed the event to specific periods — check it.
|
|
25
|
+
if (rule.year_period_ids.length > 0 && !rule.year_period_ids.includes(ctx.yearPeriodId))
|
|
26
|
+
return false;
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
// Strict by design: rule must include the weekday and the yearPeriodId.
|
|
@@ -9,8 +9,9 @@ import { EventReplacementRule, ManualRule, StopsParameterOverride, YearPeriod }
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function buildYearPeriodsPart(rule: EventReplacementRule | ManualRule | StopsParameterOverride, options: {
|
|
11
11
|
periods?: YearPeriod[];
|
|
12
|
-
}, cfg
|
|
12
|
+
}, cfg?: {
|
|
13
13
|
mode: 'long' | 'short';
|
|
14
|
+
omitIfAll?: boolean;
|
|
14
15
|
}): string;
|
|
15
16
|
/**
|
|
16
17
|
* Builds the weekday portion of a rule summary.
|
|
@@ -27,8 +28,9 @@ export declare function buildYearPeriodsPart(rule: EventReplacementRule | Manual
|
|
|
27
28
|
* @param cfg - Format mode (short or long)
|
|
28
29
|
* @returns Formatted weekday string in Portuguese
|
|
29
30
|
*/
|
|
30
|
-
export declare function buildWeekdaysPart(rule: EventReplacementRule | ManualRule | StopsParameterOverride, cfg
|
|
31
|
+
export declare function buildWeekdaysPart(rule: EventReplacementRule | ManualRule | StopsParameterOverride, cfg?: {
|
|
31
32
|
mode: 'long' | 'short';
|
|
33
|
+
omitIfAll?: boolean;
|
|
32
34
|
}): string;
|
|
33
35
|
export declare function buildDayPeriodsPart(parameter: StopsParameterOverride, cfg: {
|
|
34
36
|
mode: 'long' | 'short';
|
|
@@ -7,11 +7,13 @@ import { DAY_PERIOD_LABELS, WEEKDAY_OPTIONS } from '@tmlmobilidade/types';
|
|
|
7
7
|
* - Single period: Shows period name
|
|
8
8
|
* - Multiple periods: "N períodos" (short) or "Durante os períodos de X e Y" (long)
|
|
9
9
|
*/
|
|
10
|
-
export function buildYearPeriodsPart(rule, options, cfg) {
|
|
10
|
+
export function buildYearPeriodsPart(rule, options, cfg = { mode: 'long', omitIfAll: false }) {
|
|
11
11
|
const allPeriodIds = options?.periods?.map(p => p._id) ?? [];
|
|
12
12
|
const selectedPeriodIds = rule.year_period_ids || [];
|
|
13
13
|
const isAll = allPeriodIds.length > 0 && selectedPeriodIds.length === allPeriodIds.length && allPeriodIds.every(id => selectedPeriodIds.includes(id));
|
|
14
14
|
if (!selectedPeriodIds.length || isAll) {
|
|
15
|
+
if (cfg.omitIfAll)
|
|
16
|
+
return '';
|
|
15
17
|
return cfg.mode === 'short' ? 'Todos os períodos' : 'Em todos os períodos';
|
|
16
18
|
}
|
|
17
19
|
const labels = selectedPeriodIds.map(id => options?.periods?.find(p => p._id === id)?.name ?? 'período desconhecido');
|
|
@@ -39,13 +41,12 @@ export function buildYearPeriodsPart(rule, options, cfg) {
|
|
|
39
41
|
* @param cfg - Format mode (short or long)
|
|
40
42
|
* @returns Formatted weekday string in Portuguese
|
|
41
43
|
*/
|
|
42
|
-
export function buildWeekdaysPart(rule, cfg) {
|
|
43
|
-
if (!rule.weekdays || rule.weekdays.length === 0) {
|
|
44
|
+
export function buildWeekdaysPart(rule, cfg = { mode: 'long' }) {
|
|
45
|
+
if (!rule.weekdays || rule.weekdays.length === 0 || rule.weekdays.length === 7) {
|
|
46
|
+
if (cfg.omitIfAll)
|
|
47
|
+
return '';
|
|
44
48
|
return cfg.mode === 'short' ? 'Todos os dias' : 'em todos os dias';
|
|
45
49
|
}
|
|
46
|
-
if (rule.weekdays.length === 7) {
|
|
47
|
-
return 'Todos os dias';
|
|
48
|
-
}
|
|
49
50
|
const weekdaySet = new Set(rule.weekdays);
|
|
50
51
|
const hasMonFri = [1, 2, 3, 4, 5].every(d => weekdaySet.has(d));
|
|
51
52
|
const hasWeekend = weekdaySet.has(6) && weekdaySet.has(7);
|
|
@@ -18,21 +18,6 @@ export interface RuleSummary {
|
|
|
18
18
|
* - **short**: Compact label for badges/pills (e.g., "Dias úteis · Período Escolar")
|
|
19
19
|
* - **long**: Full description for tooltips (e.g., "Durante o Período Escolar, nos dias úteis")
|
|
20
20
|
* - **tooltip**: Detailed event information with dates and times
|
|
21
|
-
*
|
|
22
|
-
* @param rule - The scheduling rule to summarize
|
|
23
|
-
* @param options - Configuration options (periods array for name resolution)
|
|
24
|
-
* @returns RuleSummary with short, long, and tooltip text
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const rule = { kind: 'manual', weekdays: [1,2,3,4,5], year_period_ids: ['school'], ... };
|
|
29
|
-
* const summary = buildRuleSummary(rule, { periods });
|
|
30
|
-
* // summary = {
|
|
31
|
-
* // short: "Dias úteis · Período Escolar",
|
|
32
|
-
* // long: "Durante o Período Escolar, nos dias úteis",
|
|
33
|
-
* // tooltip: ""
|
|
34
|
-
* // }
|
|
35
|
-
* ```
|
|
36
21
|
*/
|
|
37
22
|
export declare function buildRuleSummary(rule: ScheduleRule, options: {
|
|
38
23
|
events?: Event[];
|
|
@@ -46,7 +31,7 @@ export declare function buildRuleSummary(rule: ScheduleRule, options: {
|
|
|
46
31
|
* - ALL
|
|
47
32
|
* - ALL_DU
|
|
48
33
|
* - VER-FER_SAB-DOM
|
|
49
|
-
* -
|
|
34
|
+
* - Rock in Rio_VER_DU
|
|
50
35
|
*/
|
|
51
36
|
export declare function buildRuleSummaryGtfs(rule: ScheduleRule, options: {
|
|
52
37
|
events?: Event[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dates } from '../../../dates.js';
|
|
2
2
|
import { FORMATS } from '../../../format.js';
|
|
3
|
-
import { WEEKDAY_OPTIONS } from '@tmlmobilidade/types';
|
|
3
|
+
import { WEEKDAY_OPTIONS, } from '@tmlmobilidade/types';
|
|
4
4
|
import { buildWeekdaysPart, buildYearPeriodsPart } from './common.js';
|
|
5
5
|
/**
|
|
6
6
|
* Builds human-readable summaries of a scheduling rule.
|
|
@@ -9,21 +9,6 @@ import { buildWeekdaysPart, buildYearPeriodsPart } from './common.js';
|
|
|
9
9
|
* - **short**: Compact label for badges/pills (e.g., "Dias úteis · Período Escolar")
|
|
10
10
|
* - **long**: Full description for tooltips (e.g., "Durante o Período Escolar, nos dias úteis")
|
|
11
11
|
* - **tooltip**: Detailed event information with dates and times
|
|
12
|
-
*
|
|
13
|
-
* @param rule - The scheduling rule to summarize
|
|
14
|
-
* @param options - Configuration options (periods array for name resolution)
|
|
15
|
-
* @returns RuleSummary with short, long, and tooltip text
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* const rule = { kind: 'manual', weekdays: [1,2,3,4,5], year_period_ids: ['school'], ... };
|
|
20
|
-
* const summary = buildRuleSummary(rule, { periods });
|
|
21
|
-
* // summary = {
|
|
22
|
-
* // short: "Dias úteis · Período Escolar",
|
|
23
|
-
* // long: "Durante o Período Escolar, nos dias úteis",
|
|
24
|
-
* // tooltip: ""
|
|
25
|
-
* // }
|
|
26
|
-
* ```
|
|
27
12
|
*/
|
|
28
13
|
export function buildRuleSummary(rule, options) {
|
|
29
14
|
return {
|
|
@@ -46,27 +31,48 @@ const getEventForManualRule = (rule, events) => {
|
|
|
46
31
|
return events?.find(event => event._id === rule.event_id);
|
|
47
32
|
};
|
|
48
33
|
/* ---------------- helpers ---------------- */
|
|
34
|
+
function dateMatchesWeekdays(date, weekdays) {
|
|
35
|
+
if (!weekdays?.length)
|
|
36
|
+
return true;
|
|
37
|
+
const jsDay = Dates.fromOperationalDate(date, 'Europe/Lisbon').js_date.getDay();
|
|
38
|
+
const isoWeekday = (jsDay === 0 ? 7 : jsDay);
|
|
39
|
+
return weekdays.includes(isoWeekday);
|
|
40
|
+
}
|
|
41
|
+
function dateMatchesPeriods(date, yearPeriodIds, periods) {
|
|
42
|
+
if (!yearPeriodIds?.length)
|
|
43
|
+
return true;
|
|
44
|
+
const allowedDates = new Set(periods
|
|
45
|
+
?.filter(p => yearPeriodIds.includes(p._id))
|
|
46
|
+
.flatMap(p => p.dates ?? []) ?? []);
|
|
47
|
+
return allowedDates.has(date);
|
|
48
|
+
}
|
|
49
49
|
/**
|
|
50
50
|
* Builds the short summary format for a rule.
|
|
51
51
|
*
|
|
52
|
-
* Event rules:
|
|
53
|
-
* Manual rules:
|
|
52
|
+
* Event restriction / replacement rules: event title
|
|
53
|
+
* Manual rules:
|
|
54
|
+
* - event-based: "Event · Period · Weekdays"
|
|
55
|
+
* - normal: "Period · Weekdays"
|
|
54
56
|
*/
|
|
55
57
|
function buildRuleSummaryShort(rule, options) {
|
|
56
58
|
if (isEventRestriction(rule)) {
|
|
57
|
-
// Restriction: show event name
|
|
58
59
|
return rule.event?.title ?? '';
|
|
59
60
|
}
|
|
60
61
|
if (isEventReplacement(rule)) {
|
|
61
|
-
// Replacement: show event name
|
|
62
62
|
return rule.event?.title ?? '';
|
|
63
63
|
}
|
|
64
64
|
if (rule.kind === 'manual' && rule.event_id) {
|
|
65
65
|
const title = getEventForManualRule(rule, options?.events)?.title ?? '';
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const parts = [];
|
|
67
|
+
if (title)
|
|
68
|
+
parts.push(title);
|
|
69
|
+
const periodPart = buildYearPeriodsPart(rule, options, { mode: 'short', omitIfAll: true });
|
|
70
|
+
if (periodPart)
|
|
71
|
+
parts.push(periodPart);
|
|
72
|
+
const weekdayPart = buildWeekdaysPart(rule, { mode: 'short', omitIfAll: true });
|
|
73
|
+
if (weekdayPart)
|
|
74
|
+
parts.push(weekdayPart);
|
|
75
|
+
return parts.join(' · ');
|
|
70
76
|
}
|
|
71
77
|
// manual
|
|
72
78
|
const parts = [];
|
|
@@ -81,8 +87,10 @@ function buildRuleSummaryShort(rule, options) {
|
|
|
81
87
|
/**
|
|
82
88
|
* Builds the long summary format for a rule.
|
|
83
89
|
*
|
|
84
|
-
* Event rules:
|
|
85
|
-
* Manual rules:
|
|
90
|
+
* Event restriction / replacement rules: event title
|
|
91
|
+
* Manual rules:
|
|
92
|
+
* - event-based: "Event, Period, Weekdays"
|
|
93
|
+
* - normal: "Period, Weekdays"
|
|
86
94
|
*/
|
|
87
95
|
function buildRuleSummaryLong(rule, options) {
|
|
88
96
|
if (isEventReplacement(rule) || isEventRestriction(rule)) {
|
|
@@ -90,10 +98,16 @@ function buildRuleSummaryLong(rule, options) {
|
|
|
90
98
|
}
|
|
91
99
|
if (rule.kind === 'manual' && rule.event_id) {
|
|
92
100
|
const title = getEventForManualRule(rule, options?.events)?.title ?? '';
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
const parts = [];
|
|
102
|
+
if (title)
|
|
103
|
+
parts.push(title);
|
|
104
|
+
const periodPart = buildYearPeriodsPart(rule, options, { mode: 'long', omitIfAll: true });
|
|
105
|
+
if (periodPart)
|
|
106
|
+
parts.push(periodPart);
|
|
107
|
+
const weekdayPart = buildWeekdaysPart(rule, { mode: 'long', omitIfAll: true });
|
|
108
|
+
if (weekdayPart)
|
|
109
|
+
parts.push(weekdayPart);
|
|
110
|
+
return parts.join(', ');
|
|
97
111
|
}
|
|
98
112
|
// manual
|
|
99
113
|
const parts = [];
|
|
@@ -128,7 +142,7 @@ function truncateDates(dates, max = 5) {
|
|
|
128
142
|
*
|
|
129
143
|
* Restriction rules: "Oferta excluída [on dates] [time window]"
|
|
130
144
|
* Replacement rules: "Funcionará como [weekdays] · [periods] · [dates]"
|
|
131
|
-
* Manual rules:
|
|
145
|
+
* Manual event rules: filtered event dates based on weekdays and/or periods
|
|
132
146
|
*/
|
|
133
147
|
function buildRuleSummaryTooltip(rule, options) {
|
|
134
148
|
if (isEventRestriction(rule)) {
|
|
@@ -161,11 +175,9 @@ function buildRuleSummaryTooltip(rule, options) {
|
|
|
161
175
|
if (rule.kind === 'manual' && rule.event_id) {
|
|
162
176
|
const event = getEventForManualRule(rule, options?.events);
|
|
163
177
|
const filteredDates = (event?.dates ?? []).filter((date) => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const isoWeekday = (jsDay === 0 ? 7 : jsDay);
|
|
168
|
-
return rule.weekdays.includes(isoWeekday);
|
|
178
|
+
const matchesWeekdays = dateMatchesWeekdays(date, rule.weekdays);
|
|
179
|
+
const matchesPeriods = dateMatchesPeriods(date, rule.year_period_ids, options?.periods);
|
|
180
|
+
return matchesWeekdays && matchesPeriods;
|
|
169
181
|
});
|
|
170
182
|
const dates = truncateDates(filteredDates.map(formatDateWithWeekday));
|
|
171
183
|
if (!dates)
|
|
@@ -231,19 +243,29 @@ function mapWeekdaysToGtfsAbbreviation(weekdays) {
|
|
|
231
243
|
* - ALL
|
|
232
244
|
* - ALL_DU
|
|
233
245
|
* - VER-FER_SAB-DOM
|
|
234
|
-
* -
|
|
246
|
+
* - Rock in Rio_VER_DU
|
|
235
247
|
*/
|
|
236
248
|
export function buildRuleSummaryGtfs(rule, options) {
|
|
237
249
|
if (isEventRestriction(rule) || isEventReplacement(rule)) {
|
|
238
250
|
return rule.event?.title ?? rule.name ?? rule._id;
|
|
239
251
|
}
|
|
240
|
-
if (rule.kind === 'manual' && rule.event_id) {
|
|
241
|
-
return getEventForManualRule(rule, options?.events)?.title ?? rule.name ?? rule._id;
|
|
242
|
-
}
|
|
243
252
|
const periodIds = rule.year_period_ids ?? [];
|
|
244
253
|
const weekdays = rule.weekdays ?? [];
|
|
245
254
|
const periodPart = mapPeriodsToGtfsAbbreviation(periodIds);
|
|
246
255
|
const weekdayPart = mapWeekdaysToGtfsAbbreviation(weekdays);
|
|
256
|
+
if (rule.kind === 'manual' && rule.event_id) {
|
|
257
|
+
const title = getEventForManualRule(rule, options?.events)?.title ?? rule.name ?? rule._id;
|
|
258
|
+
if (periodPart === 'ALL' && weekdayPart === 'ALL') {
|
|
259
|
+
return title;
|
|
260
|
+
}
|
|
261
|
+
if (periodPart === 'ALL') {
|
|
262
|
+
return `${title}_${weekdayPart}`;
|
|
263
|
+
}
|
|
264
|
+
if (weekdayPart === 'ALL') {
|
|
265
|
+
return `${title}_${periodPart}`;
|
|
266
|
+
}
|
|
267
|
+
return `${title}_${periodPart}_${weekdayPart}`;
|
|
268
|
+
}
|
|
247
269
|
if (periodPart === 'ALL' && weekdayPart === 'ALL') {
|
|
248
270
|
return 'ALL';
|
|
249
271
|
}
|
|
@@ -59,6 +59,11 @@ export function getManualRuleAffectedDates(rule, ctx) {
|
|
|
59
59
|
if (!rule.weekdays.includes(weekday))
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
62
|
+
// If year periods are specified, narrow event dates to matching periods only
|
|
63
|
+
if (rule.year_period_ids?.length) {
|
|
64
|
+
if (!isInPeriod(rule, key, ctx))
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
62
67
|
affected.push(key);
|
|
63
68
|
}
|
|
64
69
|
}
|