frontend: run lint

Signed-off-by: Nicolas Froger <nicolas@kektus.xyz>
This commit is contained in:
Nicolas Froger 2024-07-27 02:10:59 +02:00
commit 2647ac244d
No known key found for this signature in database
61 changed files with 426 additions and 592 deletions

View file

@ -9,12 +9,16 @@ const appVersion = ref(import.meta.env.VITE_APP_VERSION)
<template>
<div class="fixed w-full top-0 z-50 flex items-center backdrop-blur-md bg-black/10 gap-2 px-3">
<a href="https://www.kektus.fr"
><h1 id="kektus" class="font-bold select-none mx-3 my-3 text-2xl hover:text-3xl transition-all duration-300">
kektus</h1></a
><h1
id="kektus"
class="font-bold select-none mx-3 my-3 text-2xl hover:text-3xl transition-all duration-300"
>
kektus
</h1></a
>
<h1 class="font-light text-lg my-2 mr-3">carnet de voyage été 2024</h1>
<div class="flex-grow"></div>
<RouterLink :to="{name: 'home'}">
<RouterLink :to="{ name: 'home' }">
<div class="p-2 bg-black/10 hover:bg-black/20 transition-colors duration-300 rounded-lg">
<Images size="20" />
</div>
@ -26,8 +30,11 @@ const appVersion = ref(import.meta.env.VITE_APP_VERSION)
</RouterLink>
</div>
<RouterView />
<p class="fixed right-0 bottom-0 z-50 p-1 font-extralight text-sm"><a
href="https://gitlab.kektus.xyz/kektus/services/summer2024" target="_blank">v{{ appVersion }}</a></p>
<p class="fixed right-0 bottom-0 z-50 p-1 font-extralight text-sm">
<a href="https://gitlab.kektus.xyz/kektus/services/summer2024" target="_blank"
>v{{ appVersion }}</a
>
</p>
</template>
<style scoped></style>

View file

@ -2,20 +2,20 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
body {
font-family: 'Raleway', sans-serif;
font-family: 'Raleway', sans-serif;
}
#kektus {
font-family: 'Montserrat', sans-serif;
font-family: 'Montserrat', sans-serif;
}
.ol-zoom {
top: 120px !important;
left: .5em;
top: 120px !important;
left: 0.5em;
}
@media screen and (min-width: 500px) {
.ol-zoom {
top: 70px !important;
}
}
.ol-zoom {
top: 70px !important;
}
}

View file

@ -10,7 +10,7 @@ const countdownInterval = setInterval(() => {
const now = new Date().getTime()
const distance = countdownDate - now
if (distance < 0) {
countdownDisplay.value = 'c\'est parti !'
countdownDisplay.value = "c'est parti !"
clearInterval(countdownInterval)
return
}
@ -20,18 +20,20 @@ const countdownInterval = setInterval(() => {
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((distance % (1000 * 60)) / 1000)
countdownDisplay.value = `${days} jour` + (days > 1 ? 's' : '') +
` ${hours} heure` + (hours > 1 ? 's' : '') +
` ${minutes} minute` + (minutes > 1 ? 's' : '') +
` ${seconds} seconde` + (seconds > 1 ? 's' : '')
countdownDisplay.value =
`${days} jour` +
(days > 1 ? 's' : '') +
` ${hours} heure` +
(hours > 1 ? 's' : '') +
` ${minutes} minute` +
(minutes > 1 ? 's' : '') +
` ${seconds} seconde` +
(seconds > 1 ? 's' : '')
}, 1000)
</script>
<template>
<p :class="props.class">{{ countdownDisplay }}</p>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -14,42 +14,42 @@ const postActionBtn = ref(null)
function postDataToggle() {
if (postDataVisible.value) {
for (const data of postData.value) {
data.classList.remove('backdrop-blur-sm');
data.classList.remove('bg-gray-400/10');
data.classList.add('bg-gray-400/0');
data.classList.remove('backdrop-blur-sm')
data.classList.remove('bg-gray-400/10')
data.classList.add('bg-gray-400/0')
}
for (const title of postDataTitle.value) {
title.classList.add("opacity-0");
title.classList.add('opacity-0')
}
for (const desc of postDataDescription.value) {
desc.classList.add("opacity-0");
desc.classList.remove("scrollable-element");
desc.classList.add('opacity-0')
desc.classList.remove('scrollable-element')
}
for (const btn of postActionBtn.value) {
btn.classList.remove("top-0");
btn.classList.remove("right-0");
btn.classList.add("left-0");
btn.classList.add("bottom-0");
btn.classList.remove('top-0')
btn.classList.remove('right-0')
btn.classList.add('left-0')
btn.classList.add('bottom-0')
}
postDataVisible.value = false
} else {
for (const data of postData.value) {
data.classList.add('backdrop-blur-sm');
data.classList.add('bg-gray-400/10');
data.classList.remove('bg-gray-400/0');
data.classList.add('backdrop-blur-sm')
data.classList.add('bg-gray-400/10')
data.classList.remove('bg-gray-400/0')
}
for (const title of postDataTitle.value) {
title.classList.remove("opacity-0");
title.classList.remove('opacity-0')
}
for (const desc of postDataDescription.value) {
desc.classList.remove("opacity-0");
desc.classList.add("scrollable-element");
desc.classList.remove('opacity-0')
desc.classList.add('scrollable-element')
}
for (const btn of postActionBtn.value) {
btn.classList.add("top-0");
btn.classList.add("right-0");
btn.classList.remove("left-0");
btn.classList.remove("bottom-0");
btn.classList.add('top-0')
btn.classList.add('right-0')
btn.classList.remove('left-0')
btn.classList.remove('bottom-0')
}
postDataVisible.value = true
}
@ -83,5 +83,4 @@ function postDataToggle() {
</div>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -1,11 +1,11 @@
<script setup>
import { alertVariants } from ".";
import { cn } from "@/lib/utils";
import { alertVariants } from '.'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
variant: { type: null, required: false },
});
variant: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,21 +1,21 @@
import { cva } from "class-variance-authority";
import { cva } from 'class-variance-authority'
export { default as Alert } from "./Alert.vue";
export { default as AlertTitle } from "./AlertTitle.vue";
export { default as AlertDescription } from "./AlertDescription.vue";
export { default as Alert } from './Alert.vue'
export { default as AlertTitle } from './AlertTitle.vue'
export { default as AlertDescription } from './AlertDescription.vue'
export const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
{
variants: {
variant: {
default: "bg-background text-foreground",
default: 'bg-background text-foreground',
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive'
}
},
defaultVariants: {
variant: "default",
},
},
);
variant: 'default'
}
}
)

