feat: add chore, kindness, and penalty management components
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m34s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m34s
- Implemented ChoreAssignView for assigning chores to children. - Created ChoreConfirmDialog for confirming chore completion. - Developed KindnessAssignView for assigning kindness acts. - Added PenaltyAssignView for assigning penalties. - Introduced ChoreEditView and ChoreView for editing and viewing chores. - Created KindnessEditView and KindnessView for managing kindness acts. - Developed PenaltyEditView and PenaltyView for managing penalties. - Added TaskSubNav for navigation between chores, kindness acts, and penalties.
This commit is contained in:
@@ -61,8 +61,16 @@ export function isPasswordStrong(password: string): boolean {
|
||||
*/
|
||||
export async function getTrackingEventsForChild(params: {
|
||||
childId: string
|
||||
entityType?: 'task' | 'reward' | 'penalty'
|
||||
action?: 'activated' | 'requested' | 'redeemed' | 'cancelled'
|
||||
entityType?: 'task' | 'reward' | 'penalty' | 'chore' | 'kindness'
|
||||
action?:
|
||||
| 'activated'
|
||||
| 'requested'
|
||||
| 'redeemed'
|
||||
| 'cancelled'
|
||||
| 'confirmed'
|
||||
| 'approved'
|
||||
| 'rejected'
|
||||
| 'reset'
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<Response> {
|
||||
@@ -160,3 +168,67 @@ export async function extendChoreTime(
|
||||
body: JSON.stringify({ date: localDate }),
|
||||
})
|
||||
}
|
||||
|
||||
// ── Chore Confirmation API ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Child confirms they completed a chore.
|
||||
*/
|
||||
export async function confirmChore(childId: string, taskId: string): Promise<Response> {
|
||||
return fetch(`/api/child/${childId}/confirm-chore`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Child cancels a pending chore confirmation.
|
||||
*/
|
||||
export async function cancelConfirmChore(childId: string, taskId: string): Promise<Response> {
|
||||
return fetch(`/api/child/${childId}/cancel-confirm-chore`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent approves a pending chore confirmation (awards points).
|
||||
*/
|
||||
export async function approveChore(childId: string, taskId: string): Promise<Response> {
|
||||
return fetch(`/api/child/${childId}/approve-chore`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent rejects a pending chore confirmation (no points, resets to available).
|
||||
*/
|
||||
export async function rejectChore(childId: string, taskId: string): Promise<Response> {
|
||||
return fetch(`/api/child/${childId}/reject-chore`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent resets a completed chore (points kept, chore can be confirmed again).
|
||||
*/
|
||||
export async function resetChore(childId: string, taskId: string): Promise<Response> {
|
||||
return fetch(`/api/child/${childId}/reset-chore`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId }),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all pending confirmations (both chores and rewards) for the current user.
|
||||
*/
|
||||
export async function fetchPendingConfirmations(): Promise<Response> {
|
||||
return fetch('/api/pending-confirmations')
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
export type TaskType = 'chore' | 'kindness' | 'penalty'
|
||||
|
||||
export interface Task {
|
||||
id: string
|
||||
name: string
|
||||
points: number
|
||||
is_good: boolean
|
||||
type: TaskType
|
||||
image_id: string | null
|
||||
image_url?: string | null // optional, for resolved URLs
|
||||
}
|
||||
export const TASK_FIELDS = ['id', 'name', 'points', 'is_good', 'image_id'] as const
|
||||
export const TASK_FIELDS = ['id', 'name', 'points', 'type', 'image_id'] as const
|
||||
|
||||
export interface DayConfig {
|
||||
day: number // 0=Sun, 1=Mon, ..., 6=Sat
|
||||
@@ -37,13 +39,15 @@ export interface ChoreSchedule {
|
||||
export interface ChildTask {
|
||||
id: string
|
||||
name: string
|
||||
is_good: boolean
|
||||
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 {
|
||||
@@ -100,25 +104,31 @@ export const REWARD_STATUS_FIELDS = [
|
||||
'image_id',
|
||||
] as const
|
||||
|
||||
export interface PendingReward {
|
||||
export interface PendingConfirmation {
|
||||
id: string
|
||||
child_id: string
|
||||
child_name: string
|
||||
child_image_id: string | null
|
||||
child_image_url?: string | null // optional, for resolved URLs
|
||||
reward_id: string
|
||||
reward_name: string
|
||||
reward_image_id: string | null
|
||||
reward_image_url?: string | null // optional, for resolved URLs
|
||||
child_image_url?: string | null
|
||||
entity_id: string
|
||||
entity_type: 'chore' | 'reward'
|
||||
entity_name: string
|
||||
entity_image_id: string | null
|
||||
entity_image_url?: string | null
|
||||
status: 'pending' | 'approved' | 'rejected'
|
||||
approved_at: string | null
|
||||
}
|
||||
export const PENDING_REWARD_FIELDS = [
|
||||
export const PENDING_CONFIRMATION_FIELDS = [
|
||||
'id',
|
||||
'child_id',
|
||||
'child_name',
|
||||
'child_image_id',
|
||||
'reward_id',
|
||||
'reward_name',
|
||||
'reward_image_id',
|
||||
'entity_id',
|
||||
'entity_type',
|
||||
'entity_name',
|
||||
'entity_image_id',
|
||||
'status',
|
||||
'approved_at',
|
||||
] as const
|
||||
|
||||
export interface Event {
|
||||
@@ -137,6 +147,7 @@ export interface Event {
|
||||
| ChildOverrideDeletedPayload
|
||||
| ChoreScheduleModifiedPayload
|
||||
| ChoreTimeExtendedPayload
|
||||
| ChildChoreConfirmationPayload
|
||||
}
|
||||
|
||||
export interface ChildModifiedEventPayload {
|
||||
@@ -197,8 +208,16 @@ export interface ChildOverrideDeletedPayload {
|
||||
entity_type: string
|
||||
}
|
||||
|
||||
export type EntityType = 'task' | 'reward' | 'penalty'
|
||||
export type ActionType = 'activated' | 'requested' | 'redeemed' | 'cancelled'
|
||||
export type EntityType = 'task' | 'reward' | 'penalty' | 'chore' | 'kindness'
|
||||
export type ActionType =
|
||||
| 'activated'
|
||||
| 'requested'
|
||||
| 'redeemed'
|
||||
| 'cancelled'
|
||||
| 'confirmed'
|
||||
| 'approved'
|
||||
| 'rejected'
|
||||
| 'reset'
|
||||
|
||||
export interface TrackingEvent {
|
||||
id: string
|
||||
@@ -232,7 +251,7 @@ export const TRACKING_EVENT_FIELDS = [
|
||||
'metadata',
|
||||
] as const
|
||||
|
||||
export type OverrideEntityType = 'task' | 'reward'
|
||||
export type OverrideEntityType = 'task' | 'reward' | 'chore' | 'kindness' | 'penalty'
|
||||
|
||||
export interface ChildOverride {
|
||||
id: string
|
||||
@@ -264,3 +283,9 @@ export interface ChoreTimeExtendedPayload {
|
||||
child_id: string
|
||||
task_id: string
|
||||
}
|
||||
|
||||
export interface ChildChoreConfirmationPayload {
|
||||
child_id: string
|
||||
task_id: string
|
||||
operation: 'CONFIRMED' | 'APPROVED' | 'REJECTED' | 'CANCELLED' | 'RESET'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user