Refactor components to use ModalDialog and StatusMessage; update styles and remove unused files
Some checks failed
Gitea Actions Demo / build-and-push (push) Failing after 9s

- Replaced inline modal dialogs in ParentView with a reusable ModalDialog component.
- Introduced StatusMessage component for loading and error states in ParentView.
- Updated styles to use new colors.css and styles.css for consistent theming.
- Removed ChildRewardList.vue and ChildTaskList.vue components as they were no longer needed.
- Adjusted RewardAssignView and TaskAssignView to use new styles and shared button styles.
- Cleaned up imports across components to reflect the new styles and removed unused CSS files.
This commit is contained in:
2026-01-22 16:37:53 -05:00
parent a0a059472b
commit 63769fbe32
20 changed files with 438 additions and 674 deletions

View File

@@ -1,10 +1,12 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'
import ModalDialog from '../shared/ModalDialog.vue'
import { useRoute, useRouter } from 'vue-router'
import ChildDetailCard from './ChildDetailCard.vue'
import ScrollingList from '../shared/ScrollingList.vue'
import { eventBus } from '@/common/eventBus'
import '@/assets/view-shared.css'
import '@/assets/styles.css'
import type {
Child,
Event,
@@ -317,8 +319,91 @@ onUnmounted(() => {
<template>
<div>
<div v-if="loading" class="loading">Loading...</div>
<div v-else-if="error" class="error">Error: {{ error }}</div>
<StatusMessage :loading="loading" :error="error" />
<div v-if="!loading && !error" class="layout">
<div class="main">
<ChildDetailCard :child="child" />
<ScrollingList
title="Chores"
ref="childChoreListRef"
:fetchBaseUrl="`/api/task/list?ids=${tasks.join(',')}`"
:ids="tasks"
itemKey="tasks"
imageField="image_id"
@trigger-item="triggerTask"
:getItemClass="(item) => ({ bad: !item.is_good, good: item.is_good })"
:filter-fn="
(item) => {
return item.is_good
}
"
>
<template #item="{ item }">
<div class="item-name">{{ item.name }}</div>
<img v-if="item.image_url" :src="item.image_url" alt="Task Image" class="item-image" />
<div
class="item-points"
:class="{ 'good-points': item.is_good, 'bad-points': !item.is_good }"
>
{{ item.is_good ? item.points : -item.points }} Points
</div>
</template>
</ScrollingList>
<ScrollingList
title="Penalties"
ref="childPenaltyListRef"
:fetchBaseUrl="`/api/task/list?ids=${tasks.join(',')}`"
:ids="tasks"
itemKey="tasks"
imageField="image_id"
@trigger-item="triggerTask"
:getItemClass="(item) => ({ bad: !item.is_good, good: item.is_good })"
:filter-fn="
(item) => {
return !item.is_good
}
"
>
<template #item="{ item }">
<div class="item-name">{{ item.name }}</div>
<img v-if="item.image_url" :src="item.image_url" alt="Task Image" class="item-image" />
<div
class="item-points"
:class="{ 'good-points': item.is_good, 'bad-points': !item.is_good }"
>
{{ item.is_good ? item.points : -item.points }} Points
</div>
</template>
</ScrollingList>
<ScrollingList
title="Rewards"
ref="childRewardListRef"
:fetchBaseUrl="`/api/child/${child?.id}/reward-status`"
itemKey="reward_status"
imageField="image_id"
@trigger-item="triggerReward"
:getItemClass="
(item) => ({ reward: true, disabled: hasPendingRewards && !item.redeeming })
"
>
<template #item="{ item }: { item: RewardStatus }">
<div class="item-name">{{ item.name }}</div>
<img
v-if="item.image_url"
:src="item.image_url"
alt="Reward Image"
class="item-image"
/>
<div class="item-points">
<span v-if="item.redeeming" class="pending">PENDING</span>
<span v-if="item.points_needed <= 0" class="ready">REWARD READY</span>
<span v-else>{{ item.points_needed }} more points</span>
</div>
</template>
</ScrollingList>
</div>
</div>
<div v-else class="layout">
<div class="main">
@@ -404,76 +489,38 @@ onUnmounted(() => {
</div>
</div>
<div v-if="showRewardDialog && dialogReward" class="modal-backdrop">
<div class="modal">
<div class="reward-info">
<img
v-if="dialogReward.image_url"
:src="dialogReward.image_url"
alt="Reward Image"
class="reward-image"
/>
<div class="reward-details">
<div class="reward-name">{{ dialogReward.name }}</div>
<div class="reward-points">{{ dialogReward.cost }} pts</div>
</div>
</div>
<div class="dialog-message" style="margin-bottom: 1.2rem">
Would you like to redeem this reward?
</div>
<div class="actions">
<button @click="confirmRedeemReward" class="btn btn-primary">Yes</button>
<button @click="cancelRedeemReward" class="btn btn-secondary">No</button>
</div>
<ModalDialog
v-if="showRewardDialog && dialogReward"
:imageUrl="dialogReward?.image_url"
:title="dialogReward.name"
:subtitle="`${dialogReward.cost} pts`"
>
<div class="modal-message">Would you like to redeem this reward?</div>
<div class="modal-actions">
<button @click="confirmRedeemReward" class="btn btn-primary">Yes</button>
<button @click="cancelRedeemReward" class="btn btn-secondary">No</button>
</div>
</div>
</ModalDialog>
<div v-if="showCancelDialog && dialogReward" class="modal-backdrop">
<div class="modal">
<div class="reward-info">
<img
v-if="dialogReward.image_url"
:src="dialogReward.image_url"
alt="Reward Image"
class="reward-image"
/>
<div class="reward-details">
<div class="reward-name">{{ dialogReward.name }}</div>
<div class="reward-points">{{ dialogReward.cost }} pts</div>
</div>
</div>
<div class="dialog-message" style="margin-bottom: 1.2rem">
This reward is pending.<br />
Would you like to cancel the pending reward request?
</div>
<div class="actions">
<button @click="cancelPendingReward" class="btn btn-primary">Yes</button>
<button @click="closeCancelDialog" class="btn btn-secondary">No</button>
</div>
<ModalDialog
v-if="showCancelDialog && dialogReward"
:imageUrl="dialogReward?.image_url"
:title="dialogReward.name"
:subtitle="`${dialogReward.cost} pts`"
>
<div class="modal-message">
This reward is pending.<br />
Would you like to cancel the pending reward request?
</div>
</div>
<div class="modal-actions">
<button @click="cancelPendingReward" class="btn btn-primary">Yes</button>
<button @click="closeCancelDialog" class="btn btn-secondary">No</button>
</div>
</ModalDialog>
</div>
</template>
<style scoped>
.assign-buttons {
display: flex;
gap: 1rem;
justify-content: center;
margin: 2rem 0;
}
.back-btn {
background: var(--back-btn-bg);
border: 0;
padding: 0.6rem 1rem;
border-radius: 8px;
cursor: pointer;
margin-bottom: 1.5rem;
color: var(--back-btn-color);
font-weight: 600;
}
.item-points {
color: var(--item-points-color, #ffd166);
font-size: 1rem;