c9911cc262
New standalone apps/website (Nuxt 4) serving the public marketing site at dezky.local / www.dezky.local. The customer portal moves off the root domain to app.dezky.local only. Landing page ported from the Dezky design handoff: light theme, Danish default, hero variant A, with a working da/en toggle. Self-contained colour system threaded through components (utils/landingTokens.ts), full bilingual copy (utils/landingCopy.ts), and shared state (composables/useLanding.ts). Sections live under components/landing/* with the Node logo under components/brand/*. Wired into docker-compose (website service, volume, Traefik labels, network aliases) and bootstrap.sh (hosts list + service URLs).
126 lines
6.8 KiB
Vue
126 lines
6.8 KiB
Vue
<script setup lang="ts">
|
|
// Stylized customer-portal dashboard shown under the hero. Illustrative only —
|
|
// labels are intentionally Danish in both languages. Ported from
|
|
// landing-sections.jsx ProductMockup (light mode).
|
|
import { computed } from 'vue'
|
|
import { C } from '~/utils/landingTokens'
|
|
import { useTheme, useDark } from '~/composables/useLanding'
|
|
|
|
const t = useTheme()
|
|
const dark = useDark()
|
|
|
|
const m = computed(() => ({
|
|
bg: dark.value ? '#171715' : '#FFFFFF',
|
|
border: dark.value ? 'rgba(255,255,255,0.08)' : 'rgba(10,10,10,0.08)',
|
|
fg: dark.value ? C.bone : C.carbon,
|
|
muted: dark.value ? 'rgba(244,243,238,0.55)' : 'rgba(10,10,10,0.55)',
|
|
subtle: dark.value ? 'rgba(244,243,238,0.08)' : 'rgba(10,10,10,0.04)',
|
|
}))
|
|
|
|
const apps = [
|
|
{ name: 'mail', badge: '12', active: true },
|
|
{ name: 'drev' },
|
|
{ name: 'møder' },
|
|
{ name: 'chat', pill: '3' },
|
|
{ name: 'admin' },
|
|
]
|
|
|
|
const stats: [string, string, string][] = [
|
|
['Ulæste', '12', 'mail'],
|
|
['Møder i dag', '3', 'møder'],
|
|
['Delte filer', '47', 'drev'],
|
|
]
|
|
|
|
const recent: [string, string, string][] = [
|
|
['Lone Frederiksen', 'Tilbud — Q3 retainer', '09:42'],
|
|
['ops@stalwart.io', 'Sikkerhedsopdatering 1.12 udrullet', '08:30'],
|
|
['Mads Holm', 'Re: Onboarding for nye seniors', 'i går'],
|
|
]
|
|
</script>
|
|
|
|
<template>
|
|
<div :style="{
|
|
background: m.bg, border: `1px solid ${m.border}`, borderRadius: '8px',
|
|
boxShadow: dark ? '0 40px 80px rgba(0,0,0,0.5)' : '0 30px 80px rgba(10,10,10,0.08)',
|
|
overflow: 'hidden',
|
|
}">
|
|
<!-- Window chrome -->
|
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '14px 18px', borderBottom: `1px solid ${m.border}` }">
|
|
<span :style="{ width: '10px', height: '10px', borderRadius: '999px', background: '#E23030' }" />
|
|
<span :style="{ width: '10px', height: '10px', borderRadius: '999px', background: '#E89A1F' }" />
|
|
<span :style="{ width: '10px', height: '10px', borderRadius: '999px', background: '#1F8A5B' }" />
|
|
<div :style="{ marginLeft: '16px', padding: '4px 12px', background: m.subtle, borderRadius: '4px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted }">app.dezky.com / dashboard</div>
|
|
</div>
|
|
|
|
<div :style="{ display: 'grid', gridTemplateColumns: '220px 1fr', minHeight: '460px' }">
|
|
<!-- Sidebar -->
|
|
<div :style="{ borderRight: `1px solid ${m.border}`, padding: '20px 0' }">
|
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '0 20px', marginBottom: '24px' }">
|
|
<BrandNodeMark :size="20" :fg="m.fg" :accent="t.signal" />
|
|
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', fontWeight: 600, color: m.fg }">dezky</span>
|
|
</div>
|
|
<div
|
|
v-for="(a, i) in apps" :key="a.name"
|
|
:style="{
|
|
padding: '9px 20px',
|
|
background: a.active ? m.subtle : 'transparent',
|
|
borderLeft: `2px solid ${a.active ? t.signal : 'transparent'}`,
|
|
fontFamily: '\'Inter\', sans-serif', fontSize: '13px', fontWeight: a.active ? 600 : 500,
|
|
color: a.active ? m.fg : m.muted,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
}"
|
|
>
|
|
<span>{{ a.name }}</span>
|
|
<span v-if="a.badge" :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted }">{{ a.badge }}</span>
|
|
<span v-else-if="a.pill" :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', background: t.signal, padding: '1px 6px', borderRadius: '2px', color: C.carbon }">{{ a.pill }}</span>
|
|
</div>
|
|
<div :style="{ margin: '32px 20px 0', padding: '14px', borderRadius: '4px', background: m.subtle, fontFamily: '\'Inter\', sans-serif' }">
|
|
<div :style="{ fontSize: '10px', fontFamily: '\'JetBrains Mono\', monospace', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">lagring</div>
|
|
<div :style="{ fontSize: '16px', fontWeight: 600, color: m.fg, marginTop: '6px' }">184 GB / 500</div>
|
|
<div :style="{ height: '4px', background: m.border, borderRadius: '999px', marginTop: '8px', overflow: 'hidden' }">
|
|
<div :style="{ height: '100%', width: '37%', background: t.signal }" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main -->
|
|
<div :style="{ padding: '28px 32px' }">
|
|
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }">
|
|
<div>
|
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">indbakke</div>
|
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '28px', color: m.fg, marginTop: '6px', letterSpacing: '-0.02em' }">god morgen, anne</div>
|
|
</div>
|
|
<div :style="{ display: 'flex', gap: '8px' }">
|
|
<div :style="{ width: '32px', height: '32px', borderRadius: '999px', background: m.subtle, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.fg, fontWeight: 600 }">AB</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div :style="{ marginTop: '24px', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px' }">
|
|
<div v-for="(s, i) in stats" :key="i" :style="{ padding: '16px 18px', background: m.subtle, borderRadius: '4px' }">
|
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">{{ s[2] }}</div>
|
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontSize: '28px', fontWeight: 600, color: m.fg, marginTop: '4px' }">{{ s[1] }}</div>
|
|
<div :style="{ fontSize: '12px', color: m.muted, marginTop: '2px' }">{{ s[0] }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div :style="{ marginTop: '24px' }">
|
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: '12px' }">seneste · indbakke</div>
|
|
<div
|
|
v-for="(r, i) in recent" :key="i"
|
|
:style="{
|
|
display: 'grid', gridTemplateColumns: '200px 1fr 60px',
|
|
padding: '12px 0', borderTop: i === 0 ? `1px solid ${m.border}` : 'none',
|
|
borderBottom: `1px solid ${m.border}`,
|
|
fontFamily: '\'Inter\', sans-serif', fontSize: '13px', alignItems: 'center',
|
|
}"
|
|
>
|
|
<span :style="{ fontWeight: 600, color: m.fg }">{{ r[0] }}</span>
|
|
<span :style="{ color: m.muted }">{{ r[1] }}</span>
|
|
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted, textAlign: 'right' }">{{ r[2] }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|