Refactor and enhance various components and tests
All checks were successful
Chore App Build and Push Docker Images / build-and-push (push) Successful in 1m23s

- Remove OverrideEditModal.spec.ts test file.
- Update ParentPinSetup.vue to handle Enter key for code and PIN inputs.
- Modify ChildEditView.vue to add maxlength for age input.
- Enhance ChildView.vue with reward confirmation and cancellation dialogs.
- Update ParentView.vue to handle pending rewards and confirm edits.
- Revise PendingRewardDialog.vue to accept a dynamic message prop.
- Expand ChildView.spec.ts to cover reward dialog interactions.
- Add tests for ParentView.vue to validate pending reward handling.
- Update UserProfile.vue to simplify button styles.
- Adjust RewardView.vue to improve delete confirmation handling.
- Modify ChildrenListView.vue to clarify child creation instructions.
- Refactor EntityEditForm.vue to improve input handling and focus management.
- Enhance ItemList.vue to support item selection.
- Update LoginButton.vue to focus PIN input on error.
- Change ScrollingList.vue empty state color for better visibility.
- Remove capture attribute from ImagePicker.vue file input.
- Update router/index.ts to redirect logged-in users from auth routes.
- Add authGuard.spec.ts to test router authentication logic.
This commit is contained in:
2026-02-19 09:57:59 -05:00
parent 31ea76f013
commit 725bf518ea
21 changed files with 630 additions and 445 deletions

View File

@@ -280,8 +280,8 @@ onBeforeUnmount(() => {
<div>
<MessageBlock v-if="children.length === 0" message="No children">
<span v-if="!isParentAuthenticated">
<button class="round-btn" @click="eventBus.emit('open-login')">Sign in</button> to create a
child
<button class="round-btn" @click="eventBus.emit('open-login')">Switch</button> to parent
mode to create a child
</span>
<span v-else> <button class="round-btn" @click="createChild">Create</button> a child </span>
</MessageBlock>

View File

@@ -1,7 +1,7 @@
<template>
<h2>{{ title ?? (isEdit ? `Edit ${entityLabel}` : `Create ${entityLabel}`) }}</h2>
<div v-if="loading" class="loading-message">Loading {{ entityLabel.toLowerCase() }}...</div>
<form v-else @submit.prevent="submit" class="entity-form">
<form v-else @submit.prevent="submit" class="entity-form" ref="formRef">
<template v-for="field in fields" :key="field.name">
<div class="group">
<label :for="field.name">
@@ -14,14 +14,31 @@
>
<!-- Default rendering if no slot provided -->
<input
v-if="field.type === 'text' || field.type === 'number'"
v-if="field.type === 'text'"
:id="field.name"
v-model="formData[field.name]"
:type="field.type"
type="text"
:required="field.required"
:maxlength="field.maxlength"
/>
<input
v-else-if="field.type === 'number'"
:id="field.name"
v-model="formData[field.name]"
type="number"
:required="field.required"
:min="field.min"
:max="field.max"
inputmode="numeric"
pattern="\\d{1,3}"
@input="
(e) => {
if (field.maxlength && e.target.value.length > field.maxlength) {
e.target.value = e.target.value.slice(0, field.maxlength)
formData[field.name] = e.target.value
}
}
"
/>
<ImagePicker
v-else-if="field.type === 'image'"
@@ -88,12 +105,31 @@ const emit = defineEmits(['submit', 'cancel', 'add-image'])
const router = useRouter()
const formData = ref<Record<string, any>>({ ...props.initialData })
const baselineData = ref<Record<string, any>>({ ...props.initialData })
const formRef = ref<HTMLFormElement | null>(null)
async function focusFirstInput() {
await nextTick()
const firstInput = formRef.value?.querySelector<HTMLElement>('input, select, textarea')
firstInput?.focus()
}
onMounted(async () => {
await nextTick()
isDirty.value = false
if (!props.loading) {
focusFirstInput()
}
})
watch(
() => props.loading,
(newVal, oldVal) => {
if (!newVal && oldVal === true) {
focusFirstInput()
}
},
)
function onAddImage({ id, file }: { id: string; file: File }) {
emit('add-image', { id, file })
}

View File

@@ -90,6 +90,14 @@ onMounted(fetchItems)
watch(() => props.fetchUrl, fetchItems)
const handleClicked = (item: any) => {
if (props.selectable) {
const idx = selectedItems.value.indexOf(item.id)
if (idx === -1) {
selectedItems.value.push(item.id)
} else {
selectedItems.value.splice(idx, 1)
}
}
emit('clicked', item)
props.onClicked?.(item)
}

View File

@@ -126,6 +126,9 @@ const submit = async () => {
}
if (!data.valid) {
error.value = 'Incorrect PIN'
pin.value = ''
await nextTick()
pinInput.value?.focus()
return
}
// Authenticate parent and navigate

View File

@@ -383,6 +383,6 @@ onBeforeUnmount(() => {
.empty {
text-align: center;
padding: 2rem 0;
color: #888;
color: #d6d6d6;
}
</style>