@aleonnet/healthcare-scheduler 0.1.6
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/README.md +264 -0
- package/dist/core/EventBus.d.ts +14 -0
- package/dist/core/EventBus.d.ts.map +1 -0
- package/dist/core/EventBus.js +47 -0
- package/dist/core/EventBus.js.map +1 -0
- package/dist/core/HealthcareScheduler.d.ts +63 -0
- package/dist/core/HealthcareScheduler.d.ts.map +1 -0
- package/dist/core/HealthcareScheduler.js +269 -0
- package/dist/core/HealthcareScheduler.js.map +1 -0
- package/dist/core/types.d.ts +234 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/database/adapters/MockAdapter.d.ts +37 -0
- package/dist/database/adapters/MockAdapter.d.ts.map +1 -0
- package/dist/database/adapters/MockAdapter.js +518 -0
- package/dist/database/adapters/MockAdapter.js.map +1 -0
- package/dist/database/adapters/SQLiteAdapter.d.ts +40 -0
- package/dist/database/adapters/SQLiteAdapter.d.ts.map +1 -0
- package/dist/database/adapters/SQLiteAdapter.js +147 -0
- package/dist/database/adapters/SQLiteAdapter.js.map +1 -0
- package/dist/database/adapters/SupabaseAdapter.d.ts +20 -0
- package/dist/database/adapters/SupabaseAdapter.d.ts.map +1 -0
- package/dist/database/adapters/SupabaseAdapter.js +310 -0
- package/dist/database/adapters/SupabaseAdapter.js.map +1 -0
- package/dist/database/interfaces.d.ts +20 -0
- package/dist/database/interfaces.d.ts.map +1 -0
- package/dist/database/interfaces.js +3 -0
- package/dist/database/interfaces.js.map +1 -0
- package/dist/database/repositories/EventsRepository.d.ts +26 -0
- package/dist/database/repositories/EventsRepository.d.ts.map +1 -0
- package/dist/database/repositories/EventsRepository.js +91 -0
- package/dist/database/repositories/EventsRepository.js.map +1 -0
- package/dist/database/repositories/ItemsRepository.d.ts +11 -0
- package/dist/database/repositories/ItemsRepository.d.ts.map +1 -0
- package/dist/database/repositories/ItemsRepository.js +44 -0
- package/dist/database/repositories/ItemsRepository.js.map +1 -0
- package/dist/database/repositories/MedicationsRepository.d.ts +40 -0
- package/dist/database/repositories/MedicationsRepository.d.ts.map +1 -0
- package/dist/database/repositories/MedicationsRepository.js +136 -0
- package/dist/database/repositories/MedicationsRepository.js.map +1 -0
- package/dist/database/repositories/OccurrencesRepository.d.ts +25 -0
- package/dist/database/repositories/OccurrencesRepository.d.ts.map +1 -0
- package/dist/database/repositories/OccurrencesRepository.js +150 -0
- package/dist/database/repositories/OccurrencesRepository.js.map +1 -0
- package/dist/database/repositories/PlansRepository.d.ts +13 -0
- package/dist/database/repositories/PlansRepository.d.ts.map +1 -0
- package/dist/database/repositories/PlansRepository.js +76 -0
- package/dist/database/repositories/PlansRepository.js.map +1 -0
- package/dist/database/repositories/index.d.ts +6 -0
- package/dist/database/repositories/index.d.ts.map +1 -0
- package/dist/database/repositories/index.js +14 -0
- package/dist/database/repositories/index.js.map +1 -0
- package/dist/database/schema.base.d.ts +3 -0
- package/dist/database/schema.base.d.ts.map +1 -0
- package/dist/database/schema.base.js +110 -0
- package/dist/database/schema.base.js.map +1 -0
- package/dist/database/sync/interfaces.d.ts +20 -0
- package/dist/database/sync/interfaces.d.ts.map +1 -0
- package/dist/database/sync/interfaces.js +3 -0
- package/dist/database/sync/interfaces.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/adapters/MockNotificationAdapter.d.ts +18 -0
- package/dist/notifications/adapters/MockNotificationAdapter.d.ts.map +1 -0
- package/dist/notifications/adapters/MockNotificationAdapter.js +109 -0
- package/dist/notifications/adapters/MockNotificationAdapter.js.map +1 -0
- package/dist/notifications/adapters/NotifeeAdapter.d.ts +46 -0
- package/dist/notifications/adapters/NotifeeAdapter.d.ts.map +1 -0
- package/dist/notifications/adapters/NotifeeAdapter.js +479 -0
- package/dist/notifications/adapters/NotifeeAdapter.js.map +1 -0
- package/dist/notifications/interfaces.d.ts +8 -0
- package/dist/notifications/interfaces.d.ts.map +1 -0
- package/dist/notifications/interfaces.js +3 -0
- package/dist/notifications/interfaces.js.map +1 -0
- package/dist/planning/buildAgenda.d.ts +22 -0
- package/dist/planning/buildAgenda.d.ts.map +1 -0
- package/dist/planning/buildAgenda.js +148 -0
- package/dist/planning/buildAgenda.js.map +1 -0
- package/dist/planning/calculateTotalDoses.d.ts +3 -0
- package/dist/planning/calculateTotalDoses.d.ts.map +1 -0
- package/dist/planning/calculateTotalDoses.js +19 -0
- package/dist/planning/calculateTotalDoses.js.map +1 -0
- package/dist/planning/expandPlan.d.ts +3 -0
- package/dist/planning/expandPlan.d.ts.map +1 -0
- package/dist/planning/expandPlan.js +200 -0
- package/dist/planning/expandPlan.js.map +1 -0
- package/dist/planning/utils.d.ts +15 -0
- package/dist/planning/utils.d.ts.map +1 -0
- package/dist/planning/utils.js +40 -0
- package/dist/planning/utils.js.map +1 -0
- package/dist/planning/windowPlanner.d.ts +6 -0
- package/dist/planning/windowPlanner.d.ts.map +1 -0
- package/dist/planning/windowPlanner.js +18 -0
- package/dist/planning/windowPlanner.js.map +1 -0
- package/dist/plugins/InventoryPlugin.d.ts +26 -0
- package/dist/plugins/InventoryPlugin.d.ts.map +1 -0
- package/dist/plugins/InventoryPlugin.js +166 -0
- package/dist/plugins/InventoryPlugin.js.map +1 -0
- package/dist/plugins/PluginRegistry.d.ts +13 -0
- package/dist/plugins/PluginRegistry.d.ts.map +1 -0
- package/dist/plugins/PluginRegistry.js +43 -0
- package/dist/plugins/PluginRegistry.js.map +1 -0
- package/dist/plugins/SyncPluginEventDriven.d.ts +32 -0
- package/dist/plugins/SyncPluginEventDriven.d.ts.map +1 -0
- package/dist/plugins/SyncPluginEventDriven.js +609 -0
- package/dist/plugins/SyncPluginEventDriven.js.map +1 -0
- package/dist/plugins/SyncPluginPolling.d.ts +24 -0
- package/dist/plugins/SyncPluginPolling.d.ts.map +1 -0
- package/dist/plugins/SyncPluginPolling.js +266 -0
- package/dist/plugins/SyncPluginPolling.js.map +1 -0
- package/dist/plugins/gamification/GamificationPlugin.d.ts +26 -0
- package/dist/plugins/gamification/GamificationPlugin.d.ts.map +1 -0
- package/dist/plugins/gamification/GamificationPlugin.js +346 -0
- package/dist/plugins/gamification/GamificationPlugin.js.map +1 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.d.ts +2 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.d.ts.map +1 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.js +22 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.js.map +1 -0
- package/dist/plugins/gamification/index.d.ts +6 -0
- package/dist/plugins/gamification/index.d.ts.map +1 -0
- package/dist/plugins/gamification/index.js +14 -0
- package/dist/plugins/gamification/index.js.map +1 -0
- package/dist/plugins/gamification/levelsConfig.example.d.ts +17 -0
- package/dist/plugins/gamification/levelsConfig.example.d.ts.map +1 -0
- package/dist/plugins/gamification/levelsConfig.example.js +18 -0
- package/dist/plugins/gamification/levelsConfig.example.js.map +1 -0
- package/dist/plugins/gamification/levelsEngine.d.ts +19 -0
- package/dist/plugins/gamification/levelsEngine.d.ts.map +1 -0
- package/dist/plugins/gamification/levelsEngine.js +50 -0
- package/dist/plugins/gamification/levelsEngine.js.map +1 -0
- package/dist/plugins/gamification/streak.d.ts +7 -0
- package/dist/plugins/gamification/streak.d.ts.map +1 -0
- package/dist/plugins/gamification/streak.js +39 -0
- package/dist/plugins/gamification/streak.js.map +1 -0
- package/dist/plugins/index.d.ts +8 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +28 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/interfaces.d.ts +56 -0
- package/dist/plugins/interfaces.d.ts.map +1 -0
- package/dist/plugins/interfaces.js +3 -0
- package/dist/plugins/interfaces.js.map +1 -0
- package/dist/reconciler/WindowScheduler.d.ts +29 -0
- package/dist/reconciler/WindowScheduler.d.ts.map +1 -0
- package/dist/reconciler/WindowScheduler.js +396 -0
- package/dist/reconciler/WindowScheduler.js.map +1 -0
- package/dist/services/MedicationsServiceCompat.d.ts +24 -0
- package/dist/services/MedicationsServiceCompat.d.ts.map +1 -0
- package/dist/services/MedicationsServiceCompat.js +98 -0
- package/dist/services/MedicationsServiceCompat.js.map +1 -0
- package/dist/utils/queue.d.ts +8 -0
- package/dist/utils/queue.d.ts.map +1 -0
- package/dist/utils/queue.js +28 -0
- package/dist/utils/queue.js.map +1 -0
- package/dist/utils/timeOffset.d.ts +4 -0
- package/dist/utils/timeOffset.d.ts.map +1 -0
- package/dist/utils/timeOffset.js +61 -0
- package/dist/utils/timeOffset.js.map +1 -0
- package/dist/utils/timestamp.d.ts +6 -0
- package/dist/utils/timestamp.d.ts.map +1 -0
- package/dist/utils/timestamp.js +29 -0
- package/dist/utils/timestamp.js.map +1 -0
- package/dist/utils/timezone.d.ts +4 -0
- package/dist/utils/timezone.d.ts.map +1 -0
- package/dist/utils/timezone.js +30 -0
- package/dist/utils/timezone.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GamificationPlugin = void 0;
|
|
4
|
+
const levelsEngine_1 = require("./levelsEngine");
|
|
5
|
+
const timestamp_1 = require("../../utils/timestamp");
|
|
6
|
+
const queue_1 = require("../../utils/queue");
|
|
7
|
+
function ymd(d) {
|
|
8
|
+
const yyyy = d.getFullYear();
|
|
9
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
10
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
11
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
12
|
+
}
|
|
13
|
+
function plusDays(d, n) {
|
|
14
|
+
const c = new Date(d.getTime());
|
|
15
|
+
c.setDate(c.getDate() + n);
|
|
16
|
+
return c;
|
|
17
|
+
}
|
|
18
|
+
function getLocalDayBoundsUTCISO(dateLocal) {
|
|
19
|
+
const yyyy = dateLocal.getFullYear();
|
|
20
|
+
const mm = dateLocal.getMonth();
|
|
21
|
+
const dd = dateLocal.getDate();
|
|
22
|
+
const startMs = Date.UTC(yyyy, mm, dd, 0, 0, 0, 0);
|
|
23
|
+
const endMs = Date.UTC(yyyy, mm, dd, 23, 59, 59, 999);
|
|
24
|
+
return [new Date(startMs).toISOString(), new Date(endMs).toISOString()];
|
|
25
|
+
}
|
|
26
|
+
const recomputingDays = new Set();
|
|
27
|
+
class GamificationPlugin {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.name = 'gamification';
|
|
30
|
+
this.schemaReady = false;
|
|
31
|
+
this.config = config;
|
|
32
|
+
}
|
|
33
|
+
async bootstrap(context) {
|
|
34
|
+
this.storage = context.storage;
|
|
35
|
+
this.eventBus = context.eventBus;
|
|
36
|
+
await this.ensureSchema();
|
|
37
|
+
const recomputeLast30Days = async () => {
|
|
38
|
+
console.log(`🎮 [GamificationPlugin] Recomputing last ${this.config.recomputeWindowDays} days...`);
|
|
39
|
+
const today = new Date();
|
|
40
|
+
const promises = [];
|
|
41
|
+
for (let i = 0; i < this.config.recomputeWindowDays; i++) {
|
|
42
|
+
const d = new Date(today);
|
|
43
|
+
d.setDate(d.getDate() - i);
|
|
44
|
+
promises.push(this.recomputeDay(d));
|
|
45
|
+
}
|
|
46
|
+
await Promise.all(promises);
|
|
47
|
+
console.log('🎮 [GamificationPlugin] Recompute complete');
|
|
48
|
+
};
|
|
49
|
+
this.eventBus.on('occurrences:changed', async (data) => {
|
|
50
|
+
if (data.occurrenceIds && Array.isArray(data.occurrenceIds)) {
|
|
51
|
+
await this.recomputeByOccurrenceIds(data.occurrenceIds);
|
|
52
|
+
}
|
|
53
|
+
else if (data.occurrenceId) {
|
|
54
|
+
await this.recomputeByOccurrenceIds([data.occurrenceId]);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.eventBus.on('plans:changed', recomputeLast30Days);
|
|
58
|
+
this.eventBus.on('window:filled', recomputeLast30Days);
|
|
59
|
+
}
|
|
60
|
+
async dispose() {
|
|
61
|
+
}
|
|
62
|
+
async ensureSchema() {
|
|
63
|
+
if (this.schemaReady)
|
|
64
|
+
return;
|
|
65
|
+
await this.storage.runAsync(`
|
|
66
|
+
CREATE TABLE IF NOT EXISTS adherence_daily (
|
|
67
|
+
date_local TEXT PRIMARY KEY,
|
|
68
|
+
start_utc_iso TEXT NOT NULL,
|
|
69
|
+
end_utc_iso TEXT NOT NULL,
|
|
70
|
+
total INTEGER NOT NULL,
|
|
71
|
+
done INTEGER NOT NULL,
|
|
72
|
+
skipped INTEGER NOT NULL,
|
|
73
|
+
pending INTEGER NOT NULL,
|
|
74
|
+
percent INTEGER NOT NULL,
|
|
75
|
+
created_at DATETIME DEFAULT (datetime('now')),
|
|
76
|
+
updated_at DATETIME DEFAULT (datetime('now'))
|
|
77
|
+
);
|
|
78
|
+
`, []);
|
|
79
|
+
await this.storage.runAsync(`
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_adherence_daily_percent ON adherence_daily(percent);
|
|
81
|
+
`, []);
|
|
82
|
+
await this.storage.runAsync(`
|
|
83
|
+
CREATE TABLE IF NOT EXISTS levels_state (
|
|
84
|
+
date_local TEXT PRIMARY KEY,
|
|
85
|
+
streak INTEGER NOT NULL,
|
|
86
|
+
level_id TEXT NOT NULL,
|
|
87
|
+
level_min INTEGER NOT NULL,
|
|
88
|
+
level_max INTEGER NOT NULL,
|
|
89
|
+
to_next INTEGER NOT NULL,
|
|
90
|
+
progress REAL NOT NULL,
|
|
91
|
+
updated_at DATETIME DEFAULT (datetime('now'))
|
|
92
|
+
);
|
|
93
|
+
`, []);
|
|
94
|
+
this.schemaReady = true;
|
|
95
|
+
}
|
|
96
|
+
async recomputeDay(dateLocal) {
|
|
97
|
+
await this.ensureSchema();
|
|
98
|
+
const [startISO, endISO] = getLocalDayBoundsUTCISO(dateLocal);
|
|
99
|
+
const dateKey = ymd(dateLocal);
|
|
100
|
+
const row = await this.storage.getFirstAsync(`
|
|
101
|
+
SELECT
|
|
102
|
+
SUM(CASE WHEN o.kind='MED' THEN 1 ELSE 0 END) AS total,
|
|
103
|
+
SUM(CASE WHEN o.kind='MED' AND o.status='DONE' THEN 1 ELSE 0 END) AS done,
|
|
104
|
+
SUM(CASE WHEN o.kind='MED' AND o.status='SKIPPED' THEN 1 ELSE 0 END) AS skipped,
|
|
105
|
+
SUM(CASE WHEN o.kind='MED' AND o.status IN ('PENDING','SNOOZED') THEN 1 ELSE 0 END) AS pending
|
|
106
|
+
FROM occurrences o
|
|
107
|
+
JOIN plans p ON o.plan_id = p.id
|
|
108
|
+
WHERE datetime(o.scheduled_at) BETWEEN datetime(?) AND datetime(?)
|
|
109
|
+
AND p.deleted_at IS NULL
|
|
110
|
+
AND o.deleted_at IS NULL
|
|
111
|
+
AND datetime(p.starts_at) <= datetime(o.scheduled_at)
|
|
112
|
+
AND (p.ends_at IS NULL OR datetime(p.ends_at) >= datetime(o.scheduled_at))
|
|
113
|
+
`, [startISO, endISO]);
|
|
114
|
+
const total = Number(row?.total || 0);
|
|
115
|
+
const done = Number(row?.done || 0);
|
|
116
|
+
const skipped = Number(row?.skipped || 0);
|
|
117
|
+
const pending = Number(row?.pending || 0);
|
|
118
|
+
const percent = total > 0 ? Math.floor((done / total) * 100) : -1;
|
|
119
|
+
const today = new Date();
|
|
120
|
+
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
|
|
121
|
+
const isFrozenDay = dateLocal.getTime() < new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate(), 23, 59, 59, 999).getTime();
|
|
122
|
+
const existing = await this.storage.getFirstAsync('SELECT date_local FROM adherence_daily WHERE date_local = ?', [dateKey]);
|
|
123
|
+
if (isFrozenDay && existing) {
|
|
124
|
+
console.log(`🎮 [GamificationPlugin] Freeze ON: skipping update for ${dateKey}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
128
|
+
await this.storage.runAsync(`INSERT INTO adherence_daily (date_local, start_utc_iso, end_utc_iso, total, done, skipped, pending, percent, created_at, updated_at)
|
|
129
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, COALESCE((SELECT created_at FROM adherence_daily WHERE date_local = ?), datetime('now')), datetime('now'))
|
|
130
|
+
ON CONFLICT(date_local) DO UPDATE SET
|
|
131
|
+
start_utc_iso=excluded.start_utc_iso,
|
|
132
|
+
end_utc_iso=excluded.end_utc_iso,
|
|
133
|
+
total=excluded.total,
|
|
134
|
+
done=excluded.done,
|
|
135
|
+
skipped=excluded.skipped,
|
|
136
|
+
pending=excluded.pending,
|
|
137
|
+
percent=excluded.percent,
|
|
138
|
+
updated_at=datetime('now')
|
|
139
|
+
`, [dateKey, startISO, endISO, total, done, skipped, pending, percent, dateKey]);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return { date: dateKey, startISO, endISO, total, done, skipped, pending, percent };
|
|
143
|
+
}
|
|
144
|
+
async recomputeRange(startLocal, endLocal) {
|
|
145
|
+
const results = [];
|
|
146
|
+
let cur = new Date(startLocal.getTime());
|
|
147
|
+
while (cur <= endLocal) {
|
|
148
|
+
const result = await this.recomputeDay(cur);
|
|
149
|
+
results.push(result);
|
|
150
|
+
cur = plusDays(cur, 1);
|
|
151
|
+
}
|
|
152
|
+
return results;
|
|
153
|
+
}
|
|
154
|
+
async recomputeByOccurrenceIds(occurrenceIds) {
|
|
155
|
+
if (!Array.isArray(occurrenceIds) || occurrenceIds.length === 0)
|
|
156
|
+
return;
|
|
157
|
+
const placeholders = occurrenceIds.map(() => '?').join(',');
|
|
158
|
+
const rows = await this.storage.getAllAsync(`SELECT scheduled_at FROM occurrences WHERE id IN (${placeholders})`, occurrenceIds);
|
|
159
|
+
const days = new Map();
|
|
160
|
+
for (const r of rows) {
|
|
161
|
+
const d = (0, timestamp_1.parseUTC)(r.scheduled_at);
|
|
162
|
+
days.set(ymd(d), d);
|
|
163
|
+
}
|
|
164
|
+
const daysToProcess = Array.from(days.values()).filter(d => {
|
|
165
|
+
const key = ymd(d);
|
|
166
|
+
return !recomputingDays.has(key);
|
|
167
|
+
});
|
|
168
|
+
if (daysToProcess.length === 0)
|
|
169
|
+
return;
|
|
170
|
+
daysToProcess.forEach(d => recomputingDays.add(ymd(d)));
|
|
171
|
+
try {
|
|
172
|
+
for (const d of daysToProcess) {
|
|
173
|
+
await this.recomputeDay(d);
|
|
174
|
+
}
|
|
175
|
+
await this.recomputeLevelsToday();
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
daysToProcess.forEach(d => recomputingDays.delete(ymd(d)));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async getDayAdherence(dateLocal) {
|
|
182
|
+
await this.ensureSchema();
|
|
183
|
+
const key = ymd(dateLocal);
|
|
184
|
+
let row = await this.storage.getFirstAsync('SELECT * FROM adherence_daily WHERE date_local = ?', [key]);
|
|
185
|
+
if (!row) {
|
|
186
|
+
await this.recomputeDay(dateLocal);
|
|
187
|
+
row = await this.storage.getFirstAsync('SELECT * FROM adherence_daily WHERE date_local = ?', [key]);
|
|
188
|
+
}
|
|
189
|
+
return row ? this.mapAdherenceRow(row) : { date: key, startISO: '', endISO: '', total: 0, done: 0, skipped: 0, pending: 0, percent: -1 };
|
|
190
|
+
}
|
|
191
|
+
async getAdherenceRange(startLocal, endLocal) {
|
|
192
|
+
await this.ensureSchema();
|
|
193
|
+
const startKey = ymd(startLocal);
|
|
194
|
+
const endKey = ymd(endLocal);
|
|
195
|
+
const rows = await this.storage.getAllAsync('SELECT * FROM adherence_daily WHERE date_local BETWEEN ? AND ? ORDER BY date_local ASC', [startKey, endKey]);
|
|
196
|
+
const byKey = new Map(rows.map(r => [r.date_local, r]));
|
|
197
|
+
const result = [];
|
|
198
|
+
let cur = new Date(startLocal.getTime());
|
|
199
|
+
while (cur <= endLocal) {
|
|
200
|
+
const key = ymd(cur);
|
|
201
|
+
let row = byKey.get(key);
|
|
202
|
+
if (!row) {
|
|
203
|
+
await this.recomputeDay(cur);
|
|
204
|
+
row = await this.storage.getFirstAsync('SELECT * FROM adherence_daily WHERE date_local = ?', [key]);
|
|
205
|
+
}
|
|
206
|
+
result.push(this.mapAdherenceRow(row));
|
|
207
|
+
cur = plusDays(cur, 1);
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
async getCurrentLevelsState() {
|
|
212
|
+
await this.ensureSchema();
|
|
213
|
+
const key = ymd(new Date());
|
|
214
|
+
const row = await this.storage.getFirstAsync('SELECT * FROM levels_state WHERE date_local = ?', [key]);
|
|
215
|
+
if (!row) {
|
|
216
|
+
return await this.recomputeLevelsDay(new Date());
|
|
217
|
+
}
|
|
218
|
+
return this.mapLevelsRow(row);
|
|
219
|
+
}
|
|
220
|
+
async recomputeLevelsToday() {
|
|
221
|
+
return await this.recomputeLevelsDay(new Date());
|
|
222
|
+
}
|
|
223
|
+
async recomputeLevelsDay(dateLocal) {
|
|
224
|
+
await this.ensureSchema();
|
|
225
|
+
const start = plusDays(dateLocal, -this.config.streakCalculationWindowDays);
|
|
226
|
+
const end = dateLocal;
|
|
227
|
+
const days = await this.getAdherenceRange(start, end);
|
|
228
|
+
const streak = (0, levelsEngine_1.computeSoftStreak)(days, dateLocal, this.config.streakThresholdPercent);
|
|
229
|
+
let lvl = (0, levelsEngine_1.getLevelForStreak)(streak, this.config.levels);
|
|
230
|
+
const key = ymd(dateLocal);
|
|
231
|
+
try {
|
|
232
|
+
const prevKey = ymd(plusDays(dateLocal, -1));
|
|
233
|
+
const prevRow = await this.storage.getFirstAsync('SELECT * FROM levels_state WHERE date_local = ?', [prevKey]);
|
|
234
|
+
if (prevRow) {
|
|
235
|
+
const prevLevelId = String(prevRow.level_id);
|
|
236
|
+
const levels = this.config.levels;
|
|
237
|
+
const idxById = new Map(levels.map((l, i) => [l.id, i]));
|
|
238
|
+
const prevIdx = idxById.get(prevLevelId) ?? 0;
|
|
239
|
+
const newIdx = idxById.get(lvl.levelId) ?? 0;
|
|
240
|
+
if (newIdx < prevIdx) {
|
|
241
|
+
let bad = 0;
|
|
242
|
+
for (let i = days.length - 1; i >= 0; i--) {
|
|
243
|
+
const d = days[i];
|
|
244
|
+
if (d.date === key)
|
|
245
|
+
continue;
|
|
246
|
+
if (d.percent === -1)
|
|
247
|
+
continue;
|
|
248
|
+
if (d.percent < this.config.streakThresholdPercent)
|
|
249
|
+
bad++;
|
|
250
|
+
else
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
const tolerance = levels[prevIdx]?.tolerance ?? 0;
|
|
254
|
+
if (bad <= tolerance) {
|
|
255
|
+
const prevLevel = levels[prevIdx];
|
|
256
|
+
const [pmin, pmax] = prevLevel.range;
|
|
257
|
+
const span = Math.max(1, pmax - pmin);
|
|
258
|
+
const progress = Math.max(0, Math.min(1, (streak - pmin) / span));
|
|
259
|
+
const next = levels[prevIdx + 1] || null;
|
|
260
|
+
lvl = {
|
|
261
|
+
levelId: prevLevel.id,
|
|
262
|
+
levelName: prevLevel.name,
|
|
263
|
+
levelMin: pmin,
|
|
264
|
+
levelMax: pmax,
|
|
265
|
+
nextLevelId: next?.id || null,
|
|
266
|
+
nextLevelName: next?.name || null,
|
|
267
|
+
progress,
|
|
268
|
+
toNext: next ? Math.max(0, next.range[0] - streak) : 0
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch { }
|
|
275
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
276
|
+
await this.storage.runAsync(`
|
|
277
|
+
INSERT INTO levels_state (date_local, streak, level_id, level_min, level_max, to_next, progress, updated_at)
|
|
278
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
279
|
+
ON CONFLICT(date_local) DO UPDATE SET
|
|
280
|
+
streak=excluded.streak,
|
|
281
|
+
level_id=excluded.level_id,
|
|
282
|
+
level_min=excluded.level_min,
|
|
283
|
+
level_max=excluded.level_max,
|
|
284
|
+
to_next=excluded.to_next,
|
|
285
|
+
progress=excluded.progress,
|
|
286
|
+
updated_at=datetime('now')
|
|
287
|
+
`, [key, streak, lvl.levelId, lvl.levelMin, lvl.levelMax, lvl.toNext, lvl.progress]);
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
date: key,
|
|
291
|
+
streak,
|
|
292
|
+
levelId: lvl.levelId,
|
|
293
|
+
levelName: lvl.levelName,
|
|
294
|
+
levelMin: lvl.levelMin,
|
|
295
|
+
levelMax: lvl.levelMax,
|
|
296
|
+
nextLevelId: lvl.nextLevelId,
|
|
297
|
+
nextLevelName: lvl.nextLevelName,
|
|
298
|
+
toNext: lvl.toNext,
|
|
299
|
+
progress: lvl.progress
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async backfill(daysBack = 90) {
|
|
303
|
+
const today = new Date();
|
|
304
|
+
const start = plusDays(today, -Math.max(1, daysBack));
|
|
305
|
+
await this.recomputeRange(start, today);
|
|
306
|
+
}
|
|
307
|
+
mapAdherenceRow(row) {
|
|
308
|
+
if (!row) {
|
|
309
|
+
return {
|
|
310
|
+
date: '',
|
|
311
|
+
startISO: '',
|
|
312
|
+
endISO: '',
|
|
313
|
+
total: 0,
|
|
314
|
+
done: 0,
|
|
315
|
+
skipped: 0,
|
|
316
|
+
pending: 0,
|
|
317
|
+
percent: -1
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
date: row.date_local,
|
|
322
|
+
startISO: row.start_utc_iso,
|
|
323
|
+
endISO: row.end_utc_iso,
|
|
324
|
+
total: Number(row.total || 0),
|
|
325
|
+
done: Number(row.done || 0),
|
|
326
|
+
skipped: Number(row.skipped || 0),
|
|
327
|
+
pending: Number(row.pending || 0),
|
|
328
|
+
percent: (row.percent === null || row.percent === undefined) ? -1 : Number(row.percent)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
mapLevelsRow(row) {
|
|
332
|
+
return {
|
|
333
|
+
date: row.date_local,
|
|
334
|
+
streak: Number(row.streak || 0),
|
|
335
|
+
levelId: row.level_id,
|
|
336
|
+
levelMin: Number(row.level_min || 0),
|
|
337
|
+
levelMax: Number(row.level_max || 0),
|
|
338
|
+
toNext: Number(row.to_next || 0),
|
|
339
|
+
progress: Number(row.progress || 0),
|
|
340
|
+
updatedAt: row.updated_at
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
exports.GamificationPlugin = GamificationPlugin;
|
|
345
|
+
exports.default = GamificationPlugin;
|
|
346
|
+
//# sourceMappingURL=GamificationPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GamificationPlugin.js","sourceRoot":"","sources":["../../../src/plugins/gamification/GamificationPlugin.ts"],"names":[],"mappings":";;;AAcA,iDAAsE;AACtE,qDAAiD;AACjD,6CAAiD;AAEjD,SAAS,GAAG,CAAC,CAAO;IAClB,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChD,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,QAAQ,CAAC,CAAO,EAAE,CAAS;IAClC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC;AACX,CAAC;AAMD,SAAS,uBAAuB,CAAC,SAAe;IAC9C,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IAG/B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAEtD,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC1E,CAAC;AAGD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;AAE1C,MAAa,kBAAkB;IAQ7B,YAAY,MAA0B;QAP7B,SAAI,GAAG,cAAc,CAAC;QAIvB,gBAAW,GAAG,KAAK,CAAC;QAI1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAsB;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAEjC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAG1B,MAAM,mBAAmB,GAAG,KAAK,IAAI,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,4CAA4C,IAAI,CAAC,MAAM,CAAC,mBAAmB,UAAU,CAAC,CAAC;YACnG,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,QAAQ,GAA8B,EAAE,CAAC;YAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;YACtC,CAAC;YACD,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAC5D,CAAC,CAAC;QAGF,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,qBAAqB,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;YAC1D,IAAI,IAAI,CAAC,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC5D,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC7B,MAAM,IAAI,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC,CAAC,CAAC;QAGH,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;QAGvD,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,OAAO;IAEb,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAG7B,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;;;;KAa3B,EAAE,EAAE,CAAC,CAAC;QAEP,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;;KAE3B,EAAE,EAAE,CAAC,CAAC;QAGP,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;;KAW3B,EAAE,EAAE,CAAC,CAAC;QAEP,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAe;QAChC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QAG/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;;;;;;;;;;;;;KAa5C,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QAEvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAGlE,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QACvF,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,EAAC,EAAE,EAAC,EAAE,EAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAC/I,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAC/C,6DAA6D,EAC7D,CAAC,OAAO,CAAC,CACV,CAAC;QACF,IAAI,WAAW,IAAI,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,0DAA0D,OAAO,EAAE,CAAC,CAAC;QACnF,CAAC;aAAM,CAAC;YACN,MAAM,IAAA,oBAAY,EAAC,KAAK,IAAI,EAAE;gBAC5B,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CACzB;;;;;;;;;;;WAWC,EACD,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAC7E,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,UAAgB,EAAE,QAAc;QACnD,MAAM,OAAO,GAAqB,EAAE,CAAC;QACrC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,aAAuB;QACpD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAGxE,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CACzC,qDAAqD,YAAY,GAAG,EACpE,aAAa,CACd,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAgB,CAAC;QACrC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,IAAA,oBAAQ,EAAE,CAAS,CAAC,YAAY,CAAC,CAAC;YAC5C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtB,CAAC;QAGD,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACzD,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACnB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAGvC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAExD,IAAI,CAAC;YACH,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;gBAC9B,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;YAED,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACpC,CAAC;gBAAS,CAAC;YAET,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAe;QACnC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CACxC,oDAAoD,EACpD,CAAC,GAAG,CAAC,CACN,CAAC;QAEF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;YACnC,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CACpC,oDAAoD,EACpD,CAAC,GAAG,CAAC,CACN,CAAC;QACJ,CAAC;QAED,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;IAC3I,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,UAAgB,EAAE,QAAc;QACtD,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CACzC,wFAAwF,EACxF,CAAC,QAAQ,EAAE,MAAM,CAAC,CACnB,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,MAAM,GAAqB,EAAE,CAAC;QACpC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QAEzC,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC7B,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CACpC,oDAAoD,EACpD,CAAC,GAAG,CAAC,CACN,CAAC;YACJ,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;YACvC,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzB,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAC1C,iDAAiD,EACjD,CAAC,GAAG,CAAC,CACN,CAAC;QAEF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,oBAAoB;QACxB,OAAO,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,SAAe;QAC9C,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAG1B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,SAAS,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEtD,MAAM,MAAM,GAAG,IAAA,gCAAiB,EAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACtF,IAAI,GAAG,GAAG,IAAA,gCAAiB,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QAG3B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAC9C,iDAAiD,EACjD,CAAC,OAAO,CAAC,CACV,CAAC;YACF,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;gBAClC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC7C,IAAI,MAAM,GAAG,OAAO,EAAE,CAAC;oBAErB,IAAI,GAAG,GAAG,CAAC,CAAC;oBACZ,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;wBAClB,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG;4BAAE,SAAS;wBAC7B,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC;4BAAE,SAAS;wBAC/B,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,sBAAsB;4BAAE,GAAG,EAAE,CAAC;;4BACrD,MAAM;oBACb,CAAC;oBACD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;oBAClD,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;wBAErB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;wBAClC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC;wBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;wBACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;wBAClE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;wBACzC,GAAG,GAAG;4BACJ,OAAO,EAAE,SAAS,CAAC,EAAE;4BACrB,SAAS,EAAE,SAAS,CAAC,IAAI;4BACzB,QAAQ,EAAE,IAAI;4BACd,QAAQ,EAAE,IAAI;4BACd,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;4BAC7B,aAAa,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI;4BACjC,QAAQ;4BACR,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;yBACvD,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,MAAM,IAAA,oBAAY,EAAC,KAAK,IAAI,EAAE;YAC5B,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;;OAW3B,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,IAAI,EAAE,GAAG;YACT,MAAM;YACN,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,WAAmB,EAAE;QAClC,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAEO,eAAe,CAAC,GAAQ;QAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;gBACL,IAAI,EAAE,EAAE;gBACR,QAAQ,EAAE,EAAE;gBACZ,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,CAAC;gBACR,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC,CAAC;aACZ,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,GAAG,CAAC,UAAU;YACpB,QAAQ,EAAE,GAAG,CAAC,aAAa;YAC3B,MAAM,EAAE,GAAG,CAAC,WAAW;YACvB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;YAC7B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;YAC3B,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACjC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACjC,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;SACxF,CAAC;IACJ,CAAC;IAEO,YAAY,CAAC,GAAQ;QAC3B,OAAO;YACL,IAAI,EAAE,GAAG,CAAC,UAAU;YACpB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;YAC/B,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC;YACpC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC;YACpC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YAChC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnC,SAAS,EAAE,GAAG,CAAC,UAAU;SAC1B,CAAC;IACJ,CAAC;CACF;AAhZD,gDAgZC;AAED,kBAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hysteresis.spec.d.ts","sourceRoot":"","sources":["../../../../src/plugins/gamification/__tests__/hysteresis.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = require("node:test");
|
|
7
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
8
|
+
const levelsEngine_1 = require("../../gamification/levelsEngine");
|
|
9
|
+
(0, node_test_1.describe)('Levels hysteresis (concept)', () => {
|
|
10
|
+
(0, node_test_1.it)('keeps previous level if badDaysInRow <= tolerance', () => {
|
|
11
|
+
const levels = [
|
|
12
|
+
{ id: 'novice', name: 'Novato', range: [0, 6], tolerance: 0 },
|
|
13
|
+
{ id: 'bronze', name: 'Bronze', range: [7, 13], tolerance: 0 },
|
|
14
|
+
{ id: 'silver', name: 'Prata', range: [14, 29], tolerance: 0 },
|
|
15
|
+
{ id: 'gold', name: 'Ouro', range: [30, 59], tolerance: 1 },
|
|
16
|
+
];
|
|
17
|
+
const streak = 29;
|
|
18
|
+
const suggested = (0, levelsEngine_1.getLevelForStreak)(streak, levels);
|
|
19
|
+
node_assert_1.default.equal(suggested.levelId, 'silver');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=hysteresis.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hysteresis.spec.js","sourceRoot":"","sources":["../../../../src/plugins/gamification/__tests__/hysteresis.spec.ts"],"names":[],"mappings":";;;;;AAGA,yCAAyC;AACzC,8DAAiC;AACjC,kEAAoE;AAEpE,IAAA,oBAAQ,EAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,IAAA,cAAE,EAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAC7D,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAC9D,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAC9D,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;SAC5D,CAAC;QAEF,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,IAAA,gCAAiB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,qBAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { GamificationPlugin } from './GamificationPlugin';
|
|
2
|
+
export { levels as exampleLevels, rules as exampleRules } from './levelsConfig.example';
|
|
3
|
+
export { computeStreak } from './streak';
|
|
4
|
+
export { getLevelForStreak, computeSoftStreak } from './levelsEngine';
|
|
5
|
+
export type { LevelForStreakResult } from './levelsEngine';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/gamification/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeSoftStreak = exports.getLevelForStreak = exports.computeStreak = exports.exampleRules = exports.exampleLevels = exports.GamificationPlugin = void 0;
|
|
4
|
+
var GamificationPlugin_1 = require("./GamificationPlugin");
|
|
5
|
+
Object.defineProperty(exports, "GamificationPlugin", { enumerable: true, get: function () { return GamificationPlugin_1.GamificationPlugin; } });
|
|
6
|
+
var levelsConfig_example_1 = require("./levelsConfig.example");
|
|
7
|
+
Object.defineProperty(exports, "exampleLevels", { enumerable: true, get: function () { return levelsConfig_example_1.levels; } });
|
|
8
|
+
Object.defineProperty(exports, "exampleRules", { enumerable: true, get: function () { return levelsConfig_example_1.rules; } });
|
|
9
|
+
var streak_1 = require("./streak");
|
|
10
|
+
Object.defineProperty(exports, "computeStreak", { enumerable: true, get: function () { return streak_1.computeStreak; } });
|
|
11
|
+
var levelsEngine_1 = require("./levelsEngine");
|
|
12
|
+
Object.defineProperty(exports, "getLevelForStreak", { enumerable: true, get: function () { return levelsEngine_1.getLevelForStreak; } });
|
|
13
|
+
Object.defineProperty(exports, "computeSoftStreak", { enumerable: true, get: function () { return levelsEngine_1.computeSoftStreak; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/gamification/index.ts"],"names":[],"mappings":";;;AAKA,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,+DAAwF;AAA/E,qHAAA,MAAM,OAAiB;AAAE,oHAAA,KAAK,OAAgB;AACvD,mCAAyC;AAAhC,uGAAA,aAAa,OAAA;AACtB,+CAAsE;AAA7D,iHAAA,iBAAiB,OAAA;AAAE,iHAAA,iBAAiB,OAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LevelConfig } from '../interfaces';
|
|
2
|
+
export declare const levels: LevelConfig[];
|
|
3
|
+
export declare const rules: {
|
|
4
|
+
color: string;
|
|
5
|
+
title: string;
|
|
6
|
+
text: string;
|
|
7
|
+
}[];
|
|
8
|
+
declare const _default: {
|
|
9
|
+
levels: LevelConfig[];
|
|
10
|
+
rules: {
|
|
11
|
+
color: string;
|
|
12
|
+
title: string;
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
|
17
|
+
//# sourceMappingURL=levelsConfig.example.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levelsConfig.example.d.ts","sourceRoot":"","sources":["../../../src/plugins/gamification/levelsConfig.example.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,eAAO,MAAM,MAAM,EAAE,WAAW,EAM/B,CAAC;AAEF,eAAO,MAAM,KAAK;;;;GAKjB,CAAC;;;;;;;;;AAEF,wBAAiC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rules = exports.levels = void 0;
|
|
4
|
+
exports.levels = [
|
|
5
|
+
{ id: 'novice', name: 'Novato', range: [0, 6], tolerance: 0, tagline: 'Começando a jornada' },
|
|
6
|
+
{ id: 'bronze', name: 'Bronze', range: [7, 13], tolerance: 0, tagline: 'Criando o hábito' },
|
|
7
|
+
{ id: 'silver', name: 'Prata', range: [14, 29], tolerance: 0, tagline: 'Consistência incrível' },
|
|
8
|
+
{ id: 'gold', name: 'Ouro', range: [30, 59], tolerance: 1, tagline: 'Disciplina exemplar' },
|
|
9
|
+
{ id: 'diamond', name: 'Diamante', range: [60, 9999], tolerance: 2, tagline: 'Lenda da adesão' },
|
|
10
|
+
];
|
|
11
|
+
exports.rules = [
|
|
12
|
+
{ color: '#22c55e', title: 'Dia perfeito', text: 'Tome 100% dos medicamentos para ganhar +1 dia de sequência' },
|
|
13
|
+
{ color: '#60a5fa', title: 'Tolerância', text: 'Níveis mais altos permitem alguns esquecimentos sem perder progresso' },
|
|
14
|
+
{ color: '#8b5cf6', title: 'Manter nível', text: 'Você mantém seu nível mesmo com pequenos deslizes' },
|
|
15
|
+
{ color: '#f97316', title: 'Sequência amigável', text: 'O importante é a consistência — não seja tão duro consigo mesmo' },
|
|
16
|
+
];
|
|
17
|
+
exports.default = { levels: exports.levels, rules: exports.rules };
|
|
18
|
+
//# sourceMappingURL=levelsConfig.example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levelsConfig.example.js","sourceRoot":"","sources":["../../../src/plugins/gamification/levelsConfig.example.ts"],"names":[],"mappings":";;;AAQa,QAAA,MAAM,GAAkB;IACnC,EAAE,EAAE,EAAE,QAAQ,EAAI,IAAI,EAAE,QAAQ,EAAG,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAI,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE;IAClG,EAAE,EAAE,EAAE,QAAQ,EAAI,IAAI,EAAE,QAAQ,EAAG,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAG,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE;IAC/F,EAAE,EAAE,EAAE,QAAQ,EAAI,IAAI,EAAE,OAAO,EAAI,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE;IACpG,EAAE,EAAE,EAAE,MAAM,EAAM,IAAI,EAAE,MAAM,EAAK,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE;IAClG,EAAE,EAAE,EAAE,SAAS,EAAG,IAAI,EAAE,UAAU,EAAC,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE;CACjG,CAAC;AAEW,QAAA,KAAK,GAAG;IACnB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,4DAA4D,EAAE;IAC/G,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,sEAAsE,EAAE;IACvH,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,mDAAmD,EAAE;IACtG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,iEAAiE,EAAE;CAC3H,CAAC;AAEF,kBAAe,EAAE,MAAM,EAAN,cAAM,EAAE,KAAK,EAAL,aAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LevelConfig, DailyAdherence } from '../interfaces';
|
|
2
|
+
export interface LevelForStreakResult {
|
|
3
|
+
levelId: string;
|
|
4
|
+
levelName: string;
|
|
5
|
+
levelMin: number;
|
|
6
|
+
levelMax: number;
|
|
7
|
+
nextLevelId: string | null;
|
|
8
|
+
nextLevelName: string | null;
|
|
9
|
+
progress: number;
|
|
10
|
+
toNext: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function getLevelForStreak(streak: number, levels?: LevelConfig[]): LevelForStreakResult;
|
|
13
|
+
export declare function computeSoftStreak(daysAsc: DailyAdherence[], today?: Date, thresholdPercent?: number): number;
|
|
14
|
+
declare const _default: {
|
|
15
|
+
getLevelForStreak: typeof getLevelForStreak;
|
|
16
|
+
computeSoftStreak: typeof computeSoftStreak;
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=levelsEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levelsEngine.d.ts","sourceRoot":"","sources":["../../../src/plugins/gamification/levelsEngine.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAajE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,WAAW,EAAkB,GAAG,oBAAoB,CAkC7G;AAQD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,KAAK,GAAE,IAAiB,EAAE,gBAAgB,GAAE,MAAW,GAAG,MAAM,CAE5H;;;;;AAED,wBAAwD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLevelForStreak = getLevelForStreak;
|
|
4
|
+
exports.computeSoftStreak = computeSoftStreak;
|
|
5
|
+
const levelsConfig_example_1 = require("./levelsConfig.example");
|
|
6
|
+
const streak_1 = require("./streak");
|
|
7
|
+
function clamp01(x) {
|
|
8
|
+
return Math.max(0, Math.min(1, x));
|
|
9
|
+
}
|
|
10
|
+
function nextLevel(current, levels) {
|
|
11
|
+
const idx = levels.findIndex(l => l.id === current.id);
|
|
12
|
+
return idx >= 0 && idx < levels.length - 1 ? levels[idx + 1] : null;
|
|
13
|
+
}
|
|
14
|
+
function getLevelForStreak(streak, levels = levelsConfig_example_1.levels) {
|
|
15
|
+
const s = Math.max(0, Number(streak || 0));
|
|
16
|
+
for (const lvl of levels) {
|
|
17
|
+
const [min, max] = lvl.range;
|
|
18
|
+
if (s >= min && s <= max) {
|
|
19
|
+
const next = nextLevel(lvl, levels);
|
|
20
|
+
const progress = clamp01((s - min) / Math.max(1, (max - min)));
|
|
21
|
+
const toNext = next ? Math.max(0, next.range[0] - s) : 0;
|
|
22
|
+
return {
|
|
23
|
+
levelId: lvl.id,
|
|
24
|
+
levelName: lvl.name,
|
|
25
|
+
levelMin: min,
|
|
26
|
+
levelMax: max,
|
|
27
|
+
nextLevelId: next?.id || null,
|
|
28
|
+
nextLevelName: next?.name || null,
|
|
29
|
+
progress,
|
|
30
|
+
toNext
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const top = levels[levels.length - 1];
|
|
35
|
+
return {
|
|
36
|
+
levelId: top.id,
|
|
37
|
+
levelName: top.name,
|
|
38
|
+
levelMin: top.range[0],
|
|
39
|
+
levelMax: top.range[1],
|
|
40
|
+
nextLevelId: null,
|
|
41
|
+
nextLevelName: null,
|
|
42
|
+
progress: 1,
|
|
43
|
+
toNext: 0
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function computeSoftStreak(daysAsc, today = new Date(), thresholdPercent = 80) {
|
|
47
|
+
return (0, streak_1.computeStreak)(daysAsc, today, thresholdPercent);
|
|
48
|
+
}
|
|
49
|
+
exports.default = { getLevelForStreak, computeSoftStreak };
|
|
50
|
+
//# sourceMappingURL=levelsEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levelsEngine.js","sourceRoot":"","sources":["../../../src/plugins/gamification/levelsEngine.ts"],"names":[],"mappings":";;AAgCA,8CAkCC;AAQD,8CAEC;AAtED,iEAAiE;AACjE,qCAAyC;AAEzC,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,SAAS,CAAC,OAAoB,EAAE,MAAqB;IAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC;IACvD,OAAO,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAgBD,SAAgB,iBAAiB,CAAC,MAAc,EAAE,SAAwB,6BAAa;IACrF,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IAE3C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,OAAO;gBACL,OAAO,EAAE,GAAG,CAAC,EAAE;gBACf,SAAS,EAAE,GAAG,CAAC,IAAI;gBACnB,QAAQ,EAAE,GAAG;gBACb,QAAQ,EAAE,GAAG;gBACb,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;gBAC7B,aAAa,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI;gBACjC,QAAQ;gBACR,MAAM;aACP,CAAC;QACJ,CAAC;IACH,CAAC;IAGD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACtC,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,EAAE;QACf,SAAS,EAAE,GAAG,CAAC,IAAI;QACnB,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACtB,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACtB,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,IAAI;QACnB,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,CAAC;KACV,CAAC;AACJ,CAAC;AAQD,SAAgB,iBAAiB,CAAC,OAAyB,EAAE,QAAc,IAAI,IAAI,EAAE,EAAE,mBAA2B,EAAE;IAClH,OAAO,IAAA,sBAAa,EAAC,OAAO,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;AACzD,CAAC;AAED,kBAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DailyAdherence } from '../interfaces';
|
|
2
|
+
export declare function computeStreak(daysAsc: DailyAdherence[], today?: Date, thresholdPercent?: number): number;
|
|
3
|
+
declare const _default: {
|
|
4
|
+
computeStreak: typeof computeStreak;
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
|
7
|
+
//# sourceMappingURL=streak.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streak.d.ts","sourceRoot":"","sources":["../../../src/plugins/gamification/streak.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAiBpD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,KAAK,GAAE,IAAiB,EAAE,gBAAgB,GAAE,MAAW,GAAG,MAAM,CA6BxH;;;;AAED,wBAAiC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeStreak = computeStreak;
|
|
4
|
+
function ymd(d) {
|
|
5
|
+
const yyyy = d.getFullYear();
|
|
6
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
7
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
8
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
9
|
+
}
|
|
10
|
+
function computeStreak(daysAsc, today = new Date(), thresholdPercent = 80) {
|
|
11
|
+
const todayKey = ymd(today);
|
|
12
|
+
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
|
|
13
|
+
let startIndex = daysAsc.length - 1;
|
|
14
|
+
if (startIndex >= 0) {
|
|
15
|
+
const last = daysAsc[startIndex];
|
|
16
|
+
if (last?.date === todayKey) {
|
|
17
|
+
const dayOngoing = Date.now() < endOfToday.getTime();
|
|
18
|
+
const treatTodayAsNeutral = (typeof last.percent === 'number' && last.percent < thresholdPercent && dayOngoing) ||
|
|
19
|
+
(Number(last?.pending || 0) > 0);
|
|
20
|
+
if (treatTodayAsNeutral) {
|
|
21
|
+
startIndex = startIndex - 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
let count = 0;
|
|
26
|
+
for (let i = startIndex; i >= 0; i--) {
|
|
27
|
+
const d = daysAsc[i];
|
|
28
|
+
if (d.total === 0)
|
|
29
|
+
continue;
|
|
30
|
+
if (d.percent >= thresholdPercent) {
|
|
31
|
+
count++;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return Math.max(0, count);
|
|
37
|
+
}
|
|
38
|
+
exports.default = { computeStreak };
|
|
39
|
+
//# sourceMappingURL=streak.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streak.js","sourceRoot":"","sources":["../../../src/plugins/gamification/streak.ts"],"names":[],"mappings":";;AAsBA,sCA6BC;AA5CD,SAAS,GAAG,CAAC,CAAO;IAClB,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChD,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;AAC/B,CAAC;AAUD,SAAgB,aAAa,CAAC,OAAyB,EAAE,QAAc,IAAI,IAAI,EAAE,EAAE,mBAA2B,EAAE;IAC9G,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAErG,IAAI,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACpC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,IAAI,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;YACrD,MAAM,mBAAmB,GACvB,CAAC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,GAAG,gBAAgB,IAAI,UAAU,CAAC;gBACnF,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACnC,IAAI,mBAAmB,EAAE,CAAC;gBACxB,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC;YAClC,KAAK,EAAE,CAAC;YACR,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,kBAAe,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './interfaces';
|
|
2
|
+
export { PluginRegistry } from './PluginRegistry';
|
|
3
|
+
export * from './gamification';
|
|
4
|
+
export { InventoryPlugin } from './InventoryPlugin';
|
|
5
|
+
export { SyncPluginEventDriven } from './SyncPluginEventDriven';
|
|
6
|
+
export { SyncPluginEventDriven as SyncPlugin } from './SyncPluginEventDriven';
|
|
7
|
+
export type { GamificationConfig, GamificationRule, LevelConfig } from './interfaces';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAKA,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,qBAAqB,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG9E,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACZ,MAAM,cAAc,CAAC"}
|