Files
pc_builder/inertia/pages/computers/create.vue
T
Kevin L'Hereec 3579c9dfec refactor: uniform validation, fix create form UX, dedupe filter dropdowns
- Remove dead registerValidator (Auth/register.ts), unused since signupValidator
  is the one actually wired to NewAccountController
- computers/create.vue: switch to useForm() + @submit.prevent so the form
  submits on Enter and shows a processing state, matching components/create.vue
- components/edit.vue: drop the 'origin' field, which was never provided by
  the backend (edit() never shared it) and therefore always undefined
- Extract FilterDropdown.vue and reuse it in ComponentsViewer/ComputersViewer,
  removing ~80 lines of duplicated dropdown markup/logic and fixing duplicate
  HTML ids (id="used" appeared twice in ComputersViewer)
- Move MIGRATION_*.md artifacts into docs/migration-history/
2026-07-23 11:10:17 +02:00

187 lines
5.8 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import { Link } from '@adonisjs/inertia/vue'
import { useForm, usePage } from '@inertiajs/vue3'
import type { Component, ComputerState } from '~/pages/types/UITypes'
const { props } = usePage<{
components: Component[]
states: ComputerState[]
errors: Record<string, string>
}>()
const { components, states } = props
const componentTypeOrder = [
'Processor',
'Motherboard',
'Graphic Card',
'Memory',
'SSD',
'Storage',
'Cooler',
'Power Supply',
'Case',
'WiFi-Card',
'Fan',
'Other',
]
const componentsByType = computed(() => {
const groups = new Map<string, Component[]>()
components.forEach((component) => {
if (!groups.has(component.type.name)) {
groups.set(component.type.name, [])
}
groups.get(component.type.name)!.push(component)
})
return Array.from(groups)
.map(([type, components]) => ({ type, components }))
.sort((a, b) => componentTypeOrder.indexOf(a.type) - componentTypeOrder.indexOf(b.type))
})
const form = useForm<{
title: string
sellPrice: number
gpuScore: number
cpuScore: number
stateId: number
globalScore: number
componentsId: number[]
pictures: Record<string, never>[]
}>({
title: '',
sellPrice: 0,
gpuScore: 0,
cpuScore: 0,
stateId: 1,
globalScore: 0,
componentsId: [],
pictures: [],
})
function submitForm() {
form.post('/computers')
}
</script>
<template>
<div class="max-w-4xl mx-auto p-8 bg-white shadow-lg rounded-lg">
<h1 class="text-2xl font-semibold mb-8 text-gray-800">Create New Computer</h1>
<form class="space-y-6" @submit.prevent="submitForm">
<div class="mt-2">
<label class="block text-gray-700 text-sm font-bold mb-2" for="title">Title</label>
<input
name="title"
v-model="form.title"
class="w-full mt-2 border-gray-200 focus:border-indigo-600 focus:ring focus:ring-indigo-500/40 px-3 py-2 border rounded text-gray-700"
type="text"
/>
</div>
<div class="mt-2 grid grid-cols-3 gap-4">
<div>
<label class="block text-gray-700 text-sm font-bold mb-2" for="gpuScore">GPU Score</label>
<input
name="gpuScore"
v-model="form.gpuScore"
class="w-full border-gray-200 focus:border-indigo-600 focus:ring focus:ring-indigo-500/40 px-3 py-2 border rounded text-gray-700"
type="number"
/>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-2" for="cpuScore">CPU Score</label>
<input
name="cpuScore"
v-model="form.cpuScore"
class="w-full border-gray-200 focus:border-indigo-600 focus:ring focus:ring-indigo-500/40 px-3 py-2 border rounded text-gray-700"
type="number"
/>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-2" for="globalScore"
>Global Score</label
>
<input
name="globalScore"
v-model="form.globalScore"
class="w-full border-gray-200 focus:border-indigo-600 focus:ring focus:ring-indigo-500/40 px-3 py-2 border rounded text-gray-700"
type="number"
/>
</div>
</div>
<div class="mt-2">
<label class="block text-gray-700 text-sm font-bold mb-2" for="components"
>Components</label
>
<div v-for="{ type, components } in componentsByType" :key="type">
<label :for="`select-${type}`" class="block text-gray-700 text-sm font-bold mb-1 mt-2">
{{ type }}
</label>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<div
v-for="component in components"
:key="component.id"
class="flex items-center mt-1"
>
<input
type="checkbox"
:id="`checkbox-${component.id}`"
:value="component.id"
v-model="form.componentsId"
class="mr-2"
/>
<label :for="`checkbox-${component.id}`" class="text-sm text-gray-700">
{{ component.name }}
</label>
</div>
</div>
</div>
</div>
<div class="mt-2 grid grid-cols-3 gap-4">
<div class="pt-2">
<label class="block text-gray-700 text-sm font-bold mb-2" for="components">State</label>
<select
v-model="form.stateId"
class="w-full mt-2 border-gray-200 focus:border-indigo-600 focus:ring-indigo-500 px-3 py-2.5 focus:ring-1 border rounded text-gray-700 bg-white"
>
<option disabled value="">Please select one</option>
<option v-for="state in states" :key="state.id" :value="state.id">
{{ state.name }}
</option>
</select>
</div>
<div />
<div class="mt-2">
<label class="block text-gray-700 text-sm font-bold mb-2" for="sellPrice"
>Sell Price</label
>
<input
v-model="form.sellPrice"
class="w-full mt-2 border-gray-200 focus:border-indigo-600 focus:ring focus:ring-indigo-500/40 px-3 py-2 border rounded text-gray-700"
type="number"
step="0.01"
/>
</div>
</div>
<div class="flex gap-4">
<button
type="submit"
:disabled="form.processing"
class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 transition disabled:opacity-50"
>
{{ form.processing ? 'Creating…' : 'Create' }}
</button>
<Link
href="/computers"
class="px-4 py-2 bg-gray-300 text-gray-800 rounded hover:bg-gray-400 transition"
>
Cancel
</Link>
</div>
</form>
</div>
</template>
<style scoped></style>