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
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:
@@ -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>
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -383,6 +383,6 @@ onBeforeUnmount(() => {
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
color: #888;
|
||||
color: #d6d6d6;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user