View file

@ -1,20 +1,13 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
<div
:class="
cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
props.class,
)
"
>
<div :class="cn('rounded-lg border bg-card text-card-foreground shadow-sm', props.class)">
<slot />
</div>
</template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,17 +1,13 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
<h3
:class="
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
"
>
<h3 :class="cn('text-2xl font-semibold leading-none tracking-tight', props.class)">
<slot />
</h3>
</template>

View file

@ -1,6 +1,6 @@
export { default as Card } from "./Card.vue";
export { default as CardHeader } from "./CardHeader.vue";
export { default as CardTitle } from "./CardTitle.vue";
export { default as CardDescription } from "./CardDescription.vue";
export { default as CardContent } from "./CardContent.vue";
export { default as CardFooter } from "./CardFooter.vue";
export { default as Card } from './Card.vue'
export { default as CardHeader } from './CardHeader.vue'
export { default as CardTitle } from './CardTitle.vue'
export { default as CardDescription } from './CardDescription.vue'
export { default as CardContent } from './CardContent.vue'
export { default as CardFooter } from './CardFooter.vue'

View file

@ -1,34 +1,34 @@
<script setup>
import { useProvideCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { useProvideCarousel } from './useCarousel'
import { cn } from '@/lib/utils'
const props = defineProps({
opts: { type: null, required: false },
plugins: { type: null, required: false },
orientation: { type: String, required: false, default: "horizontal" },
class: { type: null, required: false },
});
orientation: { type: String, required: false, default: 'horizontal' },
class: { type: null, required: false }
})
const emits = defineEmits(["init-api"]);
const emits = defineEmits(['init-api'])
const carouselArgs = useProvideCarousel(props, emits);
const carouselArgs = useProvideCarousel(props, emits)
defineExpose(carouselArgs);
defineExpose(carouselArgs)
function onKeyDown(event) {
const prevKey = props.orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
const nextKey = props.orientation === "vertical" ? "ArrowDown" : "ArrowRight";
const prevKey = props.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'
const nextKey = props.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'
if (event.key === prevKey) {
event.preventDefault();
carouselArgs.scrollPrev();
event.preventDefault()
carouselArgs.scrollPrev()
return;
return
}
if (event.key === nextKey) {
event.preventDefault();
carouselArgs.scrollNext();
event.preventDefault()
carouselArgs.scrollNext()
}
}
</script>

View file

@ -1,28 +1,22 @@
<script setup>
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { useCarousel } from './useCarousel'
import { cn } from '@/lib/utils'
defineOptions({
inheritAttrs: false,
});
inheritAttrs: false
})
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { carouselRef, orientation } = useCarousel();
const { carouselRef, orientation } = useCarousel()
</script>
<template>
<div ref="carouselRef" class="overflow-hidden">
<div
:class="
cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
props.class,
)
"
:class="cn('flex', orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col', props.class)"
v-bind="$attrs"
>
<slot />

View file

@ -1,12 +1,12 @@
<script setup>
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { useCarousel } from './useCarousel'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { orientation } = useCarousel();
const { orientation } = useCarousel()
</script>
<template>
@ -17,7 +17,7 @@ const { orientation } = useCarousel();
cn(
'min-w-0 shrink-0 grow-0 basis-full',
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
props.class,
props.class
)
"
>

