Add admin customer edit, status management, and admin notes

Adds edit/update endpoints for customer management with admin notes
field, form request validation, status change audit logging, and
8 new tests (171 total, 846 assertions).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Dev
2026-02-09 20:09:57 -05:00
parent 1b2acbe96e
commit 76c49e9ed7
11 changed files with 416 additions and 10 deletions

View File

@@ -0,0 +1,153 @@
<script lang="ts" setup>
import { Link, useForm } from '@inertiajs/vue3'
import AdminLayout from '@/Layouts/AdminLayout.vue'
import AppTextField from '@/Components/app-form-elements/AppTextField.vue'
import AppSelect from '@/Components/app-form-elements/AppSelect.vue'
import AppTextarea from '@/Components/app-form-elements/AppTextarea.vue'
import type { User } from '@/types'
defineOptions({ layout: AdminLayout })
interface Props {
customer: User
}
const props = defineProps<Props>()
const form = useForm({
name: props.customer.name,
email: props.customer.email,
phone: props.customer.phone ?? '',
company: props.customer.company ?? '',
status: props.customer.status,
admin_notes: props.customer.admin_notes ?? '',
})
const statusOptions = [
{ title: 'Active', value: 'active' },
{ title: 'Suspended', value: 'suspended' },
{ title: 'Banned', value: 'banned' },
]
function submit(): void {
form.put(`/customers/${props.customer.id}`)
}
</script>
<template>
<div>
<!-- Header -->
<div class="d-flex align-center justify-space-between mb-6">
<div>
<div class="d-flex align-center gap-2 mb-1">
<Link :href="`/customers/${customer.id}`" class="text-decoration-none">
<VBtn icon="tabler-arrow-left" variant="text" size="small" />
</Link>
<span class="text-h4 font-weight-bold">Edit Customer</span>
</div>
<div class="text-body-2 text-medium-emphasis ms-10">
Update details for "{{ customer.name }}"
</div>
</div>
</div>
<form @submit.prevent="submit">
<VRow>
<!-- Customer Details -->
<VCol cols="12" lg="8">
<VCard title="Customer Details" class="mb-6">
<VCardText>
<VRow>
<VCol cols="12" md="6">
<AppTextField
v-model="form.name"
label="Name"
placeholder="Full name"
:error-messages="form.errors.name"
/>
</VCol>
<VCol cols="12" md="6">
<AppTextField
v-model="form.email"
label="Email"
type="email"
placeholder="email@example.com"
:error-messages="form.errors.email"
/>
</VCol>
<VCol cols="12" md="6">
<AppTextField
v-model="form.phone"
label="Phone"
placeholder="Phone number"
:error-messages="form.errors.phone"
/>
</VCol>
<VCol cols="12" md="6">
<AppTextField
v-model="form.company"
label="Company"
placeholder="Company name"
:error-messages="form.errors.company"
/>
</VCol>
<VCol cols="12" md="6">
<AppSelect
v-model="form.status"
label="Status"
:items="statusOptions"
:error-messages="form.errors.status"
/>
</VCol>
</VRow>
</VCardText>
</VCard>
<!-- Admin Notes -->
<VCard title="Admin Notes" class="mb-6">
<VCardText>
<AppTextarea
v-model="form.admin_notes"
label="Internal Notes"
placeholder="Add internal notes about this customer (only visible to admins)..."
rows="4"
:error-messages="form.errors.admin_notes"
/>
<div class="text-caption text-medium-emphasis mt-2">
These notes are only visible to administrators and will not be shown to the customer.
</div>
</VCardText>
</VCard>
</VCol>
<!-- Sidebar -->
<VCol cols="12" lg="4">
<!-- Actions -->
<VCard>
<VCardText>
<VBtn
type="submit"
color="primary"
block
:loading="form.processing"
:disabled="form.processing"
prepend-icon="tabler-check"
class="mb-3"
>
Save Changes
</VBtn>
<Link :href="`/customers/${customer.id}`" class="text-decoration-none">
<VBtn
variant="outlined"
block
>
Cancel
</VBtn>
</Link>
</VCardText>
</VCard>
</VCol>
</VRow>
</form>
</div>
</template>

View File

@@ -23,6 +23,7 @@ interface Customer {
email: string
phone: string | null
company: string | null
admin_notes: string | null
status: string
created_at: string
email_verified_at: string | null
@@ -230,6 +231,16 @@ function formatBillingAddress(profile: CustomerProfile | null): string {
</div>
<div class="d-flex align-center ga-2">
<Link :href="`/customers/${customer.id}/edit`" class="text-decoration-none">
<VBtn
color="primary"
variant="tonal"
size="small"
>
<VIcon icon="tabler-edit" start />
Edit
</VBtn>
</Link>
<VBtn
color="info"
variant="tonal"
@@ -390,6 +401,19 @@ function formatBillingAddress(profile: CustomerProfile | null): string {
</VCardText>
</VCard>
<!-- Admin Notes -->
<VCard v-if="customer.admin_notes" class="mb-4">
<VCardTitle class="d-flex align-center gap-2">
<VIcon icon="tabler-notes" size="22" />
<span>Admin Notes</span>
</VCardTitle>
<VCardText>
<div class="text-body-2" style="white-space: pre-line;">
{{ customer.admin_notes }}
</div>
</VCardText>
</VCard>
<!-- Quick Stats -->
<VCard>
<VCardTitle class="d-flex align-center gap-2">