Files
Twyco d18fffe665
CI/CD / preconditions (pull_request) Successful in 1s
Native JavaScript checks / Native launcher and release tests (pull_request) Successful in 1m3s
macOS checks / Native Rust tests and unsigned build (pull_request) Successful in 1m41s
CI/CD / test_and_check_app (pull_request) Successful in 6m53s
CI/CD / Publish immutable image (pull_request) Skipped
feat: add stable macOS app download to website
2026-09-28 09:12:37 +02:00

276 lines
12 KiB
Vue

<script setup lang="ts">
import { Head, Link } from '@inertiajs/vue3';
import {
ArrowRight,
Columns3,
Download,
GitBranch,
ListChecks,
} from 'lucide-vue-next';
import { computed } from 'vue';
import AppLogo from '@/components/AppLogo.vue';
import { Button } from '@/components/ui/button';
import { useLocalization } from '@/composables/useLocalization';
import { useMacDownload } from '@/composables/useMacDownload';
import { dashboard, home, login } from '@/routes/index';
const { t } = useLocalization();
const { macDownloadUrl, showMacDownload } = useMacDownload();
const features = computed(() => [
{
icon: Columns3,
title: t('welcome.features.workflows.title'),
description: t('welcome.features.workflows.description'),
},
{
icon: ListChecks,
title: t('welcome.features.tickets.title'),
description: t('welcome.features.tickets.description'),
},
{
icon: GitBranch,
title: t('welcome.features.git.title'),
description: t('welcome.features.git.description'),
},
]);
const previewColumns = computed(() => [
{
title: t('welcome.preview.backlog'),
tickets: [
t('welcome.preview.ticketOnboarding'),
t('welcome.preview.ticketRelease'),
],
},
{
title: t('welcome.preview.inProgress'),
tickets: [t('welcome.preview.ticketRepository')],
},
{
title: t('welcome.preview.review'),
tickets: [t('welcome.preview.ticketPullRequest')],
},
]);
</script>
<template>
<div
class="bg-background text-foreground relative isolate min-h-dvh overflow-x-hidden"
>
<Head :title="t('welcome.metaTitle')" />
<div
aria-hidden="true"
class="bg-brand/10 mask-[linear-gradient(to_bottom,black,transparent)] absolute inset-x-0 top-0 -z-10 h-[34rem]"
/>
<div
aria-hidden="true"
class="bg-primary/15 absolute left-1/2 top-12 -z-10 size-80 -translate-x-1/2 rounded-full blur-3xl sm:size-[32rem]"
/>
<header
class="mx-auto flex w-full min-w-0 max-w-6xl items-center justify-between gap-3 px-4 py-4 sm:gap-4 sm:px-6 lg:px-8"
>
<Link
:href="home()"
:aria-label="t('welcome.homeLabel')"
class="focus-visible:ring-ring/35 min-w-0 rounded-md outline-none focus-visible:ring-2"
>
<AppLogo />
</Link>
<nav :aria-label="t('welcome.navigation')" class="shrink-0">
<Button as-child variant="outline" size="sm">
<Link v-if="$page.props.auth.user" :href="dashboard()">
{{ t('welcome.action.dashboardShort') }}
<ArrowRight aria-hidden="true" />
</Link>
<Link v-else :href="login()">
{{ t('welcome.action.signInShort') }}
<ArrowRight aria-hidden="true" />
</Link>
</Button>
</nav>
</header>
<main class="min-w-0">
<section
aria-labelledby="welcome-title"
class="mx-auto grid w-full min-w-0 max-w-6xl gap-10 px-4 pb-16 pt-12 sm:px-6 sm:pt-20 lg:px-8 lg:pt-24"
>
<div
class="mx-auto grid w-full min-w-0 max-w-3xl justify-items-center gap-6 text-center"
>
<p
class="border-primary/20 bg-card/80 text-primary text-label shadow-xs rounded-full border px-3 py-1.5 uppercase backdrop-blur"
>
{{ t('welcome.eyebrow') }}
</p>
<div class="grid gap-4">
<h1
id="welcome-title"
class="text-balance text-4xl font-semibold leading-tight tracking-[-0.04em] sm:text-5xl lg:text-6xl"
>
{{ t('welcome.title') }}
</h1>
<p
class="text-muted-foreground mx-auto max-w-2xl text-pretty text-base leading-7 sm:text-lg"
>
{{ t('welcome.description') }}
</p>
</div>
<div
class="flex flex-wrap items-start justify-center gap-3"
>
<Button as-child size="lg">
<Link
v-if="$page.props.auth.user"
:href="dashboard()"
>
{{ t('welcome.action.dashboard') }}
<ArrowRight aria-hidden="true" />
</Link>
<Link v-else :href="login()">
{{ t('welcome.action.signIn') }}
<ArrowRight aria-hidden="true" />
</Link>
</Button>
<div
v-if="showMacDownload"
class="grid justify-items-center gap-1"
>
<Button as-child size="lg" variant="outline">
<a :href="macDownloadUrl"
><Download aria-hidden="true" />{{
t('navigation.downloadApp')
}}</a
>
</Button>
<p class="text-caption text-muted-foreground">
{{ t('welcome.downloadAppHint') }}
</p>
</div>
</div>
<p class="text-caption text-muted-foreground">
{{ t('welcome.actionHint') }}
</p>
</div>
<section
:aria-label="t('welcome.preview.label')"
class="bg-card/90 min-w-0 rounded-xl border p-3 shadow-[0_24px_80px_-32px_rgba(0,110,47,0.35)] backdrop-blur sm:p-5 dark:shadow-[0_24px_80px_-32px_rgba(74,225,118,0.22)]"
>
<div
class="border-border/80 mb-3 flex min-w-0 items-center justify-between gap-3 border-b px-1 pb-3 sm:mb-5 sm:gap-4 sm:px-2 sm:pb-4"
>
<div class="flex shrink-0 gap-1.5" aria-hidden="true">
<span class="bg-border size-2 rounded-full" />
<span class="bg-border size-2 rounded-full" />
<span class="bg-brand size-2 rounded-full" />
</div>
<p
class="text-caption text-muted-foreground min-w-0 truncate"
>
{{ t('welcome.preview.project') }}
</p>
<span
class="bg-success text-success-foreground text-label shrink-0 rounded-full px-2 py-1 uppercase"
>
{{ t('welcome.preview.connected') }}
</span>
</div>
<div class="grid min-w-0 gap-3 md:grid-cols-3">
<section
v-for="column in previewColumns"
:key="column.title"
class="bg-muted/70 grid min-w-0 content-start gap-3 rounded-lg border p-3 sm:p-4"
>
<h2 class="text-caption font-semibold">
{{ column.title }}
</h2>
<article
v-for="ticket in column.tickets"
:key="ticket"
class="bg-card text-card-foreground shadow-xs grid min-w-0 gap-3 rounded-md border p-3"
>
<p class="text-body break-words font-medium">
{{ ticket }}
</p>
<div
class="flex items-center justify-between gap-3"
aria-hidden="true"
>
<span
class="bg-accent h-1.5 w-16 rounded-full"
/>
<span
class="bg-brand size-5 rounded-full"
/>
</div>
</article>
</section>
</div>
</section>
</section>
<section
aria-labelledby="welcome-features-title"
class="border-border/80 bg-card/60 border-y"
>
<div
class="mx-auto grid w-full max-w-6xl gap-8 px-4 py-14 sm:px-6 lg:px-8"
>
<div class="mx-auto grid max-w-2xl gap-3 text-center">
<p class="text-label text-primary uppercase">
{{ t('welcome.features.eyebrow') }}
</p>
<h2
id="welcome-features-title"
class="text-headline-mobile sm:text-headline"
>
{{ t('welcome.features.title') }}
</h2>
</div>
<ul class="grid gap-4 md:grid-cols-3">
<li
v-for="feature in features"
:key="feature.title"
class="bg-card shadow-xs grid gap-4 rounded-lg border p-5"
>
<span
class="bg-accent text-primary flex size-10 items-center justify-center rounded-md"
>
<component
:is="feature.icon"
class="size-5"
aria-hidden="true"
/>
</span>
<div class="grid gap-2">
<h3 class="text-title">
{{ feature.title }}
</h3>
<p
class="text-muted-foreground text-pretty leading-6"
>
{{ feature.description }}
</p>
</div>
</li>
</ul>
</div>
</section>
</main>
<footer
class="text-caption text-muted-foreground mx-auto flex w-full max-w-6xl flex-col gap-2 px-4 py-8 text-center sm:px-6 lg:px-8"
>
<p class="text-foreground font-semibold">KanbanGit</p>
<p>{{ t('welcome.footer') }}</p>
</footer>
</div>
</template>