View file

@ -1,14 +1,14 @@
<script setup>
import { ArrowRight } from "lucide-vue-next";
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { ArrowRight } from 'lucide-vue-next'
import { useCarousel } from './useCarousel'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { orientation, canScrollNext, scrollNext } = useCarousel();
const { orientation, canScrollNext, scrollNext } = useCarousel()
</script>
<template>
@ -20,7 +20,7 @@ const { orientation, canScrollNext, scrollNext } = useCarousel();
orientation === 'horizontal'
? '-right-12 top-1/2 -translate-y-1/2'
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
props.class,
props.class
)
"
variant="outline"

View file

@ -1,14 +1,14 @@
<script setup>
import { ArrowLeft } from "lucide-vue-next";
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { ArrowLeft } from 'lucide-vue-next'
import { useCarousel } from './useCarousel'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { orientation, canScrollPrev, scrollPrev } = useCarousel();
const { orientation, canScrollPrev, scrollPrev } = useCarousel()
</script>
<template>
@ -20,7 +20,7 @@ const { orientation, canScrollPrev, scrollPrev } = useCarousel();
orientation === 'horizontal'
? '-left-12 top-1/2 -translate-y-1/2'
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
props.class,
props.class
)
"
variant="outline"

View file

@ -1,6 +1,6 @@
export { default as Carousel } from "./Carousel.vue";
export { default as CarouselContent } from "./CarouselContent.vue";
export { default as CarouselItem } from "./CarouselItem.vue";
export { default as CarouselPrevious } from "./CarouselPrevious.vue";
export { default as CarouselNext } from "./CarouselNext.vue";
export { useCarousel } from "./useCarousel";
export { default as Carousel } from './Carousel.vue'
export { default as CarouselContent } from './CarouselContent.vue'
export { default as CarouselItem } from './CarouselItem.vue'
export { default as CarouselPrevious } from './CarouselPrevious.vue'
export { default as CarouselNext } from './CarouselNext.vue'
export { useCarousel } from './useCarousel'

View file

@ -1 +1 @@
export {};
export {}

View file

@ -1,41 +1,41 @@
import { createInjectionState } from "@vueuse/core";
import emblaCarouselVue from "embla-carousel-vue";
import { onMounted, ref } from "vue";
import { createInjectionState } from '@vueuse/core'
import emblaCarouselVue from 'embla-carousel-vue'
import { onMounted, ref } from 'vue'
const [useProvideCarousel, useInjectCarousel] = createInjectionState(
({ opts, orientation, plugins }, emits) => {
const [emblaNode, emblaApi] = emblaCarouselVue(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
axis: orientation === 'horizontal' ? 'x' : 'y'
},
plugins,
);
plugins
)
function scrollPrev() {
emblaApi.value?.scrollPrev();
emblaApi.value?.scrollPrev()
}
function scrollNext() {
emblaApi.value?.scrollNext();
emblaApi.value?.scrollNext()
}
const canScrollNext = ref(false);
const canScrollPrev = ref(false);
const canScrollNext = ref(false)
const canScrollPrev = ref(false)
function onSelect(api) {
canScrollNext.value = api?.canScrollNext() || false;
canScrollPrev.value = api?.canScrollPrev() || false;
canScrollNext.value = api?.canScrollNext() || false
canScrollPrev.value = api?.canScrollPrev() || false
}
onMounted(() => {
if (!emblaApi.value) return;
if (!emblaApi.value) return
emblaApi.value?.on("init", onSelect);
emblaApi.value?.on("reInit", onSelect);
emblaApi.value?.on("select", onSelect);
emblaApi.value?.on('init', onSelect)
emblaApi.value?.on('reInit', onSelect)
emblaApi.value?.on('select', onSelect)
emits("init-api", emblaApi.value);
});
emits('init-api', emblaApi.value)
})
return {
carouselRef: emblaNode,
@ -44,18 +44,17 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
canScrollNext,
scrollPrev,
scrollNext,
orientation,
};
},
);
orientation
}
}
)
function useCarousel() {
const carouselState = useInjectCarousel();
const carouselState = useInjectCarousel()
if (!carouselState)
throw new Error("useCarousel must be used within a <Carousel />");
if (!carouselState) throw new Error('useCarousel must be used within a <Carousel />')
return carouselState;
return carouselState
}
export { useCarousel, useProvideCarousel };
export { useCarousel, useProvideCarousel }

View file

@ -1,16 +1,14 @@
<script setup>
import { Slot } from "radix-vue";
import { useFormField } from "./useFormField";
import { Slot } from 'radix-vue'
import { useFormField } from './useFormField'
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
</script>
<template>
<Slot
:id="formItemId"
:aria-describedby="
!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`
"
:aria-describedby="!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`"
:aria-invalid="!!error"
>
<slot />

