feat: update reward version to 1.0.13 and modify dialog button labels for clarity
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m31s

This commit is contained in:
2026-05-01 23:32:17 -04:00
parent e77254eabf
commit 8308d205e8
12 changed files with 223 additions and 93 deletions

View File

@@ -452,6 +452,7 @@ function isChoreScheduledToday(item: ChildTask): boolean {
}
function isChoreExpired(item: ChildTask): boolean {
if (item.pending_status === 'pending') return false
if (!item.schedule) return false
const today = new Date()
const due = getDueTimeToday(item.schedule, today)

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { computed, ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import ModalDialog from '../shared/ModalDialog.vue'
import ScheduleModal from '../shared/ScheduleModal.vue'
import PendingRewardDialog from './PendingRewardDialog.vue'
@@ -95,6 +95,14 @@ const kebabBtnRefs = ref<Map<string, HTMLElement>>(new Map())
const showScheduleModal = ref(false)
const scheduleTarget = ref<ChildTask | null>(null)
const pendingDialogReward = computed<Reward | RewardStatus | null>(() => {
if (pendingEditOverrideTarget.value?.type === 'reward') {
return pendingEditOverrideTarget.value.entity as Reward
}
return selectedReward.value
})
// Expiry timers
const expiryTimers = ref<number[]>([])
@@ -449,6 +457,7 @@ function isChoreScheduledToday(item: ChildTask): boolean {
}
function isChoreExpired(item: ChildTask): boolean {
if (item.pending_status === 'pending') return false
if (!item.schedule) return false
const now = new Date()
if (!isScheduledToday(item.schedule, now)) return false
@@ -777,6 +786,7 @@ const triggerTask = (task: ChildTask) => {
selectedTask.value = task
const pendingRewardIds = getPendingRewardIds()
if (pendingRewardIds.length > 0) {
selectedReward.value = null
showPendingRewardDialog.value = true
return
}
@@ -841,13 +851,13 @@ const confirmTriggerTask = async () => {
const triggerReward = (reward: RewardStatus) => {
if (reward.points_needed > 0) return
selectedReward.value = reward
// If there is a pending reward and it's not this one, show the pending dialog
const pendingRewardIds = getPendingRewardIds()
if (pendingRewardIds.length > 0 && !reward.redeeming) {
showPendingRewardDialog.value = true
return
}
selectedReward.value = reward
setTimeout(() => {
showRewardConfirm.value = true
}, 150)
@@ -1158,6 +1168,16 @@ function goToAssignRewards() {
<!-- Pending Reward Dialog -->
<PendingRewardDialog
v-if="showPendingRewardDialog"
:reward-name="pendingDialogReward?.name"
:child-name="pendingDialogReward ? child?.name : undefined"
:image-url="pendingDialogReward?.image_url ?? null"
:subtitle="
pendingDialogReward
? pendingDialogReward.points_needed === 0
? 'Reward Ready!'
: pendingDialogReward.points_needed + ' more points'
: undefined
"
:message="
pendingEditOverrideTarget
? 'This reward is currently pending. Changing its cost will cancel the pending request. Would you like to proceed?'
@@ -1168,6 +1188,7 @@ function goToAssignRewards() {
() => {
showPendingRewardDialog = false
pendingEditOverrideTarget = null
selectedReward = null
}
"
/>

View File

@@ -1,11 +1,18 @@
<template>
<ModalDialog title="Warning!" @backdrop-click="$emit('cancel')">
<div class="modal-message">
{{ message }}
</div>
<div class="modal-actions">
<button @click="$emit('confirm')" class="btn btn-primary">Yes</button>
<button @click="$emit('cancel')" class="btn btn-secondary">No</button>
<ModalDialog @backdrop-click="$emit('cancel')">
<div class="approve-dialog">
<img v-if="imageUrl" :src="imageUrl" alt="Reward" class="reward-image" />
<p v-if="rewardName" class="item-label">{{ rewardName }}</p>
<p v-if="subtitle" class="subtitle">{{ subtitle }}</p>
<p v-if="rewardName && childName" class="message">
Redeem this reward for <strong>{{ childName }}</strong
>?
</p>
<p class="message">{{ message }}</p>
<div class="actions">
<button @click="$emit('confirm')" class="btn btn-primary">Approve</button>
<button @click="$emit('cancel')" class="btn btn-secondary">Reject</button>
</div>
</div>
</ModalDialog>
</template>
@@ -16,9 +23,17 @@ import ModalDialog from '../shared/ModalDialog.vue'
withDefaults(
defineProps<{
message?: string
rewardName?: string
childName?: string
imageUrl?: string | null
subtitle?: string
}>(),
{
message: 'A reward is currently pending. It will be cancelled. Would you like to proceed?',
rewardName: undefined,
childName: undefined,
imageUrl: null,
subtitle: undefined,
},
)
@@ -29,9 +44,58 @@ defineEmits<{
</script>
<style scoped>
.modal-message {
margin-bottom: 1.2rem;
.approve-dialog {
text-align: center;
padding: 0.5rem;
}
.reward-image {
width: 72px;
height: 72px;
object-fit: cover;
border-radius: 8px;
background: var(--info-image-bg);
margin-bottom: 0.75rem;
}
.item-label {
font-size: 1.2rem;
font-weight: 700;
color: var(--dialog-child-name);
margin-bottom: 0.15rem;
}
.subtitle {
font-size: 1rem;
color: var(--modal-message-color, #333);
font-weight: 600;
color: var(--dialog-child-name);
margin-bottom: 1rem;
}
.message {
font-size: 1rem;
color: var(--dialog-message);
margin-bottom: 0.15rem;
}
.message:last-of-type {
margin-bottom: 1.5rem;
}
.actions {
display: flex;
gap: 1.5rem;
justify-content: center;
}
.actions button {
padding: 0.7rem 1.8rem;
border-radius: 10px;
border: 0;
cursor: pointer;
font-weight: 700;
font-size: 1.05rem;
transition: background 0.18s;
min-width: 100px;
}
</style>

View File

@@ -1,20 +1,22 @@
<template>
<ModalDialog
v-if="reward"
:imageUrl="reward.image_url"
:title="reward.name"
:subtitle="reward.points_needed === 0 ? 'Reward Ready!' : reward.points_needed + ' more points'"
dialogMaxWidth="760px"
@backdrop-click="$emit('cancel')"
>
<div class="modal-message">
Redeem this reward for <span class="child-name">{{ childName }}</span
>?
</div>
<div class="modal-actions">
<button @click="$emit('confirm')" class="btn btn-primary">Yes</button>
<button v-if="reward?.redeeming" @click="$emit('deny')" class="btn btn-danger">Deny</button>
<button @click="$emit('cancel')" class="btn btn-secondary">Cancel</button>
<ModalDialog v-if="reward" @backdrop-click="$emit('cancel')">
<div class="approve-dialog">
<img v-if="reward.image_url" :src="reward.image_url" alt="Reward" class="reward-image" />
<p class="item-label">{{ reward.name }}</p>
<p class="subtitle">
{{ reward.points_needed === 0 ? 'Reward Ready!' : reward.points_needed + ' more points' }}
</p>
<p class="message">
Redeem this reward for <strong>{{ childName }}</strong
>?
</p>
<div class="actions">
<button @click="$emit('confirm')" class="btn btn-primary">Yes</button>
<button v-if="reward.redeeming" @click="$emit('deny')" class="btn btn-secondary">
Reject
</button>
<button v-else @click="$emit('cancel')" class="btn btn-secondary">No</button>
</div>
</div>
</ModalDialog>
</template>
@@ -36,14 +38,54 @@ defineEmits<{
</script>
<style scoped>
.modal-message {
margin-bottom: 1.2rem;
font-size: 1rem;
color: var(--modal-message-color, #333);
.approve-dialog {
text-align: center;
padding: 0.5rem;
}
.child-name {
.reward-image {
width: 72px;
height: 72px;
object-fit: cover;
border-radius: 8px;
background: var(--info-image-bg);
margin-bottom: 0.75rem;
}
.item-label {
font-size: 1.2rem;
font-weight: 700;
color: var(--dialog-child-name);
margin-bottom: 0.15rem;
}
.subtitle {
font-size: 1rem;
font-weight: 600;
color: var(--text-primary, #333);
color: var(--dialog-child-name);
margin-bottom: 1rem;
}
.message {
font-size: 1rem;
color: var(--dialog-message);
margin-bottom: 1.5rem;
}
.actions {
display: flex;
gap: 1.5rem;
justify-content: center;
}
.actions button {
padding: 0.7rem 1.8rem;
border-radius: 10px;
border: 0;
cursor: pointer;
font-weight: 700;
font-size: 1.05rem;
transition: background 0.18s;
min-width: 100px;
}
</style>