Refactor forms to use EntityEditForm component; enhance styles and improve structure
Some checks failed
Gitea Actions Demo / build-and-push (push) Failing after 6s

This commit is contained in:
2026-01-23 19:29:27 -05:00
parent 63769fbe32
commit 74d6f5819c
7 changed files with 416 additions and 310 deletions

View File

@@ -0,0 +1,169 @@
<template>
<div class="entity-edit-view">
<h2>{{ 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">
<template v-for="field in fields" :key="field.name">
<div class="group">
<label :for="field.name">
{{ field.label }}
<!-- Custom field slot -->
<slot
:name="`custom-field-${field.name}`"
:modelValue="formData[field.name]"
:update="(val) => (formData[field.name] = val)"
>
<!-- Default rendering if no slot provided -->
<input
v-if="field.type === 'text' || field.type === 'number'"
:id="field.name"
v-model="formData[field.name]"
:type="field.type"
:required="field.required"
:maxlength="field.maxlength"
:min="field.min"
:max="field.max"
/>
<ImagePicker
v-else-if="field.type === 'image'"
:id="field.name"
v-model="formData[field.name]"
:image-type="field.imageType || 1"
@add-image="onAddImage"
/>
</slot>
</label>
</div>
</template>
<div v-if="error" class="error">{{ error }}</div>
<div class="actions">
<button type="button" class="btn btn-secondary" @click="onCancel" :disabled="loading">
Cancel
</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
{{ isEdit ? 'Save' : 'Create' }}
</button>
</div>
</form>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue'
import ImagePicker from '@/components/utils/ImagePicker.vue'
import { useRouter } from 'vue-router'
import '@/assets/colors.css'
import '@/assets/styles.css'
type Field = {
name: string
label: string
type: 'text' | 'number' | 'image' | 'custom'
required?: boolean
maxlength?: number
min?: number
max?: number
imageType?: number
}
const props = defineProps<{
entityLabel: string
fields: Field[]
initialData?: Record<string, any>
isEdit?: boolean
loading?: boolean
error?: string | null
}>()
const emit = defineEmits(['submit', 'cancel', 'add-image'])
const router = useRouter()
const formData = ref<Record<string, any>>({ ...props.initialData })
watch(
() => props.initialData,
(newVal) => {
if (newVal) {
formData.value = { ...newVal }
}
},
{ immediate: true, deep: true },
)
onMounted(async () => {
await nextTick()
// Optionally focus first input
})
function onAddImage({ id, file }: { id: string; file: File }) {
emit('add-image', { id, file })
}
function onCancel() {
emit('cancel')
router.back()
}
function submit() {
emit('submit', { ...formData.value })
}
</script>
<style scoped>
.entity-edit-view {
max-width: 420px;
margin: 0 auto;
background: var(--edit-view-bg, #fff);
border-radius: 14px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
padding: 2.2rem 2.2rem 1.5rem 2.2rem;
}
.entity-edit-view h2 {
text-align: center;
margin-bottom: 1.5rem;
color: var(--form-heading);
}
.entity-form .group {
margin-bottom: 1.2rem;
}
.entity-form label {
display: block;
font-weight: 600;
color: var(--form-label, #444);
margin-bottom: 0.4rem;
}
.entity-form input[type='text'],
.entity-form input[type='number'] {
width: 100%;
padding: 0.6rem;
border-radius: 7px;
border: 1px solid var(--form-input-border, #e6e6e6);
font-size: 1rem;
background: var(--form-input-bg, #fff);
box-sizing: border-box;
}
.actions {
display: flex;
gap: 3rem;
justify-content: center;
margin-top: 0.5rem;
margin-bottom: 0.4rem;
}
.actions .btn {
padding: 1rem 2.2rem;
font-weight: 700;
font-size: 1.25rem;
min-width: 120px;
}
.error {
color: var(--error-color, #e53e3e);
margin-bottom: 1rem;
font-size: 1rem;
}
.loading-message {
color: var(--loading-color, #888);
margin-bottom: 1.2rem;
font-size: 1rem;
}
</style>