Files
chore/frontend/src/common/models.ts
Ryan Kegel 5392e5af70
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m8s
Add routine management features for child and parent views
- Implemented routine child-mode flow tests to ensure proper functionality of routine assignment and task completion.
- Created notification tests for parent view to verify routine completion notifications for children.
- Developed ChildRoutineOverlay component for displaying routine tasks and handling user interactions.
- Added RoutineApproveDialog component for approving or rejecting completed routines.
- Created unit tests for ChildRoutineOverlay and RoutineEditView components to ensure correct behavior and rendering.
- Enhanced RoutineEditView with proper handling of task addition and form submission.

Co-authored-by: Copilot <copilot@github.com>
2026-05-17 23:47:12 -04:00

388 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export type TaskType = 'chore' | 'kindness' | 'penalty'
export interface Task {
id: string
name: string
points: number
type: TaskType
image_id: string | null
image_url?: string | null // optional, for resolved URLs
}
export const TASK_FIELDS = ['id', 'name', 'points', 'type', 'image_id'] as const
export interface DayConfig {
day: number // 0=Sun, 1=Mon, ..., 6=Sat
hour: number // 023 (24h)
minute: number // 0, 15, 30, or 45
}
export interface ChoreSchedule {
id: string
child_id: string
task_id: string
mode: 'days' | 'interval'
// mode='days'
day_configs: DayConfig[]
default_hour?: number // master deadline hour; present when mode='days'
default_minute?: number // master deadline minute; present when mode='days'
default_has_deadline?: boolean // false = 'Anytime', no expiry for 'days' mode
// mode='interval'
interval_days: number // 17
anchor_date: string // ISO date string e.g. "2026-02-25"; "" means use today
interval_has_deadline: boolean // false = "Anytime" (no deadline)
interval_hour: number
interval_minute: number
enabled?: boolean // false = schedule paused; chore won't appear for child
created_at: number
updated_at: number
}
export interface Routine {
id: string
name: string
points: number
image_id: string | null
image_url?: string | null
}
export const ROUTINE_FIELDS = ['id', 'name', 'points', 'image_id'] as const
export interface RoutineItem {
id: string
routine_id: string
name: string
image_id: string | null
order: number
}
export interface RoutineSchedule {
id: string
child_id: string
routine_id: string
mode: 'days' | 'interval'
day_configs: DayConfig[]
default_hour?: number
default_minute?: number
default_has_deadline?: boolean
interval_days: number
anchor_date: string
interval_has_deadline: boolean
interval_hour: number
interval_minute: number
enabled?: boolean
created_at: number
updated_at: number
}
export interface ChildRoutine {
id: string
name: string
points: number
image_id: string | null
image_url?: string | null
custom_value?: number | null
schedule?: RoutineSchedule | null
extension_date?: string | null
pending_status?: 'pending' | 'approved' | null
approved_at?: string | null
pending_confirmation_id?: string | null
items: RoutineItem[]
}
export interface ChildTask {
id: string
name: string
type: TaskType
points: number
image_id: string | null
image_url?: string | null
custom_value?: number | null
schedule?: ChoreSchedule | null
extension_date?: string | null // ISO date of today's extension, if any
pending_status?: 'pending' | 'approved' | null
approved_at?: string | null
}
export interface User {
id: string
first_name: string
last_name: string
email: string
token_version: number
image_id: string | null
marked_for_deletion: boolean
marked_for_deletion_at: string | null
deletion_in_progress: boolean
deletion_attempted_at: string | null
role: string
timezone: string | null
email_digest_enabled: boolean
}
export interface Child {
id: string
name: string
age: number
tasks: string[]
routines?: string[]
rewards: string[]
points: number
image_id: string | null
image_url?: string | null // optional, for resolved URLs
}
export const CHILD_FIELDS = [
'id',
'name',
'age',
'tasks',
'routines',
'rewards',
'points',
'image_id',
] as const
export interface Reward {
id: string
name: string
cost: number
points_needed: number
image_id: string | null
image_url?: string | null // optional, for resolved URLs
}
export const REWARD_FIELDS = ['id', 'name', 'cost', 'points_needed', 'image_id'] as const
export interface RewardStatus {
id: string
name: string
points_needed: number
cost: number
redeeming: boolean
image_id: string | null
image_url?: string | null // optional, for resolved URLs
}
export const REWARD_STATUS_FIELDS = [
'id',
'name',
'points_needed',
'cost',
'redeeming',
'image_id',
] as const
export interface PendingConfirmation {
id: string
child_id: string
child_name: string
child_image_id: string | null
child_image_url?: string | null
entity_id: string
entity_type: 'chore' | 'reward' | 'routine'
entity_name: string
entity_image_id: string | null
entity_image_url?: string | null
status: 'pending' | 'approved' | 'rejected'
approved_at: string | null
}
export const PENDING_CONFIRMATION_FIELDS = [
'id',
'child_id',
'child_name',
'child_image_id',
'entity_id',
'entity_type',
'entity_name',
'entity_image_id',
'status',
'approved_at',
] as const
export interface Event {
type: string
payload:
| ChildModifiedEventPayload
| ChildTaskTriggeredEventPayload
| ChildRewardTriggeredEventPayload
| ChildRewardRequestEventPayload
| ChildTasksSetEventPayload
| ChildRewardsSetEventPayload
| TaskModifiedEventPayload
| RewardModifiedEventPayload
| TrackingEventCreatedPayload
| ChildOverrideSetPayload
| ChildOverrideDeletedPayload
| ChoreScheduleModifiedPayload
| ChoreTimeExtendedPayload
| ChildChoreConfirmationPayload
| RoutineModifiedEventPayload
| ChildRoutinesSetEventPayload
| RoutineScheduleModifiedPayload
| RoutineTimeExtendedPayload
| ChildRoutineConfirmationPayload
}
export interface ChildModifiedEventPayload {
child_id: string
operation: 'ADD' | 'DELETE' | 'EDIT'
}
export interface ChildTaskTriggeredEventPayload {
task_id: string
child_id: string
points: number
}
export interface ChildRewardTriggeredEventPayload {
task_id: string
child_id: string
points: number
}
export interface ChildRewardRequestEventPayload {
child_id: string
reward_id: string
operation: 'GRANTED' | 'CREATED' | 'CANCELLED'
}
export interface ChildTasksSetEventPayload {
child_id: string
task_ids: string[]
}
export interface ChildRewardsSetEventPayload {
child_id: string
reward_ids: string[]
}
export interface TaskModifiedEventPayload {
task_id: string
operation: 'ADD' | 'DELETE' | 'EDIT'
}
export interface RewardModifiedEventPayload {
reward_id: string
operation: 'ADD' | 'DELETE' | 'EDIT'
}
export interface RoutineModifiedEventPayload {
routine_id: string
operation: 'ADD' | 'DELETE' | 'EDIT'
}
export interface ChildRoutinesSetEventPayload {
child_id: string
routine_ids: string[]
}
export interface TrackingEventCreatedPayload {
tracking_event_id: string
child_id: string
entity_type: EntityType
action: ActionType
}
export interface ChildOverrideSetPayload {
override: ChildOverride
}
export interface ChildOverrideDeletedPayload {
child_id: string
entity_id: string
entity_type: string
}
export type EntityType = 'task' | 'reward' | 'penalty' | 'chore' | 'kindness' | 'routine'
export type ActionType =
| 'activated'
| 'requested'
| 'redeemed'
| 'cancelled'
| 'confirmed'
| 'approved'
| 'rejected'
| 'reset'
export interface TrackingEvent {
id: string
user_id: string | null
child_id: string
entity_type: EntityType
entity_id: string
action: ActionType
points_before: number
points_after: number
delta: number
occurred_at: string
created_at: number
updated_at: number
metadata?: Record<string, any> | null
}
export const TRACKING_EVENT_FIELDS = [
'id',
'user_id',
'child_id',
'entity_type',
'entity_id',
'action',
'points_before',
'points_after',
'delta',
'occurred_at',
'created_at',
'updated_at',
'metadata',
] as const
export type OverrideEntityType = 'task' | 'reward' | 'chore' | 'kindness' | 'penalty' | 'routine'
export interface ChildOverride {
id: string
child_id: string
entity_id: string
entity_type: OverrideEntityType
custom_value: number
created_at: number
updated_at: number
}
export const CHILD_OVERRIDE_FIELDS = [
'id',
'child_id',
'entity_id',
'entity_type',
'custom_value',
'created_at',
'updated_at',
] as const
export interface ChoreScheduleModifiedPayload {
child_id: string
task_id: string
operation: 'SET' | 'DELETED'
}
export interface ChoreTimeExtendedPayload {
child_id: string
task_id: string
}
export interface ChildChoreConfirmationPayload {
child_id: string
task_id: string
operation: 'CONFIRMED' | 'APPROVED' | 'REJECTED' | 'CANCELLED' | 'RESET'
}
export interface RoutineScheduleModifiedPayload {
child_id: string
routine_id: string
operation: 'SET' | 'DELETED'
}
export interface RoutineTimeExtendedPayload {
child_id: string
routine_id: string
}
export interface ChildRoutineConfirmationPayload {
child_id: string
routine_id: string
operation: 'PENDING' | 'APPROVED' | 'REJECTED' | 'RESET'
}