diff --git a/app/app.vue b/app/app.vue index 91fe27f..a084371 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,5 +1,18 @@ <template> + <PpNavigation /> <NuxtLayout> <NuxtPage /> </NuxtLayout> -</template> \ No newline at end of file +</template> + +<style> +.page-enter-active, +.page-leave-active { + transition: all 2000ms; +} +.page-enter-from, +.page-leave-to { + opacity: 0; + filter: blur(.5rem); +} +</style> \ No newline at end of file diff --git a/app/assets/styles/dialog.css b/app/assets/styles/dialog.css index b24cc61..0f44254 100644 --- a/app/assets/styles/dialog.css +++ b/app/assets/styles/dialog.css @@ -7,7 +7,7 @@ dialog { border: none; border-radius: var(--radius-default); background: var(--color-lightest); - font-size: var(--font-size-normal); + font-size: var(--font-size-default); color: var(--color-darkest); position: relative; diff --git a/app/assets/styles/form/textfield.css b/app/assets/styles/form/textfield.css index 3c528f0..d2cfaf8 100755 --- a/app/assets/styles/form/textfield.css +++ b/app/assets/styles/form/textfield.css @@ -1,9 +1,9 @@ .TextField { --border-color: var(--color-light); --label-color: var(--color-middle); - --label-position-top: var(--font-size-normal); - --label-font-size: var(--font-size-normal); - --icon-color: var(--color-main-dark); + --label-position-top: var(--font-size-default); + --label-position-left: 2.5rem; + --label-font-size: var(--font-size-default); --message-color: var(--color-middle); position: relative; @@ -15,15 +15,11 @@ &:focus-within, &:has(input:not(:placeholder-shown)) { - --icon-color: var(--color-main-dark); --label-color: var(--color-main-dark); - --label-position-top: calc(-1 * var(--font-size-normal)); - --label-font-size: var(--font-size-s); } &.error { - --label-color: var(--color-error); - --icon-color: var(--color-error); + --label-color: var(--color-error) !important; --border-color: var(--color-error); --message-color: var(--color-error); } @@ -38,29 +34,38 @@ & label { position: absolute; - background: var(--color-lightest); - padding: var(--padding-xxs) 0; - left: var(--padding-s); + display: flex; + align-items: center; color: var(--label-color); - top: var(--label-position-top); - font-size: var(--font-size-normal); + gap: 2px; + top: -6px; + left: calc(var(--padding-xs) - 2px); + font-size: var(--font-size-s); transition: var(--transition-default); + + & > * { + background: var(--color-lightest); + padding: 0 2px; + } } & .icon { - color: var(--icon-color); + position: relative; + color: var(--label-color); + font-size: var(--font-size-default); + top: -1px; } & input { all: unset; - padding: var(--padding-s); - font-size: var(--font-size-normal); + padding: var(--padding-xxs) var(--padding-s) var(--padding-xxs) 0; + font-size: var(--font-size-s); width: 100%; flex: 25% 1 0; color: var(--color-darkest); } - & span { + & > span { color: var(--message-color); font-size: var(--font-size-xs); } diff --git a/app/assets/styles/general.css b/app/assets/styles/general.css index 9e1e996..99a8bc6 100755 --- a/app/assets/styles/general.css +++ b/app/assets/styles/general.css @@ -1,7 +1,7 @@ :root { --color-success: #328104; --color-error: #a20606; - --color-blue-light: #0ddce7; + --color-blue-light: #d7e1f1; --color-blue: #05b0ff; --color-blue-dark: #0266f2; --color-blue-darkest: #013174; @@ -19,6 +19,7 @@ --color-green-darkest-most: #157c2a; --color-main: var(--color-blue); + --color-main-lightest: var(--color-blue-light); --color-main-light: var(--color-blue-light); --color-main-dark: var(--color-blue-dark); --color-main-darkest: var(--color-blue-darkest); @@ -44,24 +45,24 @@ /* Font Sizes & Scaling Factor*/ --scaling-factor: 1.25; - --font-size-normal: 1rem; - --font-size-s: calc(var(--font-size-normal) / var(--scaling-factor)); --font-size-xs: calc(var(--font-size-s) / var(--scaling-factor)); - --font-size-l: calc(var(--font-size-normal) * var(--scaling-factor)); + --font-size-s: calc(var(--font-size-default) / var(--scaling-factor)); + --font-size-default: 1rem; + --font-size-l: calc(var(--font-size-default) * var(--scaling-factor)); --font-size-xl: calc(var(--font-size-l) * var(--scaling-factor)); --font-size-xxl: calc(var(--font-size-xl) * var(--scaling-factor)); /* Paddings depend on Font-Size */ - --padding-default: var(--font-size-normal); - --padding-s: calc(var(--padding-default) / var(--scaling-factor)); - --padding-xs: calc(var(--padding-s) / var(--scaling-factor)); --padding-xxs: calc(var(--padding-xs) / var(--scaling-factor)); + --padding-xs: calc(var(--padding-s) / var(--scaling-factor)); + --padding-default: var(--font-size-default); + --padding-s: calc(var(--padding-default) / var(--scaling-factor)); --padding-l: calc(var(--padding-default) * var(--scaling-factor)); --padding-xl: calc(var(--padding-l) * var(--scaling-factor)); --padding-xxl: calc(var(--padding-xl) * var(--scaling-factor)); - --radius-default: calc(var(--font-size-normal) / 3); - --radius-border: var(--font-size-normal); + --radius-default: calc(var(--font-size-default) / 3); + --radius-border: var(--font-size-default); --transition-default: 150ms; } @@ -79,7 +80,7 @@ body { font-family: sans-serif; background: var(--color-main-darkest); color: var(--color-text); - font-size: var(--font-size-normal); + font-size: var(--font-size-default); } h1, h2, h3 { @@ -96,7 +97,11 @@ h2 { h3 { margin: var(--padding-default) 0 var(--padding-default); - font-size: var(--font-size-normal); + font-size: var(--font-size-default); +} + +a:has(button) { + text-decoration: none; } .card { diff --git a/app/assets/styles/header.css b/app/assets/styles/header.css index 6b4200d..6b61bc4 100755 --- a/app/assets/styles/header.css +++ b/app/assets/styles/header.css @@ -2,20 +2,37 @@ position: sticky; top: 0; z-index: 100; - background: var(--color-main-darkest); display: flex; align-items: center; gap: var(--padding-default); padding: var(--padding-default); + &:not(.lp) { + background: var(--color-main-darkest); + } + + &.lp { + position: absolute; + backdrop-filter: blur(10px); + mask: linear-gradient(to top, transparent, black 20%); + width: 100%; + top: 0; + } + & .logo { height: 40px; } + & .burger-button { + all: unset; + color: var(--color-lightest); + } + & header { display: flex; align-items: center; justify-content: space-between; + width: 100%; font-weight: bold; & a { @@ -42,61 +59,27 @@ font-weight: 100; } } + } - & input[type="checkbox"] { - display: none; + &:not(.lp) { + &:after, &:before { + content: ''; + display: block; + position: absolute; + bottom: calc(-1 * var(--radius-border)); + background: var(--color-blue-darkest); + width: var(--radius-border); + height: var(--radius-border); } - & input[type="checkbox"]:checked + nav { - translate: 0; - } - - & nav, - & ul { - gap: var(--padding-default); - } - - & nav { - position: fixed; - padding: var(--padding-default); - translate: 100% 0; - width: 100vw; + &:after { right: 0; - top: 0; - height: 100dvh; - transition: 150ms ease-in-out; - background: var(--color-lightest); - font-size: var(--font-size-xl); - align-items: end; - z-index: 100; + mask: radial-gradient(var(--radius-border) at 0 100%,#0000 98%,#000); } - & ul { - width: 100%; - align-items: center; - & li { - list-style: none; - } + &:before { + left: 0; + mask: radial-gradient(var(--radius-border) at 100% 100%,#0000 98%,#000); } } - - &:after, &:before { - content: ''; - display: block; - position: absolute; - bottom: calc(-1 * var(--radius-border)); - background: var(--color-blue-darkest); - width: var(--radius-border); - height: var(--radius-border); - } - - &:after { - right: 0; - mask: radial-gradient(var(--radius-border) at 0 100%,#0000 98%,#000); - } - - &:before { - left: 0; - mask: radial-gradient(var(--radius-border) at 100% 100%,#0000 98%,#000); - } } \ No newline at end of file diff --git a/app/assets/styles/landingpage.css b/app/assets/styles/landingpage.css index 66e1a84..5d45215 100644 --- a/app/assets/styles/landingpage.css +++ b/app/assets/styles/landingpage.css @@ -1,5 +1,5 @@ .home-hero { - background-image: url("/img/hero-image.webp"); + background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url("/img/hero-image.webp"); /* single color gradient for dark layer over image */ background-repeat: no-repeat; background-position: center center; background-size: cover; diff --git a/app/assets/styles/navigation.css b/app/assets/styles/navigation.css new file mode 100644 index 0000000..2b12356 --- /dev/null +++ b/app/assets/styles/navigation.css @@ -0,0 +1,69 @@ +.Navigation { + position: fixed; + display: flex; + justify-content: flex-end; + align-items: center; + z-index: 5000; + width: 100vw; + height: 100dvh; + right: 0; + top: 0; + transition: 150ms ease-in-out; + pointer-events: none; + + &.open { + pointer-events: all; + background: rgba(0, 0, 0, .5); + + & nav { + translate: -1rem 0; + } + } + + nav { + background: var(--color-lightest); + align-items: end; + box-shadow: var(--box-shadow-z2); + padding: var(--padding-default); + height: calc(100% - var(--padding-xxl)); + width: 70%; + translate: 100% 0; + transition: 150ms ease-in-out; + border-radius: var(--radius-default); + } + + & button { + justify-self: flex-end; + font-size: var(--font-size-xl); + } + + & ul { + width: 100%; + align-items: flex-start; + font-size: var(--font-size-l); + gap: var(--padding-default); + + & li { + list-style: none; + width: 100%; + + & a { + text-decoration: none; + display: flex; + align-items: center; + gap: var(--padding-default); + color: var(--color-middle); + border-radius: var(--radius-default); + padding: var(--padding-xs) var(--padding-s); + transition: var(--transition-default); + background: transparent; + cursor: pointer; + + &.active { + background: var(--color-main-light); + color: var(--color-main-dark); + } + } + } + } +} \ No newline at end of file diff --git a/app/assets/styles/page.css b/app/assets/styles/page.css index 61f9345..7dd7b75 100644 --- a/app/assets/styles/page.css +++ b/app/assets/styles/page.css @@ -60,7 +60,7 @@ } .content-text { - padding: var(--padding-xl) var(--padding-default) var(--padding-default); + padding: var(--padding-xl) var(--padding-default) 0; color: var(--color-darkest); text-align: center; } diff --git a/app/assets/styles/priceCard.css b/app/assets/styles/priceCard.css index 9068df1..655f581 100755 --- a/app/assets/styles/priceCard.css +++ b/app/assets/styles/priceCard.css @@ -56,7 +56,7 @@ color: var(--color-darkest); & .icon { - font-size: var(--font-size-normal); + font-size: var(--font-size-default); cursor: pointer; } } @@ -104,7 +104,7 @@ } & > .pro { - font-size: var(--font-size-s); + font-size: var(--font-size-xs); color: var(--color-middle); font-weight: lighter; } diff --git a/app/assets/styles/toolbar.css b/app/assets/styles/toolbar.css index c9aec5a..d78e9f7 100755 --- a/app/assets/styles/toolbar.css +++ b/app/assets/styles/toolbar.css @@ -8,6 +8,6 @@ box-shadow: var(--box-shadow-upper); & > .Button { - font-size: var(--font-size-normal); + font-size: var(--font-size-default); } } \ No newline at end of file diff --git a/app/components/Pp/Form/TextField.vue b/app/components/Pp/Form/TextField.vue index 0c4f803..4e84828 100755 --- a/app/components/Pp/Form/TextField.vue +++ b/app/components/Pp/Form/TextField.vue @@ -1,9 +1,11 @@ <template> <div class="TextField"> <div class="wrapper"> - <Icon v-if="icon" class="icon" :name="icon" mode="svg" /> - <input v-model="text" :type="type" :id="id" placeholder=" " @blur="emit('blur')" @input="emit('input')" :inputmode="mode" /> - <label :for="id">{{ label }}</label> + <input v-model="text" :type="type" :id="id" :placeholder="placeholder" @blur="emit('blur')" @input="emit('input')" :inputmode="mode" /> + <label :for="id"> + <Icon v-if="icon" class="icon" :name="icon" mode="svg" /> + <span>{{ label }}</span> + </label> </div> <span v-if="message">{{ message }}</span> </div> @@ -11,12 +13,13 @@ <script setup lang="ts"> type Props = { - type?: 'text' | 'number'; - message?: string; - icon?: string; - label: string; - id: string; - mode?: 'text' | 'email' | 'search' | 'tel' | 'url' | 'none' | 'numeric' | 'decimal'; + type?: 'text' | 'number' + message?: string + icon?: string + label: string + placeholder: string + id: string + mode?: 'text' | 'email' | 'search' | 'tel' | 'url' | 'none' | 'numeric' | 'decimal' }; const { type = "text", mode = "text" } = defineProps<Props>(); diff --git a/app/components/Pp/Header.vue b/app/components/Pp/Header.vue index bba771f..ed560d3 100755 --- a/app/components/Pp/Header.vue +++ b/app/components/Pp/Header.vue @@ -1,33 +1,28 @@ <template> - <div class="Header"> - <div> - <header class="roboto-condensed"> - <NuxtLink class="header-text" to="/"> - <img class="logo" src="/img/propapier.svg" alt="ProPapier logo" /> - <div> - <span class="big">ProPapier</span> - <span class="small">Vergleichen. Schnell. Unkompliziert.</span> - </div> - </NuxtLink> - <label for="burger_nav_toggle" v-if="available"> - <Icon name="solar:hamburger-menu-broken" size="2em" /> - </label> - <input type="checkbox" id="burger_nav_toggle" v-if="available" /> - <nav class="flex-col" v-if="available"> - <label for="burger_nav_toggle"> - <Icon name="solar:close-circle-broken" /> - </label> - <ul class="flex-col"> - <li>Home</li> - <li>Übersicht</li> - </ul> - </nav> - </header> - <div id="subheader" /> - </div> + <div class="Header" :class="[type]"> + <header class="roboto-condensed"> + <NuxtLink class="header-text" to="/"> + <img class="logo" src="/img/propapier.svg" alt="ProPapier logo" /> + <div> + <span class="big">ProPapier</span> + <span class="small">Vergleichen. Schnell. Unkompliziert.</span> + </div> + </NuxtLink> + <button class="burger-button" @click="open()"> + <Icon name="uil:bars" size="2em" /> + </button> + </header> + <div id="subheader" /> </div> </template> <script setup lang="ts"> -const available = false +type Props = { + type ?: 'lp' +} + +defineProps<Props>() + +const nav = useNavigation() +const open = () => { nav.showNavigation() } </script> diff --git a/app/components/Pp/Navigation.vue b/app/components/Pp/Navigation.vue new file mode 100644 index 0000000..9e88fb8 --- /dev/null +++ b/app/components/Pp/Navigation.vue @@ -0,0 +1,41 @@ +<template> + <section class="Navigation" :class="{ open }"> + <nav> + <PpButton class="round text" @click="close()"> + <Icon name="uil:times" mode="svg" /> + </PpButton> + <ul class="flex-col"> + <li v-for="page in pages"> + <NuxtLink :to="page.route" @click="close()" active-class="active"> + <Icon class="icon" :name="`uil:${page.icon}`" mode="svg" /> + <span>{{ page.label }}</span> + </NuxtLink> + </li> + </ul> + </nav> + </section> +</template> + +<script setup lang="ts"> +const nav = useNavigation() +const close = () => nav.hideNavigation() +const open = computed(() => nav.isNavigationVisible.value) + +const pages = [ + { + label: 'Home', + icon: 'home', + route: '/' + }, + { + label: 'Schnellrechner', + icon: 'calculator', + route: '/rechner' + }, + // { + // label: 'Über uns', + // icon: 'users-alt', + // route: '/about-us' + // } +] +</script> \ No newline at end of file diff --git a/app/components/Pp/PriceCardDialog.vue b/app/components/Pp/PriceCardDialog.vue index 2d1493b..5e74207 100644 --- a/app/components/Pp/PriceCardDialog.vue +++ b/app/components/Pp/PriceCardDialog.vue @@ -16,7 +16,8 @@ v-model="currentCard.name" id="card_name" label="Name" - icon="uil:user" + icon="uil:pricetag-alt" + :placeholder="randomName" :class="{ error: !validFields.name }" :message="!validFields.name ? 'Feld darf nicht leer sein.' : ''" @input="validFields.name = true" @@ -25,6 +26,7 @@ v-model="currentCard.price" id="card_price" label="Preis" + placeholder="2,49" icon="uil:euro" mode="decimal" :class="{ error: !validFields.price }" @@ -37,6 +39,7 @@ v-model="currentCard.roles" id="card_roles" label="Rollen" + placeholder="8" icon="uil:toilet-paper" mode="decimal" :class="{ error: !validFields.roles }" @@ -47,6 +50,7 @@ v-model="currentCard.sheets" id="card_sheets" label="Blatt" + placeholder="150" icon="uil:file-landscape" mode="decimal" :class="{ error: !validFields.sheets }" @@ -57,6 +61,7 @@ v-model="currentCard.layers" id="card_layers" label="Lagen" + placeholder="3" icon="uil:layer-group" mode="decimal" :class="{ error: !validFields.layers }" @@ -94,6 +99,25 @@ const emit = defineEmits(['update']) const dialog = useTemplateRef<HTMLDialogElement>('dialog') const wrapper = useTemplateRef<HTMLElement>('wrapper') +const market = [ + 'Lotl', + 'Olda', + 'Bäwä', + 'Brutto', +] + +const product = [ + 'Weichelig', + 'Sau Rauh', + 'Bissl Sanft', + 'Ganz ok', + 'Flauschi' +] + +const generateRandomName = () => `${market[Math.floor(Math.random() * market.length)]} ${product[Math.floor(Math.random() * product.length)]}` + +const randomName = useState('randomName', () => generateRandomName()) + const checkPrice = () => { if (!currentCard) return false if (currentCard.price.length === 0) return false @@ -138,6 +162,7 @@ onMounted(() => { validFields.roles = true validFields.sheets = true validFields.layers = true + randomName.value = generateRandomName() }) onClickOutside(wrapper, () => dialog.value?.close()) diff --git a/app/composables/navigation.ts b/app/composables/navigation.ts new file mode 100644 index 0000000..e54c6f6 --- /dev/null +++ b/app/composables/navigation.ts @@ -0,0 +1,24 @@ +import { ref } from 'vue' + +const isNavigationVisible = ref(false) + +export const useNavigation = () => { + const toggleNavigation = () => { + isNavigationVisible.value = !isNavigationVisible.value + } + + const showNavigation = () => { + isNavigationVisible.value = true + } + + const hideNavigation = () => { + isNavigationVisible.value = false + } + + return { + isNavigationVisible, + toggleNavigation, + showNavigation, + hideNavigation + } +} \ No newline at end of file diff --git a/app/layouts/landingpage.vue b/app/layouts/landingpage.vue index e731440..3c91317 100644 --- a/app/layouts/landingpage.vue +++ b/app/layouts/landingpage.vue @@ -1,5 +1,6 @@ <template> <div class="page-wrapper"> + <PpHeader type="lp" /> <div class="page"> <NuxtPage /> </div> diff --git a/app/pages/index.vue b/app/pages/index.vue old mode 100755 new mode 100644 index a492a64..e1813b9 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,154 +1,25 @@ <template> - <div class="nuxt-page-wrapper flex-col"> - <ClientOnly> - <PpDeleteDialog - ref="deleteModal" - :current-card-index="currentCardIndex" - @delete="removeCard(currentCardIndex)" - /> - <PpPriceCardDialog - ref="modal" - :current-card="currentCard" - :current-card-index="currentCardIndex" - @update="updateCard()" - /> - <section class="content flex-col"> - <div class="content-text"> - <h1>Mit ProPapier Preise vergleichen und sparen</h1> - </div> - <aside class="filter-bar"> - <button v-for="(button, index) in filterButtons" @click="() => sort(index)" :class="{ 'active': button.active }"> - {{ button.label }} - </button> - </aside> - <div class="flex-col" role="list" v-if="cards.length"> - <PpPriceCard - ref="priceCard" - v-for="(card, index) in cards" - :key="card.uuid" - :deletable="cards.length > 1" - :card="card" - @update="openModal(false, index)" - @remove="openDeleteModal()" - /> - </div> - <p class="info-text grow" v-else> - Du hast noch keinerlei Einträge angelegt. - <br />Aber das ist gar nicht schlimm! - <br />Tippe einfach unten auf "+ Hinzufügen" und leg los. - </p> - </section> - <PpToolbar> - <PpButton class="mini-button text-white transparent" @click="openModal(true, -1)"> - <Icon class="icon" name="uil:plus" mode="svg" /> - <span>Hinzufügen</span> - </PpButton> - </PpToolbar> - </ClientOnly> - </div> + <section class="Home flex-col content full"> + <div class="home-hero"> + <div class="text"> + <h1> + Du zahlst zuviel fürs Papier? + </h1> + <NuxtLink to="/rechner"> + <PpButton class="cta">Preise vergleichen</PpButton> + </NuxtLink> + </div> + </div> + <div class="home-text padding "> + <p> + Mit <strong>ProPapier</strong> vergleichst du schnell & unkompliziert Preise für Klo-, Küchen- und Haushaltspapier und sparst so bares Geld. + </p> + </div> + </section> </template> <script setup lang="ts"> -import type { Card } from '../../shared/Card' -import type { Button } from '../../shared/ButtonGroup' -import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components' - -const cards = useLocalStorage<Card[]>('cards', []) -const currentSort = useLocalStorage<number>('sort', 0) -const currentCard = ref<Card>() -const currentCardIndex = ref<number>(-1) -const modal = useTemplateRef<typeof PpPriceCardDialog>('modal') -const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal') -const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard') - -const createCard = (uuid : string) : Card => ({ - uuid, - name: '', - price: '', - roles: '', - sheets: '', - layers: '', +definePageMeta({ + layout: 'landingpage' }) - - -const addCard = (card : Card) => { - cards.value.unshift({ ...card }) - sort() -} - -const removeCard = (index : number) => { - cards.value.splice(index, 1) - sort() -} - -const updateCard = () => { - if (currentCardIndex.value === -1) { - addCard(currentCard.value!) - return - } - - const newCard = { ...currentCard.value! } - cards.value.splice(currentCardIndex.value, 1, newCard) - sort() -} - -const openModal = (createNew : boolean, index : number) => { - if (createNew) { - currentCardIndex.value = -1 - currentCard.value = createCard(randomUUID()) - modal.value?.$el.showModal() - return - } - - currentCardIndex.value = index - currentCard.value = { ...cards.value[index]! } - - modal.value?.$el.showModal() - return -} - -const openDeleteModal = () => { - deleteModal.value?.$el.showModal() -} - -const filterButtons = ref<Button[]>([ - { - label: 'Rollen', - icon: 'uil:toilet-paper', - active: currentSort.value === 0, - }, - { - label: 'Blatt', - icon: 'uil:file-landscape', - active: currentSort.value === 1, - }, - { - label: 'Lagen', - icon: 'uil:layer-group', - active: currentSort.value === 2, - }, -]) - -const sortBy = (key : 'ppr' | 'pps' | 'ppl') => { - cards.value.sort((a, b) => { - const aCard = priceCards.value?.find(card => card.uuid === a.uuid) || null - const bCard = priceCards.value?.find(card => card.uuid === b.uuid) || null - if (!aCard || !bCard) return 0 - return aCard[key] - bCard[key] - }) -} - -const sort = async (index : number = currentSort.value) => { - currentSort.value = index - filterButtons.value.forEach(button => { button.active = false }) - filterButtons.value[index]!.active = true - - await nextTick() - - switch (index) { - case 0: return sortBy('ppr') - case 1: return sortBy('pps') - case 2: return sortBy('ppl') - } -} -</script> +</script> \ No newline at end of file diff --git a/app/pages/landingpage.vue b/app/pages/landingpage.vue deleted file mode 100644 index 102b124..0000000 --- a/app/pages/landingpage.vue +++ /dev/null @@ -1,24 +0,0 @@ -<template> - <section class="Home flex-col content full"> - <div class="home-hero"> - <div class="text"> - <h1> - Du zahlst zuviel fürs Papier? - </h1> - <Button class="cta">Preise vergleichen</Button> - </div> - </div> - <div class="home-text padding "> - <p> - Mit <strong>ProPapier</strong> vergleichst du schnell & unkompliziert Preise für Klo-, Küchen- und Haushaltspapier und sparst so bares Geld. - </p> - </div> - </section> -</template> - -<script setup lang="ts"> -import Button from "~/components/Pp/Button.vue"; -definePageMeta({ - layout: 'landingpage' -}) -</script> \ No newline at end of file diff --git a/app/pages/other.vue b/app/pages/rechner.vue old mode 100644 new mode 100755 similarity index 74% rename from app/pages/other.vue rename to app/pages/rechner.vue index bd60632..a492a64 --- a/app/pages/other.vue +++ b/app/pages/rechner.vue @@ -1,10 +1,10 @@ <template> - <div> + <div class="nuxt-page-wrapper flex-col"> <ClientOnly> <PpDeleteDialog - ref="deleteModal" - :current-card-index="currentCardIndex" - @delete="removeCard(currentCardIndex)" + ref="deleteModal" + :current-card-index="currentCardIndex" + @delete="removeCard(currentCardIndex)" /> <PpPriceCardDialog ref="modal" @@ -12,41 +12,33 @@ :current-card-index="currentCardIndex" @update="updateCard()" /> - <div class="search-bar"> - <PpFormSearch - v-model="search" - label="Suche nach Klopapier!" - id="search_field" - /> - </div> <section class="content flex-col"> + <div class="content-text"> + <h1>Mit ProPapier Preise vergleichen und sparen</h1> + </div> <aside class="filter-bar"> <button v-for="(button, index) in filterButtons" @click="() => sort(index)" :class="{ 'active': button.active }"> {{ button.label }} </button> </aside> - <div class="flex-col" role="list"> + <div class="flex-col" role="list" v-if="cards.length"> <PpPriceCard - ref="priceCard" - v-for="(card, index) in cards" - :key="card.uuid" - :deletable="cards.length > 1" - :card="card" - @update="openModal(false, index)" - @remove="openDeleteModal()" + ref="priceCard" + v-for="(card, index) in cards" + :key="card.uuid" + :deletable="cards.length > 1" + :card="card" + @update="openModal(false, index)" + @remove="openDeleteModal()" /> </div> + <p class="info-text grow" v-else> + Du hast noch keinerlei Einträge angelegt. + <br />Aber das ist gar nicht schlimm! + <br />Tippe einfach unten auf "+ Hinzufügen" und leg los. + </p> </section> <PpToolbar> - <PpButton class="mini-button text-white transparent" @click="sort(currentSort)"> - <Icon class="icon" name="uil:refresh" mode="svg" /> - <span>Neu sortieren</span> - <span - class="dot" - :class="{ visible : isDirty}" - aria-hidden="true" - /> - </PpButton> <PpButton class="mini-button text-white transparent" @click="openModal(true, -1)"> <Icon class="icon" name="uil:plus" mode="svg" /> <span>Hinzufügen</span> @@ -63,15 +55,12 @@ import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components' const cards = useLocalStorage<Card[]>('cards', []) const currentSort = useLocalStorage<number>('sort', 0) -const isDirty = ref(false) const currentCard = ref<Card>() const currentCardIndex = ref<number>(-1) const modal = useTemplateRef<typeof PpPriceCardDialog>('modal') const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal') const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard') -const search = ref('') - const createCard = (uuid : string) : Card => ({ uuid, name: '', @@ -84,12 +73,12 @@ const createCard = (uuid : string) : Card => ({ const addCard = (card : Card) => { cards.value.unshift({ ...card }) - isDirty.value = true + sort() } const removeCard = (index : number) => { cards.value.splice(index, 1) - isDirty.value = true + sort() } const updateCard = () => { @@ -100,7 +89,7 @@ const updateCard = () => { const newCard = { ...currentCard.value! } cards.value.splice(currentCardIndex.value, 1, newCard) - isDirty.value = true + sort() } const openModal = (createNew : boolean, index : number) => { @@ -149,12 +138,12 @@ const sortBy = (key : 'ppr' | 'pps' | 'ppl') => { }) } -const sort = (index : number) => { +const sort = async (index : number = currentSort.value) => { currentSort.value = index filterButtons.value.forEach(button => { button.active = false }) filterButtons.value[index]!.active = true - isDirty.value = false + await nextTick() switch (index) { case 0: return sortBy('ppr') diff --git a/nuxt.config.ts b/nuxt.config.ts index 68a8b95..d0a40f0 100755 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -48,6 +48,7 @@ export default defineNuxtConfig({ css : [ './app/assets/styles/general.css', './app/assets/styles/header.css', + './app/assets/styles/navigation.css', './app/assets/styles/footer.css', './app/assets/styles/button.css', './app/assets/styles/buttonGroup.css',