View file

@ -1,19 +1,16 @@
<script setup>
import { useFormField } from "./useFormField";
import { cn } from "@/lib/utils";
import { useFormField } from './useFormField'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { formDescriptionId } = useFormField();
const { formDescriptionId } = useFormField()
</script>
<template>
<p
:id="formDescriptionId"
:class="cn('text-sm text-muted-foreground', props.class)"
>
<p :id="formDescriptionId" :class="cn('text-sm text-muted-foreground', props.class)">
<slot />
</p>
</template>

View file

@ -1,15 +1,15 @@
<script setup>
import { provide } from "vue";
import { useId } from "radix-vue";
import { FORM_ITEM_INJECTION_KEY } from "./injectionKeys";
import { cn } from "@/lib/utils";
import { provide } from 'vue'
import { useId } from 'radix-vue'
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const id = useId();
provide(FORM_ITEM_INJECTION_KEY, id);
const id = useId()
provide(FORM_ITEM_INJECTION_KEY, id)
</script>
<template>

View file

@ -1,23 +1,20 @@
<script setup>
import { useFormField } from "./useFormField";
import { cn } from "@/lib/utils";
import { Label } from "@/components/ui/label";
import { useFormField } from './useFormField'
import { cn } from '@/lib/utils'
import { Label } from '@/components/ui/label'
const props = defineProps({
for: { type: String, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const { error, formItemId } = useFormField();
const { error, formItemId } = useFormField()
</script>
<template>
<Label
:class="cn(error && 'text-destructive', props.class)"
:for="formItemId"
>
<Label :class="cn(error && 'text-destructive', props.class)" :for="formItemId">
<slot />
</Label>
</template>

View file

@ -1,9 +1,9 @@
<script setup>
import { ErrorMessage } from "vee-validate";
import { toValue } from "vue";
import { useFormField } from "./useFormField";
import { ErrorMessage } from 'vee-validate'
import { toValue } from 'vue'
import { useFormField } from './useFormField'
const { name, formMessageId } = useFormField();
const { name, formMessageId } = useFormField()
</script>
<template>

View file

@ -1,7 +1,7 @@
export { Form, Field as FormField } from "vee-validate";
export { default as FormItem } from "./FormItem.vue";
export { default as FormLabel } from "./FormLabel.vue";
export { default as FormControl } from "./FormControl.vue";
export { default as FormMessage } from "./FormMessage.vue";
export { default as FormDescription } from "./FormDescription.vue";
export { FORM_ITEM_INJECTION_KEY } from "./injectionKeys";
export { Form, Field as FormField } from 'vee-validate'
export { default as FormItem } from './FormItem.vue'
export { default as FormLabel } from './FormLabel.vue'
export { default as FormControl } from './FormControl.vue'
export { default as FormMessage } from './FormMessage.vue'
export { default as FormDescription } from './FormDescription.vue'
export { FORM_ITEM_INJECTION_KEY } from './injectionKeys'

View file

@ -1 +1 @@
export const FORM_ITEM_INJECTION_KEY = Symbol();
export const FORM_ITEM_INJECTION_KEY = Symbol()

View file

@ -3,27 +3,26 @@ import {
useFieldError,
useIsFieldDirty,
useIsFieldTouched,
useIsFieldValid,
} from "vee-validate";
import { inject } from "vue";
import { FORM_ITEM_INJECTION_KEY } from "./injectionKeys";
useIsFieldValid
} from 'vee-validate'
import { inject } from 'vue'
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys'
export function useFormField() {
const fieldContext = inject(FieldContextKey);
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY);
const fieldContext = inject(FieldContextKey)
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY)
if (!fieldContext)
throw new Error("useFormField should be used within <FormField>");
if (!fieldContext) throw new Error('useFormField should be used within <FormField>')
const { name } = fieldContext;
const id = fieldItemContext;
const { name } = fieldContext
const id = fieldItemContext
const fieldState = {
valid: useIsFieldValid(name),
isDirty: useIsFieldDirty(name),
isTouched: useIsFieldTouched(name),
error: useFieldError(name),
};
error: useFieldError(name)
}
return {
id,
@ -31,6 +30,6 @@ export function useFormField() {
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
...fieldState
}
}

View file

