Merge pull request #23 from AlexisNP/features/add-hidden-event-functionnality
Add hidden events UI
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
@layer base {
|
||||||
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 222.2 84% 4.9%;
|
--foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.dark {
|
.dark {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 210 40% 98%;
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
--ring: 212.7 26.8% 83.9%;
|
--ring: 212.7 26.8% 83.9%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"$schema": "https://shadcn-vue.com/schema.json",
|
"$schema": "https://shadcn-vue.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"typescript": true,
|
"typescript": true,
|
||||||
|
"tsConfigPath": ".nuxt/tsconfig.json",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
"config": "tailwind.config.js",
|
||||||
"css": "assets/main.css",
|
"css": "assets/main.css",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ onMounted(() => {
|
|||||||
class="text-xs px-2 py-1 block w-full text-left rounded-sm focus-visible:bg-red-200"
|
class="text-xs px-2 py-1 block w-full text-left rounded-sm focus-visible:bg-red-200"
|
||||||
:class="
|
:class="
|
||||||
cn({
|
cn({
|
||||||
|
'italic': event.hidden,
|
||||||
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
||||||
'text-white bg-lime-600 hover:bg-lime-700': event.category?.name === 'naissance',
|
'text-white bg-lime-600 hover:bg-lime-700': event.category?.name === 'naissance',
|
||||||
'text-white bg-stone-500 hover:bg-stone-700': event.category?.name === 'mort',
|
'text-white bg-stone-500 hover:bg-stone-700': event.category?.name === 'mort',
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import {
|
|||||||
PhHourglassHigh,
|
PhHourglassHigh,
|
||||||
PhHourglassLow,
|
PhHourglassLow,
|
||||||
PhMapPinArea,
|
PhMapPinArea,
|
||||||
PhDotsThreeOutlineVertical
|
PhDotsThreeOutlineVertical,
|
||||||
|
PhEye
|
||||||
} from '@phosphor-icons/vue'
|
} from '@phosphor-icons/vue'
|
||||||
|
|
||||||
const { defaultDate, getFormattedDateTitle, jumpToDate, getRelativeString } = useCalendar()
|
const { defaultDate, getFormattedDateTitle, jumpToDate, getRelativeString } = useCalendar()
|
||||||
@@ -189,6 +190,19 @@ function deployDeleteModal() {
|
|||||||
</button>
|
</button>
|
||||||
</UiBadge>
|
</UiBadge>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<UiTooltipProvider v-if="event.hidden" :delay-duration="250">
|
||||||
|
<UiTooltip>
|
||||||
|
<UiTooltipTrigger as-child>
|
||||||
|
<UiBadge class="absolute -top-2 right-2 flex gap-1 hover:bg-slate-300 hover:opacity-100">
|
||||||
|
<PhEye size="16" weight="fill" /> Évènement privé
|
||||||
|
</UiBadge>
|
||||||
|
</UiTooltipTrigger>
|
||||||
|
<UiTooltipContent>
|
||||||
|
<p>Cet évènement est uniquement visible pour vous</p>
|
||||||
|
</UiTooltipContent>
|
||||||
|
</UiTooltip>
|
||||||
|
</UiTooltipProvider>
|
||||||
</UiPopoverContent>
|
</UiPopoverContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { RPGDate } from '~/models/Date';
|
import type { RPGDate } from '~/models/Date';
|
||||||
|
import { PopoverAnchor } from 'radix-vue';
|
||||||
import { PhAlarm, PhCircleNotch, PhMapPinArea } from '@phosphor-icons/vue'
|
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea } from '@phosphor-icons/vue'
|
||||||
|
|
||||||
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendarEvents())
|
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendarEvents())
|
||||||
const { resetSkeleton, submitSkeleton, cancelLatestRequest } = useCalendarEvents()
|
const { resetSkeleton, submitSkeleton, cancelLatestRequest } = useCalendarEvents()
|
||||||
@@ -80,9 +80,9 @@ function handleCancel() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UiPopover v-model:open="popoverOpen">
|
<UiPopover v-model:open="popoverOpen">
|
||||||
<UiPopoverTrigger as-child>
|
<PopoverAnchor as-child>
|
||||||
<button :class="btnClass" @click="openEventCreatePopover()" />
|
<button :class="btnClass" @dblclick="openEventCreatePopover()" />
|
||||||
</UiPopoverTrigger>
|
</PopoverAnchor>
|
||||||
<UiPopoverContent
|
<UiPopoverContent
|
||||||
:align="'center'"
|
:align="'center'"
|
||||||
:side="'right'"
|
:side="'right'"
|
||||||
@@ -96,7 +96,7 @@ function handleCancel() {
|
|||||||
@pointer-down-outside="handleClosing"
|
@pointer-down-outside="handleClosing"
|
||||||
>
|
>
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="grid grid-cols-2 gap-y-4">
|
<div class="grid grid-cols-2 gap-y-6">
|
||||||
<div class="col-span-2 pl-8">
|
<div class="col-span-2 pl-8">
|
||||||
<input
|
<input
|
||||||
id="new-event-title"
|
id="new-event-title"
|
||||||
@@ -119,7 +119,7 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-4">
|
||||||
<PhAlarm size="18" weight="fill" />
|
<PhAlarm size="18" weight="fill" />
|
||||||
|
|
||||||
<CalendarInputRPGDate
|
<CalendarInputRPGDate
|
||||||
@@ -140,7 +140,7 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-4">
|
||||||
<PhMapPinArea size="18" weight="fill" />
|
<PhMapPinArea size="18" weight="fill" />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@@ -153,6 +153,25 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-span-2">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<PhEye v-if="!eventSkeleton.hidden" size="18" weight="fill" />
|
||||||
|
<PhEyeClosed v-else size="18" />
|
||||||
|
|
||||||
|
<div class="flex items-center gap-x-2">
|
||||||
|
<UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" />
|
||||||
|
<UiLabel for="new-event-visibility">
|
||||||
|
<template v-if="!eventSkeleton.hidden">
|
||||||
|
Évènement visible
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Évènement caché
|
||||||
|
</template>
|
||||||
|
</UiLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-red-500 pl-8">
|
<div class="text-red-500 pl-8">
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
{{ formErrors.message }}
|
{{ formErrors.message }}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PhAlarm, PhCircleNotch, PhMapPinArea, PhPencilSimpleLine } from '@phosphor-icons/vue'
|
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhPencilSimpleLine } from '@phosphor-icons/vue'
|
||||||
import { VisuallyHidden } from 'radix-vue'
|
import { VisuallyHidden } from 'radix-vue'
|
||||||
|
|
||||||
const { isEditEventModalOpen } = storeToRefs(useCalendarEvents())
|
const { isEditEventModalOpen } = storeToRefs(useCalendarEvents())
|
||||||
@@ -81,9 +81,9 @@ function handleCancel() {
|
|||||||
</VisuallyHidden>
|
</VisuallyHidden>
|
||||||
|
|
||||||
<form @submit.prevent="handleAction">
|
<form @submit.prevent="handleAction">
|
||||||
<div class="grid grid-cols-2 gap-y-4">
|
<div class="grid grid-cols-2 gap-y-6">
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-4">
|
||||||
<PhPencilSimpleLine size="20" weight="fill" />
|
<PhPencilSimpleLine size="20" weight="fill" />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@@ -109,7 +109,7 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-4">
|
||||||
<PhAlarm size="18" weight="fill" />
|
<PhAlarm size="18" weight="fill" />
|
||||||
|
|
||||||
<CalendarInputRPGDate
|
<CalendarInputRPGDate
|
||||||
@@ -130,7 +130,7 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-4">
|
||||||
<PhMapPinArea size="18" weight="fill" />
|
<PhMapPinArea size="18" weight="fill" />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@@ -143,6 +143,25 @@ function handleCancel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-span-2">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<PhEye v-if="!eventSkeleton.hidden" size="18" weight="fill" />
|
||||||
|
<PhEyeClosed v-else size="18" />
|
||||||
|
|
||||||
|
<div class="flex items-center gap-x-2">
|
||||||
|
<UiSwitch id="new-event-visibility" v-model:checked="eventSkeleton.hidden" />
|
||||||
|
<UiLabel for="new-event-visibility">
|
||||||
|
<template v-if="!eventSkeleton.hidden">
|
||||||
|
Évènement visible par tous
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Évènement caché
|
||||||
|
</template>
|
||||||
|
</UiLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-red-500 ml-8">
|
<div class="text-red-500 ml-8">
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
{{ formErrors.message }}
|
{{ formErrors.message }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { RPGDate } from '@/models/Date'
|
|||||||
import type { CalendarEvent } from '@/models/CalendarEvent'
|
import type { CalendarEvent } from '@/models/CalendarEvent'
|
||||||
import { useCalendar } from '@/stores/CalendarStore'
|
import { useCalendar } from '@/stores/CalendarStore'
|
||||||
|
|
||||||
import { PhArrowSquareOut, PhHourglassMedium, PhAlarm, PhMapPinArea } from '@phosphor-icons/vue'
|
import { PhArrowSquareOut, PhHourglassMedium, PhAlarm, PhMapPinArea, PhEye } from '@phosphor-icons/vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
event: CalendarEvent
|
event: CalendarEvent
|
||||||
@@ -27,6 +27,7 @@ const dateDuration: string | null = props.event.endDate
|
|||||||
<button
|
<button
|
||||||
class="relative block w-full text-left py-3 px-4 rounded-sm transition-colors"
|
class="relative block w-full text-left py-3 px-4 rounded-sm transition-colors"
|
||||||
:class="{
|
:class="{
|
||||||
|
'pt-4': event.hidden,
|
||||||
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
||||||
'text-white bg-lime-600 hover:bg-lime-700': event.category?.name === 'naissance',
|
'text-white bg-lime-600 hover:bg-lime-700': event.category?.name === 'naissance',
|
||||||
'text-white bg-stone-500 hover:bg-stone-700': event.category?.name === 'mort',
|
'text-white bg-stone-500 hover:bg-stone-700': event.category?.name === 'mort',
|
||||||
@@ -49,8 +50,8 @@ const dateDuration: string | null = props.event.endDate
|
|||||||
}"
|
}"
|
||||||
@click="$emit('query:date-jump', event.startDate)"
|
@click="$emit('query:date-jump', event.startDate)"
|
||||||
>
|
>
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center mb-1">
|
||||||
<h2 class="font-bold">
|
<h2 class="font-bold text-lg">
|
||||||
{{ event.title }}
|
{{ event.title }}
|
||||||
</h2>
|
</h2>
|
||||||
<div v-if="event.wiki">
|
<div v-if="event.wiki">
|
||||||
@@ -115,5 +116,18 @@ const dateDuration: string | null = props.event.endDate
|
|||||||
{{ event.description }}
|
{{ event.description }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<UiTooltipProvider v-if="event.hidden" :delay-duration="250">
|
||||||
|
<UiTooltip>
|
||||||
|
<UiTooltipTrigger as-child>
|
||||||
|
<UiBadge class="absolute -top-2 left-2 flex gap-1 border-[1px] border-slate-900 hover:bg-slate-300 hover:opacity-100">
|
||||||
|
<PhEye size="16" weight="fill" /> Évènement privé
|
||||||
|
</UiBadge>
|
||||||
|
</UiTooltipTrigger>
|
||||||
|
<UiTooltipContent>
|
||||||
|
<p>Cet évènement est uniquement visible pour vous</p>
|
||||||
|
</UiTooltipContent>
|
||||||
|
</UiTooltip>
|
||||||
|
</UiTooltipProvider>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
27
components/ui/label/Label.vue
Normal file
27
components/ui/label/Label.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type HTMLAttributes, computed } from 'vue'
|
||||||
|
import { Label, type LabelProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<LabelProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Label
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
|
</template>
|
||||||
1
components/ui/label/index.ts
Normal file
1
components/ui/label/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as Label } from './Label.vue'
|
||||||
37
components/ui/switch/Switch.vue
Normal file
37
components/ui/switch/Switch.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type HTMLAttributes, computed } from 'vue'
|
||||||
|
import {
|
||||||
|
SwitchRoot,
|
||||||
|
type SwitchRootEmits,
|
||||||
|
type SwitchRootProps,
|
||||||
|
SwitchThumb,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SwitchRootProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<SwitchRootEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SwitchRoot
|
||||||
|
v-bind="forwarded"
|
||||||
|
:class="cn(
|
||||||
|
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||||
|
props.class,
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<SwitchThumb
|
||||||
|
:class="cn('pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0')"
|
||||||
|
/>
|
||||||
|
</SwitchRoot>
|
||||||
|
</template>
|
||||||
1
components/ui/switch/index.ts
Normal file
1
components/ui/switch/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as Switch } from './Switch.vue'
|
||||||
@@ -24,7 +24,8 @@ export const postEventBodySchema = z.object({
|
|||||||
description: z.string().optional().nullable(),
|
description: z.string().optional().nullable(),
|
||||||
location: z.string().optional().nullable(),
|
location: z.string().optional().nullable(),
|
||||||
startDate: dateSchema.required(),
|
startDate: dateSchema.required(),
|
||||||
endDate: dateSchema.optional().nullable()
|
endDate: dateSchema.optional().nullable(),
|
||||||
|
hidden: z.boolean().optional().nullable()
|
||||||
}),
|
}),
|
||||||
calendarId: z.number({ coerce: true }).int().positive()
|
calendarId: z.number({ coerce: true }).int().positive()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
title: bodyData?.event.title,
|
title: bodyData?.event.title,
|
||||||
description: bodyData.event.description,
|
description: bodyData.event.description,
|
||||||
location: bodyData.event.location,
|
location: bodyData.event.location,
|
||||||
|
hidden: bodyData.event.hidden,
|
||||||
calendar_id: bodyData?.calendarId
|
calendar_id: bodyData?.calendarId
|
||||||
} as never
|
} as never
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
title: bodyData?.event.title,
|
title: bodyData?.event.title,
|
||||||
description: bodyData.event.description,
|
description: bodyData.event.description,
|
||||||
location: bodyData.event.location,
|
location: bodyData.event.location,
|
||||||
|
hidden: bodyData.event.hidden,
|
||||||
calendar_id: bodyData?.calendarId
|
calendar_id: bodyData?.calendarId
|
||||||
} as never
|
} as never
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user