ComplaintForm
The "raise a complaint" form: subject, optional related order and a description, validated with valibot before emitting a CreateComplaintPayload. It owns its own state and exposes reset() / submit() for parents that drive it from outside (as ComplaintCreateDialog does).
vue
<script setup lang="ts">
import { ref } from "vue"
import RyderComplaintForm from "@opeolluwa/ryder/components/complaints/ComplaintForm.vue"
import type { CreateComplaintPayload } from "@opeolluwa/ryder/types"
const loading = ref(false)
function onSubmit(payload: CreateComplaintPayload) {
// POST the payload, then flip `loading` back off.
}
</script>
<template>
<RyderComplaintForm
:loading="loading"
:orders="orders"
@submit="onSubmit"
@cancel="close"
/>
</template>Examples
vue
<script setup lang="ts">
import { ref } from "vue";
import ComplaintForm from "../../../src/components/complaints/ComplaintForm.vue";
import { orders } from "./fixtures";
import type { CreateComplaintPayload } from "../../../src/types/complaints";
const loading = ref(false);
const lastSubmit = ref("—");
// Append the emitted payload so the demo output is visible under the form.
async function onSubmit(payload: CreateComplaintPayload) {
lastSubmit.value = JSON.stringify(payload);
loading.value = true;
await new Promise((resolve) => setTimeout(resolve, 500));
loading.value = false;
}
</script>
<template>
<div class="max-w-2xl">
<ComplaintForm
:loading="loading"
:orders="orders"
@submit="onSubmit"
@cancel="lastSubmit = 'cancel'"
/>
<p class="mt-3 text-xs text-gray-500 dark:text-white/40">
Last submit: {{ lastSubmit }}
</p>
</div>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
loading | boolean | — | Submit button spinner and disabled while the POST is in flight. |
showActions | boolean | true | Renders the Cancel / Submit footer. false when a host (the dialog) supplies its own actions. |
orders | Array<{ identifier: string; status?: string; itemsCount?: number }> | [] | Options for the "Related order" select. |
ordersLoading | boolean | false | Disables the select while the orders fetch is in flight. |
orderStatusLabel | (status: any) => string | (status) => String(status ?? "") | Formats an order's status inside the option label. |
orderItemCount | (order: any) => number | () => 0 | Supplies the item count inside the option label. |
Emits
| Event | Payload | Description |
|---|---|---|
submit | [payload: CreateComplaintPayload] | Validation passed: { subject, description, orderIdentifier? }. An empty order maps to undefined. |
cancel | [] | The Cancel button was clicked. |
Slots
None.
Notes
- Fixed
propsbinding. ThewithDefaults(defineProps<…>(), …)result used to be unbound while theorderItemscomputed readprops.orders,props.orderItemCountandprops.orderStatusLabel, which threwReferenceError: props is not definedon first render. The result is now assigned (const props = withDefaults(defineProps<…>(), { … })), matching the rest of the kit. - Validation (valibot, on
UForm):subjecttrimmed, required, max 255 (the hint shows the counter);descriptiontrimmed and required;orderIdentifieroptional. - Option labels read
#<first 8 chars> · <n> items · <status>— the label pieces come fromorderItemCountandorderStatusLabel, which is why both are props. - The dialog embeds it with
show-actions: falseand drives it through the exposed API:reset()clears the three fields,submit()callsuform.submit()so validation still runs.