@ -1,19 +1,19 @@
<script setup>
import { useVModel } from "@vueuse/core";
import { cn } from "@/lib/utils";
import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils'
const props = defineProps({
defaultValue: { type: [String, Number], required: false },
modelValue: { type: [String, Number], required: false },
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const emits = defineEmits(["update:modelValue"]);
const emits = defineEmits(['update:modelValue'])
const modelValue = useVModel(props, "modelValue", emits, {
const modelValue = useVModel(props, 'modelValue', emits, {
passive: true,
defaultValue: props.defaultValue,
});
defaultValue: props.defaultValue
})
</script>
<template>
@ -22,7 +22,7 @@ const modelValue = useVModel(props, "modelValue", emits, {
:class="
cn(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
props.class,
props.class
)
"
/>

View file

@ -1 +1 @@
export { default as Input } from "./Input.vue";
export { default as Input } from './Input.vue'

View file

@ -1,20 +1,20 @@
<script setup>
import { computed } from "vue";
import { Label } from "radix-vue";
import { cn } from "@/lib/utils";
import { computed } from 'vue'
import { Label } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps({
for: { type: String, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
const { class: _, ...delegated } = props
return delegated;
});
return delegated
})
</script>
<template>
@ -23,7 +23,7 @@ const delegatedProps = computed(() => {
:class="
cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class,
props.class
)
"
>

View file

@ -1 +1 @@
export { default as Label } from "./Label.vue";
export { default as Label } from './Label.vue'

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,15 +1,13 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
<td
:class="cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', props.class)"
>
<td :class="cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', props.class)">
<slot />
</td>
</template>

View file

@ -1,29 +1,25 @@
<script setup>
import { computed } from "vue";
import TableRow from "./TableRow.vue";
import TableCell from "./TableCell.vue";
import { cn } from "@/lib/utils";
import { computed } from 'vue'
import TableRow from './TableRow.vue'
import TableCell from './TableCell.vue'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
colspan: { type: Number, required: false, default: 1 },
});
colspan: { type: Number, required: false, default: 1 }
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
const { class: _, ...delegated } = props
return delegated;
});
return delegated
})
</script>
<template>
<TableRow>
<TableCell
:class="
cn(
'p-4 whitespace-nowrap align-middle text-sm text-foreground',
props.class
)"
:class="cn('p-4 whitespace-nowrap align-middle text-sm text-foreground', props.class)"
v-bind="delegatedProps"
>
<div class="flex items-center justify-center py-10">

View file

@ -1,17 +1,13 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
<tfoot
:class="
cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', props.class)
"
>
<tfoot :class="cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', props.class)">
<slot />
</tfoot>
</template>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
@ -11,7 +11,7 @@ const props = defineProps({
:class="
cn(
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
props.class,
props.class
)
"
>

View file

@ -1,9 +1,9 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>

View file

@ -1,18 +1,15 @@
<script setup>
import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
});
class: { type: null, required: false }
})
</script>
<template>
<tr
:class="
cn(
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
props.class,
)
cn('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', props.class)
"
>
<slot />

View file

@ -1,9 +1,9 @@
export { default as Table } from "./Table.vue";
export { default as TableBody } from "./TableBody.vue";
export { default as TableCell } from "./TableCell.vue";
export { default as TableHead } from "./TableHead.vue";
export { default as TableHeader } from "./TableHeader.vue";
export { default as TableFooter } from "./TableFooter.vue";
export { default as TableRow } from "./TableRow.vue";
export { default as TableCaption } from "./TableCaption.vue";
export { default as TableEmpty } from "./TableEmpty.vue";
export { default as Table } from './Table.vue'
export { default as TableBody } from './TableBody.vue'
export { default as TableCell } from './TableCell.vue'
export { default as TableHead } from './TableHead.vue'
export { default as TableHeader } from './TableHeader.vue'
export { default as TableFooter } from './TableFooter.vue'
export { default as TableRow } from './TableRow.vue'
export { default as TableCaption } from './TableCaption.vue'
export { default as TableEmpty } from './TableEmpty.vue'

View file

@ -1,19 +1,19 @@
<script setup>
import { useVModel } from "@vueuse/core";
import { cn } from "@/lib/utils";
import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils'
const props = defineProps({
class: { type: null, required: false },
defaultValue: { type: [String, Number], required: false },
modelValue: { type: [String, Number], required: false },
});
modelValue: { type: [String, Number], required: false }
})
const emits = defineEmits(["update:modelValue"]);
const emits = defineEmits(['update:modelValue'])
const modelValue = useVModel(props, "modelValue", emits, {
const modelValue = useVModel(props, 'modelValue', emits, {
passive: true,
defaultValue: props.defaultValue,
});
defaultValue: props.defaultValue
})
</script>
<template>
@ -22,7 +22,7 @@ const modelValue = useVModel(props, "modelValue", emits, {
:class="
cn(
'flex min-h-20 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
props.class,
props.class
)
"
/>

View file

@ -1 +1 @@
export { default as Textarea } from "./Textarea.vue";
export { default as Textarea } from './Textarea.vue'

View file

@ -1,5 +1,5 @@
<script setup>
import { TooltipRoot, useForwardPropsEmits } from "radix-vue";
import { TooltipRoot, useForwardPropsEmits } from 'radix-vue'
const props = defineProps({
defaultOpen: { type: Boolean, required: false },
@ -8,11 +8,11 @@ const props = defineProps({
disableHoverableContent: { type: Boolean, required: false },
disableClosingTrigger: { type: Boolean, required: false },
disabled: { type: Boolean, required: false },
ignoreNonKeyboardFocus: { type: Boolean, required: false },
});
const emits = defineEmits(["update:open"]);
ignoreNonKeyboardFocus: { type: Boolean, required: false }
})
const emits = defineEmits(['update:open'])
const forwarded = useForwardPropsEmits(props, emits);
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>

View file

@ -1,11 +1,11 @@
<script setup>
import { computed } from "vue";
import { TooltipContent, TooltipPortal, useForwardPropsEmits } from "radix-vue";
import { cn } from "@/lib/utils";
import { computed } from 'vue'
import { TooltipContent, TooltipPortal, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils'
defineOptions({
inheritAttrs: false,
});
inheritAttrs: false
})
const props = defineProps({
forceMount: { type: Boolean, required: false },
@ -22,18 +22,18 @@ const props = defineProps({
arrowPadding: { type: Number, required: false },
sticky: { type: String, required: false },
hideWhenDetached: { type: Boolean, required: false },
class: { type: null, required: false },
});
class: { type: null, required: false }
})
const emits = defineEmits(["escapeKeyDown", "pointerDownOutside"]);
const emits = defineEmits(['escapeKeyDown', 'pointerDownOutside'])
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
const { class: _, ...delegated } = props
return delegated;
});
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits);
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
@ -43,7 +43,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
:class="
cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
props.class
)
"
>

