Redesigned home screen. Error Message for not unique entry
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
"vaul-vue": "^0.1.0",
|
"vaul-vue": "^0.1.0",
|
||||||
"vee-validate": "^4.12.5",
|
"vee-validate": "^4.12.5",
|
||||||
"vue": "^3.4.19",
|
"vue": "^3.4.19",
|
||||||
|
"vue-router": "^4.3.0",
|
||||||
|
"vue-sonner": "^1.1.2",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -41,6 +41,12 @@ dependencies:
|
|||||||
vue:
|
vue:
|
||||||
specifier: ^3.4.19
|
specifier: ^3.4.19
|
||||||
version: 3.4.19(typescript@5.3.3)
|
version: 3.4.19(typescript@5.3.3)
|
||||||
|
vue-router:
|
||||||
|
specifier: ^4.3.0
|
||||||
|
version: 4.3.0(vue@3.4.19)
|
||||||
|
vue-sonner:
|
||||||
|
specifier: ^1.1.2
|
||||||
|
version: 1.1.2
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.22.4
|
specifier: ^3.22.4
|
||||||
version: 3.22.4
|
version: 3.22.4
|
||||||
@@ -4507,6 +4513,19 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.4.19(typescript@5.3.3)
|
vue: 3.4.19(typescript@5.3.3)
|
||||||
|
|
||||||
|
/vue-router@4.3.0(vue@3.4.19):
|
||||||
|
resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.2.0
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.6.1
|
||||||
|
vue: 3.4.19(typescript@5.3.3)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/vue-sonner@1.1.2:
|
||||||
|
resolution: {integrity: sha512-yg4f5s0a3oiiI7cNvO0Dajux1Y7s04lxww3vnQtnwQawJ3KqaKA9RIRMdI9wGTosRGIOwgYFniFRGl4+IuKPZw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vue-template-compiler@2.7.16:
|
/vue-template-compiler@2.7.16:
|
||||||
resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
|
resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
99
src/App.vue
99
src/App.vue
@@ -1,103 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Table, TableCaption, TableHeader, TableRow, TableHead, TableCell } from '@/components/ui/table';
|
import { Toaster } from '@/components/ui/sonner';
|
||||||
import { Button } from './components/ui/button';
|
|
||||||
import { Plus } from 'lucide-vue-next';
|
|
||||||
import { entries, save } from './data/entries';
|
|
||||||
import { Drawer, DrawerHeader, DrawerTitle, DrawerTrigger, DrawerContent, DrawerFooter, DrawerClose } from './components/ui/drawer';
|
|
||||||
import { Form, FormItem, FormLabel, FormField, FormControl } from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { Textarea } from './components/ui/textarea';
|
|
||||||
import { toTypedSchema } from '@vee-validate/zod';
|
|
||||||
import * as z from 'zod'
|
|
||||||
import moment, { Moment } from 'moment';
|
|
||||||
|
|
||||||
const createEntryZodSchema = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
text: z.string()
|
|
||||||
})
|
|
||||||
|
|
||||||
type CreateEntrySchema = z.infer<typeof createEntryZodSchema>
|
|
||||||
|
|
||||||
const createEntrySchema = toTypedSchema(createEntryZodSchema)
|
|
||||||
|
|
||||||
function getDifferenceToToday(date: Moment) {
|
|
||||||
return Math.abs(date.diff(moment(), 'days'))
|
|
||||||
}
|
|
||||||
|
|
||||||
function createEntry(value: CreateEntrySchema) {
|
|
||||||
entries.value.push({
|
|
||||||
last_reset: moment(),
|
|
||||||
...value
|
|
||||||
})
|
|
||||||
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="flex justify-center">
|
<Toaster/>
|
||||||
<div class="wrapper sm:w-3/4 w-11/12">
|
<RouterView/>
|
||||||
<Table>
|
|
||||||
<TableCaption>List of Entries</TableCaption>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>Name</TableHead>
|
|
||||||
<TableHead>Last Accident</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
<TableRow v-for="entry in entries">
|
|
||||||
<TableCell> {{ entry.name }} </TableCell>
|
|
||||||
<TableCell> {{ getDifferenceToToday(entry.last_reset) }} </TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<Drawer>
|
|
||||||
<DrawerTrigger as-child>
|
|
||||||
<div>
|
|
||||||
<Button variant="outline" size="icon" class="w-20 h-20 fixed right-10 bottom-10">
|
|
||||||
<Plus />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</DrawerTrigger>
|
|
||||||
|
|
||||||
<DrawerContent>
|
|
||||||
<DrawerHeader>
|
|
||||||
<DrawerTitle>Create new Entry</DrawerTitle>
|
|
||||||
</DrawerHeader>
|
|
||||||
<div id="content" class="p-5">
|
|
||||||
<Form :validation-schema="createEntrySchema" @submit="createEntry">
|
|
||||||
<FormField v-slot="{ componentField }" name="name">
|
|
||||||
<FormItem>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Name" v-bind="componentField" />
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription />
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="{ componentField }" name="text">
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel></FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Textarea placeholder="Text" v-bind="componentField" />
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription />
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
</FormField>
|
|
||||||
<DrawerFooter>
|
|
||||||
<DrawerClose>
|
|
||||||
<Button type="submit">Create</Button>
|
|
||||||
</DrawerClose>
|
|
||||||
</DrawerFooter>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
16
src/components/ui/badge/Badge.vue
Normal file
16
src/components/ui/badge/Badge.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { type BadgeVariants, badgeVariants } from '.'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
variant?: BadgeVariants['variant']
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn(badgeVariants({ variant }), props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
25
src/components/ui/badge/index.ts
Normal file
25
src/components/ui/badge/index.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { type VariantProps, cva } from 'class-variance-authority'
|
||||||
|
|
||||||
|
export { default as Badge } from './Badge.vue'
|
||||||
|
|
||||||
|
export const badgeVariants = cva(
|
||||||
|
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||||
|
secondary:
|
||||||
|
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||||
|
destructive:
|
||||||
|
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||||
|
outline: 'text-foreground',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export type BadgeVariants = VariantProps<typeof badgeVariants>
|
||||||
21
src/components/ui/card/Card.vue
Normal file
21
src/components/ui/card/Card.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'rounded-lg border bg-card text-card-foreground shadow-sm',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
14
src/components/ui/card/CardContent.vue
Normal file
14
src/components/ui/card/CardContent.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('p-6 pt-0', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
14
src/components/ui/card/CardDescription.vue
Normal file
14
src/components/ui/card/CardDescription.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
||||||
|
<slot />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
14
src/components/ui/card/CardFooter.vue
Normal file
14
src/components/ui/card/CardFooter.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('flex items-center p-6 pt-0', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
14
src/components/ui/card/CardHeader.vue
Normal file
14
src/components/ui/card/CardHeader.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('flex flex-col gap-y-1.5 p-6', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
18
src/components/ui/card/CardTitle.vue
Normal file
18
src/components/ui/card/CardTitle.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h3
|
||||||
|
:class="
|
||||||
|
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
6
src/components/ui/card/index.ts
Normal file
6
src/components/ui/card/index.ts
Normal file
@@ -0,0 +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'
|
||||||
29
src/components/ui/scroll-area/ScrollArea.vue
Normal file
29
src/components/ui/scroll-area/ScrollArea.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type HTMLAttributes, computed } from 'vue'
|
||||||
|
import {
|
||||||
|
ScrollAreaCorner,
|
||||||
|
ScrollAreaRoot,
|
||||||
|
type ScrollAreaRootProps,
|
||||||
|
ScrollAreaViewport,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import ScrollBar from './ScrollBar.vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ScrollAreaRoot v-bind="delegatedProps" :class="cn('relative overflow-hidden', props.class)">
|
||||||
|
<ScrollAreaViewport class="h-full w-full rounded-[inherit]">
|
||||||
|
<slot />
|
||||||
|
</ScrollAreaViewport>
|
||||||
|
<ScrollBar />
|
||||||
|
<ScrollAreaCorner />
|
||||||
|
</ScrollAreaRoot>
|
||||||
|
</template>
|
||||||
30
src/components/ui/scroll-area/ScrollBar.vue
Normal file
30
src/components/ui/scroll-area/ScrollBar.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type HTMLAttributes, computed } from 'vue'
|
||||||
|
import { ScrollAreaScrollbar, type ScrollAreaScrollbarProps, ScrollAreaThumb } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes['class'] }>(), {
|
||||||
|
orientation: 'vertical',
|
||||||
|
})
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ScrollAreaScrollbar
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn('flex touch-none select-none transition-colors',
|
||||||
|
orientation === 'vertical'
|
||||||
|
&& 'h-full w-2.5 border-l border-l-transparent p-px',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
&& 'h-2.5 flex-col border-t border-t-transparent p-px',
|
||||||
|
props.class)"
|
||||||
|
>
|
||||||
|
<ScrollAreaThumb class="relative flex-1 rounded-full bg-border" />
|
||||||
|
</ScrollAreaScrollbar>
|
||||||
|
</template>
|
||||||
2
src/components/ui/scroll-area/index.ts
Normal file
2
src/components/ui/scroll-area/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as ScrollArea } from './ScrollArea.vue'
|
||||||
|
export { default as ScrollBar } from './ScrollBar.vue'
|
||||||
22
src/components/ui/sonner/Sonner.vue
Normal file
22
src/components/ui/sonner/Sonner.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { Toaster as Sonner, type ToasterProps } from 'vue-sonner'
|
||||||
|
|
||||||
|
const props = defineProps<ToasterProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Sonner
|
||||||
|
class="toaster group"
|
||||||
|
v-bind="props"
|
||||||
|
:toast-options="{
|
||||||
|
classes: {
|
||||||
|
toast: 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||||
|
description: 'group-[.toast]:text-muted-foreground',
|
||||||
|
actionButton:
|
||||||
|
'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||||
|
cancelButton:
|
||||||
|
'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
1
src/components/ui/sonner/index.ts
Normal file
1
src/components/ui/sonner/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as Toaster } from './Sonner.vue'
|
||||||
18
src/main.ts
18
src/main.ts
@@ -1,5 +1,19 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import App from './App.vue'
|
import App from '@/App.vue'
|
||||||
|
import Home from '@/pages/Home.vue'
|
||||||
|
import Detail from '@/pages/Detail.vue'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App)
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: Home},
|
||||||
|
{ path: '/:name', component: Detail}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
13
src/pages/Detail.vue
Normal file
13
src/pages/Detail.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
131
src/pages/Home.vue
Normal file
131
src/pages/Home.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Plus } from 'lucide-vue-next';
|
||||||
|
import { entries, save } from '@/data/entries';
|
||||||
|
import { Drawer, DrawerHeader, DrawerTitle, DrawerTrigger, DrawerContent, DrawerFooter, DrawerClose } from '@/components/ui/drawer';
|
||||||
|
import { Form, FormItem, FormLabel, FormField, FormControl, FormMessage, FormDescription } from '@/components/ui/form';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod';
|
||||||
|
import * as z from 'zod'
|
||||||
|
import moment, { Moment } from 'moment';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const createEntryZodSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
text: z.string().optional()
|
||||||
|
})
|
||||||
|
|
||||||
|
type CreateEntrySchema = z.infer<typeof createEntryZodSchema>
|
||||||
|
|
||||||
|
const createEntrySchema = toTypedSchema(createEntryZodSchema)
|
||||||
|
|
||||||
|
function getDifferenceToToday(date: Moment) {
|
||||||
|
return Math.abs(date.diff(moment(), 'days'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEntry(value: CreateEntrySchema) {
|
||||||
|
if (entries.value.map(entry => entry.name).includes(value.name)) {
|
||||||
|
toast('Accident Entry allready exists', {
|
||||||
|
important: true,
|
||||||
|
description: 'please use a unique name'
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.value.push({
|
||||||
|
last_reset: moment(),
|
||||||
|
name: value.name,
|
||||||
|
text: value.text
|
||||||
|
})
|
||||||
|
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDetailWithName(name: string) {
|
||||||
|
console.log('opening', name)
|
||||||
|
router.push({
|
||||||
|
path: `${name}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ScrollArea class="w-full">
|
||||||
|
<div class="w-full flex justify-center flex-col items-center">
|
||||||
|
<header id="title" class="flex w-full h-56 justify-center items-center flex-row">Accident Board</header>
|
||||||
|
<main class="wrapper sm:w-3/4 w-11/12 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
||||||
|
<Button v-for="entry in entries" class="w-full p-10 flex flex-col items-center gap-2"
|
||||||
|
@click="openDetailWithName(entry.name)">
|
||||||
|
<span>{{ entry.name }}</span>
|
||||||
|
<Badge variant="secondary">{{ getDifferenceToToday(entry.last_reset) }} days</Badge>
|
||||||
|
</Button>
|
||||||
|
<!-- <Table> -->
|
||||||
|
<!-- <TableCaption>List of Entries</TableCaption> -->
|
||||||
|
<!-- <TableHeader> -->
|
||||||
|
<!-- <TableRow> -->
|
||||||
|
<!-- <TableHead>Name</TableHead> -->
|
||||||
|
<!-- <TableHead>Last Accident</TableHead> -->
|
||||||
|
<!-- </TableRow> -->
|
||||||
|
<!-- </TableHeader> -->
|
||||||
|
<!---->
|
||||||
|
<!-- <TableRow v-for="entry in entries"> -->
|
||||||
|
<!-- <TableCell> {{ entry.name }} </TableCell> -->
|
||||||
|
<!-- <TableCell> {{ getDifferenceToToday(entry.last_reset) }} </TableCell> -->
|
||||||
|
<!-- </TableRow> -->
|
||||||
|
<!-- </Table> -->
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
<Drawer>
|
||||||
|
<DrawerTrigger as-child>
|
||||||
|
<div>
|
||||||
|
<Button variant="outline" size="icon" class="w-20 h-20 fixed right-10 bottom-10">
|
||||||
|
<Plus />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DrawerTrigger>
|
||||||
|
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerHeader>
|
||||||
|
<DrawerTitle>Create new Entry</DrawerTitle>
|
||||||
|
</DrawerHeader>
|
||||||
|
<div id="content" class="p-5">
|
||||||
|
<Form :validation-schema="createEntrySchema" @submit="(val) => createEntry(val as CreateEntrySchema)">
|
||||||
|
<FormField v-slot="{ componentField }" name="name">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="Name" v-bind="componentField" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage/>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<FormField v-slot="{ componentField }" name="text">
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel></FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea placeholder="Text" v-bind="componentField" />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription />
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<DrawerFooter>
|
||||||
|
<DrawerClose>
|
||||||
|
<Button type="submit">Create</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</DrawerFooter>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Reference in New Issue
Block a user