add: OpenGraph image, timeline card state
Added three states to timeline card, added open graph image and description
This commit is contained in:
parent
f75d66a0d0
commit
d71e59b9c0
12 changed files with 112 additions and 34 deletions
|
@ -26,6 +26,10 @@
|
||||||
.home-text {
|
.home-text {
|
||||||
padding: var(--padding-xxl) var(--padding-default);
|
padding: var(--padding-xxl) var(--padding-default);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
& h3:has(+ .padding) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline {
|
.timeline {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--color-darkest);
|
color: var(--color-darkest);
|
||||||
border-bottom: 1px solid var(--color-light);
|
border-bottom: 1px dashed var(--color-light);
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
border-radius: var(--radius-default);
|
border-radius: var(--radius-default);
|
||||||
padding: var(--padding-xs);
|
padding: var(--padding-xs);
|
||||||
|
|
||||||
& .icon {
|
& > .icon {
|
||||||
flex: 0 0 25%;
|
flex: 0 0 25%;
|
||||||
font-size: var(--font-size-xxl);
|
font-size: var(--font-size-xxl);
|
||||||
color: var(--color-main-dark);
|
color: var(--color-main-dark);
|
||||||
|
@ -16,4 +16,13 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .state {
|
||||||
|
--color: var(--color-darkest);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--padding-xxs);
|
||||||
|
margin-top: var(--padding-s);
|
||||||
|
color: var(--color);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -58,11 +58,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Card } from '../../../shared/Card'
|
import type { PriceCard } from '../../../shared/PriceCard'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
deletable: boolean
|
deletable: boolean
|
||||||
card: Card
|
card: PriceCard
|
||||||
}
|
}
|
||||||
|
|
||||||
const { card } = defineProps<Props>()
|
const { card } = defineProps<Props>()
|
||||||
|
|
|
@ -86,11 +86,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Card } from '../../../shared/Card'
|
import type { PriceCard } from '../../../shared/PriceCard'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
currentCardIndex: number
|
currentCardIndex: number
|
||||||
currentCard?: Card
|
currentCard?: PriceCard
|
||||||
}
|
}
|
||||||
|
|
||||||
const { currentCardIndex, currentCard } = defineProps<Props>()
|
const { currentCardIndex, currentCard } = defineProps<Props>()
|
||||||
|
|
|
@ -4,16 +4,48 @@
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<strong>{{ title }}</strong>
|
<strong>{{ title }}</strong>
|
||||||
<p>{{ description }}</p>
|
<p>{{ description }}</p>
|
||||||
|
<div class="state" :style="{
|
||||||
|
'--color': stateColor,
|
||||||
|
}">
|
||||||
|
<Icon :name="stateIcon" mode="svg" />
|
||||||
|
<span>{{ stateMessage }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
type Props = {
|
import type { TimelineCard, TimelineState } from '../../../shared/TimelineCard'
|
||||||
icon: string
|
|
||||||
title: string
|
|
||||||
description: string
|
const { state } = defineProps<TimelineCard>()
|
||||||
|
|
||||||
|
const icons : Record<TimelineState, string> = {
|
||||||
|
planned: 'uil:clock',
|
||||||
|
inProgress: 'uil:cog',
|
||||||
|
done: 'uil:check-circle'
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>()
|
const colors : Record<TimelineState, string> = {
|
||||||
|
planned: 'var(--color-darkest)',
|
||||||
|
inProgress: 'var(--color-main-dark)',
|
||||||
|
done: 'var(--color-accent-darkest)',
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateColor = computed(() => colors[state.value])
|
||||||
|
const stateIcon = computed(() => icons[state.value])
|
||||||
|
const stateMessage = computed(() => {
|
||||||
|
switch (state.value) {
|
||||||
|
case 'planned':
|
||||||
|
let planned = 'Geplant'
|
||||||
|
if (state.message) planned += ` für ${state.message}`
|
||||||
|
return planned
|
||||||
|
case 'inProgress':
|
||||||
|
return 'In Bearbeitung'
|
||||||
|
case 'done':
|
||||||
|
let done = 'Abgeschlossen'
|
||||||
|
if (state.message) done += ` am ${state.message}`
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
|
@ -13,15 +13,18 @@
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-text padding ">
|
<div class="home-text padding">
|
||||||
<p>
|
<p>
|
||||||
Mit <strong>ProPapier</strong> vergleichst du schnell & unkompliziert Preise für Klopapier und sparst so bares Geld.
|
Mit <strong>ProPapier</strong> vergleichst du schnell & unkompliziert Preise für Klopapier und sparst so bares Geld.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-text padding ">
|
<div class="home-text padding">
|
||||||
<h3>
|
<h3>
|
||||||
Wir haben noch viel vor!
|
Wir haben noch viel vor!
|
||||||
</h3>
|
</h3>
|
||||||
|
<p class="padding">
|
||||||
|
Für ProPapier sind über die nächste Zeit noch einige weitere Features geplant.
|
||||||
|
</p>
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<PpTimelineCard
|
<PpTimelineCard
|
||||||
v-for="card in timeline"
|
v-for="card in timeline"
|
||||||
|
@ -33,46 +36,62 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { TimelineCard } from '../../shared/TimelineCard'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'landingpage'
|
layout: 'landingpage'
|
||||||
})
|
})
|
||||||
|
|
||||||
type Card = {
|
const timeline : TimelineCard[] = [
|
||||||
icon: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeline : Card[] = [
|
|
||||||
{
|
{
|
||||||
icon: 'uil:chart-bar',
|
icon: 'uil:chart-bar',
|
||||||
title: 'Mehr Vergleiche',
|
title: 'Mehr Vergleiche',
|
||||||
description: 'Zusätzliche Kategorien für Taschentücher und Küchenrolle',
|
description: 'Zusätzliche Kategorien für Taschentücher und Küchenrolle',
|
||||||
|
state: {
|
||||||
|
value: 'inProgress',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'uil:cloud-database-tree',
|
icon: 'uil:cloud-database-tree',
|
||||||
title: 'Datenbank',
|
title: 'Datenbank',
|
||||||
description: 'Eine von der Community gestützte Datenbank mit Preisen für alle Produkte'
|
description: 'Eine von der Community gestützte Datenbank mit Preisen für alle Produkte',
|
||||||
|
state: {
|
||||||
|
value: 'planned',
|
||||||
|
message: '2025',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'uil:qrcode-scan',
|
icon: 'uil:qrcode-scan',
|
||||||
title: 'Barcode Scan',
|
title: 'Barcode Scan',
|
||||||
description: 'Ganz einfach Barcode Scannen und Produkt direkt zum Rechner hinzufügen'
|
description: 'Ganz einfach Barcode Scannen und Produkt direkt zum Rechner hinzufügen',
|
||||||
|
state: {
|
||||||
|
value: 'planned',
|
||||||
|
message: '2025',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'uil:user',
|
icon: 'uil:user',
|
||||||
title: 'Optionale Accounts',
|
title: 'Optionale Accounts',
|
||||||
description: 'Zur Synchronisierung auf mehreren Geräten'
|
description: 'Zur Synchronisierung auf mehreren Geräten',
|
||||||
|
state: {
|
||||||
|
value: 'planned',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'uil:cog',
|
icon: 'uil:cog',
|
||||||
title: 'Personalisierung',
|
title: 'Personalisierung',
|
||||||
description: 'Persönliche Präferenzen zur Wortwahl, Standardsortierung und mehr'
|
description: 'Persönliche Präferenzen zur Wortwahl, Standardsortierung und mehr',
|
||||||
|
state: {
|
||||||
|
value: 'planned',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'uil:vector-square',
|
icon: 'uil:vector-square',
|
||||||
title: 'm² Preise',
|
title: 'm² Preise',
|
||||||
description: 'Quadratmeterpreise für noch genauere Vergleiche'
|
description: 'Quadratmeterpreise für noch genauere Vergleiche',
|
||||||
|
state: {
|
||||||
|
value: 'planned',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
|
@ -46,19 +46,19 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Card } from '../../shared/Card'
|
import type { PriceCard } from '../../shared/PriceCard'
|
||||||
import type { Button } from '../../shared/ButtonGroup'
|
import type { Button } from '../../shared/ButtonGroup'
|
||||||
import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components'
|
import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components'
|
||||||
|
|
||||||
const cards = useLocalStorage<Card[]>('cards', [])
|
const cards = useLocalStorage<PriceCard[]>('cards', [])
|
||||||
const currentSort = useLocalStorage<number>('sort', 0)
|
const currentSort = useLocalStorage<number>('sort', 0)
|
||||||
const currentCard = ref<Card>()
|
const currentCard = ref<PriceCard>()
|
||||||
const currentCardIndex = ref<number>(-1)
|
const currentCardIndex = ref<number>(-1)
|
||||||
const modal = useTemplateRef<typeof PpPriceCardDialog>('modal')
|
const modal = useTemplateRef<typeof PpPriceCardDialog>('modal')
|
||||||
const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal')
|
const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal')
|
||||||
const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard')
|
const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard')
|
||||||
|
|
||||||
const createCard = (uuid : string) : Card => ({
|
const createCard = (uuid : string) : PriceCard => ({
|
||||||
uuid,
|
uuid,
|
||||||
name: '',
|
name: '',
|
||||||
price: '',
|
price: '',
|
||||||
|
@ -68,7 +68,7 @@ const createCard = (uuid : string) : Card => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const addCard = (card : Card) => {
|
const addCard = (card : PriceCard) => {
|
||||||
cards.value.unshift({ ...card })
|
cards.value.unshift({ ...card })
|
||||||
sort()
|
sort()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
||||||
|
const description = 'Bezahlt du zuviel fürs Papier? Vergleiche schnell und unkompliziert die Preise für Toiletten-, Küchen- und andere Papier hier.'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
devtools: { enabled: false },
|
devtools: { enabled: false },
|
||||||
|
@ -73,7 +75,7 @@ export default defineNuxtConfig({
|
||||||
seo: {
|
seo: {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'ProPapier',
|
title: 'ProPapier',
|
||||||
description: '"Bezahlt du zuviel fürs Papier? Vergleiche schnell und unkompliziert die Preise für Toiletten-, Küchen- und andere Papier hier."',
|
description,
|
||||||
themeColor: [
|
themeColor: [
|
||||||
{ content: '#18181b', media: '(prefers-color-scheme: dark)' },
|
{ content: '#18181b', media: '(prefers-color-scheme: dark)' },
|
||||||
{ content: 'white', media: '(prefers-color-scheme: light)' },
|
{ content: 'white', media: '(prefers-color-scheme: light)' },
|
||||||
|
@ -90,9 +92,10 @@ export default defineNuxtConfig({
|
||||||
ogType: 'website',
|
ogType: 'website',
|
||||||
ogUrl: 'https://pro-papier.de',
|
ogUrl: 'https://pro-papier.de',
|
||||||
ogTitle: 'ProPapier',
|
ogTitle: 'ProPapier',
|
||||||
|
ogDescription: description,
|
||||||
|
|
||||||
// Other Nuxt SEO modules handles these
|
// Other Nuxt SEO modules handles these
|
||||||
ogImage: 'https://example.com/my-og-image.png',
|
ogImage: '/img/og.png',
|
||||||
robots: 'index, follow',
|
robots: 'index, follow',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
BIN
public/img/og.png
Executable file
BIN
public/img/og.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 384 KiB |
|
@ -1,4 +1,4 @@
|
||||||
export type Card = {
|
export type PriceCard = {
|
||||||
uuid : string
|
uuid : string
|
||||||
name : string
|
name : string
|
||||||
price : string
|
price : string
|
11
shared/TimelineCard.ts
Normal file
11
shared/TimelineCard.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export type TimelineState = 'planned' | 'inProgress' | 'done'
|
||||||
|
|
||||||
|
export type TimelineCard = {
|
||||||
|
icon: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
state: {
|
||||||
|
value: TimelineState
|
||||||
|
message ?: string
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue