[Perl] DAQ system for the FAPG.
feat promote timeframe at natural boundaries
24 hours → 1 day, 7 days → 1 week, 4 weeks → 1 month, 12 months → 1 year. Demoting lands at threshold-1 of the previous unit (e.g. week→6 days).
Changed files
roles/dashboard/public/js/dashboard/timeframe.js
@@ -7,6 +7,9 @@
7
7
const ranges = Object.fromEntries(Object.keys(TIMEFRAMES).map(timeframe => [timeframe, 1]));
8
8
const order = Object.keys(TIMEFRAMES);
9
9
10
Added:
// Natural promotion thresholds: when range of current == ratio, promote to next
11
Added:
const promotionAt = { hour: 24, day: 7, week: 4, month: 12 };
12
Added:
10
13
function selected() {
11
14
return TIMEFRAMES[active] ? active : DEFAULT_TIMEFRAME;
12
15
}
@@ -30,17 +33,25 @@
30
33
active = timeframe;
31
34
const current = ranges[timeframe] || 1;
32
35
const next = current + adjustment;
36
Added:
const index = order.indexOf(active);
33
37
34
38
if (next < 1) {
35
Removed:
// Step down to previous timeframe at range 1
36
Removed:
const index = order.indexOf(active);
39
Added:
// Step down to previous timeframe at its promotion threshold minus one
37
40
if (index > 0) {
38
41
active = order[index - 1];
42
Added:
const threshold = promotionAt[active];
43
Added:
ranges[active] = threshold ? threshold - 1 : 1;
44
Added:
}
45
Added:
} else if (promotionAt[active] && next >= promotionAt[active]) {
46
Added:
// Promote to next timeframe at range 1
47
Added:
if (index < order.length - 1) {
48
Added:
active = order[index + 1];
39
49
ranges[active] = 1;
50
Added:
} else {
51
Added:
ranges[timeframe] = Math.min(next, MAX_TIMEFRAME_RANGE);
40
52
}
41
53
} else if (next > MAX_TIMEFRAME_RANGE) {
42
Removed:
// Step up to next timeframe at range 1
43
Removed:
const index = order.indexOf(active);
54
Added:
// At absolute max without a promotion path
44
55
if (index < order.length - 1) {
45
56
active = order[index + 1];
46
57
ranges[active] = 1;
roles/dashboard/t/js/frontend.test.cjs
@@ -75,27 +75,50 @@
75
75
assert.equal(state.selected(), "day");
76
76
assert.equal(state.range(), 1);
77
77
78
Removed:
// Decrease below range 1 should step down to hour
78
Added:
// Decrease below range 1 should step down to hour at threshold-1 (23)
79
79
state.adjust("day", -1);
80
80
assert.equal(state.selected(), "hour");
81
Added:
assert.equal(state.range(), 23);
82
Added:
83
Added:
// Increase from 23 hours to 24 should promote to day
84
Added:
state.adjust("hour", 1);
85
Added:
assert.equal(state.selected(), "day");
81
86
assert.equal(state.range(), 1);
82
87
83
88
// Decrease at hour/1 should stay at hour (lowest)
89
Added:
state.select("hour");
90
Added:
state.ranges.hour = 1;
84
91
state.adjust("hour", -1);
85
92
assert.equal(state.selected(), "hour");
86
93
assert.equal(state.range(), 1);
87
94
88
Removed:
// Increase from hour should increment range
89
Removed:
state.adjust("hour", 1);
90
Removed:
assert.equal(state.selected(), "hour");
91
Removed:
assert.equal(state.range(), 2);
92
Removed:
93
Removed:
// Select day, then increase past MAX should step to week
95
Added:
// 6 days + 1 = 7 days → promotes to week
94
96
state.select("day");
95
Removed:
state.ranges.day = 24;
97
Added:
state.ranges.day = 6;
96
98
state.adjust("day", 1);
97
99
assert.equal(state.selected(), "week");
98
100
assert.equal(state.range(), 1);
101
Added:
102
Added:
// 3 weeks + 1 = 4 weeks → promotes to month
103
Added:
state.select("week");
104
Added:
state.ranges.week = 3;
105
Added:
state.adjust("week", 1);
106
Added:
assert.equal(state.selected(), "month");
107
Added:
assert.equal(state.range(), 1);
108
Added:
109
Added:
// 11 months + 1 = 12 → promotes to year
110
Added:
state.select("month");
111
Added:
state.ranges.month = 11;
112
Added:
state.adjust("month", 1);
113
Added:
assert.equal(state.selected(), "year");
114
Added:
assert.equal(state.range(), 1);
115
Added:
116
Added:
// Demoting from week should land at day/6
117
Added:
state.select("week");
118
Added:
state.ranges.week = 1;
119
Added:
state.adjust("week", -1);
120
Added:
assert.equal(state.selected(), "day");
121
Added:
assert.equal(state.range(), 6);
99
122
100
123
delete globalThis.document;
101
124
});