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

- 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:
2026-02-28 11:25:56 -05:00
parent 65e987ceb6
commit d7316bb00a
61 changed files with 7364 additions and 647 deletions

View File

@@ -6,10 +6,10 @@
<ItemList
v-else
:key="refreshKey"
:fetchUrl="`/api/pending-rewards`"
itemKey="rewards"
:itemFields="PENDING_REWARD_FIELDS"
:imageFields="['child_image_id', 'reward_image_id']"
:fetchUrl="`/api/pending-confirmations`"
itemKey="confirmations"
:itemFields="PENDING_CONFIRMATION_FIELDS"
:imageFields="['child_image_id', 'entity_image_id']"
@clicked="handleNotificationClick"
@loading-complete="(count) => (notificationListCountRef = count)"
>
@@ -19,10 +19,12 @@
<img v-if="item.child_image_url" :src="item.child_image_url" alt="Child" />
<span>{{ item.child_name }}</span>
</div>
<span class="requested-text">requested</span>
<span class="requested-text">{{
item.entity_type === 'chore' ? 'completed' : 'requested'
}}</span>
<div class="reward-info">
<span>{{ item.reward_name }}</span>
<img v-if="item.reward_image_url" :src="item.reward_image_url" alt="Reward" />
<span>{{ item.entity_name }}</span>
<img v-if="item.entity_image_url" :src="item.entity_image_url" alt="" />
</div>
</div>
</template>
@@ -35,8 +37,13 @@ import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import ItemList from '../shared/ItemList.vue'
import MessageBlock from '../shared/MessageBlock.vue'
import type { PendingReward, Event, ChildRewardRequestEventPayload } from '@/common/models'
import { PENDING_REWARD_FIELDS } from '@/common/models'
import type {
PendingConfirmation,
Event,
ChildRewardRequestEventPayload,
ChildChoreConfirmationPayload,
} from '@/common/models'
import { PENDING_CONFIRMATION_FIELDS } from '@/common/models'
import { eventBus } from '@/common/eventBus'
const router = useRouter()
@@ -44,7 +51,7 @@ const router = useRouter()
const notificationListCountRef = ref(-1)
const refreshKey = ref(0)
function handleNotificationClick(item: PendingReward) {
function handleNotificationClick(item: PendingConfirmation) {
router.push({ name: 'ParentView', params: { id: item.child_id } })
}
@@ -55,7 +62,19 @@ function handleRewardRequest(event: Event) {
payload.operation === 'CANCELLED' ||
payload.operation === 'GRANTED'
) {
// Reset count and bump key to force ItemList to re-mount and refetch
notificationListCountRef.value = -1
refreshKey.value++
}
}
function handleChoreConfirmation(event: Event) {
const payload = event.payload as ChildChoreConfirmationPayload
if (
payload.operation === 'CONFIRMED' ||
payload.operation === 'APPROVED' ||
payload.operation === 'REJECTED' ||
payload.operation === 'CANCELLED'
) {
notificationListCountRef.value = -1
refreshKey.value++
}
@@ -63,10 +82,12 @@ function handleRewardRequest(event: Event) {
onMounted(() => {
eventBus.on('child_reward_request', handleRewardRequest)
eventBus.on('child_chore_confirmation', handleChoreConfirmation)
})
onUnmounted(() => {
eventBus.off('child_reward_request', handleRewardRequest)
eventBus.off('child_chore_confirmation', handleChoreConfirmation)
})
</script>