Added category input component

This commit is contained in:
Alexis
2024-06-10 18:58:08 +02:00
parent e9ec2cb3ae
commit 17b9937c6a
11 changed files with 178 additions and 11 deletions

View File

@@ -12,11 +12,12 @@ const route = useRoute()
const worldId = route.params.id
const { setCalendarId, setMonths, setDefaultDate, currentConfig, selectedDate, jumpToDate } = useCalendar()
const { setEvents } = useCalendarEvents()
const { setEvents, setCategories } = useCalendarEvents()
const { setCharacters } = useCharacters()
const { data: calendar, pending: calPending, refresh: calRefresh } = await useLazyFetch(`/api/calendars/query?world_id=${worldId}`)
const { data: characters, pending: charPending, refresh: charRefresh } = await useLazyFetch(`/api/characters/query?world_id=${worldId}`)
const { data: categories, pending: categoryPending, refresh: categoryRefresh } = await useLazyFetch(`/api/calendars/categories/query`)
if (!calendar.value) {
await calRefresh()
@@ -37,6 +38,15 @@ if (!calendar.value) {
setEvents(calendar.value?.data?.events)
}
}
if (!categories.value) {
await categoryRefresh()
} else {
if (categories.value?.data) {
setCategories(categories.value?.data)
}
}
if (!characters.value) {
await charRefresh()
} else {
@@ -61,6 +71,13 @@ watch(calPending, (newStatus) => {
}
}
})
watch(categoryPending, (newStatus) => {
if (!newStatus) {
if (categories.value?.data) {
setCategories(categories.value?.data)
}
}
})
watch(charPending, (newStatus) => {
if (!newStatus) {
if (characters.value?.data) {
@@ -103,7 +120,7 @@ onMounted(() => {
<template>
<div class="h-full">
<template v-if="calPending || charPending">
<template v-if="calPending || charPending || categoryPending">
<div class="h-full grid place-items-center">
Loading notamment
</div>

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { RPGDate } from '~/models/Date';
import { PopoverAnchor } from 'radix-vue';
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea } from '@phosphor-icons/vue'
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhTag } from '@phosphor-icons/vue'
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendarEvents())
const { resetSkeleton, submitSkeleton, cancelLatestRequest } = useCalendarEvents()
@@ -96,7 +96,7 @@ function handleCancel() {
@pointer-down-outside="handleClosing"
>
<form @submit.prevent="handleSubmit">
<div class="grid grid-cols-2 gap-y-6">
<div class="grid grid-cols-2 gap-y-3">
<div class="col-span-2 pl-8">
<input
id="new-event-title"
@@ -140,6 +140,14 @@ function handleCancel() {
</div>
<div class="col-span-2">
<div class="flex items-center gap-4">
<PhTag size="18" weight="fill" />
<CalendarInputEventCategories v-model="eventSkeleton.category" placeholder="Ajouter une catégorie principale" />
</div>
</div>
<div class="col-span-2 mb-4">
<div class="flex items-center gap-4">
<PhMapPinArea size="18" weight="fill" />
@@ -149,7 +157,7 @@ function handleCancel() {
type="text"
name="new-event-location"
placeholder="Ajouter un endroit"
class="w-full -my-1 py-1 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
</div>
</div>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhPencilSimpleLine } from '@phosphor-icons/vue'
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhPencilSimpleLine, PhTag } from '@phosphor-icons/vue'
import { VisuallyHidden } from 'radix-vue'
const { isEditEventModalOpen } = storeToRefs(useCalendarEvents())
@@ -81,7 +81,7 @@ function handleCancel() {
</VisuallyHidden>
<form @submit.prevent="handleAction">
<div class="grid grid-cols-2 gap-y-6">
<div class="grid grid-cols-2 gap-y-3">
<div class="col-span-2">
<div class="flex items-center gap-4">
<PhPencilSimpleLine size="20" weight="fill" />
@@ -130,6 +130,14 @@ function handleCancel() {
</div>
<div class="col-span-2">
<div class="flex items-center gap-4">
<PhTag size="18" weight="fill" />
<CalendarInputEventCategories v-model="eventSkeleton.category" placeholder="Ajouter une catégorie principale" />
</div>
</div>
<div class="col-span-2 mb-4">
<div class="flex items-center gap-4">
<PhMapPinArea size="18" weight="fill" />
@@ -139,7 +147,7 @@ function handleCancel() {
type="text"
name="new-event-location"
placeholder="Ajouter un endroit"
class="w-full -my-1 py-1 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
</div>
</div>

View File

@@ -0,0 +1,72 @@
<script lang="ts" setup>
import { PhCaretDown } from '@phosphor-icons/vue';
import type { Category } from '~/models/Category';
const props = defineProps<{
placeholder?: string
multiple?: boolean
}>()
const model = defineModel<Category[] | Category>()
const { categories: availableCategories } = useCalendarEvents()
const isPopoverOpen = ref<boolean>(false)
function handleCatSelect() {
if (!props.multiple) {
isPopoverOpen.value = false
}
}
const computedTextValue = computed(() => {
if (model.value) {
if ("name" in model.value) {
return model.value.name
} else {
return model.value[0].name
}
} else {
return props.placeholder
}
})
</script>
<template>
<UiPopover v-model:open="isPopoverOpen">
<UiPopoverTrigger as-child>
<UiButton
variant="outline"
role="combobox"
class="w-fit max-w-full justify-between capitalize"
>
{{ computedTextValue }}
<PhCaretDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</UiButton>
</UiPopoverTrigger>
<UiPopoverContent
:align="'start'"
class="w-fit p-0"
>
<UiCommand v-model="model" :multiple>
<UiCommandInput placeholder="Rechercher les catégories" />
<UiCommandEmpty>Aucune catégorie trouvée.</UiCommandEmpty>
<UiCommandList>
<UiCommandGroup>
<UiCommandItem
v-for="category in availableCategories"
:key="category.id"
:value="category"
@select="handleCatSelect"
>
<span class="capitalize">
{{ category.name }}
</span>
</UiCommandItem>
</UiCommandGroup>
</UiCommandList>
</UiCommand>
</UiPopoverContent>
</UiPopover>
</template>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'
import { Check } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<CheckboxRootEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<CheckboxRoot
v-bind="forwarded"
:class="
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
props.class)"
>
<CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
<slot>
<Check class="h-4 w-4" />
</slot>
</CheckboxIndicator>
</CheckboxRoot>
</template>

View File

@@ -0,0 +1 @@
export { default as Checkbox } from './Checkbox.vue'

View File

@@ -25,7 +25,10 @@ export const postEventBodySchema = z.object({
location: z.string().optional().nullable(),
startDate: dateSchema.required(),
endDate: dateSchema.optional().nullable(),
hidden: z.boolean().optional().nullable()
hidden: z.boolean().optional().nullable(),
category: z.object({
id: z.number().int()
}),
}),
calendarId: z.number({ coerce: true }).int().positive()
})

View File

@@ -0,0 +1,15 @@
import { serverSupabaseClient } from "#supabase/server";
import type { Category } from "~/models/Category";
export default defineEventHandler(async (event) => {
const client = await serverSupabaseClient(event)
const output = client
.from('calendar_event_categories')
.select(`
id,
name
`)
return output.returns<Category[]>()
})

View File

@@ -22,7 +22,6 @@ export default defineEventHandler(async (event) => {
}
if (bodyError) {
console.log(bodyData)
throw createError({
cause: 'Utilisateur',
fatal: false,
@@ -42,6 +41,7 @@ export default defineEventHandler(async (event) => {
description: bodyData.event.description,
location: bodyData.event.location,
hidden: bodyData.event.hidden,
category: bodyData.event.category.id,
calendar_id: bodyData?.calendarId
} as never
)

View File

@@ -26,6 +26,7 @@ export default defineEventHandler(async (event) => {
description: bodyData.event.description,
location: bodyData.event.location,
hidden: bodyData.event.hidden,
category: bodyData.event.category.id,
calendar_id: bodyData?.calendarId
} as never
)

View File

@@ -1,7 +1,8 @@
import type { RPGDate } from '@/models/Date'
import type { CalendarEvent } from '@/models/CalendarEvent'
import type { RPGDate } from '@/models/Date'
import { defineStore } from 'pinia'
import { ref, watch, type Ref } from 'vue'
import type { Category } from '~/models/Category'
import { useCalendar } from './CalendarStore'
export const useCalendarEvents = defineStore('calendar-events', () => {
@@ -14,6 +15,12 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
baseEvents.value = data
}
const categories = ref<Category[]>([])
function setCategories(data: Category[]) {
categories.value = data
}
// Order base events by dates
const allEvents = computed(() => baseEvents.value.sort((a, b) => {
return compareDates(a.startDate, b.startDate, 'desc')
@@ -269,6 +276,8 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
return {
allEvents,
setEvents,
categories,
setCategories,
currentEvents,
getRelativeEventFromDate,
getRelativeEventFromEvent,