View file

@ -1,5 +1,5 @@
<script setup>
import { TooltipProvider } from "radix-vue";
import { TooltipProvider } from 'radix-vue'
const props = defineProps({
delayDuration: { type: Number, required: false },
@ -7,8 +7,8 @@ const props = defineProps({
disableHoverableContent: { type: Boolean, required: false },
disableClosingTrigger: { type: Boolean, required: false },
disabled: { type: Boolean, required: false },
ignoreNonKeyboardFocus: { type: Boolean, required: false },
});
ignoreNonKeyboardFocus: { type: Boolean, required: false }
})
</script>
<template>

View file

@ -1,10 +1,10 @@
<script setup>
import { TooltipTrigger } from "radix-vue";
import { TooltipTrigger } from 'radix-vue'
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
});
as: { type: null, required: false }
})
</script>
<template>

View file

@ -1,4 +1,4 @@
export { default as Tooltip } from "./Tooltip.vue";
export { default as TooltipContent } from "./TooltipContent.vue";
export { default as TooltipTrigger } from "./TooltipTrigger.vue";
export { default as TooltipProvider } from "./TooltipProvider.vue";
export { default as Tooltip } from './Tooltip.vue'
export { default as TooltipContent } from './TooltipContent.vue'
export { default as TooltipTrigger } from './TooltipTrigger.vue'
export { default as TooltipProvider } from './TooltipProvider.vue'

View file

@ -9,14 +9,14 @@ import VueFullPage from 'vue-fullpage.js'
import App from './App.vue'
import router from './router'
import "vue3-openlayers/styles.css";
import OpenLayersMap from "vue3-openlayers";
import 'vue3-openlayers/styles.css'
import OpenLayersMap from 'vue3-openlayers'
const app = createApp(App)
app.use(VueFullPage)
app.use(createPinia())
app.use(router)
app.use(OpenLayersMap);
app.use(OpenLayersMap)
app.mount('#app')

View file

