project-bifrost-platform/src/admin/components/FieldRenderer.astro
Jonathan Hvid c509dc66ed feat(events): event photo upload + photo-as-background hero card
Admin can now upload a png/jpg event photo (SPEC §8 exception added):
- new image-upload admin field kind with live preview, uploading via
  POST /api/admin/upload (fenja-only, type + 5MB validation);
- files stored under data/uploads (gitignored, BIFROST_UPLOAD_DIR
  overridable) and served by GET /uploads/[file] with a traversal guard.

Reworks the /pulse event card: the greeting moved inside a taller box, the
"next gathering" label sits above the date + title, and the photo renders
as a top-right background that blends into the indigo via gradient masks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 17:18:23 +02:00

59 lines
2.6 KiB
Text

---
/* ---------------------------------------------------------------------------
* FieldRenderer — dispatches on field.kind to the right input component
* and wraps it with label + helper + error.
*
* Branches must stay exhaustive; the `never` fallback flags any unhandled
* Field kind at compile time.
* ------------------------------------------------------------------------- */
import TextField from './fields/TextField.astro';
import TextareaField from './fields/TextareaField.astro';
import MarkdownField from './fields/MarkdownField.astro';
import SelectField from './fields/SelectField.astro';
import SelectAsyncField from './fields/SelectAsyncField.astro';
import MultiSelectAsyncField from './fields/MultiSelectAsyncField.astro';
import MultiTextField from './fields/MultiTextField.astro';
import DateField from './fields/DateField.astro';
import DatetimeField from './fields/DatetimeField.astro';
import NumberField from './fields/NumberField.astro';
import ReadonlyField from './fields/ReadonlyField.astro';
import ImageUploadField from './fields/ImageUploadField.astro';
import type { Field } from '../resource-types';
interface Props {
field: Field;
value: unknown;
error?: string;
item: Record<string, unknown> | null;
}
const { field, value, error, item } = Astro.props;
---
<div class="bs-field" data-field={field.key}>
<label class="bs-label" for={`f-${field.key}`}>
{field.label}
{field.required && <span class="bs-required" aria-hidden="true">*</span>}
</label>
{field.kind === 'text' && <TextField field={field} value={value} />}
{field.kind === 'textarea' && <TextareaField field={field} value={value} />}
{field.kind === 'markdown' && <MarkdownField field={field} value={value} />}
{field.kind === 'select' && <SelectField field={field} value={value} />}
{field.kind === 'select-async' && <SelectAsyncField field={field} value={value} />}
{field.kind === 'multi-select-async' && <MultiSelectAsyncField field={field} value={value} />}
{field.kind === 'multi-text' && <MultiTextField field={field} value={value} />}
{field.kind === 'date' && <DateField field={field} value={value} />}
{field.kind === 'datetime' && <DatetimeField field={field} value={value} />}
{field.kind === 'number' && <NumberField field={field} value={value} />}
{field.kind === 'readonly' && <ReadonlyField field={field} value={value} item={item} />}
{field.kind === 'image-upload' && <ImageUploadField field={field} value={value} />}
{field.helperText && (
<p class="bs-helper">{field.helperText}</p>
)}
{error && (
<p class="bs-field-error" role="alert">{error}</p>
)}
</div>