feat(#7): категории напоминаний (exercise/hydration/eyes/posture)

This commit is contained in:
AnRil
2026-05-22 13:39:40 +07:00
parent 50c56fec79
commit 68998607e8
6 changed files with 117 additions and 11 deletions

View File

@@ -18,7 +18,8 @@ import type {
Settings,
Theme,
Language,
NotificationMode
NotificationMode,
ReminderCategory
} from '@shared/types'
const MAX_STR_LEN = 200
@@ -33,6 +34,12 @@ const VALID_STATS: GameStat[] = [
'denies',
'duration_min'
]
const VALID_CATEGORIES: ReminderCategory[] = [
'exercise',
'hydration',
'eyes',
'posture'
]
const HHMM_RE = /^\d{1,2}:\d{2}$/
function isObj(v: unknown): v is Record<string, unknown> {
@@ -84,6 +91,7 @@ export function validateExerciseInput(
const intervalMinutes = intInRange(raw.intervalMinutes, 1, 24 * 60)
const icon = safeStr(raw.icon, 64) ?? 'Activity'
const enabled = bool(raw.enabled) ?? true
const category = oneOf(raw.category, VALID_CATEGORIES) // undefined OK = default
if (
name === undefined ||
reps === undefined ||
@@ -91,7 +99,15 @@ export function validateExerciseInput(
) {
return null
}
return { name, reps, intervalMinutes, icon, enabled }
const out: Omit<Exercise, 'id' | 'nextFireAt' | 'lastDoneAt'> = {
name,
reps,
intervalMinutes,
icon,
enabled
}
if (category !== undefined) out.category = category
return out
}
export function validateExercisePatch(
@@ -124,6 +140,11 @@ export function validateExercisePatch(
if (v === undefined) return null
out.enabled = v
}
if ('category' in raw) {
const v = oneOf(raw.category, VALID_CATEGORIES)
if (v === undefined) return null
out.category = v
}
// Allow scheduler-controlled fields to be patched (used by store.markDone
// through this same boundary), but range-check them.
if ('nextFireAt' in raw) {