wip
This commit is contained in:
parent
5c55f1860a
commit
22c5f7bb54
@ -30,7 +30,7 @@ class ResumeRequest extends FormRequest
|
|||||||
"technologies.*.level" => ["required", Rule::in(1, 2, 3, 4, 5)],
|
"technologies.*.level" => ["required", Rule::in(1, 2, 3, 4, 5)],
|
||||||
|
|
||||||
"projects.*.description" => ["required"],
|
"projects.*.description" => ["required"],
|
||||||
"projects.*.technologies" => ["required"],
|
"projects.*.technologies" => ["exists:technologies,name", "distinct"],
|
||||||
"projects.*.startDate" => ["required", "date_format:Y-m-d"],
|
"projects.*.startDate" => ["required", "date_format:Y-m-d"],
|
||||||
"projects.*.endDate" => ["required", "date_format:Y-m-d", "after:startDate"],
|
"projects.*.endDate" => ["required", "date_format:Y-m-d", "after:startDate"],
|
||||||
"projects.*.tasks" => ["required"],
|
"projects.*.tasks" => ["required"],
|
||||||
|
@ -17,6 +17,10 @@ class ResumeResource extends JsonResource
|
|||||||
"user" => new SimpleUserResource($this->user),
|
"user" => new SimpleUserResource($this->user),
|
||||||
"name" => $this->name,
|
"name" => $this->name,
|
||||||
"description" => $this->description,
|
"description" => $this->description,
|
||||||
|
"educationCount" => $this->education->count(),
|
||||||
|
"languageCount" => $this->languages->count(),
|
||||||
|
"technologyCount" => $this->technologies->count(),
|
||||||
|
"projectCount" => $this->projects->count(),
|
||||||
"createdAt" => $this->created_at->toDisplayString(),
|
"createdAt" => $this->created_at->toDisplayString(),
|
||||||
"updatedAt" => $this->updated_at->toDisplayString(),
|
"updatedAt" => $this->updated_at->toDisplayString(),
|
||||||
];
|
];
|
||||||
|
@ -17,7 +17,6 @@ class ResumeFactory extends Factory
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"name" => fn(array $attr) => empty($attr["user_id"]) ? $this->faker->name : null,
|
"name" => fn(array $attr) => empty($attr["user_id"]) ? $this->faker->name : null,
|
||||||
"description" => $this->faker->boolean(30) ? $this->faker->sentence : null,
|
|
||||||
"education" => $this->generateEducation(),
|
"education" => $this->generateEducation(),
|
||||||
"languages" => $this->generateLanguages(),
|
"languages" => $this->generateLanguages(),
|
||||||
"technologies" => $this->generateTechnologies(),
|
"technologies" => $this->generateTechnologies(),
|
||||||
|
@ -14,7 +14,6 @@ return new class() extends Migration {
|
|||||||
$table->id();
|
$table->id();
|
||||||
$table->foreignIdFor(User::class)->nullable()->constrained()->cascadeOnDelete();
|
$table->foreignIdFor(User::class)->nullable()->constrained()->cascadeOnDelete();
|
||||||
$table->string("name")->nullable();
|
$table->string("name")->nullable();
|
||||||
$table->string("description")->nullable();
|
|
||||||
$table->json("education");
|
$table->json("education");
|
||||||
$table->json("languages");
|
$table->json("languages");
|
||||||
$table->json("technologies");
|
$table->json("technologies");
|
||||||
|
@ -263,11 +263,25 @@
|
|||||||
<template #form="{ element, index }">
|
<template #form="{ element, index }">
|
||||||
<div class="gap-4 md:grid md:grid-cols-2 ">
|
<div class="gap-4 md:grid md:grid-cols-2 ">
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
|
<label
|
||||||
|
:for="`language-${index}-level`"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>
|
||||||
|
Język
|
||||||
|
</label>
|
||||||
|
<div class="mt-2">
|
||||||
<Combobox
|
<Combobox
|
||||||
|
:id="`language-${index}-level`"
|
||||||
v-model="element.name"
|
v-model="element.name"
|
||||||
label="Język"
|
|
||||||
:items="languages"
|
:items="languages"
|
||||||
/>
|
/>
|
||||||
|
<p
|
||||||
|
v-if="form.errors[`languages.${index}.name`]"
|
||||||
|
class="mt-2 text-sm text-red-600"
|
||||||
|
>
|
||||||
|
{{ form.errors[`languages.${index}.name`] }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<label
|
<label
|
||||||
@ -313,11 +327,25 @@
|
|||||||
<template #form="{ element, index }">
|
<template #form="{ element, index }">
|
||||||
<div class="gap-4 md:grid md:grid-cols-2 ">
|
<div class="gap-4 md:grid md:grid-cols-2 ">
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
|
<label
|
||||||
|
:for="`technology-${index}-level`"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>
|
||||||
|
Technologia
|
||||||
|
</label>
|
||||||
|
<div class="mt-2">
|
||||||
<Combobox
|
<Combobox
|
||||||
|
:id="`technology-${index}-level`"
|
||||||
v-model="element.name"
|
v-model="element.name"
|
||||||
label="Technologia"
|
|
||||||
:items="technologies"
|
:items="technologies"
|
||||||
/>
|
/>
|
||||||
|
<p
|
||||||
|
v-if="form.errors[`technologies.${index}.name`]"
|
||||||
|
class="mt-2 text-sm text-red-600"
|
||||||
|
>
|
||||||
|
{{ form.errors[`technologies.${index}.name`] }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<label
|
<label
|
||||||
@ -367,6 +395,7 @@
|
|||||||
<textarea
|
<textarea
|
||||||
:id="`project-description-${index}`"
|
:id="`project-description-${index}`"
|
||||||
v-model="element.description"
|
v-model="element.description"
|
||||||
|
rows="5"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
class="block w-full rounded-md shadow-sm sm:text-sm"
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.description`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.description`] }"
|
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.description`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.description`] }"
|
||||||
/>
|
/>
|
||||||
@ -386,13 +415,11 @@
|
|||||||
Technologie
|
Technologie
|
||||||
</label>
|
</label>
|
||||||
<div class="mt-1 sm:mt-0">
|
<div class="mt-1 sm:mt-0">
|
||||||
<input
|
<MultipleCombobox
|
||||||
:id="`project-technologies-${index}`"
|
:id="`project-technologies-${index}`"
|
||||||
v-model="element.technologies"
|
v-model="element.technologies"
|
||||||
type="text"
|
:items="technologies"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
/>
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.technologies`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.technologies`] }"
|
|
||||||
>
|
|
||||||
<p
|
<p
|
||||||
v-if="form.errors[`projects.${index}.technologies`]"
|
v-if="form.errors[`projects.${index}.technologies`]"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-red-600"
|
||||||
@ -458,6 +485,7 @@
|
|||||||
<textarea
|
<textarea
|
||||||
:id="`project-tasks-${index}`"
|
:id="`project-tasks-${index}`"
|
||||||
v-model="element.tasks"
|
v-model="element.tasks"
|
||||||
|
rows="5"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
class="block w-full rounded-md shadow-sm sm:text-sm"
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.tasks`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.tasks`] }"
|
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.tasks`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.tasks`] }"
|
||||||
/>
|
/>
|
||||||
@ -495,10 +523,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Listbox, ListboxOption, ListboxOptions, ListboxLabel, ListboxButton } from '@headlessui/vue'
|
import { Listbox, ListboxOption, ListboxOptions, ListboxLabel, ListboxButton } from '@headlessui/vue'
|
||||||
import { SelectorIcon, CheckIcon } from '@heroicons/vue/outline'
|
import { SelectorIcon, CheckIcon } from '@heroicons/vue/outline'
|
||||||
|
import { ExclamationCircleIcon } from '@heroicons/vue/solid'
|
||||||
import { useForm } from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import DynamicSection from '@/Shared/Forms/DynamicSection'
|
import DynamicSection from '@/Shared/Forms/DynamicSection'
|
||||||
import Combobox from '@/Shared/Forms/Combobox'
|
import Combobox from '@/Shared/Forms/Combobox'
|
||||||
|
import MultipleCombobox from '@/Shared/Forms/MultipleCombobox'
|
||||||
import LevelPicker from '@/Shared/Forms/LevelPicker'
|
import LevelPicker from '@/Shared/Forms/LevelPicker'
|
||||||
import useLevels from '@/Composables/useLevels'
|
import useLevels from '@/Composables/useLevels'
|
||||||
|
|
||||||
@ -527,7 +557,7 @@ const form = useForm({
|
|||||||
function addProject() {
|
function addProject() {
|
||||||
form.projects.push({
|
form.projects.push({
|
||||||
description: null,
|
description: null,
|
||||||
technologies: null,
|
technologies: [],
|
||||||
tasks: null,
|
tasks: null,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
|
@ -263,9 +263,16 @@
|
|||||||
<template #form="{ element, index }">
|
<template #form="{ element, index }">
|
||||||
<div class="gap-4 md:grid md:grid-cols-2 ">
|
<div class="gap-4 md:grid md:grid-cols-2 ">
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
|
<label
|
||||||
|
:for="`language-${index}-level`"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>
|
||||||
|
Język
|
||||||
|
</label>
|
||||||
|
<div class="mt-2">
|
||||||
<Combobox
|
<Combobox
|
||||||
|
:id="`language-${index}-level`"
|
||||||
v-model="element.name"
|
v-model="element.name"
|
||||||
label="Język"
|
|
||||||
:items="languages"
|
:items="languages"
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
@ -275,6 +282,7 @@
|
|||||||
{{ form.errors[`languages.${index}.name`] }}
|
{{ form.errors[`languages.${index}.name`] }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<label
|
<label
|
||||||
:for="`language-level-${index}`"
|
:for="`language-level-${index}`"
|
||||||
@ -319,9 +327,16 @@
|
|||||||
<template #form="{ element, index }">
|
<template #form="{ element, index }">
|
||||||
<div class="gap-4 md:grid md:grid-cols-2 ">
|
<div class="gap-4 md:grid md:grid-cols-2 ">
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
|
<label
|
||||||
|
:for="`technology-${index}-level`"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>
|
||||||
|
Technologia
|
||||||
|
</label>
|
||||||
|
<div class="mt-2">
|
||||||
<Combobox
|
<Combobox
|
||||||
|
:id="`technology-${index}-level`"
|
||||||
v-model="element.name"
|
v-model="element.name"
|
||||||
label="Technologia"
|
|
||||||
:items="technologies"
|
:items="technologies"
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
@ -331,6 +346,7 @@
|
|||||||
{{ form.errors[`technologies.${index}.name`] }}
|
{{ form.errors[`technologies.${index}.name`] }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<label
|
<label
|
||||||
:for="`technology-level-${index}`"
|
:for="`technology-level-${index}`"
|
||||||
@ -379,6 +395,7 @@
|
|||||||
<textarea
|
<textarea
|
||||||
:id="`project-description-${index}`"
|
:id="`project-description-${index}`"
|
||||||
v-model="element.description"
|
v-model="element.description"
|
||||||
|
rows="5"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
class="block w-full rounded-md shadow-sm sm:text-sm"
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.description`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.description`] }"
|
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.description`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.description`] }"
|
||||||
/>
|
/>
|
||||||
@ -398,13 +415,11 @@
|
|||||||
Technologie
|
Technologie
|
||||||
</label>
|
</label>
|
||||||
<div class="mt-1 sm:mt-0">
|
<div class="mt-1 sm:mt-0">
|
||||||
<input
|
<MultipleCombobox
|
||||||
:id="`project-technologies-${index}`"
|
:id="`project-technologies-${index}`"
|
||||||
v-model="element.technologies"
|
v-model="element.technologies"
|
||||||
type="text"
|
:items="technologies"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
/>
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.technologies`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.technologies`] }"
|
|
||||||
>
|
|
||||||
<p
|
<p
|
||||||
v-if="form.errors[`projects.${index}.technologies`]"
|
v-if="form.errors[`projects.${index}.technologies`]"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-red-600"
|
||||||
@ -470,7 +485,7 @@
|
|||||||
<textarea
|
<textarea
|
||||||
:id="`project-tasks-${index}`"
|
:id="`project-tasks-${index}`"
|
||||||
v-model="element.tasks"
|
v-model="element.tasks"
|
||||||
rows="3"
|
rows="5"
|
||||||
class="block w-full rounded-md shadow-sm sm:text-sm"
|
class="block w-full rounded-md shadow-sm sm:text-sm"
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.tasks`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.tasks`] }"
|
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`projects.${index}.tasks`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`projects.${index}.tasks`] }"
|
||||||
/>
|
/>
|
||||||
@ -513,6 +528,7 @@ import { useForm } from '@inertiajs/inertia-vue3'
|
|||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import DynamicSection from '@/Shared/Forms/DynamicSection'
|
import DynamicSection from '@/Shared/Forms/DynamicSection'
|
||||||
import Combobox from '@/Shared/Forms/Combobox'
|
import Combobox from '@/Shared/Forms/Combobox'
|
||||||
|
import MultipleCombobox from '@/Shared/Forms/MultipleCombobox'
|
||||||
import LevelPicker from '@/Shared/Forms/LevelPicker'
|
import LevelPicker from '@/Shared/Forms/LevelPicker'
|
||||||
import useLevels from '@/Composables/useLevels'
|
import useLevels from '@/Composables/useLevels'
|
||||||
|
|
||||||
@ -548,7 +564,7 @@ const form = useForm({
|
|||||||
function addProject() {
|
function addProject() {
|
||||||
form.projects.push({
|
form.projects.push({
|
||||||
description: null,
|
description: null,
|
||||||
technologies: null,
|
technologies: [],
|
||||||
tasks: null,
|
tasks: null,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
|
@ -25,12 +25,6 @@
|
|||||||
>
|
>
|
||||||
Użytkownik
|
Użytkownik
|
||||||
</th>
|
</th>
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
|
||||||
>
|
|
||||||
Opis
|
|
||||||
</th>
|
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
@ -43,6 +37,30 @@
|
|||||||
>
|
>
|
||||||
Data aktualizacji
|
Data aktualizacji
|
||||||
</th>
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Szkoły
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Języki
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Technologie
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Projekty
|
||||||
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
class="py-3 px-4 text-xs font-semibold tracking-wider text-left text-gray-500 uppercase whitespace-nowrap"
|
||||||
@ -76,18 +94,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ resume.name }}
|
<span class="text-sm font-medium text-gray-900 break-all">{{ resume.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
|
||||||
{{ resume.description ?? '-' }}
|
|
||||||
</td>
|
|
||||||
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
{{ resume.createdAt }}
|
{{ resume.createdAt }}
|
||||||
</td>
|
</td>
|
||||||
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
{{ resume.updatedAt }}
|
{{ resume.updatedAt }}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
|
{{ resume.educationCount }}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
|
{{ resume.languageCount }}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
|
{{ resume.technologyCount }}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
|
||||||
|
{{ resume.projectCount }}
|
||||||
|
</td>
|
||||||
<td class="p-4 text-sm text-right text-gray-500 whitespace-nowrap">
|
<td class="p-4 text-sm text-right text-gray-500 whitespace-nowrap">
|
||||||
<Menu
|
<Menu
|
||||||
as="div"
|
as="div"
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
as="div"
|
as="div"
|
||||||
nullable
|
nullable
|
||||||
>
|
>
|
||||||
<ComboboxLabel class="block text-sm font-medium text-gray-700">
|
<div class="relative">
|
||||||
{{ label }}
|
|
||||||
</ComboboxLabel>
|
|
||||||
<div class="relative mt-2">
|
|
||||||
<ComboboxInput
|
<ComboboxInput
|
||||||
|
:id="id"
|
||||||
class="w-full h-12 rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-blumilk-500 focus:outline-none focus:ring-1 focus:ring-blumilk-500 sm:text-sm"
|
class="w-full h-12 rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-blumilk-500 focus:outline-none focus:ring-1 focus:ring-blumilk-500 sm:text-sm"
|
||||||
@change="query = $event.target.value"
|
@change="query = $event.target.value"
|
||||||
/>
|
/>
|
||||||
@ -51,14 +49,13 @@ import {
|
|||||||
Combobox,
|
Combobox,
|
||||||
ComboboxButton,
|
ComboboxButton,
|
||||||
ComboboxInput,
|
ComboboxInput,
|
||||||
ComboboxLabel,
|
|
||||||
ComboboxOption,
|
ComboboxOption,
|
||||||
ComboboxOptions,
|
ComboboxOptions,
|
||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: null,
|
|
||||||
items: Array,
|
items: Array,
|
||||||
|
id: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
</button>
|
</button>
|
||||||
<Disclosure
|
<Disclosure
|
||||||
v-slot="{ open }"
|
v-slot="{ open }"
|
||||||
:default-open="false"
|
|
||||||
as="div"
|
as="div"
|
||||||
class="flex-1 border border-gray-200"
|
class="flex-1 border border-gray-200"
|
||||||
>
|
>
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<Combobox
|
<Combobox
|
||||||
|
v-model="selectedItems"
|
||||||
as="div"
|
as="div"
|
||||||
nullable
|
nullable
|
||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
<ComboboxLabel class="block text-sm font-medium text-gray-700">
|
<div class="flex flex-wrap gap-3">
|
||||||
{{ label }}
|
<button
|
||||||
</ComboboxLabel>
|
v-for="(item, index) in selectedItems"
|
||||||
|
:key="index"
|
||||||
|
type="button"
|
||||||
|
class="py-1 px-2 bg-gray-200 rounded-md"
|
||||||
|
@click="selectedItems.splice(index, 1)"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="relative mt-2">
|
<div class="relative mt-2">
|
||||||
<ComboboxInput
|
<ComboboxInput
|
||||||
as="template"
|
:id="id"
|
||||||
class="w-full h-12 rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-blumilk-500 focus:outline-none focus:ring-1 focus:ring-blumilk-500 sm:text-sm"
|
class="w-full rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-blumilk-500 focus:outline-none focus:ring-1 focus:ring-blumilk-500 sm:text-sm"
|
||||||
:display-value="(item) => item"
|
|
||||||
@change="query = $event.target.value"
|
@change="query = $event.target.value"
|
||||||
>
|
/>
|
||||||
<span>aee</span>
|
|
||||||
</ComboboxInput>
|
|
||||||
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
|
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
|
||||||
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
||||||
</ComboboxButton>
|
</ComboboxButton>
|
||||||
@ -33,7 +39,7 @@
|
|||||||
>
|
>
|
||||||
<li :class="['relative cursor-default select-none py-2 pl-3 pr-9', active ? 'bg-blumilk-600 text-white' : 'text-gray-900']">
|
<li :class="['relative cursor-default select-none py-2 pl-3 pr-9', active ? 'bg-blumilk-600 text-white' : 'text-gray-900']">
|
||||||
<span :class="['block truncate', selected && 'font-semibold']">
|
<span :class="['block truncate', selected && 'font-semibold']">
|
||||||
{{ item.name }}
|
{{ item }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@ -56,21 +62,31 @@ import {
|
|||||||
Combobox,
|
Combobox,
|
||||||
ComboboxButton,
|
ComboboxButton,
|
||||||
ComboboxInput,
|
ComboboxInput,
|
||||||
ComboboxLabel,
|
|
||||||
ComboboxOption,
|
ComboboxOption,
|
||||||
ComboboxOptions,
|
ComboboxOptions,
|
||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: null,
|
|
||||||
items: Array,
|
items: Array,
|
||||||
|
modelValue: Array,
|
||||||
|
id: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
|
|
||||||
|
const selectedItems = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => {
|
||||||
|
query.value = ''
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const filteredItems = computed(() =>
|
const filteredItems = computed(() =>
|
||||||
query.value === ''
|
query.value === ''
|
||||||
? props.items
|
? props.items
|
||||||
: props.items.filter((item) => item.name.toLowerCase().includes(query.value.toLowerCase())),
|
: props.items.filter((item) => item.toLowerCase().includes(query.value.toLowerCase())),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
Loading…
x
Reference in New Issue
Block a user