mirror of
https://github.com/ThisIsBenny/wishlist-app.git
synced 2025-06-07 05:57:41 +00:00
add wishlist item page added for bookmark adding
Signed-off-by: Benny Samir Hierl <bennysamir@posteo.de>
This commit is contained in:
parent
f9147e9751
commit
2ee5c03935
8 changed files with 155 additions and 9 deletions
4
components.d.ts
vendored
4
components.d.ts
vendored
|
@ -28,8 +28,6 @@ declare module 'vue' {
|
||||||
IconToggleOn: typeof import('./src/components/icons/IconToggleOn.vue')['default']
|
IconToggleOn: typeof import('./src/components/icons/IconToggleOn.vue')['default']
|
||||||
ImagePreview: typeof import('./src/components/ImagePreview.vue')['default']
|
ImagePreview: typeof import('./src/components/ImagePreview.vue')['default']
|
||||||
ImageTile: typeof import('./src/components/ImageTile.vue')['default']
|
ImageTile: typeof import('./src/components/ImageTile.vue')['default']
|
||||||
'ImageTile copy': typeof import('./src/components/ImageTile copy.vue')['default']
|
|
||||||
ImageTilePlaceholder: typeof import('./src/components/ImageTilePlaceholder.vue')['default']
|
|
||||||
InputFile: typeof import('./src/components/InputFile.vue')['default']
|
InputFile: typeof import('./src/components/InputFile.vue')['default']
|
||||||
InputText: typeof import('./src/components/InputText.vue')['default']
|
InputText: typeof import('./src/components/InputText.vue')['default']
|
||||||
InputTextArea: typeof import('./src/components/InputTextArea.vue')['default']
|
InputTextArea: typeof import('./src/components/InputTextArea.vue')['default']
|
||||||
|
@ -39,4 +37,4 @@ declare module 'vue' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {}
|
export { }
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const fetchOpenGraph = <RouteOptions>{
|
||||||
properties: {
|
properties: {
|
||||||
title: { type: 'string' },
|
title: { type: 'string' },
|
||||||
description: { type: 'string' },
|
description: { type: 'string' },
|
||||||
image: { type: 'string' },
|
imageSrc: { type: 'string' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -42,7 +42,7 @@ export const fetchOpenGraph = <RouteOptions>{
|
||||||
reply.send({
|
reply.send({
|
||||||
title: result.ogTitle || '',
|
title: result.ogTitle || '',
|
||||||
description: result.ogDescription || '',
|
description: result.ogDescription || '',
|
||||||
image,
|
imageSrc: image,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -47,8 +47,11 @@ const deleteWishlist = async (): Promise<void> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createItem = async (values: WishlistItem): Promise<void> => {
|
const createItem = async (
|
||||||
const id = state.value?.id
|
values: WishlistItem,
|
||||||
|
wishlistId?: string
|
||||||
|
): Promise<void> => {
|
||||||
|
const id = wishlistId || state.value?.id
|
||||||
const payload = {
|
const payload = {
|
||||||
...values,
|
...values,
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,17 @@
|
||||||
"text": "Erstelle eine Wunschliste"
|
"text": "Erstelle eine Wunschliste"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"create-wishlist-item-view": {
|
||||||
|
"title": {
|
||||||
|
"text": "Eintrag hinzufügen"
|
||||||
|
},
|
||||||
|
"loading": {
|
||||||
|
"text": "Lade Daten der übermittelten URL"
|
||||||
|
},
|
||||||
|
"headline-wishlist-selection": {
|
||||||
|
"text": "Zu welcher Wunschliste möchten Sie etwas hinzufügen?"
|
||||||
|
}
|
||||||
|
},
|
||||||
"detail-view": {
|
"detail-view": {
|
||||||
"main": {
|
"main": {
|
||||||
"empty-list": {
|
"empty-list": {
|
||||||
|
|
|
@ -54,6 +54,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"create-wishlist-item-view": {
|
||||||
|
"title": {
|
||||||
|
"text": "Add item"
|
||||||
|
},
|
||||||
|
"loading": {
|
||||||
|
"text": "Loading data from provided URL"
|
||||||
|
},
|
||||||
|
"headline-wishlist-selection": {
|
||||||
|
"text": "To which wish list would you like to add the new item?"
|
||||||
|
}
|
||||||
|
},
|
||||||
"create-wishlist-view": {
|
"create-wishlist-view": {
|
||||||
"title": {
|
"title": {
|
||||||
"text": "Create a wishlist"
|
"text": "Create a wishlist"
|
||||||
|
|
|
@ -2,7 +2,10 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from '@/views/HomeView.vue'
|
import HomeView from '@/views/HomeView.vue'
|
||||||
import LoginView from '@/views/LoginView.vue'
|
import LoginView from '@/views/LoginView.vue'
|
||||||
import CreateWishlistView from '@/views/CreateWishlistView.vue'
|
import CreateWishlistView from '@/views/CreateWishlistView.vue'
|
||||||
|
import AddWishlistItemView from '@/views/AddWishlistItemView.vue'
|
||||||
import DetailView from '@/views/DetailView.vue'
|
import DetailView from '@/views/DetailView.vue'
|
||||||
|
import { useAuth } from '@/composables'
|
||||||
|
const { isAuthenticated } = useAuth()
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
@ -11,28 +14,45 @@ const router = createRouter({
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'login',
|
||||||
component: LoginView,
|
component: LoginView,
|
||||||
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/create-wishlist',
|
path: '/create-wishlist',
|
||||||
name: 'create-wishlist',
|
name: 'create-wishlist',
|
||||||
component: CreateWishlistView,
|
component: CreateWishlistView,
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/add-wishlist-item',
|
||||||
|
name: 'add-wishlist--item',
|
||||||
|
component: AddWishlistItemView,
|
||||||
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:slug',
|
path: '/:slug',
|
||||||
name: 'detail',
|
name: 'detail',
|
||||||
component: DetailView,
|
component: DetailView,
|
||||||
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'notFound',
|
name: 'notFound',
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
component: () => import('@/views/NotFoundView.vue'),
|
component: () => import('@/views/NotFoundView.vue'),
|
||||||
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.beforeEach((to, from) => {
|
||||||
|
if (!isAuthenticated.value && to.meta.requiresAuth === true) {
|
||||||
|
return { name: 'login' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
export interface WishlistItem {
|
export interface WishlistItem {
|
||||||
id: number
|
id?: number
|
||||||
title: string
|
title: string
|
||||||
url: string
|
url: string
|
||||||
imageSrc: string
|
imageSrc: string
|
||||||
description: string
|
description: string
|
||||||
bought: boolean
|
bought: boolean
|
||||||
wishlistId: boolean
|
wishlistId?: boolean
|
||||||
}
|
}
|
||||||
export interface Wishlist {
|
export interface Wishlist {
|
||||||
id?: string
|
id?: string
|
||||||
|
|
103
src/views/AddWishlistItemView.vue
Normal file
103
src/views/AddWishlistItemView.vue
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { Wishlist, WishlistItem } from '@/types'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useWishlistsStore, useWishlistStore } from '@/composables'
|
||||||
|
import { useToast } from 'vue-toastification'
|
||||||
|
import { useFetch } from '@/composables/useFetch'
|
||||||
|
import { syncRef, useTitle, watchOnce } from '@vueuse/core'
|
||||||
|
import { Ref, ref } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const isFinished = ref(true)
|
||||||
|
const selectedWishlist: Ref<Wishlist | undefined> = ref()
|
||||||
|
const prefillData = ref({
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
imageSrc: '',
|
||||||
|
url: '',
|
||||||
|
bought: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { createItem } = useWishlistStore()
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: wishlists,
|
||||||
|
isFinished: loadWishlistsFinished,
|
||||||
|
fetch,
|
||||||
|
} = useWishlistsStore()
|
||||||
|
|
||||||
|
fetch()
|
||||||
|
|
||||||
|
if (route.query.url) {
|
||||||
|
prefillData.value.url = route.query.url as string
|
||||||
|
const { data: opData, isFinished: opDataLoaded } = useFetch(
|
||||||
|
`/utils/fetch-open-graph?url=${route.query.url}`
|
||||||
|
).json()
|
||||||
|
syncRef(opDataLoaded, isFinished)
|
||||||
|
|
||||||
|
watchOnce(opData, () => {
|
||||||
|
prefillData.value = {
|
||||||
|
...prefillData.value,
|
||||||
|
...opData.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useTitle(t('pages.create-wishlist-item-view.title.text'))
|
||||||
|
|
||||||
|
const handleCreateItem = async (values: WishlistItem): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await createItem(values, selectedWishlist?.value?.id)
|
||||||
|
toast.success(t('common.saved.text'))
|
||||||
|
router.push(`/${selectedWishlist?.value?.slugUrlText}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
toast.error(t('common.saving-failed.text'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="h-full">
|
||||||
|
<div
|
||||||
|
v-if="!isFinished || !loadWishlistsFinished"
|
||||||
|
class="flex h-1/2 w-full flex-col justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="m-20 flex flex-row content-center items-center justify-center space-x-2"
|
||||||
|
>
|
||||||
|
<IconSpinner class="h-4 w-4" />
|
||||||
|
<span> {{ t('pages.create-wishlist-item-view.loading.text') }} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="loadWishlistsFinished && selectedWishlist === undefined">
|
||||||
|
<h1 class="text-center text-xl font-bold">
|
||||||
|
{{
|
||||||
|
t('pages.create-wishlist-item-view.headline-wishlist-selection.text')
|
||||||
|
}}
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
class="m-8 flex flex-row flex-wrap content-center items-center justify-center sm:space-x-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="item in wishlists"
|
||||||
|
:key="item.id"
|
||||||
|
@click="() => (selectedWishlist = item)"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<ImageTile
|
||||||
|
:title="item.title"
|
||||||
|
:image-src="item.imageSrc"
|
||||||
|
class="m-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FormWishlistItem :item="prefillData" v-else @create="handleCreateItem" />
|
||||||
|
</div>
|
||||||
|
</template>
|
Loading…
Add table
Reference in a new issue