@ -2,41 +2,39 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
import { API_BASE_URL } from '@/config.js'
export const useAuthStore = defineStore("auth", () => {
const adminToken = ref("");
const isAuth = ref(false);
const error = ref(false);
export const useAuthStore = defineStore('auth', () => {
const adminToken = ref('')
const isAuth = ref(false)
const error = ref(false)
function login(token) {
adminToken.value = "";
isAuth.value = false;
error.value = false;
adminToken.value = ''
isAuth.value = false
error.value = false
return fetch(API_BASE_URL + "/admin/auth/check", {
return fetch(API_BASE_URL + '/admin/auth/check', {
headers: {
"X-admin-token": token
'X-admin-token': token
}
}).then(resp => {
}).then((resp) => {
if (resp.ok) {
adminToken.value = token;
isAuth.value = true;
localStorage.setItem("kektus-summer-admin-token", token)
adminToken.value = token
isAuth.value = true
localStorage.setItem('kektus-summer-admin-token', token)
} else {
error.value = true;
error.value = true
}
})
}
function checkFromLocalStorage() {
const storedToken = localStorage.getItem("kektus-summer-admin-token")
const storedToken = localStorage.getItem('kektus-summer-admin-token')
if (storedToken) {
return login(storedToken)
}
return Promise.resolve();
return Promise.resolve()
}
return { adminToken, login, checkFromLocalStorage, isAuth, error }
})
})

View file

@ -1,5 +1,4 @@
<script setup>
import { Button } from '@/components/ui/button/index.js'
import { CirclePlus, Pencil, Trash, MapPin } from 'lucide-vue-next'
import { useAdminPostsStore } from '@/stores/adminPosts.js'
@ -72,6 +71,4 @@ onMounted(() => {
</div>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -8,7 +8,14 @@ import { Label } from '@/components/ui/label/index.js'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
import { onMounted, onUnmounted, ref } from 'vue'
import { AlertCircle, ArrowLeft, ArrowRight, CircleCheckBig, CirclePlus, RefreshCcw } from 'lucide-vue-next'
import {
AlertCircle,
ArrowLeft,
ArrowRight,
CircleCheckBig,
CirclePlus,
RefreshCcw
} from 'lucide-vue-next'
import {
Carousel,
CarouselContent,

View file

@ -1,5 +1,4 @@
<script setup>
import { useAuthStore } from '@/stores/auth.js'
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
@ -14,15 +13,17 @@ import router from '@/router/index.js'
import { useRoute } from 'vue-router'
const authStore = useAuthStore()
const nextView = useRoute().query.redirect;
const nextView = useRoute().query.redirect
onMounted(() => {
authStore.checkFromLocalStorage().then(onLogin);
authStore.checkFromLocalStorage().then(onLogin)
})
const formSchema = toTypedSchema(z.object({
token: z.string().min(1)
}))
const formSchema = toTypedSchema(
z.object({
token: z.string().min(1)
})
)
const form = useForm({
validationSchema: formSchema
})
@ -32,16 +33,14 @@ const onSubmit = form.handleSubmit((values) => {
})
function onLogin() {
if (!authStore.isAuth)
return;
if (!authStore.isAuth) return
if (nextView) {
router.push(nextView);
router.push(nextView)
} else {
router.push({name: "admin"});
router.push({ name: 'admin' })
}
}
</script>
<template>
@ -57,7 +56,6 @@ function onLogin() {
<AlertDescription>Token invalide</AlertDescription>
</Alert>
<div id="form-container" ref="formContainer">
<form id="login-form" class="space-y-6" @submit="onSubmit">
<FormField v-slot="{ componentField }" name="token">
@ -68,15 +66,11 @@ function onLogin() {
</FormControl>
</FormItem>
</FormField>
<Button class="mt-6 w-full" type="submit">
Login
</Button>
<Button class="mt-6 w-full" type="submit"> Login </Button>
</form>
</div>
</div>
</div>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -1,9 +1,13 @@
<script setup>
import { usePostsStore } from '@/stores/posts.js'
import { computed, onMounted, ref } from 'vue'
import { Camera, Goal, MapPin } from 'lucide-vue-next'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip/index.js'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from '@/components/ui/tooltip/index.js'
import { useLocationStore } from '@/stores/location.js'
import { fromLonLat } from 'ol/proj.js'
@ -39,16 +43,8 @@ const goalLocations = ref([
<template>
<div class="fixed h-full w-full">
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
class="h-full w-full"
>
<ol-view
ref="view"
:zoom="3.6"
:center="[ 652293.6169027708, 6425265.202945196 ]"
/>
<ol-map :loadTilesWhileAnimating="true" :loadTilesWhileInteracting="true" class="h-full w-full">
<ol-view ref="view" :zoom="3.6" :center="[652293.6169027708, 6425265.202945196]" />
<ol-tile-layer>
<ol-source-osm />
@ -56,16 +52,18 @@ const goalLocations = ref([
<ol-scaleline-control />
<ol-overlay
v-for="post in postStore.posts" :key="post.id"
v-for="post in postStore.posts"
:key="post.id"
:position="post.projectedCoordinates"
:autoPan="true"
>
<TooltipProvider>
<Tooltip :default-open="true">
<TooltipTrigger as-child>
<RouterLink :to="{ name: 'home', hash: '#post-' + post.id}">
<RouterLink :to="{ name: 'home', hash: '#post-' + post.id }">
<div
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-indigo-950 hover:bg-indigo-900 transition-colors duration-200">
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-indigo-950 hover:bg-indigo-900 transition-colors duration-200"
>
<Camera size="16" />
</div>
</RouterLink>
@ -85,7 +83,8 @@ const goalLocations = ref([
<Tooltip :default-open="true">
<TooltipTrigger as-child>
<div
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-rose-950 hover:bg-rose-900 transition-colors duration-200">
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-rose-950 hover:bg-rose-900 transition-colors duration-200"
>
<MapPin size="16" />
</div>
</TooltipTrigger>
@ -97,7 +96,8 @@ const goalLocations = ref([
</ol-overlay>
<ol-overlay
v-for="loc in goalLocations" :key="loc.name"
v-for="loc in goalLocations"
:key="loc.name"
:position="loc.coords"
:autoPan="true"
>
@ -105,7 +105,8 @@ const goalLocations = ref([
<Tooltip>
<TooltipTrigger as-child>
<div
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-emerald-800 hover:bg-emerald-700 transition-colors duration-200">
class="-translate-x-1/2 -translate-y-1/2 p-1 rounded-lg bg-emerald-800 hover:bg-emerald-700 transition-colors duration-200"
>
<Goal size="16" />
</div>
</TooltipTrigger>
@ -119,15 +120,9 @@ const goalLocations = ref([
<ol-vector-layer v-if="postsLinesCoordinates.length > 1">
<ol-source-vector>
<ol-feature ref="profileFeatureRef">
<ol-geom-line-string
:coordinates="postsLinesCoordinates"
></ol-geom-line-string>
<ol-geom-line-string :coordinates="postsLinesCoordinates"></ol-geom-line-string>
<ol-style>
<ol-style-stroke
color="white"
width="5"
:lineDash="[15, 15]"
></ol-style-stroke>
<ol-style-stroke color="white" width="5" :lineDash="[15, 15]"></ol-style-stroke>
</ol-style>
</ol-feature>
</ol-source-vector>
@ -136,5 +131,4 @@ const goalLocations = ref([
</div>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -32,8 +32,7 @@ instance it still finds a way to be alive and break when changing views????
*/
async function initFullpage() {
if (fullPageInit)
return
if (fullPageInit) return
fullpageKey.value += 5
fullpageEnable.value = true
@ -41,7 +40,9 @@ async function initFullpage() {
try {
fullpage.value.init()
} catch (e) {
console.log('failed to reload fullpage.js because it sucks with vue, reloading page as last resort')
console.log(
'failed to reload fullpage.js because it sucks with vue, reloading page as last resort'
)
window.location.reload()
return
}
@ -57,7 +58,10 @@ onMounted(() => {
onBeforeUnmount(async () => {
console.log('caught post view unmount')
if (typeof window.fullpage_api !== 'undefined' && typeof window.fullpage_api.destroy !== 'undefined') {
if (
typeof window.fullpage_api !== 'undefined' &&
typeof window.fullpage_api.destroy !== 'undefined'
) {
window.fullpage_api.destroy('all')
}
fullpageKey.value++
@ -100,11 +104,18 @@ function onLeave(origin, destination, direction, trigger) {
class="col-start-1 col-span-full row-start-1 max-h-52 h-fit mt-28 sm:mt-16 lg:m-0 lg:col-start-5 lg:col-span-1 lg:row-start-3 lg:row-span-1 pointer-events-auto overflow-y-scroll transition-opacity duration-1000 opacity-0"
ref="menu"
>
<li :data-menuanchor="'post-' + post.id" v-for="post in postsStore.posts" :key="post.id"
class="m-2 backdrop-blur-sm rounded-lg bg-black/10 hover:bg-gray-500/10 transition-colors duration-200"
ref="menuItems">
<a :href="'#post-' + post.id"
class="block text-right px-4 py-3 transition-all duration-300">{{ post.formatedDate }}</a>
<li
:data-menuanchor="'post-' + post.id"
v-for="post in postsStore.posts"
:key="post.id"
class="m-2 backdrop-blur-sm rounded-lg bg-black/10 hover:bg-gray-500/10 transition-colors duration-200"
ref="menuItems"
>
<a
:href="'#post-' + post.id"
class="block text-right px-4 py-3 transition-all duration-300"
>{{ post.formatedDate }}</a
>
</li>
</ul>
</div>
@ -123,7 +134,6 @@ function onLeave(origin, destination, direction, trigger) {
</template>
<style scoped>
#menu li.active,
#menu li.active:hover {
background-color: rgba(255, 255, 255, 0.1);

View file

@ -13,10 +13,12 @@ import { API_BASE_URL } from '@/config.js'
const authStore = useAuthStore()
const formSchema = toTypedSchema(z.object({
latitude: z.number(),
longitude: z.number()
}))
const formSchema = toTypedSchema(
z.object({
latitude: z.number(),
longitude: z.number()
})
)
const form = useForm({
validationSchema: formSchema
})
@ -33,8 +35,7 @@ const formStatus = ref({
})
const onSubmit = form.handleSubmit(async (values) => {
if (!authStore.isAuth)
return
if (!authStore.isAuth) return
console.log('Envoi de la localisation...')
formContainer.value.classList.add('hidden')
@ -55,7 +56,8 @@ const onSubmit = form.handleSubmit(async (values) => {
console.log('POST post API failed: ' + response.statusText + '\n\n' + response.body)
formStatus.value.sending = false
formStatus.value.error = true
formStatus.value.errorMsg = 'Une erreur est survenue lors de l\'envoi du poste : ' + response.statusText
formStatus.value.errorMsg =
"Une erreur est survenue lors de l'envoi du poste : " + response.statusText
formContainer.value.classList.remove('hidden')
return
}
@ -79,7 +81,6 @@ onUnmounted(() => {
navigator.geolocation.clearWatch(geoWatchId)
}
})
</script>
<template>
@ -133,5 +134,4 @@ onUnmounted(() => {
</div>
</template>
<style scoped>
</style>
<style scoped></style>