Merge branch 'features/nuxt-supabase' into features/nuxt
This commit is contained in:
@@ -2,18 +2,62 @@
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { computed, type Component, type ComputedRef } from 'vue'
|
||||
|
||||
import CalendarMenu from './CalendarMenu.vue'
|
||||
|
||||
import MonthlyLayout from './state/monthly/Layout.vue'
|
||||
import CenturyLayout from './state/centennially/Layout.vue'
|
||||
import DecadeLayout from './state/decennially/Layout.vue'
|
||||
import YearLayout from './state/yearly/Layout.vue'
|
||||
|
||||
const { currentConfig } = useCalendar()
|
||||
const { selectedDate, jumpToDate } = useCalendar()
|
||||
const route = useRoute()
|
||||
const worldId = route.params.id
|
||||
|
||||
const { fetchEvents } = useCalendarEvents()
|
||||
fetchEvents()
|
||||
const { setMonths, setDefaultDate, currentConfig, selectedDate, jumpToDate } = useCalendar()
|
||||
const { setEvents } = 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}`)
|
||||
|
||||
if (!calendar.value) {
|
||||
await calRefresh()
|
||||
} else {
|
||||
if (calendar.value?.data?.months) {
|
||||
setMonths(calendar.value?.data?.months)
|
||||
}
|
||||
if (calendar.value?.data?.today) {
|
||||
setDefaultDate(calendar.value?.data?.today)
|
||||
}
|
||||
if (calendar.value?.data?.events) {
|
||||
setEvents(calendar.value?.data?.events)
|
||||
}
|
||||
}
|
||||
if (!characters.value) {
|
||||
await charRefresh()
|
||||
} else {
|
||||
if (characters.value?.data) {
|
||||
setCharacters(characters.value?.data)
|
||||
}
|
||||
}
|
||||
|
||||
watch(calPending, (newStatus) => {
|
||||
if (!newStatus) {
|
||||
if (calendar.value?.data?.months) {
|
||||
setMonths(calendar.value?.data?.months)
|
||||
}
|
||||
if (calendar.value?.data?.today) {
|
||||
setDefaultDate(calendar.value?.data?.today)
|
||||
}
|
||||
if (calendar.value?.data?.events) {
|
||||
setEvents(calendar.value?.data?.events)
|
||||
}
|
||||
}
|
||||
})
|
||||
watch(charPending, (newStatus) => {
|
||||
if (!newStatus) {
|
||||
if (characters.value?.data) {
|
||||
setCharacters(characters.value?.data)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
@@ -38,11 +82,20 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full grid grid-rows-[auto,1fr]">
|
||||
<CalendarMenu />
|
||||
<div class="h-full">
|
||||
<template v-if="calPending || charPending">
|
||||
<div class="h-full grid place-items-center">
|
||||
Loading notamment
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="h-full grid grid-rows-[auto,1fr]">
|
||||
<CalendarMenu />
|
||||
|
||||
<KeepAlive>
|
||||
<component :is="currentViewComponent" />
|
||||
</KeepAlive>
|
||||
<KeepAlive>
|
||||
<component :is="currentViewComponent"/>
|
||||
</KeepAlive>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { getRelativeString } from '@/models/Date'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { PhMapPin } from '@phosphor-icons/vue'
|
||||
|
||||
const { defaultDate, getFormattedDateTitle } = useCalendar()
|
||||
const { defaultDate, getFormattedDateTitle, getRelativeString } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
|
||||
const mainDateTitle = computed(() => getFormattedDateTitle(selectedDate.value, true))
|
||||
// const mainDateTitle = computed(() => convertDateToDays(selectedDate.value))
|
||||
|
||||
const dateDifference = computed(() => getRelativeString(defaultDate, selectedDate.value))
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import CalendarEventDetails from './CalendarEventDetails.vue'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '~/models/CalendarEvent'
|
||||
|
||||
const props = defineProps<{
|
||||
event: CalendarEvent
|
||||
tileDate: LeimDate
|
||||
tileDate: RPGDate
|
||||
}>()
|
||||
|
||||
const { areDatesIdentical } = useCalendar()
|
||||
|
||||
const spansMultipleDays = Boolean(props.event.startDate && props.event.endDate)
|
||||
|
||||
const isStartEvent = spansMultipleDays && areDatesIdentical(props.tileDate, props.event.startDate)
|
||||
@@ -32,24 +32,24 @@ function handleClosePopover() {
|
||||
:class="
|
||||
cn({
|
||||
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
||||
'text-white bg-lime-600 hover:bg-lime-700': event.category === 'naissance',
|
||||
'text-white bg-stone-500 hover:bg-stone-700': event.category === 'mort',
|
||||
'text-white bg-orange-600 hover:bg-orange-700': event.category === 'catastrophe',
|
||||
'text-white bg-pink-600 hover:bg-pink-700': event.category === 'catastrophe naturelle',
|
||||
'text-white bg-sky-600 hover:bg-sky-700': event.category === 'législation',
|
||||
'text-white bg-purple-600 hover:bg-purple-700': event.category === 'religion',
|
||||
'text-white bg-emerald-600 hover:bg-emerald-700': event.category === 'joueurs',
|
||||
'text-slate-900 bg-amber-300 hover:bg-amber-400': event.category === 'inauguration',
|
||||
'text-slate-900 bg-emerald-200 hover:bg-emerald-300': event.category === 'invention',
|
||||
'text-slate-900 bg-cyan-300 hover:bg-cyan-400': event.category === 'science',
|
||||
'text-slate-900 bg-white hover:bg-yellow-200': event.category === 'bénédiction',
|
||||
'text-slate-900 bg-purple-200 hover:bg-purple-300': event.category === 'découverte',
|
||||
'text-slate-900 bg-indigo-200 hover:bg-indigo-300': event.category === 'exploration',
|
||||
'text-white bg-amber-600 hover:bg-amber-700': event.category === 'construction',
|
||||
'text-slate-900 bg-violet-200 hover:bg-violet-300': event.category === 'arcanologie',
|
||||
'text-white bg-rose-600 hover:bg-rose-700': event.category === 'criminalité',
|
||||
'text-white bg-stone-600 hover:bg-stone-700': event.category === 'scandale',
|
||||
'text-slate-900 bg-yellow-500 hover:bg-yellow-600': event.category === 'commerce',
|
||||
'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-orange-600 hover:bg-orange-700': event.category?.name === 'catastrophe',
|
||||
'text-white bg-pink-600 hover:bg-pink-700': event.category?.name === 'catastrophe naturelle',
|
||||
'text-white bg-sky-600 hover:bg-sky-700': event.category?.name === 'législation',
|
||||
'text-white bg-purple-600 hover:bg-purple-700': event.category?.name === 'religion',
|
||||
'text-white bg-emerald-600 hover:bg-emerald-700': event.category?.name === 'joueurs',
|
||||
'text-slate-900 bg-amber-300 hover:bg-amber-400': event.category?.name === 'inauguration',
|
||||
'text-slate-900 bg-emerald-200 hover:bg-emerald-300': event.category?.name === 'invention',
|
||||
'text-slate-900 bg-cyan-300 hover:bg-cyan-400': event.category?.name === 'science',
|
||||
'text-slate-900 bg-white hover:bg-yellow-200': event.category?.name === 'bénédiction',
|
||||
'text-slate-900 bg-purple-200 hover:bg-purple-300': event.category?.name === 'découverte',
|
||||
'text-slate-900 bg-indigo-200 hover:bg-indigo-300': event.category?.name === 'exploration',
|
||||
'text-white bg-amber-600 hover:bg-amber-700': event.category?.name === 'construction',
|
||||
'text-slate-900 bg-violet-200 hover:bg-violet-300': event.category?.name === 'arcanologie',
|
||||
'text-white bg-rose-600 hover:bg-rose-700': event.category?.name === 'criminalité',
|
||||
'text-white bg-stone-600 hover:bg-stone-700': event.category?.name === 'scandale',
|
||||
'text-slate-900 bg-yellow-500 hover:bg-yellow-600': event.category?.name === 'commerce',
|
||||
'rounded-r-none': isStartEvent,
|
||||
'rounded-l-none': isEndEvent
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { getRelativeString, type LeimDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/CalendarEvent'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
|
||||
@@ -27,12 +27,14 @@ const emit = defineEmits<{
|
||||
(e: 'query:close-popover'): void
|
||||
}>()
|
||||
|
||||
const { getRelativeString } = useCalendar()
|
||||
|
||||
const dateDifference: string = getRelativeString(defaultDate, props.event.startDate)
|
||||
const dateDuration: string | null = props.event.endDate
|
||||
? getRelativeString(props.event.startDate, props.event.endDate, 'compact')
|
||||
: null
|
||||
|
||||
function handleJumpToDate(date: LeimDate) {
|
||||
function handleJumpToDate(date: RPGDate) {
|
||||
jumpToDate(date)
|
||||
emit('query:close-popover')
|
||||
}
|
||||
@@ -58,24 +60,24 @@ function handleGotoRelativeEvent(position: 'next' | 'prev' = 'next') {
|
||||
:hide-when-detached="true"
|
||||
:class="{
|
||||
'border-slate-800': !event.category,
|
||||
'border-lime-800': event.category === 'naissance',
|
||||
'border-stone-600': event.category === 'mort',
|
||||
'border-orange-800': event.category === 'catastrophe',
|
||||
'border-pink-800': event.category === 'catastrophe naturelle',
|
||||
'border-sky-800': event.category === 'législation',
|
||||
'border-purple-800': event.category === 'religion',
|
||||
'border-emerald-800': event.category === 'joueurs',
|
||||
'border-amber-800': event.category === 'inauguration',
|
||||
'border-green-800': event.category === 'invention',
|
||||
'border-cyan-800': event.category === 'science',
|
||||
'border-slate-600': event.category === 'bénédiction',
|
||||
'border-purple-700': event.category === 'découverte',
|
||||
'border-indigo-700': event.category === 'exploration',
|
||||
'border-amber-700': event.category === 'construction',
|
||||
'border-violet-700': event.category === 'arcanologie',
|
||||
'border-rose-800': event.category === 'criminalité',
|
||||
'border-stone-700': event.category === 'scandale',
|
||||
'border-yellow-600': event.category === 'commerce'
|
||||
'border-lime-800': event.category?.name === 'naissance',
|
||||
'border-stone-600': event.category?.name === 'mort',
|
||||
'border-orange-800': event.category?.name === 'catastrophe',
|
||||
'border-pink-800': event.category?.name === 'catastrophe naturelle',
|
||||
'border-sky-800': event.category?.name === 'législation',
|
||||
'border-purple-800': event.category?.name === 'religion',
|
||||
'border-emerald-800': event.category?.name === 'joueurs',
|
||||
'border-amber-800': event.category?.name === 'inauguration',
|
||||
'border-green-800': event.category?.name === 'invention',
|
||||
'border-cyan-800': event.category?.name === 'science',
|
||||
'border-slate-600': event.category?.name === 'bénédiction',
|
||||
'border-purple-700': event.category?.name === 'découverte',
|
||||
'border-indigo-700': event.category?.name === 'exploration',
|
||||
'border-amber-700': event.category?.name === 'construction',
|
||||
'border-violet-700': event.category?.name === 'arcanologie',
|
||||
'border-rose-800': event.category?.name === 'criminalité',
|
||||
'border-stone-700': event.category?.name === 'scandale',
|
||||
'border-yellow-600': event.category?.name === 'commerce'
|
||||
}"
|
||||
>
|
||||
<div class="grid gap-1">
|
||||
@@ -110,13 +112,13 @@ function handleGotoRelativeEvent(position: 'next' | 'prev' = 'next') {
|
||||
<ul class="flex gap-1">
|
||||
<li v-if="event.category">
|
||||
<UiBadge class="mix-blend-luminosity font-bold bg-gray-600" variant="secondary">
|
||||
{{ event.category }}
|
||||
{{ event.category?.name }}
|
||||
</UiBadge>
|
||||
</li>
|
||||
|
||||
<li v-for="cat in event.secondaryCategories" :key="cat">
|
||||
<li v-for="cat in event.secondaryCategories" :key="cat.id">
|
||||
<UiBadge class="mix-blend-luminosity bg-gray-600" variant="secondary">
|
||||
{{ cat }}
|
||||
{{ cat.name }}
|
||||
</UiBadge>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import { PhMagnifyingGlass } from '@phosphor-icons/vue'
|
||||
import CalendarMenuNav from './CalendarMenuNav.vue'
|
||||
import CalendarMenuSubnav from './CalendarMenuSubnav.vue'
|
||||
import CalendarMenuToday from './CalendarMenuToday.vue'
|
||||
import CalendarSwitch from './CalendarSwitch.vue'
|
||||
import CalendarCurrentDate from './CalendarCurrentDate.vue'
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { daysPerMonth, monthsPerYear, type LeimDate } from '@/models/Date'
|
||||
import { type RPGDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
|
||||
@@ -9,7 +9,12 @@ const { currentDate, currentConfig, jumpToDate } = useCalendar()
|
||||
const { getRelativeEventFromDate } = useCalendarEvents()
|
||||
|
||||
function handleGotoPreviousEventPage(position: 'next' | 'prev' = 'next') {
|
||||
let fromDate: LeimDate
|
||||
let fromDate: RPGDate
|
||||
|
||||
// To modify, obviously
|
||||
const daysPerMonth = 32
|
||||
const monthsPerYear = 10
|
||||
|
||||
const toDay = position === 'next' ? daysPerMonth : 1
|
||||
const toMonth = position === 'next' ? monthsPerYear : 0
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@ import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { areDatesIdentical } from '@/models/Date'
|
||||
|
||||
const { defaultDate } = useCalendar()
|
||||
const { defaultDate, areDatesIdentical } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
const { jumpToDefaultDate, getFormattedDateTitle } = useCalendar()
|
||||
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
characterCategories,
|
||||
isCharacter,
|
||||
type Character,
|
||||
type CharacterCategory
|
||||
} from '@/models/Characters'
|
||||
import type { LeimDateOrder } from '@/models/Date'
|
||||
import type { RPGDateOrder } from '@/models/Date'
|
||||
import {
|
||||
calendarEventCategories,
|
||||
isCalendarEvent,
|
||||
type CalendarEvent,
|
||||
type CalendarEventCategory
|
||||
} from '@/models/Events'
|
||||
} from '~/models/CalendarEvent'
|
||||
import { useCharacters } from '@/stores/CharacterStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { capitalize } from '@/utils/Strings'
|
||||
@@ -29,6 +25,7 @@ import {
|
||||
} from 'radix-vue'
|
||||
|
||||
import SearchList from './lists/SearchList.vue'
|
||||
import type { Category } from '~/models/Category'
|
||||
|
||||
const { characters } = storeToRefs(useCharacters())
|
||||
const { allEvents } = storeToRefs(useCalendarEvents())
|
||||
@@ -41,7 +38,7 @@ const searchQuery = ref<string>('')
|
||||
const selectedEntity = useStorage('se', 'events' as SearchMode)
|
||||
|
||||
// Order
|
||||
const selectedOrder = ref<LeimDateOrder>('asc')
|
||||
const selectedOrder = ref<RPGDateOrder>('asc')
|
||||
function setOrderAsc() {
|
||||
selectedOrder.value = 'asc'
|
||||
resetPage()
|
||||
@@ -105,7 +102,7 @@ const searchResults = computed<(Character | CalendarEvent)[]>(() => {
|
||||
|
||||
// Handle categories logic
|
||||
let hitCategories: boolean = false
|
||||
const allCategories: CalendarEventCategory[] = []
|
||||
const allCategories: Category[] = []
|
||||
|
||||
if (item.category) {
|
||||
allCategories.push(item.category)
|
||||
@@ -116,7 +113,7 @@ const searchResults = computed<(Character | CalendarEvent)[]>(() => {
|
||||
}
|
||||
|
||||
hitCategories = selectedCategories.value.every((selectedCat) => {
|
||||
return allCategories.includes(selectedCat as CalendarEventCategory)
|
||||
return allCategories.includes(selectedCat as Category)
|
||||
})
|
||||
|
||||
return (hitTitle || hitDesc) && hitCategories
|
||||
@@ -139,7 +136,7 @@ const searchResults = computed<(Character | CalendarEvent)[]>(() => {
|
||||
|
||||
// Handle categories logic
|
||||
let hitCategories: boolean = false
|
||||
const allCategories: CharacterCategory[] = []
|
||||
const allCategories: Category[] = []
|
||||
|
||||
if (item.category) {
|
||||
allCategories.push(item.category)
|
||||
@@ -150,7 +147,7 @@ const searchResults = computed<(Character | CalendarEvent)[]>(() => {
|
||||
}
|
||||
|
||||
hitCategories = selectedCategories.value.every((selectedCat) => {
|
||||
return allCategories.includes(selectedCat as CharacterCategory)
|
||||
return allCategories.includes(selectedCat as Category)
|
||||
})
|
||||
|
||||
return hitTitle && hitCategories
|
||||
@@ -207,14 +204,10 @@ watch([currentPage, selectedEntity], () => {
|
||||
|
||||
// Compute categories based on current selectedEntity
|
||||
const currentCategories = computed(() => {
|
||||
if (selectedEntity.value === 'characters') {
|
||||
return [...characterCategories]
|
||||
} else {
|
||||
return [...calendarEventCategories]
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const selectedCategories = ref<(CharacterCategory | CalendarEventCategory)[]>([])
|
||||
const selectedCategories = ref<(Category)[]>([])
|
||||
const categoryFilterOpened = ref<boolean>(false)
|
||||
const searchCategory = ref<string>('')
|
||||
|
||||
@@ -227,7 +220,7 @@ const filteredCategories = computed(() =>
|
||||
*
|
||||
* @param e Radix Change Event
|
||||
*/
|
||||
function handleCategorySelect(e: (CharacterCategory | CalendarEventCategory)) {
|
||||
function handleCategorySelect(e: (Category)) {
|
||||
if (typeof e === 'string') {
|
||||
searchCategory.value = ''
|
||||
selectedCategories.value.push(e)
|
||||
@@ -290,9 +283,9 @@ function handleCategorySelect(e: (CharacterCategory | CalendarEventCategory)) {
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<UiTagsInput class="px-0 gap-0 w-72" :model-value="selectedCategories">
|
||||
<UiTagsInput class="px-0 gap-0 w-72">
|
||||
<div class="flex gap-2 flex-wrap items-center px-3">
|
||||
<UiTagsInputItem v-for="item in selectedCategories" :key="item" :value="item">
|
||||
<UiTagsInputItem v-for="item in selectedCategories" :key="item.id" :value="item.name">
|
||||
<UiTagsInputItemText class="capitalize" />
|
||||
<UiTagsInputItemDelete />
|
||||
</UiTagsInputItem>
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Character } from '@/models/Characters'
|
||||
import type { LeimDate } from '@/models/Date'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { PhArrowSquareOut, PhPlant, PhSkull } from '@phosphor-icons/vue'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
character: Character
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'query:date-jump', payload: LeimDate): void
|
||||
(e: 'query:date-jump', payload: RPGDate): void
|
||||
}>()
|
||||
|
||||
const { getFormattedDateTitle } = useCalendar()
|
||||
|
||||
console.log(props.character.birth)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { getRelativeString, type LeimDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/CalendarEvent'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { PhArrowSquareOut, PhHourglassMedium, PhAlarm } from '@phosphor-icons/vue'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -12,9 +10,11 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'query:date-jump', payload: LeimDate): void
|
||||
(e: 'query:date-jump', payload: RPGDate): void
|
||||
}>()
|
||||
|
||||
const { getRelativeString } = useCalendar()
|
||||
|
||||
const { defaultDate, getFormattedDateTitle } = useCalendar()
|
||||
|
||||
const dateDifference: string = getRelativeString(defaultDate, props.event.startDate)
|
||||
@@ -28,24 +28,24 @@ const dateDuration: string | null = props.event.endDate
|
||||
class="relative block w-full text-left py-3 px-4 rounded-sm transition-colors"
|
||||
:class="{
|
||||
'text-white bg-slate-600 hover:bg-slate-700': !event.category,
|
||||
'text-white bg-lime-600 hover:bg-lime-700': event.category === 'naissance',
|
||||
'text-white bg-stone-500 hover:bg-stone-700': event.category === 'mort',
|
||||
'text-white bg-orange-600 hover:bg-orange-700': event.category === 'catastrophe',
|
||||
'text-white bg-pink-600 hover:bg-pink-700': event.category === 'catastrophe naturelle',
|
||||
'text-white bg-sky-600 hover:bg-sky-700': event.category === 'législation',
|
||||
'text-white bg-purple-600 hover:bg-purple-700': event.category === 'religion',
|
||||
'text-white bg-emerald-600 hover:bg-emerald-700': event.category === 'joueurs',
|
||||
'text-slate-900 bg-amber-300 hover:bg-amber-400': event.category === 'inauguration',
|
||||
'text-slate-900 bg-emerald-200 hover:bg-emerald-300': event.category === 'invention',
|
||||
'text-slate-900 bg-cyan-300 hover:bg-cyan-400': event.category === 'science',
|
||||
'text-slate-900 bg-white hover:bg-yellow-200': event.category === 'bénédiction',
|
||||
'text-slate-900 bg-purple-200 hover:bg-purple-300': event.category === 'découverte',
|
||||
'text-slate-900 bg-indigo-200 hover:bg-indigo-300': event.category === 'exploration',
|
||||
'text-white bg-amber-600 hover:bg-amber-700': event.category === 'construction',
|
||||
'text-slate-900 bg-violet-200 hover:bg-violet-300': event.category === 'arcanologie',
|
||||
'text-white bg-rose-600 hover:bg-rose-700': event.category === 'criminalité',
|
||||
'text-white bg-stone-600 hover:bg-stone-700': event.category === 'scandale',
|
||||
'text-slate-900 bg-yellow-500 hover:bg-yellow-600': event.category === 'commerce'
|
||||
'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-orange-600 hover:bg-orange-700': event.category?.name === 'catastrophe',
|
||||
'text-white bg-pink-600 hover:bg-pink-700': event.category?.name === 'catastrophe naturelle',
|
||||
'text-white bg-sky-600 hover:bg-sky-700': event.category?.name === 'législation',
|
||||
'text-white bg-purple-600 hover:bg-purple-700': event.category?.name === 'religion',
|
||||
'text-white bg-emerald-600 hover:bg-emerald-700': event.category?.name === 'joueurs',
|
||||
'text-slate-900 bg-amber-300 hover:bg-amber-400': event.category?.name === 'inauguration',
|
||||
'text-slate-900 bg-emerald-200 hover:bg-emerald-300': event.category?.name === 'invention',
|
||||
'text-slate-900 bg-cyan-300 hover:bg-cyan-400': event.category?.name === 'science',
|
||||
'text-slate-900 bg-white hover:bg-yellow-200': event.category?.name === 'bénédiction',
|
||||
'text-slate-900 bg-purple-200 hover:bg-purple-300': event.category?.name === 'découverte',
|
||||
'text-slate-900 bg-indigo-200 hover:bg-indigo-300': event.category?.name === 'exploration',
|
||||
'text-white bg-amber-600 hover:bg-amber-700': event.category?.name === 'construction',
|
||||
'text-slate-900 bg-violet-200 hover:bg-violet-300': event.category?.name === 'arcanologie',
|
||||
'text-white bg-rose-600 hover:bg-rose-700': event.category?.name === 'criminalité',
|
||||
'text-white bg-stone-600 hover:bg-stone-700': event.category?.name === 'scandale',
|
||||
'text-slate-900 bg-yellow-500 hover:bg-yellow-600': event.category?.name === 'commerce'
|
||||
}"
|
||||
@click="$emit('query:date-jump', event.startDate)"
|
||||
>
|
||||
@@ -54,12 +54,12 @@ const dateDuration: string | null = props.event.endDate
|
||||
{{ event.title }}
|
||||
</h2>
|
||||
<div v-if="event.wiki">
|
||||
<Button variant="link" size="xs" as-child class="text-inherit">
|
||||
<UiButton variant="link" size="xs" as-child class="text-inherit">
|
||||
<a :href="event.wiki" target="_blank">
|
||||
Wiki
|
||||
<PhArrowSquareOut size="16" weight="fill" />
|
||||
</a>
|
||||
</Button>
|
||||
</UiButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,15 +91,15 @@ const dateDuration: string | null = props.event.endDate
|
||||
<div v-if="event.category || event.secondaryCategories" class="absolute top-3 right-4">
|
||||
<ul class="flex gap-1">
|
||||
<li v-if="event.category">
|
||||
<Badge class="mix-blend-luminosity font-bold bg-gray-600" variant="secondary">
|
||||
{{ event.category }}
|
||||
</Badge>
|
||||
<UiBadge class="mix-blend-luminosity font-bold bg-gray-600" variant="secondary">
|
||||
{{ event.category?.name }}
|
||||
</UiBadge>
|
||||
</li>
|
||||
|
||||
<li v-for="cat in event.secondaryCategories" :key="cat">
|
||||
<Badge class="mix-blend-luminosity bg-gray-600" variant="secondary">
|
||||
{{ cat }}
|
||||
</Badge>
|
||||
<li v-for="cat in event.secondaryCategories" :key="cat.id">
|
||||
<UiBadge class="mix-blend-luminosity bg-gray-600" variant="secondary">
|
||||
{{ cat.name }}
|
||||
</UiBadge>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { isCharacter, type Character } from '@/models/Characters'
|
||||
import { compareDates, type LeimDate, type LeimDateOrder } from '@/models/Date'
|
||||
import { isCalendarEvent, type CalendarEvent } from '@/models/Events'
|
||||
import type { RPGDate, RPGDateOrder } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { computed } from 'vue'
|
||||
import { isCalendarEvent, type CalendarEvent } from '~/models/CalendarEvent'
|
||||
import type { SearchMode } from '../../SearchMode'
|
||||
|
||||
import CharacterCallout from './CharacterCallout.vue'
|
||||
@@ -12,7 +12,7 @@ import EventCallout from './EventCallout.vue'
|
||||
const props = defineProps<{
|
||||
results: (Character | CalendarEvent)[]
|
||||
currentEntity: SearchMode
|
||||
order: LeimDateOrder
|
||||
order: RPGDateOrder
|
||||
startAt: number
|
||||
endAt: number
|
||||
limit?: number
|
||||
@@ -20,9 +20,9 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits(['jumpedToDate'])
|
||||
|
||||
const { jumpToDate } = useCalendar()
|
||||
const { jumpToDate, compareDates } = useCalendar()
|
||||
|
||||
function handleJumpToDate(date?: LeimDate) {
|
||||
function handleJumpToDate(date?: RPGDate) {
|
||||
if (!date) return
|
||||
|
||||
jumpToDate(date)
|
||||
@@ -32,8 +32,8 @@ function handleJumpToDate(date?: LeimDate) {
|
||||
// Initial sorting of the results
|
||||
const sortedResults = computed(() => {
|
||||
return [...props.results].sort((a, b) => {
|
||||
let firstDate: LeimDate
|
||||
let secondDate: LeimDate
|
||||
let firstDate: RPGDate
|
||||
let secondDate: RPGDate
|
||||
|
||||
if (isCalendarEvent(a)) {
|
||||
firstDate = a.startDate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { useElementBounding } from '@vueuse/core'
|
||||
@@ -7,17 +7,17 @@ import { storeToRefs } from 'pinia'
|
||||
import { computed, ref, type ComputedRef } from 'vue'
|
||||
|
||||
import CalendarEventButton from '../../CalendarEvent.vue'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import type { CalendarEvent } from '~/models/CalendarEvent'
|
||||
|
||||
const props = defineProps<{
|
||||
date: LeimDate
|
||||
date: RPGDate
|
||||
faded?: boolean
|
||||
}>()
|
||||
|
||||
const calendarTile = ref()
|
||||
const calendarEventsList = ref()
|
||||
|
||||
const { defaultDate, selectDate } = useCalendar()
|
||||
const { defaultDate, selectDate, areDatesIdentical } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
const { currentEvents } = storeToRefs(useCalendarEvents())
|
||||
|
||||
|
||||
@@ -1,38 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LeimDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
|
||||
import DayTile from './DayTile.vue'
|
||||
|
||||
const { staticConfig, currentDate, decrementMonth, incrementMonth } = useCalendar()
|
||||
|
||||
const daysPerMonth = staticConfig.daysPerMonth
|
||||
|
||||
function getNextMonthDate(day: number): LeimDate {
|
||||
let nextDay = day
|
||||
let nextMonth = currentDate.currentMonth + 1
|
||||
let nextYear = currentDate.currentYear
|
||||
let nextPeriod = currentDate.currentPeriod
|
||||
|
||||
// If the new value would exceed the max number of month per year
|
||||
if (nextMonth >= staticConfig.monthsPerYear) {
|
||||
nextMonth = 0
|
||||
// Increment the year
|
||||
nextYear++
|
||||
}
|
||||
|
||||
if (nextYear >= 0) {
|
||||
nextPeriod = 'nante'
|
||||
}
|
||||
|
||||
return {
|
||||
day: nextDay,
|
||||
month: nextMonth,
|
||||
year: nextYear,
|
||||
period: nextPeriod
|
||||
}
|
||||
}
|
||||
const { currentDate, decrementMonth, incrementMonth } = useCalendar()
|
||||
const { currentMonthData } = storeToRefs(useCalendar())
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
const isMovingUp = e.deltaY < 0
|
||||
@@ -54,8 +25,8 @@ const moveCalendarRight = useThrottleFn(() => {
|
||||
|
||||
<template>
|
||||
<div class="grid grid-cols-10" @wheel="handleWheel">
|
||||
<DayTile
|
||||
v-for="day in daysPerMonth"
|
||||
<CalendarStateMonthlyDayTile
|
||||
v-for="day in currentMonthData.days"
|
||||
:key="day"
|
||||
:date="{
|
||||
day: day,
|
||||
@@ -63,11 +34,5 @@ const moveCalendarRight = useThrottleFn(() => {
|
||||
year: currentDate.currentYear
|
||||
}"
|
||||
/>
|
||||
<DayTile
|
||||
v-for="nextMonthDay in 8"
|
||||
:key="nextMonthDay"
|
||||
faded
|
||||
:date="getNextMonthDate(nextMonthDay)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
@@ -14,7 +14,9 @@ const props = defineProps<{
|
||||
dayNumber: number
|
||||
}>()
|
||||
|
||||
const tileDate: ComputedRef<LeimDate> = computed(() => {
|
||||
const { areDatesIdentical } = useCalendar()
|
||||
|
||||
const tileDate: ComputedRef<RPGDate> = computed(() => {
|
||||
return {
|
||||
day: props.dayNumber,
|
||||
month: props.monthNumber,
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
|
||||
import MonthTile from './MonthTile.vue'
|
||||
|
||||
const { staticConfig, decrementYear, incrementYear } = useCalendar()
|
||||
const { decrementYear, incrementYear } = useCalendar()
|
||||
const { sortedMonths: months } = storeToRefs(useCalendar())
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
const isMovingUp = e.deltaY < 0
|
||||
@@ -27,10 +26,10 @@ const moveCalendarRight = useThrottleFn(() => {
|
||||
<template>
|
||||
<div class="container mt-[10vh] mb-auto" @wheel="handleWheel">
|
||||
<div class="grid grid-cols-5 gap-x-8 gap-y-16">
|
||||
<MonthTile
|
||||
v-for="month in staticConfig.monthsPerYear"
|
||||
:key="month"
|
||||
:month-number="month - 1"
|
||||
<CalendarStateYearlyMonthTile
|
||||
v-for="month in months"
|
||||
:key="month.id"
|
||||
:month
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import type { CalendarMonth } from '~/models/CalendarMonth';
|
||||
|
||||
import DayTile from './DayTile.vue'
|
||||
|
||||
const { staticConfig, getMonthName } = useCalendar()
|
||||
|
||||
const props = defineProps<{
|
||||
monthNumber: number
|
||||
defineProps<{
|
||||
month: CalendarMonth
|
||||
}>()
|
||||
|
||||
const tileMonthName: string = getMonthName(props.monthNumber)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="font-medium">
|
||||
{{ tileMonthName }}
|
||||
{{ month.name }}
|
||||
</div>
|
||||
<div class="grid grid-cols-7">
|
||||
<DayTile
|
||||
v-for="day in staticConfig.daysPerMonth"
|
||||
<CalendarStateYearlyDayTile
|
||||
v-for="day in month.days"
|
||||
:key="day"
|
||||
:month-number="monthNumber"
|
||||
:month-number="month.position"
|
||||
:day-number="day"
|
||||
/>
|
||||
</div>
|
||||
|
||||
55
components/profile/Dashboard.vue
Normal file
55
components/profile/Dashboard.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script lang="ts" setup>
|
||||
const user = useSupabaseUser()
|
||||
|
||||
const { data: worlds } = await useLazyFetch('/api/worlds')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="p-8">
|
||||
<Heading>{{ user?.user_metadata.full_name }}</Heading>
|
||||
|
||||
<section v-if="worlds?.data" class="mt-4">
|
||||
<h2 class="mb-4 text-lg font-bold">
|
||||
Mondes
|
||||
</h2>
|
||||
|
||||
<ul class="grid md:grid-cols-3 gap-2">
|
||||
<li v-for="(world, i) in worlds.data" :key="i">
|
||||
<UiCard
|
||||
class="w-full transition-all"
|
||||
:link="`/calendar/${world.id}`"
|
||||
:class="{
|
||||
'hover:bg-slate-50 dark:hover:bg-sky-950 dark:focus-within:outline-sky-900': !world.color,
|
||||
'bg-red-100 dark:bg-red-950 border-red-200 hover:bg-red-50 dark:hover:bg-red-900 dark:border-red-900 dark:focus-within:outline-red-900': world.color === 'red',
|
||||
'bg-orange-100 dark:bg-orange-950 border-orange-200 hover:bg-orange-50 dark:hover:bg-orange-900 dark:border-orange-900 dark:focus-within:outline-orange-900': world.color === 'orange',
|
||||
'bg-amber-100 dark:bg-amber-950 border-amber-200 hover:bg-amber-50 dark:hover:bg-amber-900 dark:border-amber-900 dark:focus-within:outline-amber-900': world.color === 'amber',
|
||||
'bg-yellow-100 dark:bg-yellow-950 border-yellow-200 hover:bg-yellow-50 dark:hover:bg-yellow-900 dark:border-yellow-900 dark:focus-within:outline-yellow-900': world.color === 'yellow',
|
||||
'bg-lime-100 dark:bg-lime-950 border-lime-200 hover:bg-lime-50 dark:hover:bg-lime-900 dark:border-lime-900 dark:focus-within:outline-lime-900': world.color === 'lime',
|
||||
' bg-green-100 dark:bg-green-950 border-green-200 hover:bg-green-50 dark:hover:bg-green-900 dark:border-green-900 dark:focus-within:outline-green-900': world.color === 'green',
|
||||
'bg-emerald-100 dark:bg-emerald-950 border-emerald-200 hover:bg-emerald-50 dark:hover:bg-emerald-900 dark:border-emerald-900 dark:focus-within:outline-emerald-900': world.color === 'emerald',
|
||||
'bg-teal-100 dark:bg-teal-950 border-teal-200 hover:bg-teal-50 dark:hover:bg-teal-900 dark:border-teal-900 dark:focus-within:outline-teal-900': world.color === 'teal',
|
||||
'bg-cyan-100 dark:bg-cyan-950 border-cyan-200 hover:bg-cyan-50 dark:hover:bg-cyan-900 dark:border-cyan-900 dark:focus-within:outline-cyan-900': world.color === 'cyan',
|
||||
'bg-sky-100 dark:bg-sky-950 border-sky-200 hover:bg-sky-50 dark:hover:bg-sky-900 dark:border-sky-900 dark:focus-within:outline-sky-900': world.color === 'sky',
|
||||
'bg-blue-100 dark:bg-blue-950 border-blue-200 hover:bg-blue-50 dark:hover:bg-blue-900 dark:border-blue-900 dark:focus-within:outline-blue-900': world.color === 'blue',
|
||||
'bg-indigo-100 dark:bg-indigo-950 border-indigo-200 hover:bg-indigo-50 dark:hover:bg-indigo-900 dark:border-indigo-900 dark:focus-within:outline-indigo-900': world.color === 'indigo',
|
||||
'bg-violet-100 dark:bg-violet-950 border-violet-200 hover:bg-violet-50 dark:hover:bg-violet-900 dark:border-violet-900 dark:focus-within:outline-violet-900': world.color === 'violet',
|
||||
'bg-purple-100 dark:bg-purple-950 border-purple-200 hover:bg-purple-50 dark:hover:bg-purple-900 dark:border-purple-900 dark:focus-within:outline-purple-900': world.color === 'purple',
|
||||
'bg-fuchsia-100 dark:bg-fuchsia-950 border-fuchsia-200 hover:bg-fuchsia-50 dark:hover:bg-fuchsia-900 dark:border-fuchsia-900 dark:focus-within:outline-fuchsia-900': world.color === 'fuchsia',
|
||||
'bg-pink-100 dark:bg-pink-950 border-pink-200 hover:bg-pink-50 dark:hover:bg-pink-900 dark:border-pink-900 dark:focus-within:outline-pink-900': world.color === 'pink',
|
||||
'bg-pink-100 dark:bg-rose-950 border-rose-200 hover:bg-rose-50 dark:hover:bg-rose-900 dark:border-rose-900 dark:focus-within:outline-rose-900': world.color === 'rose',
|
||||
'text-slate-100 bg-slate-900 border-slate-700 hover:bg-slate-700 dark:hover:bg-slate-800 dark:border-slate-900 dark:focus-within:outline-slate-900': world.color === 'black',
|
||||
' hover:bg-slate-50 dark:hover:text-slate-900 dark:hover:bg-slate-300 dark:border-slate-500 dark:focus-within:outline-slate-100': world.color === 'white',
|
||||
}"
|
||||
>
|
||||
<UiCardHeader>
|
||||
<UiCardTitle>{{ world.name }}</UiCardTitle>
|
||||
</UiCardHeader>
|
||||
<UiCardContent>
|
||||
<p class="italic">{{ world.description }}</p>
|
||||
</UiCardContent>
|
||||
</UiCard>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
@@ -1,9 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
const user = useSupabaseUser()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<Heading>{{ user?.user_metadata.full_name }}</Heading>
|
||||
</div>
|
||||
</template>
|
||||
@@ -12,7 +12,7 @@ const props = defineProps<{
|
||||
<div
|
||||
:class="
|
||||
cn('rounded-lg border bg-card text-card-foreground shadow-sm transition-all', props.class, {
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:bg-sky-950 hover:-translate-y-[.2rem] focus-within:outline-sky-900':
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:-translate-y-[.2rem]':
|
||||
props.link
|
||||
})
|
||||
"
|
||||
|
||||
15
models/CalendarConfig.ts
Normal file
15
models/CalendarConfig.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { CalendarEvent } from "./CalendarEvent"
|
||||
import type { CalendarMonth } from "./CalendarMonth"
|
||||
import type { RPGDate } from "./Date"
|
||||
|
||||
export interface CalendarConfig {
|
||||
months: CalendarMonth[]
|
||||
daysPerMonth: number
|
||||
today: RPGDate
|
||||
}
|
||||
|
||||
export interface Calendar extends CalendarConfig {
|
||||
id: number
|
||||
name: string
|
||||
events: CalendarEvent[]
|
||||
}
|
||||
19
models/CalendarEvent.ts
Normal file
19
models/CalendarEvent.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Category } from './Category'
|
||||
import type { RPGDate } from './Date'
|
||||
|
||||
export interface CalendarEvent {
|
||||
id: number
|
||||
title: string
|
||||
startDate: RPGDate
|
||||
endDate?: RPGDate
|
||||
description?: string
|
||||
category?: Category
|
||||
secondaryCategories?: Category[]
|
||||
hidden?: boolean
|
||||
wiki?: string
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function isCalendarEvent(object: any): object is CalendarEvent {
|
||||
return 'startDate' in object
|
||||
}
|
||||
6
models/CalendarMonth.ts
Normal file
6
models/CalendarMonth.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface CalendarMonth {
|
||||
id: number
|
||||
days: number,
|
||||
name: string,
|
||||
position: number,
|
||||
}
|
||||
4
models/Category.ts
Normal file
4
models/Category.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface Category {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
@@ -1,31 +1,19 @@
|
||||
import type { LeimDate } from './Date'
|
||||
import type { Category } from './Category'
|
||||
import type { RPGDate } from './Date'
|
||||
|
||||
export interface Character {
|
||||
name: string
|
||||
description?: string
|
||||
birth?: LeimDate
|
||||
death?: LeimDate
|
||||
birth?: RPGDate
|
||||
death?: RPGDate
|
||||
hiddenBirth?: boolean
|
||||
hiddenDeath?: boolean
|
||||
category?: CharacterCategory
|
||||
secondaryCategories?: CharacterCategory[]
|
||||
category?: Category
|
||||
secondaryCategories?: Category[]
|
||||
wiki?: string
|
||||
}
|
||||
export const characterCategories = [
|
||||
'joueur',
|
||||
'comte',
|
||||
'scientifique',
|
||||
'mage',
|
||||
'professeur',
|
||||
'criminel',
|
||||
'étincelle',
|
||||
'buse blanche',
|
||||
'ecclésiastique',
|
||||
'sentinelle'
|
||||
] as const
|
||||
|
||||
export type CharacterCategory = (typeof characterCategories)[number]
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function isCharacter(object: any): object is Character {
|
||||
return 'birth' in object
|
||||
}
|
||||
|
||||
179
models/Date.ts
179
models/Date.ts
@@ -1,187 +1,16 @@
|
||||
export interface LeimDate {
|
||||
export interface RPGDate {
|
||||
day: number
|
||||
month: number
|
||||
year: number
|
||||
period?: LeimPeriod
|
||||
}
|
||||
|
||||
export type LeimPeriod = 'ante' | 'nante'
|
||||
export type LeimPeriodShort = 'A.R' | 'N.R'
|
||||
export type LeimDateOrder = 'asc' | 'desc'
|
||||
export type RPGDateOrder = 'asc' | 'desc'
|
||||
|
||||
export const monthsPerYear: number = 10
|
||||
export const daysPerYear: number = 320
|
||||
export const daysPerMonth: number = 32
|
||||
export const daysPerWeek: number = 6
|
||||
|
||||
/**
|
||||
* Check whether two dates are identical
|
||||
*
|
||||
* @param date1 First date
|
||||
* @param date2 Second date
|
||||
* @returns True if the dates are identical
|
||||
*/
|
||||
export function areDatesIdentical(date1: LeimDate, date2: LeimDate): boolean {
|
||||
return convertDateToDays({ ...date1 }) === convertDateToDays({ ...date2 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare dates
|
||||
*
|
||||
* @param date1 First date
|
||||
* @param date2 Second date
|
||||
* @returns 1 means the first date comes before the second, -1 means the second comes before the first, and 0 if they're identical
|
||||
*/
|
||||
export function compareDates(a: LeimDate, b: LeimDate, order: LeimDateOrder = 'desc'): number {
|
||||
// Reverses the order if specified
|
||||
const orderFactor: number = order === 'desc' ? 1 : -1
|
||||
|
||||
// Compare eras
|
||||
if ((a.period === 'ante' && b.period === 'nante') || (a.year < 0 && b.year >= 0))
|
||||
return -1 * orderFactor
|
||||
if ((a.period === 'nante' && b.period === 'ante') || (a.year >= 0 && b.year < 0))
|
||||
return 1 * orderFactor
|
||||
|
||||
// Compare years
|
||||
if (a.year < b.year) return -1 * orderFactor
|
||||
if (a.year > b.year) return 1 * orderFactor
|
||||
|
||||
// Compare months
|
||||
if (a.month < b.month) return -1 * orderFactor
|
||||
if (a.month > b.month) return 1 * orderFactor
|
||||
|
||||
// Compare days
|
||||
if (a.day < b.day) return -1 * orderFactor
|
||||
if (a.day > b.day) return 1 * orderFactor
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a LeimDate to its equivalent in days
|
||||
*
|
||||
* @todo Handle negative dates
|
||||
* @param dateToConvert The date object
|
||||
* @returns How many days does it represent
|
||||
*/
|
||||
export function convertDateToDays(dateToConvert: LeimDate): number {
|
||||
let numberOfDays: number = dateToConvert.day
|
||||
|
||||
numberOfDays = numberOfDays + dateToConvert.month * daysPerMonth
|
||||
numberOfDays = numberOfDays + dateToConvert.year * daysPerYear
|
||||
|
||||
return numberOfDays
|
||||
}
|
||||
|
||||
/**
|
||||
* From two dates, get the difference in days between them
|
||||
* @param baseDate The base date
|
||||
* @param relativeDate The year to compare it to
|
||||
* @returns The number of days separating the two dates (both positive and negative numbers)
|
||||
*/
|
||||
export function getDifferenceInDays(baseDate: LeimDate, relativeDate: LeimDate): number {
|
||||
return convertDateToDays(relativeDate) - convertDateToDays(baseDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* From two dates, gives information on how many years, months and days it has been / will be between them
|
||||
*
|
||||
* @param baseDate The base date
|
||||
* @param relativeDate The year to compare it to
|
||||
* @returns A string with info on how the relative date differs to the base date
|
||||
*/
|
||||
export function getRelativeString(
|
||||
baseDate: LeimDate,
|
||||
relativeDate: LeimDate,
|
||||
formatting: 'compact' | 'complex' = 'complex'
|
||||
): string {
|
||||
const differenceInDays: number = getDifferenceInDays(baseDate, relativeDate)
|
||||
let output: string = ''
|
||||
let direction: 'past' | 'present' | 'future' = 'present'
|
||||
let directionPrefix: string = ''
|
||||
|
||||
// Check whether it's a past or future date
|
||||
if (differenceInDays > 0) {
|
||||
direction = 'future'
|
||||
} else if (differenceInDays < 0) {
|
||||
direction = 'past'
|
||||
}
|
||||
|
||||
if (formatting === 'complex') {
|
||||
// Handle if it's the same date
|
||||
if (direction === 'present') {
|
||||
return "Aujourd'hui"
|
||||
}
|
||||
if (differenceInDays === -2) {
|
||||
return 'Avant-hier'
|
||||
}
|
||||
if (differenceInDays === -1) {
|
||||
return 'Hier'
|
||||
}
|
||||
if (differenceInDays === 1) {
|
||||
return 'Demain'
|
||||
}
|
||||
if (differenceInDays === 2) {
|
||||
return 'Après-demain'
|
||||
}
|
||||
|
||||
// Get relevant prefix for the string
|
||||
if (direction === 'future') {
|
||||
directionPrefix = 'Dans '
|
||||
} else if (direction === 'past') {
|
||||
directionPrefix = 'Il y a '
|
||||
}
|
||||
|
||||
output += directionPrefix
|
||||
}
|
||||
|
||||
const yearPackets: number = Math.abs(Math.trunc(differenceInDays / daysPerYear))
|
||||
const monthPackets: number = Math.abs(Math.trunc(differenceInDays / daysPerMonth) % monthsPerYear)
|
||||
|
||||
const remainingDays: number =
|
||||
Math.abs(differenceInDays) - (yearPackets * daysPerYear + monthPackets * daysPerMonth)
|
||||
|
||||
// Assign year part
|
||||
if (yearPackets) {
|
||||
if (yearPackets === 1) {
|
||||
output += `${yearPackets} an`
|
||||
} else {
|
||||
output += `${yearPackets} ans`
|
||||
}
|
||||
}
|
||||
|
||||
// Assign month part
|
||||
if (monthPackets) {
|
||||
// If there was a year packet(s), separate from them
|
||||
if (yearPackets) {
|
||||
output += ','
|
||||
}
|
||||
|
||||
output += ` ${monthPackets} mois`
|
||||
}
|
||||
|
||||
// Assign day part
|
||||
if (remainingDays) {
|
||||
// If there was a year OR month packet(s), separate from them
|
||||
if (yearPackets || monthPackets) {
|
||||
output += ' et'
|
||||
}
|
||||
|
||||
if (remainingDays === 1) {
|
||||
output += ` ${remainingDays} jour`
|
||||
} else {
|
||||
output += ` ${remainingDays} jours`
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// export function getRelativeDate(baseDate: LeimDate, relativeDate: LeimDate) {
|
||||
// export function getRelativeDate(baseDate: RPGDate, relativeDate: RPGDate) {
|
||||
// let newDay: number
|
||||
// let newMonth: number
|
||||
// let newYear: number
|
||||
// let newPeriod: LeimPeriod
|
||||
// let newPeriod: RPGPeriod
|
||||
|
||||
// const differenceInDays = getDifferenceInDays(baseDate, relativeDate)
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import type { LeimDate } from './Date'
|
||||
|
||||
export interface CalendarEvent {
|
||||
title: string
|
||||
startDate: LeimDate
|
||||
endDate?: LeimDate
|
||||
description?: string
|
||||
category?: CalendarEventCategory
|
||||
secondaryCategories?: CalendarEventCategory[]
|
||||
hidden?: boolean
|
||||
wiki?: string
|
||||
}
|
||||
|
||||
export const calendarEventCategories = [
|
||||
'naissance',
|
||||
'mort',
|
||||
'catastrophe',
|
||||
'législation',
|
||||
'catastrophe naturelle',
|
||||
'inauguration',
|
||||
'religion',
|
||||
'invention',
|
||||
'science',
|
||||
'bénédiction',
|
||||
'joueurs',
|
||||
'découverte',
|
||||
'exploration',
|
||||
'construction',
|
||||
'arcanologie',
|
||||
'criminalité',
|
||||
'scandale',
|
||||
'commerce'
|
||||
] as const
|
||||
|
||||
export type CalendarEventCategory = (typeof calendarEventCategories)[number]
|
||||
|
||||
export function isCalendarEvent(object: any): object is CalendarEvent {
|
||||
return 'startDate' in object
|
||||
}
|
||||
7
models/World.ts
Normal file
7
models/World.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface World {
|
||||
id: number
|
||||
uuid: string
|
||||
name: string
|
||||
description?: string
|
||||
color?: string
|
||||
}
|
||||
@@ -28,7 +28,8 @@
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vue": "^3.4.27",
|
||||
"vue-router": "^4.3.2"
|
||||
"vue-router": "^4.3.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/color-mode": "^3.4.1",
|
||||
|
||||
40
pages/calendar/[id].vue
Normal file
40
pages/calendar/[id].vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhMagnifyingGlass } from '@phosphor-icons/vue';
|
||||
import type { MenuItem } from '~/components/global/SidebarProps';
|
||||
|
||||
useHead({
|
||||
title: 'Calendrier'
|
||||
})
|
||||
definePageMeta({
|
||||
middleware: ['auth-guard']
|
||||
})
|
||||
|
||||
const user = useSupabaseUser()
|
||||
// Redirect user back home when they log out on the page
|
||||
watch(user, (n, _o) => {
|
||||
if (!n) {
|
||||
navigateTo('/')
|
||||
}
|
||||
})
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
const { isAdvancedSearchOpen } = storeToRefs(useCalendar())
|
||||
|
||||
const sidebarMenu: MenuItem[] = [
|
||||
{
|
||||
phIcon: PhMagnifyingGlass,
|
||||
tooltip: 'Recherche avancée',
|
||||
clickHandler: revealAdvancedSearch
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar :menu-items="sidebarMenu" />
|
||||
|
||||
<Calendar />
|
||||
|
||||
<CalendarSearch v-model:model-value="isAdvancedSearchOpen" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -11,21 +11,5 @@ const sidebarMenu: MenuItem[] = []
|
||||
<template>
|
||||
<main class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar :menu-items="sidebarMenu" is-home />
|
||||
|
||||
<div class="h-full grid place-items-center">
|
||||
<UiCard class="w-1/3 min-w-72" link="/calendar">
|
||||
<UiCardHeader>
|
||||
<UiCardTitle>Calendrier</UiCardTitle>
|
||||
<UiCardDescription>
|
||||
<UiBadge class="mix-blend-luminosity font-bold bg-gray-600" variant="secondary">
|
||||
chronologie
|
||||
</UiBadge>
|
||||
</UiCardDescription>
|
||||
</UiCardHeader>
|
||||
<UiCardContent>
|
||||
Chronologie complète de Léïm, rassemblant les évènements et personnages clés du monde
|
||||
</UiCardContent>
|
||||
</UiCard>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -21,9 +21,9 @@ const sidebarMenu: MenuItem[] = []
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="h-full grid grid-cols-[auto_1fr]">
|
||||
<div class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar :menu-items="sidebarMenu" />
|
||||
|
||||
<ProfileLayout />
|
||||
</main>
|
||||
<ProfileDashboard />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -56,6 +56,9 @@ importers:
|
||||
vue-router:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.2(vue@3.4.27(typescript@5.4.5))
|
||||
zod:
|
||||
specifier: ^3.23.8
|
||||
version: 3.23.8
|
||||
devDependencies:
|
||||
'@nuxtjs/color-mode':
|
||||
specifier: ^3.4.1
|
||||
@@ -4734,6 +4737,9 @@ packages:
|
||||
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
zod@3.23.8:
|
||||
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
@@ -10202,3 +10208,5 @@ snapshots:
|
||||
archiver-utils: 5.0.2
|
||||
compress-commons: 6.0.2
|
||||
readable-stream: 4.5.2
|
||||
|
||||
zod@3.23.8: {}
|
||||
|
||||
37
server/api/calendars/query.get.ts
Normal file
37
server/api/calendars/query.get.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
import type { Calendar } from "~/models/CalendarConfig";
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('calendars')
|
||||
.select(`
|
||||
id,
|
||||
name,
|
||||
today,
|
||||
months:calendar_months (*),
|
||||
events:calendar_events (
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hidden,
|
||||
startDate:start_date,
|
||||
endDate:end_date,
|
||||
wiki,
|
||||
category:calendar_event_categories!calendar_events_category_fkey (*),
|
||||
secondaryCategories:calendar_event_categories!calendar_event_categories_links (*)
|
||||
)
|
||||
`)
|
||||
.eq('world_id', query.world_id)
|
||||
.limit(1)
|
||||
.single<Calendar>()
|
||||
|
||||
return output
|
||||
})
|
||||
@@ -1,252 +0,0 @@
|
||||
export default defineEventHandler(() => {
|
||||
return [
|
||||
/**
|
||||
* Player characters
|
||||
*/
|
||||
// "Les Milles Cages"
|
||||
{
|
||||
name: 'Sulvan Trois-Barbes',
|
||||
birth: { day: 20, month: 3, year: 3169 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Sulvan_Trois-Barbes',
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['criminel', 'étincelle']
|
||||
},
|
||||
{
|
||||
name: 'Vascylly',
|
||||
birth: { day: 3, month: 5, year: 3181 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vascylly',
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Tara Belyus',
|
||||
birth: { day: 14, month: 9, year: 3186 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
// "Les Cloches de Cantane"
|
||||
{
|
||||
name: 'Malik',
|
||||
birth: { day: 22, month: 8, year: 3181 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['scientifique']
|
||||
},
|
||||
{
|
||||
name: 'Elie',
|
||||
birth: { day: 5, month: 6, year: 3192 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['ecclésiastique']
|
||||
},
|
||||
{
|
||||
name: 'Clayron',
|
||||
birth: { day: 3, month: 5, year: 3178 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
|
||||
/**
|
||||
* NPC
|
||||
*/
|
||||
// Counts of the Alliance
|
||||
{
|
||||
name: 'Alfrid de Lagarde',
|
||||
birth: { day: 29, month: 7, year: 3193 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Alfrid_de_Lagarde',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Alaric de Lagarde',
|
||||
birth: { day: 23, month: 8, year: 3192 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Alaric_de_Lagarde',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Anastael III de Quillon',
|
||||
birth: { day: 1, month: 5, year: 3184 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Anastael_III_de_Quillon',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Antoine de Mireloin',
|
||||
birth: { day: 31, month: 5, year: 3190 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Antoine_de_Mireloin',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Armance Ronchère',
|
||||
birth: { day: 1, month: 1, year: 3132 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Armance_Ronchère',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Baranne Rougefer',
|
||||
birth: { day: 1, month: 0, year: 3175 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Baranne_Rougefer',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Béatrice II de Grandlac',
|
||||
birth: { day: 21, month: 4, year: 3158 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Béatrice_II_de_Grandlac',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Favio Asharos-Losantelle',
|
||||
birth: { day: 17, month: 3, year: 3091 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Favio_Asharos-Losantelle',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Firmin de Montardieu',
|
||||
birth: { day: 9, month: 2, year: 3203 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Firmin_de_Montardieu',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Laura de Montardieu',
|
||||
birth: { day: 32, month: 3, year: 3167 },
|
||||
death: { day: 1, month: 4, year: 3217 },
|
||||
hiddenDeath: true,
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Laura_de_Montardieu',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Lazarus Tymos',
|
||||
birth: { day: 29, month: 9, year: 3145 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Lazarus_Tymos',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Marion de Corambre',
|
||||
birth: { day: 14, month: 7, year: 3190 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Marion_de_Corambre',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['scientifique', 'ecclésiastique']
|
||||
},
|
||||
{
|
||||
name: 'Relforg Pergaré',
|
||||
birth: { day: 18, month: 9, year: 3182 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Relforg_Pergaré',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Vilgarde de Ternâcre',
|
||||
birth: { day: 3, month: 3, year: 2998 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vilgarde_de_Ternâcre',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Ysildy Milopée',
|
||||
birth: { day: 3, month: 1, year: 3187 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Ysildy_Milopée',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage', 'scientifique', 'professeur']
|
||||
},
|
||||
|
||||
// Sparks
|
||||
{
|
||||
name: 'Izàc Tymos',
|
||||
birth: { day: 13, month: 6, year: 3192 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Izàc_Tymos',
|
||||
category: 'mage',
|
||||
secondaryCategories: ['étincelle', 'criminel', 'professeur', 'scientifique']
|
||||
},
|
||||
{
|
||||
name: 'Tvernée',
|
||||
birth: { day: 19, month: 2, year: 3205 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tvernée',
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['criminel']
|
||||
},
|
||||
|
||||
// Pirates
|
||||
{
|
||||
name: 'Räzal',
|
||||
birth: { day: 13, month: 8, year: 3178 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Räzal'
|
||||
},
|
||||
|
||||
// Legends
|
||||
{
|
||||
name: 'Jorhas Kirendre',
|
||||
birth: { day: 2, month: 9, year: -452 },
|
||||
death: { day: 2, month: 2, year: -419 },
|
||||
category: 'sentinelle',
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Jorhas_Kirendre'
|
||||
},
|
||||
|
||||
// "Les Milles Cages"
|
||||
{
|
||||
name: 'Ernestin Pomel',
|
||||
birth: { day: 11, month: 2, year: 3179 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Ernestin_Pomel',
|
||||
category: 'mage'
|
||||
},
|
||||
{
|
||||
name: 'Quacille Lévios',
|
||||
birth: { day: 3, month: 6, year: 3162 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Quacille_Lévios',
|
||||
category: 'professeur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Morel Lévios',
|
||||
birth: { day: 26, month: 3, year: 3157 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Morel_Lévios',
|
||||
category: 'sentinelle',
|
||||
secondaryCategories: ['mage', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Anastael Simon',
|
||||
birth: { day: 32, month: 2, year: 3166 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Anastael_Simon',
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Grestain',
|
||||
birth: { day: 9, month: 2, year: 3162 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Grestain',
|
||||
category: 'criminel',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Lucien Malanth',
|
||||
birth: { day: 31, month: 4, year: 3167 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Lucien_Malanth',
|
||||
category: 'criminel',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Tivian Rodhus',
|
||||
birth: { day: 13, month: 3, year: 3157 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tivian_Rodhus',
|
||||
category: 'professeur',
|
||||
secondaryCategories: ['mage', 'criminel']
|
||||
},
|
||||
|
||||
// "Les Cloches de Cantane"
|
||||
{
|
||||
name: 'Bénédicte Vaht',
|
||||
description: "Moine d'Ikami ayant entrepris la construction du Pilier Blanc de Cantane",
|
||||
birth: { day: 28, month: 6, year: 3139 },
|
||||
category: 'ecclésiastique'
|
||||
},
|
||||
{
|
||||
name: 'Taleb Vaht',
|
||||
description:
|
||||
"Fils de Bénédicte, il s'est engagé auprès des Étincelles comme alchimiste artificier.",
|
||||
birth: { day: 9, month: 1, year: 3180 },
|
||||
death: { day: 28, month: 7, year: 3209 },
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['scientifique']
|
||||
}
|
||||
]
|
||||
})
|
||||
31
server/api/characters/query.get.ts
Normal file
31
server/api/characters/query.get.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
import type { Character } from "~/models/Characters";
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('characters')
|
||||
.select(`
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
birth,
|
||||
death,
|
||||
wiki,
|
||||
category:character_categories!characters_category_fkey (*),
|
||||
secondaryCategories:character_categories!character_categories_links (*)
|
||||
`)
|
||||
|
||||
if (query.world_id) {
|
||||
output.eq('world_id', query.world_id)
|
||||
}
|
||||
|
||||
return output.returns<Character[]>()
|
||||
})
|
||||
@@ -1,217 +0,0 @@
|
||||
export default defineEventHandler(() => {
|
||||
return [
|
||||
// Histoire Antique
|
||||
{
|
||||
title: "Laurdieu devient la première cité de l'empire de Kaliatos",
|
||||
startDate: { day: 3, month: 4, year: -1932 },
|
||||
description:
|
||||
"L'empire de Kaliatos établi sa capitale dans la cité de Laurdieu, qui connaitra une prospérité nouvelle au sein d'Aldys.",
|
||||
category: 'législation',
|
||||
secondaryCategories: ['inauguration'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Laurdieu',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: "Apparition d'Asménys",
|
||||
startDate: { day: 19, month: 7, year: -1358 },
|
||||
description:
|
||||
"La défunte chanteuse Asménys apparaît à un barde pendant son jeune public, démarrant la religion des Prêtresses d'Asménys.",
|
||||
category: 'religion',
|
||||
secondaryCategories: ['bénédiction'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Pr%C3%AAtresses_d%27Asm%C3%A9nys',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'La Rupture',
|
||||
startDate: { day: 26, month: 5, year: -756 },
|
||||
endDate: { day: 4, month: 9, year: 29 },
|
||||
description:
|
||||
"Les Abysses se déversent à la surface de Léim, à travers plusieurs brèches. Plusieurs hordes de démons se rapprochent des villes, et ce qu'on appellera l'Âge des Abysses commencent alors sur la planète entière.",
|
||||
category: 'catastrophe',
|
||||
secondaryCategories: ['catastrophe naturelle'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Seconde_Rupture',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'Marche du sang',
|
||||
startDate: { day: 18, month: 9, year: -420 },
|
||||
endDate: { day: 27, month: 1, year: -419 },
|
||||
description:
|
||||
"L'empereur de Kaliatos ordonne personnellement la traque de Jorhas Kirendre pour connivence avec les démons. Plusieurs bataillons sont affectés à la chasse de Jorhas, qui se terminera par son incarcération ainsi que la mort de plusieurs centaines de soldats.",
|
||||
category: 'criminalité',
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Jorhas_Kirendre',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'Exécution de Tyhos',
|
||||
startDate: { day: 1, month: 0, year: 0 },
|
||||
description:
|
||||
"Le léviathan Tyhos rend l'âme après un combat de plusieurs années contre Lystos, le dieu du Soleil.",
|
||||
category: 'bénédiction',
|
||||
secondaryCategories: ['mort'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tyhos',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'Traité de Kadel',
|
||||
startDate: { day: 29, month: 4, year: 100 },
|
||||
description: '',
|
||||
category: 'inauguration',
|
||||
secondaryCategories: ['législation'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Alliance_Kald%C3%A9lienne#Trait%C3%A9_de_Kadel'
|
||||
},
|
||||
{
|
||||
title: 'Découverte des Plaines de Poussières',
|
||||
startDate: { day: 17, month: 7, year: 305 },
|
||||
description:
|
||||
"Les troupes de la reconquête aldienne explorent le littoral d'une immense étendue grise et inerte.",
|
||||
category: 'découverte',
|
||||
secondaryCategories: ['exploration'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Plaines_de_poussi%C3%A8re',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'Construction du Rempart de Laurdieu',
|
||||
startDate: { day: 30, month: 2, year: 340 },
|
||||
endDate: { day: 27, month: 9, year: 355 },
|
||||
description:
|
||||
"Le Grand Conseil Kaldélien ordonne la construction d'une muraille autour des Plaines de Poussières, afin de contenir les démons y sortant.",
|
||||
category: 'construction',
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Plaines_de_poussi%C3%A8re',
|
||||
hidden: true
|
||||
},
|
||||
|
||||
// Histoire Récente
|
||||
{
|
||||
title: "Inauguration de l'Académie Artistique Arcanique",
|
||||
startDate: { day: 11, month: 6, year: 2545 },
|
||||
description:
|
||||
"Scäd Sceni ouvre son institut artistique dédié à l'apprentissage des arts arcaniques",
|
||||
category: 'inauguration',
|
||||
secondaryCategories: ['arcanologie'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Buse_(arme)'
|
||||
},
|
||||
{
|
||||
title: 'Création de la buse kaldélienne',
|
||||
startDate: { day: 4, month: 2, year: 3113 },
|
||||
description:
|
||||
'Sophia de Rougefer invente la buse kaldélienne, une arme à feu souple à deux canons.',
|
||||
category: 'invention',
|
||||
secondaryCategories: ['science'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Buse_(arme)'
|
||||
},
|
||||
{
|
||||
title: 'Feux dans les champs de Bamast',
|
||||
startDate: { day: 9, month: 5, year: 3200 },
|
||||
description:
|
||||
"Plusieurs incendies criminels se propagent à travers les champs de sérille des fermiers des littoraux de Bamast. Aucun suspect ni coupable ne sera trouvé et l'enquête sera baclée.",
|
||||
category: 'catastrophe',
|
||||
secondaryCategories: ['criminalité'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vascylly'
|
||||
},
|
||||
{
|
||||
title: 'Meurtre de Darléon Typhos',
|
||||
startDate: { day: 21, month: 6, year: 3200 },
|
||||
description:
|
||||
'Darléon Typhos surprend Vascylly fouillant sa demeure peu après la tombée de la nuit. Le majordome de la famille Typhos découvre le corps sans vie de son maître le lendemain.',
|
||||
category: 'criminalité',
|
||||
secondaryCategories: ['joueurs'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vascylly'
|
||||
},
|
||||
{
|
||||
title: 'Scandale Rodhus',
|
||||
startDate: { day: 25, month: 9, year: 3208 },
|
||||
description:
|
||||
"Tivian Rodhus, un professeur estimé de l'Académie Centrale Kaldélienne, est emprisonné pour corruption et aggression sexuelle. Le corps enseignant y est remanié sur ordre de Lazarus Tymos, comte de Nacride.",
|
||||
category: 'criminalité',
|
||||
secondaryCategories: ['scandale'],
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tivian_Rodhus'
|
||||
},
|
||||
|
||||
// "Les Milles Cages"
|
||||
{
|
||||
title: "Arrivée d'aventuriers à Borélis",
|
||||
startDate: { day: 12, month: 7, year: 3209 },
|
||||
description:
|
||||
'Tara Belyus, Vascylly et Adol Sulvan livrent 3 condamnés à Handany. Ils partent pour la mer durant la journée.',
|
||||
category: 'joueurs'
|
||||
},
|
||||
{
|
||||
title: "Naufrage de l'Éclipse",
|
||||
description:
|
||||
"L'Éclipse, le navire de la garde contenant des condamnés à destination des Cages Handaniennes, s'échoue au large des côtes montagneuses de la Lance d'Aldys.",
|
||||
startDate: { day: 14, month: 7, year: 3209 },
|
||||
category: 'catastrophe'
|
||||
},
|
||||
{
|
||||
title: 'Emprisonnement de Tivian Rodhus',
|
||||
description: "Celui qu'on surnomme la Bête d'Ambrose arrive à Handany, où il purgera sa peine.",
|
||||
startDate: { day: 14, month: 7, year: 3209 },
|
||||
category: 'législation'
|
||||
},
|
||||
{
|
||||
title: 'Sulvan et Anastael atteignent Bamast',
|
||||
startDate: { day: 19, month: 2, year: 3210 },
|
||||
category: 'joueurs'
|
||||
},
|
||||
{
|
||||
title: 'Jugement de Bormis Griloup',
|
||||
description:
|
||||
"Bromis Griloup est jugé coupable d'escroquerie et sabotage aux Cours d'Acier de Tourgrise. Il purgera une peine de 10 ans au sein des prisons royales.",
|
||||
startDate: { day: 4, month: 8, year: 3209 },
|
||||
category: 'législation'
|
||||
},
|
||||
|
||||
// "Les Cloches de Cantane"
|
||||
{
|
||||
title: 'Inauguration de la Cloche du Pilier',
|
||||
description: "Le Moine Premier inaugure la grande cloche d'argent au sommet du Pilier d'Ikami.",
|
||||
startDate: { day: 29, month: 5, year: 3209 },
|
||||
category: 'religion',
|
||||
secondaryCategories: ['inauguration']
|
||||
},
|
||||
{
|
||||
title: '1ère disparation à Cantane',
|
||||
description: "Taleb Vaht décède dans une grotte à la suite d'une attaque d'ischiels enragées.",
|
||||
startDate: { day: 28, month: 7, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '2ème disparation à Cantane',
|
||||
description: 'Donovane le mineur kéturien disparait sans laisser de traces.',
|
||||
startDate: { day: 32, month: 7, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '3ème disparation à Cantane',
|
||||
description: 'Disparition de Sébastien, gredin sauride.',
|
||||
startDate: { day: 10, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '4ème disparation à Cantane',
|
||||
description: 'Disparition de Thérence, patrouilleur sauride de la Vieille Garde.',
|
||||
startDate: { day: 19, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '5ème disparation à Cantane',
|
||||
description: 'Disparition de Mathilda Boulais, vendeuse de pierres.',
|
||||
startDate: { day: 22, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: 'Grande Banque Minérale de Cantane',
|
||||
description:
|
||||
'Les artisans et mineurs de Rougefer se réunissent à Cantane pour vendre le fruit de leur dur labeur.',
|
||||
startDate: { day: 23, month: 8, year: 3209 },
|
||||
endDate: { day: 26, month: 8, year: 3209 },
|
||||
category: 'commerce'
|
||||
}
|
||||
]
|
||||
})
|
||||
27
server/api/events/query.get.ts
Normal file
27
server/api/events/query.get.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
import type { CalendarEvent } from "~/models/CalendarEvent";
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('calendar_events')
|
||||
.select(`
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
world_calendars (id, world_id)
|
||||
`)
|
||||
|
||||
if (query.world_id) {
|
||||
output.eq('world_calendars.world_id', query.world_id)
|
||||
}
|
||||
|
||||
return output.returns<CalendarEvent[]>()
|
||||
})
|
||||
10
server/api/worlds/index.get.ts
Normal file
10
server/api/worlds/index.get.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import type { World } from "~/models/World";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
|
||||
return client.from('worlds')
|
||||
.select(`id, name, description, color`)
|
||||
.returns<World[]>()
|
||||
})
|
||||
@@ -1,26 +1,14 @@
|
||||
import {
|
||||
monthsPerYear,
|
||||
daysPerWeek,
|
||||
daysPerMonth,
|
||||
daysPerYear,
|
||||
type LeimDate,
|
||||
type LeimPeriod,
|
||||
type LeimPeriodShort
|
||||
type RPGDate,
|
||||
type RPGDateOrder,
|
||||
} from '@/models/Date'
|
||||
import { useLocalStorage, useUrlSearchParams } from '@vueuse/core'
|
||||
import { defineStore, skipHydrate } from 'pinia'
|
||||
import { computed, ref, type ComputedRef, type Ref } from 'vue'
|
||||
import type { CalendarMonth } from '~/models/CalendarMonth'
|
||||
|
||||
type CalendarViewType = 'month' | 'year' | 'decade' | 'century'
|
||||
|
||||
type CalendarStaticConfig = {
|
||||
months: string[]
|
||||
monthsPerYear: number
|
||||
daysPerYear: number
|
||||
daysPerMonth: number
|
||||
daysPerWeek: number
|
||||
}
|
||||
|
||||
type CalendarCurrentConfig = {
|
||||
viewType: CalendarViewType
|
||||
}
|
||||
@@ -30,8 +18,6 @@ type CalendarCurrentDate = {
|
||||
currentMonth: ComputedRef<number>
|
||||
currentMonthName: ComputedRef<string>
|
||||
currentYear: ComputedRef<number>
|
||||
currentPeriod: Ref<LeimPeriod>
|
||||
currentPeriodAbbr: Ref<LeimPeriodShort>
|
||||
currentDateTitle: ComputedRef<string>
|
||||
}
|
||||
|
||||
@@ -40,31 +26,6 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
* Static calendar config
|
||||
* This shouldn't change
|
||||
*/
|
||||
/**
|
||||
* Month list
|
||||
*/
|
||||
const months: string[] = [
|
||||
'Jalen',
|
||||
'Malsen',
|
||||
'Verlys',
|
||||
'Nalys',
|
||||
'Verdore',
|
||||
'Sidore',
|
||||
'Lyllion',
|
||||
'Rion',
|
||||
'Farene',
|
||||
'Dalvene'
|
||||
]
|
||||
|
||||
// Assign the static config
|
||||
const staticConfig: CalendarStaticConfig = {
|
||||
months,
|
||||
monthsPerYear,
|
||||
daysPerYear,
|
||||
daysPerMonth,
|
||||
daysPerWeek
|
||||
}
|
||||
|
||||
const currentConfig: Ref<CalendarCurrentConfig> = ref({
|
||||
viewType: 'month'
|
||||
})
|
||||
@@ -75,15 +36,34 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
'century'
|
||||
])
|
||||
|
||||
/**
|
||||
* Month list (queried from API)
|
||||
*/
|
||||
const months: Ref<CalendarMonth[]> = ref<CalendarMonth[]>([])
|
||||
|
||||
function setMonths(data: CalendarMonth[]) {
|
||||
months.value = data
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted month data using the raw months
|
||||
*/
|
||||
const sortedMonths = computed<CalendarMonth[]>(() => months.value.sort((a, b) => a.position - b.position))
|
||||
const monthsPerYear = computed(() => months.value.length)
|
||||
const daysPerYear = computed(() => months.value.reduce((acc, o) => acc + o.days, 0))
|
||||
|
||||
// Default date settings (current day in the world)
|
||||
const defaultDay: number = 23
|
||||
const defaultMonth: number = 8
|
||||
const defaultYear: number = 3209
|
||||
const defaultDate: ComputedRef<LeimDate> = computed(() => {
|
||||
// The base setting is the first day / month of year 0
|
||||
const defaultDay: Ref<number> = ref<number>(1)
|
||||
const defaultMonth: Ref<number> = ref<number>(0)
|
||||
const defaultYear: Ref<number> = ref<number>(0)
|
||||
|
||||
// Object representation
|
||||
const defaultDate: ComputedRef<RPGDate> = computed(() => {
|
||||
return {
|
||||
day: defaultDay,
|
||||
month: defaultMonth,
|
||||
year: defaultYear
|
||||
day: defaultDay.value,
|
||||
month: defaultMonth.value,
|
||||
year: defaultYear.value
|
||||
}
|
||||
})
|
||||
|
||||
@@ -97,6 +77,24 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Sets the new defaultDate (aka the "today" value from the calendar)
|
||||
*
|
||||
* @param date The new data to set as defaultDate
|
||||
*/
|
||||
function setDefaultDate(date: RPGDate) {
|
||||
defaultDay.value = date.day
|
||||
defaultMonth.value = date.month
|
||||
defaultYear.value = date.year
|
||||
}
|
||||
|
||||
// Everytime the defaultDate changes / is set, we should update the params in the URL
|
||||
watch(defaultDate, () => {
|
||||
params.day = String(defaultDate.value.day)
|
||||
params.month = String(defaultDate.value.month)
|
||||
params.year = String(defaultDate.value.year)
|
||||
})
|
||||
|
||||
const currentDay = computed<number>(() => {
|
||||
return Number(params.day)
|
||||
})
|
||||
@@ -104,6 +102,9 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
const currentMonth = computed<number>(() => {
|
||||
return Number(params.month)
|
||||
})
|
||||
const currentMonthData = computed<CalendarMonth>(() => {
|
||||
return sortedMonths.value[currentMonth.value]
|
||||
})
|
||||
// Gets the label from currentMonth index
|
||||
const currentMonthName = computed<string>(() => getMonthName(currentMonth.value))
|
||||
|
||||
@@ -111,12 +112,6 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
return Number(params.year)
|
||||
})
|
||||
|
||||
// Get period from currentYear
|
||||
const currentPeriod = computed<LeimPeriod>(() => getPeriodOfYear(currentYear.value).long)
|
||||
const currentPeriodAbbr = computed<LeimPeriodShort>(
|
||||
() => getPeriodOfYear(currentYear.value).short
|
||||
)
|
||||
|
||||
const currentDateTitle = computed<string>(() => {
|
||||
switch (currentConfig.value.viewType) {
|
||||
case 'month':
|
||||
@@ -124,17 +119,16 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
day: currentDate.currentDay.value,
|
||||
month: currentDate.currentMonth.value,
|
||||
year: currentDate.currentYear.value,
|
||||
period: currentDate.currentPeriod.value
|
||||
})
|
||||
|
||||
case 'year':
|
||||
return `Année ${currentYear.value} ${currentPeriodAbbr.value}`
|
||||
return `Année ${currentYear.value}`
|
||||
|
||||
case 'decade':
|
||||
return `Années ${currentYear.value} ${getPeriodOfYear(currentYear.value).short} - ${currentYear.value + 10} ${getPeriodOfYear(currentYear.value + 10).short}`
|
||||
return `Années ${currentYear.value} - ${currentYear.value + 10}`
|
||||
|
||||
case 'century':
|
||||
return `Années ${currentYear.value} ${getPeriodOfYear(currentYear.value).short} - ${currentYear.value + 100} ${getPeriodOfYear(currentYear.value + 100).short}`
|
||||
return `Années ${currentYear.value} - ${currentYear.value + 100}`
|
||||
|
||||
default:
|
||||
return 'Date inconnue'
|
||||
@@ -147,21 +141,18 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
currentMonth,
|
||||
currentMonthName,
|
||||
currentYear,
|
||||
currentPeriod,
|
||||
currentPeriodAbbr,
|
||||
currentDateTitle
|
||||
}
|
||||
|
||||
const currentLeimDate = computed<LeimDate>(() => {
|
||||
const currentRPGDate = computed<RPGDate>(() => {
|
||||
return {
|
||||
day: currentDate.currentDay.value,
|
||||
month: currentDate.currentMonth.value,
|
||||
year: currentDate.currentYear.value,
|
||||
period: currentDate.currentPeriod.value
|
||||
}
|
||||
})
|
||||
|
||||
const selectedDate = useLocalStorage<LeimDate>('selected-date', currentLeimDate.value, { deep: true })
|
||||
const selectedDate = useLocalStorage<RPGDate>('selected-date', currentRPGDate.value, { deep: true })
|
||||
|
||||
/**
|
||||
* Check whether the current viewType is active
|
||||
@@ -192,7 +183,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
let newValue = Number(params.month) + 1
|
||||
|
||||
// If the new value would exceed the max number of month per year
|
||||
if (newValue >= staticConfig.monthsPerYear) {
|
||||
if (newValue >= monthsPerYear.value) {
|
||||
newValue = 0
|
||||
// Increment the year
|
||||
incrementYear()
|
||||
@@ -209,7 +200,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
|
||||
// If the new value would go below 0
|
||||
if (newValue < 0) {
|
||||
newValue = staticConfig.monthsPerYear - 1
|
||||
newValue = monthsPerYear.value - 1
|
||||
// Decrement the year
|
||||
decrementYear()
|
||||
}
|
||||
@@ -227,7 +218,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
const target: number = monthNumber - 1
|
||||
|
||||
if (target < 0) {
|
||||
return monthsPerYear - 1
|
||||
return monthsPerYear.value - 1
|
||||
}
|
||||
|
||||
return target
|
||||
@@ -242,7 +233,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
function getNextMonth(monthNumber: number): number {
|
||||
const target: number = monthNumber + 1
|
||||
|
||||
if (target + 1 >= monthsPerYear) {
|
||||
if (target + 1 > monthsPerYear.value) {
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -254,7 +245,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
*/
|
||||
function setMonth(target: number): void {
|
||||
// If the target is outside the month bounds
|
||||
if (target < 0 || target >= staticConfig.monthsPerYear) {
|
||||
if (target < 0 || target >= monthsPerYear.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -279,23 +270,6 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
params.year = newValue.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* From a given year, returns a set of LeimPeriod identifier
|
||||
*
|
||||
* This is used in range use-cases
|
||||
*
|
||||
* @param year The year to display
|
||||
* @returns An object containing both short and long LeimPeriod
|
||||
*/
|
||||
function getPeriodOfYear(year: string | number): { long: LeimPeriod; short: LeimPeriodShort } {
|
||||
const numYear = year as number
|
||||
|
||||
return {
|
||||
long: numYear >= 0 ? 'nante' : 'ante',
|
||||
short: numYear >= 0 ? 'N.R' : 'A.R'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted month name given its index
|
||||
*
|
||||
@@ -304,7 +278,23 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
*/
|
||||
function getMonthName(monthNumber: number): string {
|
||||
const index = Number(monthNumber)
|
||||
return staticConfig.months[index]
|
||||
if (sortedMonths.value[index]) {
|
||||
return sortedMonths.value[index].name
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* State for advanced search modal
|
||||
*/
|
||||
const isAdvancedSearchOpen: Ref<boolean> = ref<boolean>(false)
|
||||
|
||||
/**
|
||||
* Opens the search modal
|
||||
*/
|
||||
function revealAdvancedSearch() {
|
||||
isAdvancedSearchOpen.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,6 +306,9 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
currentConfig.value.viewType = viewType
|
||||
}
|
||||
|
||||
/**
|
||||
* FORMATTING
|
||||
*/
|
||||
/**
|
||||
* Gets the formatted viewType title
|
||||
*
|
||||
@@ -340,26 +333,29 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
}
|
||||
|
||||
/**
|
||||
* From a LeimDate, returns the legible date title
|
||||
* From a RPGDate, returns the legible date title
|
||||
*
|
||||
* @param date Date target
|
||||
* @param showNumber Should the date show the day number
|
||||
* @returns Formatted date name
|
||||
*/
|
||||
function getFormattedDateTitle(date: LeimDate, showNumber?: boolean): string {
|
||||
function getFormattedDateTitle(date: RPGDate, showNumber?: boolean): string {
|
||||
if (showNumber) {
|
||||
return `${date.day} ${getMonthName(date.month)} ${date.year} ${getPeriodOfYear(date.year).short}`
|
||||
return `${date.day} ${getMonthName(date.month)} ${date.year}`
|
||||
}
|
||||
|
||||
return `${getMonthName(date.month)} ${date.year} ${getPeriodOfYear(date.year).short}`
|
||||
return `${getMonthName(date.month)} ${date.year}`
|
||||
}
|
||||
|
||||
/**
|
||||
* DATE JUMPS & SELECTION
|
||||
*/
|
||||
/**
|
||||
* Jumps the calendar to the given date
|
||||
*
|
||||
* @param date Target date
|
||||
*/
|
||||
function jumpToDate(date: LeimDate): void {
|
||||
function jumpToDate(date: RPGDate): void {
|
||||
params.day = date.day.toString()
|
||||
params.month = date.month.toString()
|
||||
params.year = date.year.toString()
|
||||
@@ -376,27 +372,247 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
/**
|
||||
* Jump the calendar to the default date
|
||||
*/
|
||||
function selectDate(date: LeimDate): void {
|
||||
function selectDate(date: RPGDate): void {
|
||||
selectedDate.value = date
|
||||
}
|
||||
|
||||
const isAdvancedSearchOpen: Ref<boolean> = ref<boolean>(false)
|
||||
/**
|
||||
* DATE OPERATIONS
|
||||
*
|
||||
* Used to convert dates, sort them and compare them
|
||||
*/
|
||||
/**
|
||||
* Converts a RPGDate to its equivalent in days
|
||||
*
|
||||
* @param dateToConvert The date object
|
||||
* @returns How many days does it represent
|
||||
*/
|
||||
function convertDateToDays(dateToConvert: RPGDate): number {
|
||||
let numberOfDays: number = dateToConvert.day
|
||||
|
||||
function revealAdvancedSearch() {
|
||||
isAdvancedSearchOpen.value = true
|
||||
// Get only the remaining months on the year
|
||||
const remainingMonths = sortedMonths.value.filter((m) => dateToConvert.month >= m.position)
|
||||
// From remaining months, reduce their days value
|
||||
const monthDaysToAdd = remainingMonths.reduce((a, b) => {
|
||||
return a + b.days
|
||||
}, 0)
|
||||
|
||||
numberOfDays = numberOfDays + monthDaysToAdd
|
||||
numberOfDays = numberOfDays + dateToConvert.year * daysPerYear.value
|
||||
|
||||
return numberOfDays - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* From two dates, get the difference in days between them
|
||||
* @param baseDate The base date
|
||||
* @param relativeDate The year to compare it to
|
||||
* @returns The number of days separating the two dates (both positive and negative numbers)
|
||||
*/
|
||||
function getDifferenceInDays(baseDate: RPGDate, relativeDate: RPGDate): number {
|
||||
return convertDateToDays(relativeDate) - convertDateToDays(baseDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether two dates are identical
|
||||
*
|
||||
* @param date1 First date
|
||||
* @param date2 Second date
|
||||
* @returns True if the dates are identical
|
||||
*/
|
||||
function areDatesIdentical(date1: RPGDate, date2: RPGDate): boolean {
|
||||
return getDifferenceInDays({ ...date1 }, { ...date2 }) === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare dates (used for array sorting)
|
||||
*
|
||||
* @param date1 First date
|
||||
* @param date2 Second date
|
||||
* @returns 1 means the first date comes before the second, -1 means the second comes before the first, and 0 if they're identical
|
||||
*/
|
||||
function compareDates(a: RPGDate, b: RPGDate, order: RPGDateOrder = 'desc'): number {
|
||||
// Reverses the order if specified
|
||||
const orderFactor: number = order === 'desc' ? 1 : -1
|
||||
|
||||
// Compare years
|
||||
if (a.year < b.year) return -1 * orderFactor
|
||||
if (a.year > b.year) return 1 * orderFactor
|
||||
|
||||
// Compare months
|
||||
if (a.month < b.month) return -1 * orderFactor
|
||||
if (a.month > b.month) return 1 * orderFactor
|
||||
|
||||
// Compare days
|
||||
if (a.day < b.day) return -1 * orderFactor
|
||||
if (a.day > b.day) return 1 * orderFactor
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* From two dates, gives information on how many years, months and days it has been / will be between them
|
||||
*
|
||||
* @param baseDate The base date
|
||||
* @param relativeDate The year to compare it to
|
||||
* @returns A string with info on how the relative date differs to the base date
|
||||
*/
|
||||
function getRelativeString(
|
||||
baseDate: RPGDate,
|
||||
relativeDate: RPGDate,
|
||||
formatting: 'compact' | 'complex' = 'complex'
|
||||
): string {
|
||||
const differenceInDays: number = getDifferenceInDays(baseDate, relativeDate)
|
||||
let output: string = ''
|
||||
let direction: 'past' | 'present' | 'future' = 'present'
|
||||
let directionPrefix: string = ''
|
||||
|
||||
// Check whether it's a past or future date
|
||||
if (differenceInDays > 0) {
|
||||
direction = 'future'
|
||||
} else if (differenceInDays < 0) {
|
||||
direction = 'past'
|
||||
}
|
||||
|
||||
if (formatting === 'complex') {
|
||||
// Handle if it's the same date
|
||||
if (direction === 'present') {
|
||||
return "Aujourd'hui"
|
||||
}
|
||||
if (differenceInDays === -2) {
|
||||
return 'Avant-hier'
|
||||
}
|
||||
if (differenceInDays === -1) {
|
||||
return 'Hier'
|
||||
}
|
||||
if (differenceInDays === 1) {
|
||||
return 'Demain'
|
||||
}
|
||||
if (differenceInDays === 2) {
|
||||
return 'Après-demain'
|
||||
}
|
||||
|
||||
// Get relevant prefix for the string
|
||||
if (direction === 'future') {
|
||||
directionPrefix = 'Dans '
|
||||
} else if (direction === 'past') {
|
||||
directionPrefix = 'Il y a '
|
||||
}
|
||||
|
||||
output += directionPrefix
|
||||
}
|
||||
|
||||
const isSameMonth = baseDate.month === relativeDate.month
|
||||
const isSameYear = baseDate.year === relativeDate.year
|
||||
|
||||
// At this point, we are beyond simple scenarios and must calculate the distance accurately
|
||||
|
||||
// Get which date should come first
|
||||
let ancientDate: RPGDate
|
||||
let futureDate: RPGDate
|
||||
|
||||
if (direction === "future") {
|
||||
ancientDate = baseDate
|
||||
futureDate = relativeDate
|
||||
} else {
|
||||
ancientDate = relativeDate
|
||||
futureDate = baseDate
|
||||
}
|
||||
|
||||
// The pivot point that will help proceed towards the relativeDate
|
||||
const datePivot: RPGDate = { ...ancientDate }
|
||||
// The accumulator that will hold the distance between the two dates
|
||||
const dateAcc: RPGDate = { day: 0, month: 0, year: 0 }
|
||||
|
||||
// If we are on the same month / year during acceleration, just don't rev up
|
||||
if (isSameMonth && isSameYear) {
|
||||
dateAcc.day = futureDate.day - datePivot.day
|
||||
|
||||
if (dateAcc.day === 1) {
|
||||
output += ` ${dateAcc.day} jour`
|
||||
} else {
|
||||
output += ` ${dateAcc.day} jours`
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// If we are on the same year during acceleration, just don't rev up the next month, it might overflow to the next year
|
||||
else if (isSameYear) {
|
||||
dateAcc.month = futureDate.month - datePivot.month
|
||||
|
||||
output += ` ${dateAcc.month} mois`
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// Else, we need to accelerate and decelerate
|
||||
else {
|
||||
// First, get all the remaining days in the current month
|
||||
const currentMonth = sortedMonths.value[datePivot.month]
|
||||
dateAcc.day = currentMonth.days - datePivot.day
|
||||
if (direction === 'future') {
|
||||
datePivot.month = getNextMonth(datePivot.month)
|
||||
} else {
|
||||
datePivot.month = getPreviousMonth(datePivot.month)
|
||||
}
|
||||
datePivot.day = 1
|
||||
|
||||
// Then, get the remaining months in the year
|
||||
const remainingMonthsStart = sortedMonths.value.filter((m) => datePivot.month < m.position )
|
||||
if (direction === 'future') {
|
||||
dateAcc.month = remainingMonthsStart.length + 1
|
||||
} else {
|
||||
dateAcc.month = remainingMonthsStart.length - 1
|
||||
}
|
||||
datePivot.month = 0
|
||||
|
||||
// Then, Accelerate through years
|
||||
dateAcc.month = dateAcc.month + ((futureDate.year - datePivot.year) - 1) * monthsPerYear.value
|
||||
datePivot.year = futureDate.year
|
||||
|
||||
// Once we reached the relativeYear, decelerate and get through the extra months
|
||||
const remainingMonthsEnd = futureDate.month
|
||||
dateAcc.month = dateAcc.month + remainingMonthsEnd
|
||||
datePivot.month = futureDate.month
|
||||
|
||||
// Then at the destination month, decelerate to get the rest of the days
|
||||
dateAcc.day = dateAcc.day + futureDate.day
|
||||
datePivot.day = futureDate.day
|
||||
|
||||
const computedYear = Math.trunc(dateAcc.month / monthsPerYear.value)
|
||||
const remainderMonths = dateAcc.month % monthsPerYear.value
|
||||
|
||||
if (computedYear >= 1 && remainderMonths) {
|
||||
output += ` ${computedYear} an(s) et ${remainderMonths} mois`
|
||||
}
|
||||
else if (computedYear >= 1 && !remainderMonths) {
|
||||
output += ` ${computedYear} an(s)`;
|
||||
}
|
||||
else {
|
||||
output += ` ${remainderMonths} mois`
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
staticConfig,
|
||||
months,
|
||||
setMonths,
|
||||
sortedMonths,
|
||||
daysPerYear,
|
||||
monthsPerYear,
|
||||
viewTypeOptions,
|
||||
currentConfig,
|
||||
currentDate,
|
||||
currentLeimDate,
|
||||
currentRPGDate,
|
||||
currentMonthData,
|
||||
defaultDate,
|
||||
setDefaultDate,
|
||||
selectedDate: skipHydrate(selectedDate),
|
||||
selectDate,
|
||||
params,
|
||||
getPeriodOfYear,
|
||||
incrementMonth,
|
||||
decrementMonth,
|
||||
setMonth,
|
||||
@@ -412,6 +628,11 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
getViewTypeTitle,
|
||||
isCurrentScreenActive,
|
||||
isAdvancedSearchOpen,
|
||||
convertDateToDays,
|
||||
getDifferenceInDays,
|
||||
areDatesIdentical,
|
||||
compareDates,
|
||||
getRelativeString,
|
||||
revealAdvancedSearch
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,25 +3,10 @@ import { defineStore } from 'pinia'
|
||||
|
||||
export const useCharacters = defineStore('characters', () => {
|
||||
const characters = ref<Character[]>([])
|
||||
const charactersAreLoading = ref<boolean>(false)
|
||||
const charactersLoaded = ref<boolean>(false)
|
||||
|
||||
async function fetchCharacters() {
|
||||
try {
|
||||
charactersAreLoading.value = true
|
||||
const fetched = await useFetch<Character[]>('/api/characters')
|
||||
|
||||
if (fetched.data.value) {
|
||||
charactersLoaded.value = true
|
||||
characters.value = fetched.data.value
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
charactersAreLoading.value = false
|
||||
}
|
||||
async function setCharacters(data: Character[]) {
|
||||
characters.value = data
|
||||
}
|
||||
fetchCharacters()
|
||||
|
||||
return { characters }
|
||||
return { characters, setCharacters }
|
||||
})
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { compareDates, convertDateToDays, daysPerMonth, type LeimDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import type { RPGDate } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/CalendarEvent'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, watch, type Ref } from 'vue'
|
||||
import { useCalendar } from './CalendarStore'
|
||||
|
||||
export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
const { currentDate, currentConfig } = useCalendar()
|
||||
const { currentDate, currentConfig, convertDateToDays, compareDates } = useCalendar()
|
||||
|
||||
const baseEvents = ref<CalendarEvent[]>([])
|
||||
const eventsAreLoading = ref<boolean>(false)
|
||||
const eventsLoaded = ref<boolean>(false)
|
||||
|
||||
function setEvents(data: CalendarEvent[]) {
|
||||
baseEvents.value = data
|
||||
}
|
||||
|
||||
const allEvents = computed(() => baseEvents.value.sort((a, b) => {
|
||||
return compareDates(a.startDate, b.startDate, 'desc')
|
||||
@@ -23,22 +25,6 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
currentEvents.value = computeCurrentEvents()
|
||||
})
|
||||
|
||||
async function fetchEvents() {
|
||||
try {
|
||||
eventsAreLoading.value = true
|
||||
const fetched = await useFetch<CalendarEvent[]>('/api/events')
|
||||
|
||||
if (fetched.data.value) {
|
||||
eventsLoaded.value = true
|
||||
baseEvents.value = fetched.data.value
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
eventsAreLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the event can appear in the front end
|
||||
*
|
||||
@@ -48,8 +34,8 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
* @returns Whether the event should appear in the current view
|
||||
*/
|
||||
function shouldEventBeDisplayed(event: CalendarEvent): boolean {
|
||||
const eventStartDateToDays = convertDateToDays(event.startDate)
|
||||
const eventEndDateToDays: number = event.endDate ? convertDateToDays(event.endDate) : 0
|
||||
// const eventStartDateToDays = convertDateToDays(event.startDate)
|
||||
// const eventEndDateToDays: number = event.endDate ? convertDateToDays(event.endDate) : 0
|
||||
|
||||
const isEventOnCurrentScreen =
|
||||
(event.startDate.year === currentDate.currentYear &&
|
||||
@@ -60,23 +46,26 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
|
||||
// Check whether the event is on the last 8 tiles
|
||||
// This is to allow leap events from appearing on the last 8 tiles
|
||||
const firstDayOfCurrentMonth = convertDateToDays({
|
||||
day: 1,
|
||||
month: currentDate.currentMonth,
|
||||
year: currentDate.currentYear
|
||||
})
|
||||
const lastDayOfCurrentMonth = firstDayOfCurrentMonth + daysPerMonth
|
||||
const last8Tiles = lastDayOfCurrentMonth + 8
|
||||
//
|
||||
// This is not used for now
|
||||
//
|
||||
// const firstDayOfCurrentMonth = convertDateToDays({
|
||||
// day: 1,
|
||||
// month: currentDate.currentMonth,
|
||||
// year: currentDate.currentYear
|
||||
// })
|
||||
// const lastDayOfCurrentMonth = firstDayOfCurrentMonth + daysPerMonth
|
||||
// const last8Tiles = lastDayOfCurrentMonth + 8
|
||||
|
||||
const isEventOnNext8Tiles =
|
||||
(eventStartDateToDays <= last8Tiles && eventStartDateToDays >= lastDayOfCurrentMonth) ||
|
||||
(Boolean(event.endDate) &&
|
||||
eventEndDateToDays <= last8Tiles &&
|
||||
eventEndDateToDays >= lastDayOfCurrentMonth)
|
||||
// const isEventOnNext8Tiles =
|
||||
// (eventStartDateToDays <= last8Tiles && eventStartDateToDays >= lastDayOfCurrentMonth) ||
|
||||
// (Boolean(event.endDate) &&
|
||||
// eventEndDateToDays <= last8Tiles &&
|
||||
// eventEndDateToDays >= lastDayOfCurrentMonth)
|
||||
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
return isEventOnCurrentScreen || isEventOnNext8Tiles
|
||||
return isEventOnCurrentScreen!
|
||||
|
||||
case 'year':
|
||||
return event.startDate.year === currentDate.currentYear
|
||||
@@ -119,8 +108,8 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
event: CalendarEvent,
|
||||
position: 'next' | 'prev' = 'next',
|
||||
initialIsEnd: boolean = false
|
||||
): { event: CalendarEvent; targetDate: LeimDate } {
|
||||
let dateToParse: LeimDate // Day value of the date that the user interacted with
|
||||
): { event: CalendarEvent; targetDate: RPGDate } {
|
||||
let dateToParse: RPGDate // Day value of the date that the user interacted with
|
||||
|
||||
if (initialIsEnd && event.endDate) {
|
||||
dateToParse = event.endDate
|
||||
@@ -132,9 +121,9 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
}
|
||||
|
||||
function getRelativeEventFromDate(
|
||||
date: LeimDate,
|
||||
date: RPGDate,
|
||||
position: 'next' | 'prev' = 'next'
|
||||
): { event: CalendarEvent; targetDate: LeimDate } {
|
||||
): { event: CalendarEvent; targetDate: RPGDate } {
|
||||
const pivotValue = convertDateToDays(date)
|
||||
let t: { eventData: CalendarEvent; distance: number; targetKey: 'startDate' | 'endDate' }[] = []
|
||||
|
||||
@@ -210,5 +199,5 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
}
|
||||
}
|
||||
|
||||
return { allEvents, eventsAreLoading, eventsLoaded, currentEvents, fetchEvents, getRelativeEventFromDate, getRelativeEventFromEvent }
|
||||
return { allEvents, setEvents, currentEvents, getRelativeEventFromDate, getRelativeEventFromEvent }
|
||||
})
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
-- Custom types
|
||||
create type public.app_permission as enum ('events.see.hidden', 'users.ban');
|
||||
create type public.app_role as enum ('sa', 'gm');
|
||||
create type public.app_role as enum ('sa', 'admin', 'moderator', 'user');
|
||||
create type public.app_colors as enum ('red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose', 'black', 'white');
|
||||
|
||||
-- USERS
|
||||
-- DATA STRUCTURES
|
||||
-- Users
|
||||
create table public.users (
|
||||
id uuid references auth.users not null primary key, -- UUID from auth.users
|
||||
username text
|
||||
@@ -14,16 +16,16 @@ create table public.users (
|
||||
comment on table public.users is 'Profile data for each user.';
|
||||
comment on column public.users.id is 'References the internal Supabase Auth user.';
|
||||
|
||||
-- USER ROLES
|
||||
-- Roles
|
||||
create table public.user_roles (
|
||||
id bigint generated by default as identity primary key,
|
||||
user_id uuid references public.users on delete cascade not null,
|
||||
role app_role not null,
|
||||
user_id uuid references public.users on delete cascade not null,
|
||||
unique (user_id, role)
|
||||
);
|
||||
comment on table public.user_roles is 'Application roles for each user.';
|
||||
|
||||
-- ROLE PERMISSIONS
|
||||
-- Permissions
|
||||
create table public.role_permissions (
|
||||
id bigint generated by default as identity primary key,
|
||||
role app_role not null,
|
||||
@@ -32,6 +34,96 @@ create table public.role_permissions (
|
||||
);
|
||||
comment on table public.role_permissions is 'Application permissions for each role.';
|
||||
|
||||
-- Worlds
|
||||
create table public.worlds (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
description text,
|
||||
color app_colors,
|
||||
gm_id uuid references public.users on delete cascade
|
||||
);
|
||||
comment on table public.worlds is 'Worlds belonging to a single user ; a game master.';
|
||||
|
||||
-- World Calendars
|
||||
create table public.calendars (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
today json not null,
|
||||
world_id bigint references public.worlds on delete cascade not null
|
||||
);
|
||||
comment on table public.calendars is 'Calendar settings and configuration attached to a single world.';
|
||||
|
||||
-- Calendar Months
|
||||
create table public.calendar_months (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
days int not null,
|
||||
position int not null,
|
||||
calendar_id bigint references public.calendars on delete cascade not null
|
||||
);
|
||||
comment on table public.calendar_months is 'A calendar month.';
|
||||
|
||||
-- Event categories
|
||||
create table public.calendar_event_categories (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
unique (name)
|
||||
);
|
||||
comment on table public.calendar_event_categories is 'Categories describing events.';
|
||||
|
||||
-- Events
|
||||
create table public.calendar_events (
|
||||
id bigint generated by default as identity primary key,
|
||||
title text not null,
|
||||
description text,
|
||||
start_date json not null,
|
||||
end_date json,
|
||||
category bigint references public.calendar_event_categories on delete cascade,
|
||||
hidden boolean,
|
||||
wiki text,
|
||||
calendar_id bigint references public.calendars on delete cascade not null
|
||||
);
|
||||
comment on table public.calendar_events is 'Events linked to a world';
|
||||
|
||||
-- Link table for events - categories
|
||||
create table public.calendar_event_categories_links (
|
||||
calendar_event_id bigint references public.calendar_events on delete cascade,
|
||||
calendar_event_category_id bigint references public.calendar_event_categories on delete cascade,
|
||||
primary key (calendar_event_id, calendar_event_category_id)
|
||||
);
|
||||
comment on table public.calendar_event_categories_links is 'Link tables for multiple event categories.';
|
||||
|
||||
-- Character categories
|
||||
create table public.character_categories (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
unique (name)
|
||||
);
|
||||
comment on table public.character_categories is 'Categories describing characters';
|
||||
|
||||
-- Characters
|
||||
create table public.characters (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
description text,
|
||||
birth json not null,
|
||||
death json,
|
||||
category bigint references public.character_categories on delete cascade,
|
||||
hidden_birth boolean,
|
||||
hidden_death boolean,
|
||||
wiki text,
|
||||
world_id bigint references public.worlds on delete cascade not null
|
||||
);
|
||||
comment on table public.characters is 'Characters linked to a world';
|
||||
|
||||
-- Link table for events - categories
|
||||
create table public.character_categories_links (
|
||||
character_id bigint references public.characters on delete cascade,
|
||||
character_category_id bigint references public.character_categories on delete cascade,
|
||||
primary key (character_id, character_category_id)
|
||||
);
|
||||
comment on table public.character_categories_links is 'Link tables for multiple character categories.';
|
||||
|
||||
-- authorize with role-based access control (RBAC)
|
||||
create function public.authorize(
|
||||
requested_permission app_permission
|
||||
@@ -54,11 +146,114 @@ $$ language plpgsql security definer set search_path = public;
|
||||
alter table public.users enable row level security;
|
||||
alter table public.user_roles enable row level security;
|
||||
alter table public.role_permissions enable row level security;
|
||||
alter table public.worlds enable row level security;
|
||||
alter table public.character_categories enable row level security;
|
||||
alter table public.character_categories_links enable row level security;
|
||||
alter table public.characters enable row level security;
|
||||
alter table public.calendars enable row level security;
|
||||
alter table public.calendar_months enable row level security;
|
||||
alter table public.calendar_events enable row level security;
|
||||
alter table public.calendar_event_categories enable row level security;
|
||||
alter table public.calendar_event_categories_links enable row level security;
|
||||
|
||||
-- User policies
|
||||
create policy "Allow logged-in read access" on public.users for select using ( auth.role() = 'authenticated' );
|
||||
create policy "Allow individual insert access" on public.users for insert with check ( auth.uid() = id );
|
||||
create policy "Allow individual update access" on public.users for update using ( auth.uid() = id );
|
||||
create policy "Allow individual read access" on public.user_roles for select using ( auth.uid() = user_id );
|
||||
|
||||
-- World policies
|
||||
create policy "Allow individual read access for GMs" on public.worlds for select using ( ( auth.uid() = gm_id ) );
|
||||
create policy "Allow individual insert access for GMs" on public.worlds for insert with check ( auth.uid() = gm_id );
|
||||
create policy "Allow individual update access for GMs" on public.worlds for update using ( auth.uid() = gm_id );
|
||||
|
||||
-- Calendar policies
|
||||
create policy "Allow individual read access for GMs" on public.calendars for select using (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = calendars.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.calendars for insert with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = calendars.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.calendars for update with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = calendars.world_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Month policies
|
||||
create policy "Allow individual read access for GMs" on public.calendar_months for select using (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_months.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.calendar_months for insert with check (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_months.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.calendar_months for update with check (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_months.calendar_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Event policies
|
||||
create policy "Allow individual read access for GMs" on public.calendar_events for select using (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_events.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.calendar_events for insert with check (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_events.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.calendar_events for update with check (
|
||||
exists (
|
||||
select 1 from calendars
|
||||
where calendars.id = calendar_events.calendar_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Character policies
|
||||
create policy "Allow individual read access for GMs" on public.characters for select using (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.characters for insert with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.characters for update with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Categories are public to view but not to insert
|
||||
-- Needs to be refactored maybe, if in the future we want a default set AND user defined ones
|
||||
create policy "Allow logged-in read access" on public.calendar_event_categories for select using ( auth.role() = 'authenticated' );
|
||||
create policy "Allow logged-in read access" on public.calendar_event_categories_links for select using ( auth.role() = 'authenticated' );
|
||||
create policy "Allow logged-in read access" on public.character_categories for select using ( auth.role() = 'authenticated' );
|
||||
create policy "Allow logged-in read access" on public.character_categories_links for select using ( auth.role() = 'authenticated' );
|
||||
|
||||
-- Send "previous data" on change
|
||||
alter table public.users replica identity full;
|
||||
|
||||
@@ -69,6 +264,8 @@ declare is_admin boolean;
|
||||
begin
|
||||
insert into public.users (id, username)
|
||||
values (new.id, new.email);
|
||||
insert into public.user_roles (role, user_id)
|
||||
values ('user', new.id);
|
||||
|
||||
return new;
|
||||
end;
|
||||
|
||||
@@ -1,4 +1,301 @@
|
||||
insert into public.role_permissions (role, permission) values ('gm', 'events.see.hidden');
|
||||
|
||||
insert into public.role_permissions (role, permission) values ('sa', 'events.see.hidden');
|
||||
insert into public.role_permissions (role, permission) values ('sa', 'users.ban');
|
||||
|
||||
-- Event categories
|
||||
insert into public.calendar_event_categories (name) values ('naissance');
|
||||
insert into public.calendar_event_categories (name) values ('mort');
|
||||
insert into public.calendar_event_categories (name) values ('catastrophe');
|
||||
insert into public.calendar_event_categories (name) values ('catastrophe naturelle');
|
||||
insert into public.calendar_event_categories (name) values ('inauguration');
|
||||
insert into public.calendar_event_categories (name) values ('religion');
|
||||
insert into public.calendar_event_categories (name) values ('invention');
|
||||
insert into public.calendar_event_categories (name) values ('science');
|
||||
insert into public.calendar_event_categories (name) values ('bénédiction');
|
||||
insert into public.calendar_event_categories (name) values ('joueurs');
|
||||
insert into public.calendar_event_categories (name) values ('découverte');
|
||||
insert into public.calendar_event_categories (name) values ('exploration');
|
||||
insert into public.calendar_event_categories (name) values ('construction');
|
||||
insert into public.calendar_event_categories (name) values ('arcanologie');
|
||||
insert into public.calendar_event_categories (name) values ('criminalité');
|
||||
insert into public.calendar_event_categories (name) values ('scandale');
|
||||
insert into public.calendar_event_categories (name) values ('commerce');
|
||||
insert into public.calendar_event_categories (name) values ('législation');
|
||||
|
||||
-- Character categories
|
||||
insert into public.character_categories (name) values ('joueur');
|
||||
insert into public.character_categories (name) values ('comte');
|
||||
insert into public.character_categories (name) values ('scientifique');
|
||||
insert into public.character_categories (name) values ('mage');
|
||||
insert into public.character_categories (name) values ('professeur');
|
||||
insert into public.character_categories (name) values ('criminel');
|
||||
insert into public.character_categories (name) values ('étincelle');
|
||||
insert into public.character_categories (name) values ('buse blanche');
|
||||
insert into public.character_categories (name) values ('ecclésiastique');
|
||||
insert into public.character_categories (name) values ('militaire');
|
||||
insert into public.character_categories (name) values ('activiste');
|
||||
insert into public.character_categories (name) values ('commerçant');
|
||||
|
||||
-- Worlds
|
||||
insert into public.worlds (name, description, color) values ('Léïm', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis aliquet congue aliquet. Curabitur eu iaculis diam. Nunc iaculis nibh orci, eu semper nunc congue congue. Praesent euismod tortor eget metus tristique lobortis vel in risus. In volutpat ligula orci, id pharetra lectus egestas at.', 'black');
|
||||
|
||||
-- Worlds' calendars
|
||||
insert into public.calendars (world_id, name, today) values (1, 'Calendrier solaire', '{ "day": 23, "month": 8, "year": 3209 }');
|
||||
|
||||
-- Calendar's months
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Jalen', 32, 1);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Malsen', 32, 2);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Verlys', 32, 3);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Nalys', 32, 4);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Verdore', 32, 5);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Sidore', 32, 6);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Lyllion', 32, 7);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Rion', 32, 8);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Farene', 32, 9);
|
||||
insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Dalvene', 32, 10);
|
||||
|
||||
-- Events
|
||||
insert into public.calendar_events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Laurdieu devient la première cité de l''empire de Kaliatos',
|
||||
'L''empire de Kaliatos établi sa capitale dans la cité de Laurdieu, qui connaitra une prospérité nouvelle au sein d''Aldys.',
|
||||
'{ "day": 3, "month": 4, "year": -1932 }',
|
||||
18,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Laurdieu',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Apparition d''Asménys',
|
||||
'La défunte chanteuse Asménys apparaît à un barde pendant son jeune public, démarrant la religion des Prêtresses d''Asménys.',
|
||||
'{ "day": 19, "month": 7, "year": -1358 }',
|
||||
6,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Pr%C3%AAtresses_d%27Asm%C3%A9nys',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'La Rupture',
|
||||
'Les Abysses se déversent à la surface de Léim, à travers plusieurs brèches. Plusieurs hordes de démons se rapprochent des villes, et ce qu''on appellera l''Âge des Abysses commencent alors sur la planète entière.',
|
||||
'{ "day": 26, "month": 5, "year": -756 }',
|
||||
'{ "day": 4, "month": 9, "year": 29 }',
|
||||
3,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Seconde_Rupture',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Marche du sang',
|
||||
'L''empereur de Kaliatos ordonne personnellement la traque de Jorhas Kirendre pour connivence avec les démons. Plusieurs bataillons sont affectés à la chasse de Jorhas, qui se terminera par son incarcération ainsi que la mort de plusieurs centaines de soldats.',
|
||||
'{ "day": 18, "month": 9, "year": -420 }',
|
||||
'{ "day": 27, "month": 1, "year": -419 }',
|
||||
15,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Jorhas_Kirendre',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Exécution de Tyhos',
|
||||
'Le léviathan Tyhos rend l''âme après un combat de plusieurs années contre Lystos, le dieu du Soleil.',
|
||||
'{ "day": 1, "month": 0, "year": 0 }',
|
||||
9,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Tyhos',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, start_date, category, wiki, calendar_id) values (
|
||||
'Traité de Kadel',
|
||||
'{ "day": 29, "month": 4, "year": 100 }',
|
||||
5,
|
||||
'https://alexcreates.fr/leim/index.php/Alliance_Kald%C3%A9lienne#Trait%C3%A9_de_Kadel',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Découverte des Plaines de Poussières',
|
||||
'Les troupes de la reconquête aldienne explorent le littoral d''une immense étendue grise et inerte.',
|
||||
'{ "day": 17, "month": 7, "year": 305 }',
|
||||
11,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Plaines_de_poussi%C3%A8re',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Construction du Rempart de Laurdieu',
|
||||
'Le Grand Conseil Kaldélien ordonne la construction d''une muraille autour des Plaines de Poussières, afin de contenir les démons y sortant.',
|
||||
'{ "day": 30, "month": 2, "year": 340 }',
|
||||
'{ "day": 27, "month": 9, "year": 355 }',
|
||||
13,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Plaines_de_poussi%C3%A8re',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Inauguration de l''Académie Artistique Arcanique',
|
||||
'Scäd Sceni ouvre son institut artistique dédié à l''apprentissage des arts arcaniques.',
|
||||
'{ "day": 11, "month": 6, "year": 2545 }',
|
||||
null,
|
||||
5,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Acad%C3%A9mie_Artistique_Arcanique',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Création de la buse kaldélienne',
|
||||
'Sophia de Rougefer invente la buse kaldélienne, une arme à feu souple à deux canons.',
|
||||
'{ "day": 4, "month": 2, "year": 3113 }',
|
||||
null,
|
||||
7,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Buse_(arme)',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Feux dans les champs de Bamast',
|
||||
'Plusieurs incendies criminels se propagent à travers les champs de sérille des fermiers des littoraux de Bamast. Aucun suspect ni coupable ne sera trouvé et l''enquête sera baclée.',
|
||||
'{ "day": 9, "month": 5, "year": 3200 }',
|
||||
null,
|
||||
15,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Vascylly',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Meurtre de Darléon Typhos',
|
||||
'Darléon Typhos surprend Vascylly fouillant sa demeure peu après la tombée de la nuit. Le majordome de la famille Typhos découvre le corps sans vie de son maître le lendemain.',
|
||||
'{ "day": 21, "month": 6, "year": 3200 }',
|
||||
null,
|
||||
15,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Vascylly',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Scandale Rodhus',
|
||||
'Tivian Rodhus, un professeur estimé de l''Académie Centrale Kaldélienne, est emprisonné pour corruption et aggression sexuelle. Le corps enseignant y est remanié sur ordre de Lazarus Tymos, comte de Nacride.',
|
||||
'{ "day": 25, "month": 9, "year": 3208 }',
|
||||
null,
|
||||
15,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Tivian_Rodhus',
|
||||
1
|
||||
);
|
||||
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Arrivée d''aventuriers à Borélis',
|
||||
'Tara Belyus, Vascylly et Adol Sulvan livrent 3 condamnés à Handany. Ils partent pour la mer durant la journée.',
|
||||
'{ "day": 12, "month": 7, "year": 3209 }',
|
||||
null,
|
||||
10,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Naufrage de l''Éclipse',
|
||||
'L''Éclipse, le navire de la garde contenant des condamnés à destination des Cages Handaniennes, s''échoue au large des côtes montagneuses de la Lance d''Aldys.',
|
||||
'{ "day": 14, "month": 7, "year": 3209 }',
|
||||
null,
|
||||
3,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Emprisonnement de Tivian Rodhus',
|
||||
'Celui qu''on surnomme la Bête d''Ambrose arrive à Handany, où il purgera sa peine.',
|
||||
'{ "day": 14, "month": 7, "year": 3209 }',
|
||||
null,
|
||||
18,
|
||||
false,
|
||||
'https://alexcreates.fr/leim/index.php/Tivian_Rodhus',
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Sulvan et Anastael atteignent Bamast',
|
||||
null,
|
||||
'{ "day": 19, "month": 2, "year": 3210 }',
|
||||
null,
|
||||
10,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Jugement de Bormis Griloup',
|
||||
'Bormis Griloup est jugé coupable d''escroquerie et sabotage aux Cours d''Acier de Tourgrise. Il purgera une peine de 10 ans au sein des prisons royales.',
|
||||
'{ "day": 4, "month": 8, "year": 3209 }',
|
||||
null,
|
||||
18,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Inauguration de la Cloche du Pilier',
|
||||
'Le Moine Premier inaugure la grande cloche d''argent au sommet du Pilier d''Ikami.',
|
||||
'{ "day": 29, "month": 5, "year": 3209 }',
|
||||
null,
|
||||
6,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'1ère disparation à Cantane',
|
||||
'Taleb Vaht décède dans une grotte à la suite d''une attaque d''ischiels enragées.',
|
||||
'{ "day": 28, "month": 7, "year": 3209 }',
|
||||
null,
|
||||
2,
|
||||
true,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'2ème disparation à Cantane',
|
||||
'Donovane le mineur kéturien disparait sans laisser de traces.',
|
||||
'{ "day": 32, "month": 7, "year": 3209 }',
|
||||
null,
|
||||
2,
|
||||
true,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'3ème disparation à Cantane',
|
||||
'Disparition de Sébastien, gredin sauride.',
|
||||
'{ "day": 10, "month": 8, "year": 3209 }',
|
||||
null,
|
||||
2,
|
||||
true,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'4ème disparation à Cantane',
|
||||
'Disparition de Thérence, patrouilleur sauride de la Vieille Garde.',
|
||||
'{ "day": 19, "month": 8, "year": 3209 }',
|
||||
null,
|
||||
2,
|
||||
true,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'5ème disparation à Cantane',
|
||||
'Disparition de Mathilda Boulais, vendeuse de pierres.',
|
||||
'{ "day": 22, "month": 8, "year": 3209 }',
|
||||
null,
|
||||
2,
|
||||
true,
|
||||
null,
|
||||
1
|
||||
);
|
||||
insert into public.calendar_events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Grande Banque Minérale de Cantane',
|
||||
'Les artisans et mineurs de Rougefer se réunissent à Cantane pour vendre le fruit de leur dur labeur.',
|
||||
'{ "day": 23, "month": 8, "year": 3209 }',
|
||||
'{ "day": 26, "month": 8, "year": 3209 }',
|
||||
17,
|
||||
false,
|
||||
null,
|
||||
1
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user