feat: entry editor at /content/:key/new and /content/:key/:id

This commit is contained in:
Florian Sylvain committed 2026-10-21 00:12:34 +02:00
1 parent 91d916fb74
commit fba20f83c5
94 files changed
+7116 -423

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed, nextTick, watch } from 'vue'
import EntryField from '@/components/EntryField.vue'
import type { EntryEditorPage } from '@/composables/useEntryEditorPage'
import { groupFields } from '@/lib/entryLayout'
const FOCUSABLE =
'input:not([type="hidden"]), textarea, [role="spinbutton"], [role="switch"], [role="combobox"]'
const props = defineProps<{ page: EntryEditorPage }>()
const { form, fields, locked, focusToken } = props.page
const rows = computed(() => groupFields(fields.value))
const dimmed = computed(() => locked.value)
async function focusFirstIssue() {
await nextTick()
const key = form.firstIssue.value
if (!key) {
return
}
const wrapper = [...document.querySelectorAll<HTMLElement>('[data-entry-field]')].find(
(candidate) => candidate.dataset.entryField === key,
)
const control = wrapper?.querySelector<HTMLElement>(FOCUSABLE)
control?.focus()
;(wrapper ?? control)?.scrollIntoView?.({ block: 'center' })
}
watch(focusToken, focusFirstIssue)
</script>
<template>
<form
id="entry-form"
novalidate
data-testid="entry-form"
class="flex max-w-190 flex-col gap-5 border border-default bg-default p-4 transition-opacity md:p-6"
:class="dimmed ? 'opacity-70' : ''"
:aria-busy="page.saving.value ? 'true' : undefined"
@submit.prevent="page.submit"
>
<div
v-for="row in rows"
:key="row.map((field) => field.key).join('+')"
class="grid gap-4"
:class="row.length > 1 ? 'md:grid-cols-2' : ''"
>
<EntryField
v-for="field in row"
:key="field.key"
:field="field"
:value="form.values.value[field.key]"
:issue="form.issues.value[field.key]"
:is-default="form.isDefault(field)"
:disabled="locked"
@update="page.change"
/>
</div>
<dl
data-testid="entry-meta"
class="grid grid-cols-1 gap-x-4 gap-y-1.5 border-t border-default pt-4 text-xs/4.5 md:grid-cols-[auto_1fr]"
>
<dt class="text-muted">{{ t('Entry') }}</dt>
<dd :class="page.creating.value ? '' : 'font-mono'">{{ page.entryLine.value }}</dd>
<dt class="text-muted">{{ t('Created by') }}</dt>
<dd>{{ page.createdLine.value }}</dd>
<dt class="text-muted">{{ t('Updated by') }}</dt>
<dd>{{ page.updatedLine.value }}</dd>
</dl>
</form>
</template>