refactor(ui): standardize page layouts and component styling
Introduce structural CSS classes for page shells, headers, and surface cards Update primary theme color to red and neutral to zinc across the application
This commit is contained in:
11
AGENTS.md
11
AGENTS.md
@@ -29,6 +29,17 @@ The Git history follows Conventional Commit-style messages, commonly `feat(scope
|
|||||||
|
|
||||||
Create local configuration from `.env.example`. Do not commit secrets, session keys, database URLs with credentials, or WhatsApp tokens. Keep `NUXT_PUBLIC_APP_URL` aligned with the browser origin because WebAuthn requires a stable relying-party origin.
|
Create local configuration from `.env.example`. Do not commit secrets, session keys, database URLs with credentials, or WhatsApp tokens. Keep `NUXT_PUBLIC_APP_URL` aligned with the browser origin because WebAuthn requires a stable relying-party origin.
|
||||||
|
|
||||||
|
## UI Safety Rules (CRITICAL)
|
||||||
|
|
||||||
|
User-facing UI must NEVER contain:
|
||||||
|
|
||||||
|
- prompts
|
||||||
|
- remarks
|
||||||
|
- planning notes
|
||||||
|
- debug messages
|
||||||
|
- explanations of what was changed
|
||||||
|
- internal field names like `debug`, `meta`, `internal`
|
||||||
|
|
||||||
## Agent-Specific Instructions
|
## Agent-Specific Instructions
|
||||||
|
|
||||||
Keep implementations concise and scoped to the requested behavior. Prefer the smallest code change that fits existing patterns, and avoid unrelated refactors, formatting churn, or broad rewrites.
|
Keep implementations concise and scoped to the requested behavior. Prefer the smallest code change that fits existing patterns, and avoid unrelated refactors, formatting churn, or broad rewrites.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
ui: {
|
ui: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: 'amber',
|
primary: 'red',
|
||||||
neutral: 'stone'
|
neutral: 'zinc'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,169 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "@nuxt/ui";
|
@import "@nuxt/ui";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
background: var(--ui-bg);
|
background:
|
||||||
|
linear-gradient(180deg, color-mix(in srgb, var(--ui-primary) 7%, transparent) 0, transparent 22rem),
|
||||||
|
var(--ui-bg-muted);
|
||||||
|
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
a,
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: color-mix(in srgb, var(--ui-primary) 18%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-shell {
|
||||||
|
width: min(100%, 80rem);
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 1.5rem 1rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-shell-narrow {
|
||||||
|
width: min(100%, 64rem);
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 1.5rem 1rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-shell-compact {
|
||||||
|
width: min(100%, 48rem);
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 1.5rem 1rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-eyebrow {
|
||||||
|
display: inline-flex;
|
||||||
|
width: fit-content;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--ui-primary) 18%, var(--ui-border));
|
||||||
|
background: color-mix(in srgb, var(--ui-primary) 8%, var(--ui-bg));
|
||||||
|
padding: 0.25rem 0.625rem;
|
||||||
|
color: var(--ui-primary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
max-width: 44rem;
|
||||||
|
color: var(--ui-text-highlighted);
|
||||||
|
font-size: clamp(1.875rem, 1.5rem + 1.25vw, 3rem);
|
||||||
|
font-weight: 760;
|
||||||
|
line-height: 1.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-description {
|
||||||
|
max-width: 42rem;
|
||||||
|
color: var(--ui-text-muted);
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.surface-card {
|
||||||
|
border: 1px solid color-mix(in srgb, var(--ui-border) 86%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--ui-bg) 95%, transparent);
|
||||||
|
box-shadow: 0 1px 2px color-mix(in srgb, black 4%, transparent), 0 18px 48px color-mix(in srgb, black 7%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.surface-panel {
|
||||||
|
border: 1px solid color-mix(in srgb, var(--ui-border) 88%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--ui-bg-elevated) 76%, var(--ui-bg));
|
||||||
|
box-shadow: inset 0 1px 0 color-mix(in srgb, white 48%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--ui-border) 82%, transparent);
|
||||||
|
background: linear-gradient(180deg, color-mix(in srgb, var(--ui-bg) 98%, white) 0, color-mix(in srgb, var(--ui-bg-elevated) 82%, var(--ui-bg)) 100%);
|
||||||
|
box-shadow: 0 1px 2px color-mix(in srgb, black 4%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 auto 0 0;
|
||||||
|
width: 3px;
|
||||||
|
background: var(--ui-primary);
|
||||||
|
opacity: 0.82;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-table {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-table :where(th) {
|
||||||
|
background: var(--ui-bg-muted);
|
||||||
|
color: var(--ui-text-muted);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 650;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
gap: 0.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.action-row {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.page-shell,
|
||||||
|
.page-shell-narrow,
|
||||||
|
.page-shell-compact {
|
||||||
|
padding: 2rem 1.5rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative min-h-dvh bg-default text-default">
|
<div class="relative min-h-dvh bg-muted text-default">
|
||||||
<div class="pointer-events-none absolute inset-x-0 top-0 h-72 bg-gradient-to-b from-primary/10 via-primary/0 to-transparent opacity-80" />
|
|
||||||
|
|
||||||
<template v-if="auth.user.value">
|
<template v-if="auth.user.value">
|
||||||
<Transition
|
<Transition
|
||||||
enter-active-class="transition-opacity duration-200 ease-out"
|
enter-active-class="transition-opacity duration-200 ease-out"
|
||||||
@@ -30,11 +28,11 @@
|
|||||||
>
|
>
|
||||||
<aside
|
<aside
|
||||||
v-if="mobileMenuOpen"
|
v-if="mobileMenuOpen"
|
||||||
class="fixed inset-y-2 left-2 z-50 flex w-[min(20rem,calc(100vw-1rem))] flex-col rounded-[28px] border border-default/80 bg-default/96 p-3 shadow-2xl backdrop-blur-xl lg:hidden"
|
class="fixed inset-y-2 left-2 z-50 flex w-[min(20rem,calc(100vw-1rem))] flex-col rounded-lg border border-default/80 bg-default/96 p-3 shadow-2xl backdrop-blur-xl lg:hidden"
|
||||||
>
|
>
|
||||||
<div class="flex items-start justify-between gap-3 rounded-3xl border border-default bg-gradient-to-br from-primary/12 via-default to-default px-4 py-4">
|
<div class="flex items-start justify-between gap-3 rounded-lg border border-default bg-elevated px-4 py-4">
|
||||||
<div class="flex min-w-0 items-center gap-3">
|
<div class="flex min-w-0 items-center gap-3">
|
||||||
<div class="flex size-11 shrink-0 items-center justify-center rounded-2xl bg-primary/12 text-primary">
|
<div class="flex size-11 shrink-0 items-center justify-center rounded-lg bg-primary/12 text-primary">
|
||||||
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -64,11 +62,11 @@
|
|||||||
:key="item.to"
|
:key="item.to"
|
||||||
:to="item.to"
|
:to="item.to"
|
||||||
:aria-current="isMenuItemActive(item) ? 'page' : undefined"
|
:aria-current="isMenuItemActive(item) ? 'page' : undefined"
|
||||||
class="group flex min-h-14 items-center gap-3 rounded-2xl border px-4 py-3 text-sm font-medium transition-all duration-200"
|
class="group flex min-h-14 items-center gap-3 rounded-lg border px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||||
:class="mobileMenuItemClasses(item)"
|
:class="mobileMenuItemClasses(item)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex size-10 shrink-0 items-center justify-center rounded-2xl transition-colors duration-200"
|
class="flex size-10 shrink-0 items-center justify-center rounded-lg transition-colors duration-200"
|
||||||
:class="mobileMenuIconClasses(item)"
|
:class="mobileMenuIconClasses(item)"
|
||||||
>
|
>
|
||||||
<UIcon :name="item.icon" class="size-5" />
|
<UIcon :name="item.icon" class="size-5" />
|
||||||
@@ -79,9 +77,9 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="mt-auto space-y-3 border-t border-default pt-3">
|
<div class="mt-auto space-y-3 border-t border-default pt-3">
|
||||||
<div class="rounded-3xl border border-default bg-default/90 px-4 py-4 shadow-sm">
|
<div class="rounded-lg border border-default bg-elevated px-4 py-4 shadow-sm">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="flex size-11 shrink-0 items-center justify-center rounded-2xl bg-muted text-highlighted">
|
<div class="flex size-11 shrink-0 items-center justify-center rounded-lg bg-muted text-highlighted">
|
||||||
<UIcon name="i-lucide-user-round" class="size-5" />
|
<UIcon name="i-lucide-user-round" class="size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -109,7 +107,7 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon="i-lucide-house"
|
icon="i-lucide-house"
|
||||||
class="w-full justify-start rounded-2xl"
|
class="w-full justify-start rounded-lg"
|
||||||
@click="mobileMenuOpen = false"
|
@click="mobileMenuOpen = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -118,7 +116,7 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon="i-lucide-languages"
|
icon="i-lucide-languages"
|
||||||
class="w-full justify-start rounded-2xl"
|
class="w-full justify-start rounded-lg"
|
||||||
:aria-label="t('common.switchLanguage')"
|
:aria-label="t('common.switchLanguage')"
|
||||||
@click="toggleLocale"
|
@click="toggleLocale"
|
||||||
/>
|
/>
|
||||||
@@ -129,7 +127,7 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-lucide-log-out"
|
icon="i-lucide-log-out"
|
||||||
class="w-full justify-start rounded-2xl"
|
class="w-full justify-start rounded-lg"
|
||||||
@click="logout"
|
@click="logout"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,9 +136,9 @@
|
|||||||
|
|
||||||
<div class="relative lg:grid lg:min-h-dvh lg:grid-cols-[17.5rem_minmax(0,1fr)]">
|
<div class="relative lg:grid lg:min-h-dvh lg:grid-cols-[17.5rem_minmax(0,1fr)]">
|
||||||
<aside class="sticky top-0 hidden h-dvh flex-col border-r border-default/80 bg-default/92 px-5 py-5 backdrop-blur-xl lg:flex">
|
<aside class="sticky top-0 hidden h-dvh flex-col border-r border-default/80 bg-default/92 px-5 py-5 backdrop-blur-xl lg:flex">
|
||||||
<div class="rounded-[28px] border border-default bg-gradient-to-br from-primary/12 via-default to-default px-4 py-4 shadow-sm">
|
<div class="rounded-lg border border-default bg-elevated px-4 py-4 shadow-sm">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="flex size-12 shrink-0 items-center justify-center rounded-2xl bg-primary/12 text-primary">
|
<div class="flex size-12 shrink-0 items-center justify-center rounded-lg bg-primary/12 text-primary">
|
||||||
<UIcon name="i-lucide-grid-2x2" class="size-6" />
|
<UIcon name="i-lucide-grid-2x2" class="size-6" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -161,11 +159,11 @@
|
|||||||
:key="item.to"
|
:key="item.to"
|
||||||
:to="item.to"
|
:to="item.to"
|
||||||
:aria-current="isMenuItemActive(item) ? 'page' : undefined"
|
:aria-current="isMenuItemActive(item) ? 'page' : undefined"
|
||||||
class="group flex min-h-14 items-center gap-3 rounded-2xl border px-4 py-3 text-sm font-medium transition-all duration-200"
|
class="group flex min-h-14 items-center gap-3 rounded-lg border px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||||
:class="desktopMenuItemClasses(item)"
|
:class="desktopMenuItemClasses(item)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex size-10 shrink-0 items-center justify-center rounded-2xl transition-colors duration-200"
|
class="flex size-10 shrink-0 items-center justify-center rounded-lg transition-colors duration-200"
|
||||||
:class="desktopMenuIconClasses(item)"
|
:class="desktopMenuIconClasses(item)"
|
||||||
>
|
>
|
||||||
<UIcon :name="item.icon" class="size-5" />
|
<UIcon :name="item.icon" class="size-5" />
|
||||||
@@ -179,9 +177,9 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="mt-auto space-y-3">
|
<div class="mt-auto space-y-3">
|
||||||
<div class="rounded-[28px] border border-default bg-default/90 px-4 py-4 shadow-sm">
|
<div class="rounded-lg border border-default bg-elevated px-4 py-4 shadow-sm">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="flex size-11 shrink-0 items-center justify-center rounded-2xl bg-muted text-highlighted">
|
<div class="flex size-11 shrink-0 items-center justify-center rounded-lg bg-muted text-highlighted">
|
||||||
<UIcon name="i-lucide-user-round" class="size-5" />
|
<UIcon name="i-lucide-user-round" class="size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -210,7 +208,7 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon="i-lucide-house"
|
icon="i-lucide-house"
|
||||||
class="justify-start rounded-2xl"
|
class="justify-start rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
@@ -218,7 +216,7 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon="i-lucide-languages"
|
icon="i-lucide-languages"
|
||||||
class="justify-start rounded-2xl"
|
class="justify-start rounded-lg"
|
||||||
:aria-label="t('common.switchLanguage')"
|
:aria-label="t('common.switchLanguage')"
|
||||||
@click="toggleLocale"
|
@click="toggleLocale"
|
||||||
/>
|
/>
|
||||||
@@ -229,19 +227,19 @@
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-lucide-log-out"
|
icon="i-lucide-log-out"
|
||||||
class="justify-start rounded-2xl"
|
class="justify-start rounded-lg"
|
||||||
@click="logout"
|
@click="logout"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div class="min-w-0">
|
<div class="min-w-0 bg-muted">
|
||||||
<header class="sticky top-0 z-30 border-b border-default/80 bg-default/92 backdrop-blur-xl lg:hidden">
|
<header class="sticky top-0 z-30 border-b border-default/80 bg-default/92 backdrop-blur-xl lg:hidden">
|
||||||
<UContainer class="py-3">
|
<UContainer class="py-3">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div class="flex min-w-0 items-center gap-3 rounded-2xl border border-default bg-default/80 px-3 py-2 shadow-sm">
|
<div class="flex min-w-0 items-center gap-3 rounded-lg border border-default bg-elevated px-3 py-2 shadow-sm">
|
||||||
<div class="flex size-10 shrink-0 items-center justify-center rounded-2xl bg-primary/12 text-primary">
|
<div class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/12 text-primary">
|
||||||
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -286,8 +284,8 @@
|
|||||||
<header class="relative border-b border-default/80 bg-default/92 backdrop-blur-xl">
|
<header class="relative border-b border-default/80 bg-default/92 backdrop-blur-xl">
|
||||||
<UContainer class="py-4 md:py-5">
|
<UContainer class="py-4 md:py-5">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div class="flex min-w-0 items-center gap-3 rounded-2xl border border-default bg-default/80 px-3 py-2 shadow-sm">
|
<div class="flex min-w-0 items-center gap-3 rounded-lg border border-default bg-elevated px-3 py-2 shadow-sm">
|
||||||
<div class="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
<div class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||||
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
<UIcon name="i-lucide-grid-2x2" class="size-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<UContainer class="py-6">
|
<UContainer class="page-shell">
|
||||||
<div class="mx-auto max-w-7xl space-y-4">
|
<div class="space-y-5">
|
||||||
<div class="flex flex-col gap-2 lg:flex-row lg:items-end lg:justify-between">
|
<div class="page-header lg:flex-row lg:items-end lg:justify-between">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<UBadge label="Bookings" color="primary" variant="soft" size="sm" class="rounded-full" />
|
<UBadge label="Bookings" color="primary" variant="soft" size="sm" class="page-eyebrow" />
|
||||||
<h1 class="text-2xl font-bold tracking-tight text-highlighted">
|
<h1 class="page-title">
|
||||||
Booking list
|
Booking list
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-sm text-muted">
|
<p class="page-description">
|
||||||
{{ auth.isSuperAdmin.value ? 'All submitted bookings across every PIC.' : 'Bookings assigned to you as PIC.' }}
|
{{ auth.isSuperAdmin.value ? 'All submitted bookings across every PIC.' : 'Bookings assigned to you as PIC.' }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="auth.isSuperAdmin.value" class="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_18rem]">
|
<div v-if="auth.isSuperAdmin.value" class="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_18rem]">
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
|
<UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-base font-semibold text-highlighted">
|
<h2 class="text-base font-semibold text-highlighted">
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
</UForm>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
|
<UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-base font-semibold text-highlighted">
|
<h2 class="text-base font-semibold text-highlighted">
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<div class="rounded-lg border border-default bg-muted/20 p-3">
|
<div class="surface-panel rounded-lg p-3">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Pending bookings
|
Pending bookings
|
||||||
</p>
|
</p>
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-lg border border-default bg-muted/20 p-3">
|
<div class="surface-panel rounded-lg p-3">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Pending seats
|
Pending seats
|
||||||
</p>
|
</p>
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<UCard
|
<UCard
|
||||||
v-for="item in inventoryCards"
|
v-for="item in inventoryCards"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
class="border border-default bg-default shadow-sm"
|
class="metric-card rounded-lg"
|
||||||
:ui="{ body: 'px-4 py-3' }"
|
:ui="{ body: 'px-4 py-3' }"
|
||||||
>
|
>
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-3 md:grid-cols-3">
|
<div class="grid gap-3 md:grid-cols-3">
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
|
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
Total Seats
|
Total Seats
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
|
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
Total bookings
|
Total bookings
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
|
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
Booking status
|
Booking status
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
class="border border-default bg-default shadow-sm"
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
:ui="{ header: 'px-4 py-3', body: 'p-0 sm:p-0' }"
|
:ui="{ header: 'px-4 py-3', body: 'p-0 sm:p-0' }"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
:empty="searchQuery.trim() ? 'No matching bookings found.' : 'No bookings available yet.'"
|
:empty="searchQuery.trim() ? 'No matching bookings found.' : 'No bookings available yet.'"
|
||||||
sticky="header"
|
sticky="header"
|
||||||
caption="Bookings"
|
caption="Bookings"
|
||||||
class="min-w-[1120px]"
|
class="compact-table min-w-[1120px]"
|
||||||
>
|
>
|
||||||
<template #customerName-cell="{ row }">
|
<template #customerName-cell="{ row }">
|
||||||
<div class="min-w-0 space-y-0.5 py-0.5">
|
<div class="min-w-0 space-y-0.5 py-0.5">
|
||||||
@@ -363,7 +363,7 @@
|
|||||||
class="space-y-4"
|
class="space-y-4"
|
||||||
@submit="saveBookingDetails"
|
@submit="saveBookingDetails"
|
||||||
>
|
>
|
||||||
<div v-if="detailsBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
|
<div v-if="detailsBooking" class="surface-panel rounded-lg px-3 py-2">
|
||||||
<p class="text-sm font-medium text-highlighted">
|
<p class="text-sm font-medium text-highlighted">
|
||||||
{{ detailsBooking.customerName }}
|
{{ detailsBooking.customerName }}
|
||||||
</p>
|
</p>
|
||||||
@@ -426,12 +426,12 @@
|
|||||||
:disabled="savingDetails || !ticketCatalogItems.length"
|
:disabled="savingDetails || !ticketCatalogItems.length"
|
||||||
:ui="{
|
:ui="{
|
||||||
fieldset: 'grid grid-cols-2 gap-3',
|
fieldset: 'grid grid-cols-2 gap-3',
|
||||||
item: 'rounded-lg border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
|
|
||||||
<div class="rounded-lg border border-default bg-muted/30 px-4 py-3">
|
<div class="surface-panel rounded-lg px-4 py-3">
|
||||||
<div class="flex items-center justify-between gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<span class="text-sm font-medium text-muted">Updated total</span>
|
<span class="text-sm font-medium text-muted">Updated total</span>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
@@ -490,7 +490,7 @@
|
|||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div v-if="editingBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
|
<div v-if="editingBooking" class="surface-panel rounded-lg px-3 py-2">
|
||||||
<p class="text-sm font-medium text-highlighted">
|
<p class="text-sm font-medium text-highlighted">
|
||||||
{{ editingBooking.customerName }}
|
{{ editingBooking.customerName }}
|
||||||
</p>
|
</p>
|
||||||
@@ -543,7 +543,7 @@
|
|||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div v-if="transferringBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
|
<div v-if="transferringBooking" class="surface-panel rounded-lg px-3 py-2">
|
||||||
<p class="text-sm font-medium text-highlighted">
|
<p class="text-sm font-medium text-highlighted">
|
||||||
{{ transferringBooking.customerName }}
|
{{ transferringBooking.customerName }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -160,20 +160,20 @@ async function cancelBookingConfirmation() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UContainer class="py-8">
|
<UContainer class="page-shell-compact">
|
||||||
<div class="mx-auto max-w-3xl space-y-5">
|
<div class="space-y-5">
|
||||||
<div class="space-y-2 text-center">
|
<div class="page-header text-center sm:items-center">
|
||||||
<UBadge :label="t('confirm.badge')" color="primary" variant="soft" class="rounded-full" />
|
<UBadge :label="t('confirm.badge')" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
|
<h1 class="page-title">
|
||||||
{{ t('confirm.title') }}
|
{{ t('confirm.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-sm text-muted">
|
<p class="page-description">
|
||||||
{{ t('confirm.description') }}
|
{{ t('confirm.description') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
class="border border-default bg-default shadow-sm"
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
:ui="{ body: 'space-y-4 p-4 sm:p-5' }"
|
:ui="{ body: 'space-y-4 p-4 sm:p-5' }"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
@@ -197,7 +197,7 @@ async function cancelBookingConfirmation() {
|
|||||||
icon="i-lucide-badge-check"
|
icon="i-lucide-badge-check"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="overflow-hidden rounded-xl border border-default">
|
<div class="overflow-hidden rounded-lg border border-default">
|
||||||
<div
|
<div
|
||||||
v-for="row in detailRows"
|
v-for="row in detailRows"
|
||||||
:key="row.label"
|
:key="row.label"
|
||||||
@@ -221,7 +221,7 @@ async function cancelBookingConfirmation() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col-reverse gap-3 sm:flex-row sm:justify-end">
|
<div class="action-row">
|
||||||
<UButton
|
<UButton
|
||||||
to="/"
|
to="/"
|
||||||
:label="t('confirm.backToForm')"
|
:label="t('confirm.backToForm')"
|
||||||
|
|||||||
@@ -234,30 +234,58 @@ async function bookTicket(event: FormSubmitEvent<typeof form>) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UContainer class="py-8">
|
<UContainer class="page-shell-narrow">
|
||||||
<div class="mx-auto max-w-2xl">
|
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_34rem] xl:items-start">
|
||||||
<div class="mb-8 text-center">
|
<section class="space-y-6 xl:sticky xl:top-6">
|
||||||
<h1 class="text-3xl font-extrabold tracking-tight text-highlighted sm:text-4xl">
|
<div class="page-header">
|
||||||
{{ bookingConfig.event.title }}
|
<UBadge :label="t('layout.brand')" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
</h1>
|
<h1 class="page-title">
|
||||||
</div>
|
{{ bookingConfig.event.title }}
|
||||||
|
</h1>
|
||||||
|
<p class="page-description">
|
||||||
|
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UCard id="booking-form" class="border border-default bg-default" :ui="{
|
<div class="grid gap-3 sm:grid-cols-3 xl:grid-cols-1">
|
||||||
header: 'space-y-4',
|
<div
|
||||||
body: 'space-y-6'
|
v-for="detail in eventDetails"
|
||||||
}">
|
:key="detail.label"
|
||||||
<template #header>
|
class="surface-card rounded-lg p-4"
|
||||||
<div class="space-y-3">
|
>
|
||||||
<div v-for="detail in eventDetails" :key="detail.label"
|
<div class="flex items-start gap-3">
|
||||||
class="flex items-center gap-3 text-sm text-default">
|
<div class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||||
<UIcon :name="detail.icon" class="size-4 text-muted" />
|
<UIcon :name="detail.icon" class="size-5" />
|
||||||
<span>{{ detail.value }}</span>
|
</div>
|
||||||
|
<div class="min-w-0">
|
||||||
|
<p class="text-xs font-semibold uppercase text-muted">
|
||||||
|
{{ detail.label }}
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 text-sm font-semibold leading-6 text-highlighted break-words">
|
||||||
|
{{ detail.value }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<UCard
|
||||||
|
id="booking-form"
|
||||||
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
|
:ui="{ body: 'space-y-6 p-5 sm:p-6' }"
|
||||||
|
>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<p class="text-sm font-semibold text-primary">
|
||||||
|
{{ t('booking.bookNow') }}
|
||||||
|
</p>
|
||||||
|
<h2 class="text-xl font-semibold text-highlighted">
|
||||||
|
{{ t('booking.ticketCategory') }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UForm :state="form" :validate="validateBooking" class="space-y-6" @submit="bookTicket">
|
<UForm :state="form" :validate="validateBooking" class="space-y-6" @submit="bookTicket">
|
||||||
<div class="space-y-5">
|
<div class="grid gap-5 sm:grid-cols-2">
|
||||||
<UFormField name="name" :label="t('booking.name')" required>
|
<UFormField name="name" :label="t('booking.name')" required>
|
||||||
<UInput v-model="form.name" size="xl" class="w-full" :placeholder="t('booking.namePlaceholder')" />
|
<UInput v-model="form.name" size="xl" class="w-full" :placeholder="t('booking.namePlaceholder')" />
|
||||||
</UFormField>
|
</UFormField>
|
||||||
@@ -268,47 +296,68 @@ async function bookTicket(event: FormSubmitEvent<typeof form>) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UFormField :label="t('booking.bookingMode')" name="bookingMode">
|
<UFormField :label="t('booking.bookingMode')" name="bookingMode">
|
||||||
<URadioGroup v-model="form.bookingMode" orientation="horizontal" variant="card" indicator="hidden"
|
<URadioGroup
|
||||||
:items="bookingModeOptions" :ui="{
|
v-model="form.bookingMode"
|
||||||
|
orientation="horizontal"
|
||||||
|
variant="card"
|
||||||
|
indicator="hidden"
|
||||||
|
:items="bookingModeOptions"
|
||||||
|
:ui="{
|
||||||
fieldset: 'grid grid-cols-2 gap-3',
|
fieldset: 'grid grid-cols-2 gap-3',
|
||||||
item: 'rounded-xl border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
||||||
}" />
|
}"
|
||||||
</UFormField>
|
|
||||||
|
|
||||||
<UFormField :label="quantityLabel" name="quantity">
|
|
||||||
<UInputNumber v-model="form.quantity" size="xl" class="w-full" :min="1" :step="1" />
|
|
||||||
<template #help>
|
|
||||||
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
|
|
||||||
</template>
|
|
||||||
</UFormField>
|
|
||||||
|
|
||||||
<UFormField :label="t('booking.ticketCategory')" name="ticketType">
|
|
||||||
<URadioGroup v-model="form.ticketType" orientation="horizontal" variant="card" indicator="hidden"
|
|
||||||
:items="ticketCatalogOptions" :ui="{
|
|
||||||
fieldset: 'grid grid-cols-2 gap-3',
|
|
||||||
item: 'rounded-xl border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
|
||||||
}" />
|
|
||||||
</UFormField>
|
|
||||||
|
|
||||||
<div class="rounded-xl border border-default bg-muted px-4 py-4">
|
|
||||||
<div class="flex items-center justify-between gap-4">
|
|
||||||
<span class="text-sm font-medium text-muted">{{ t('common.totalPrice') }}</span>
|
|
||||||
<span class="text-2xl font-bold text-highlighted">{{ totalFormatted }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<UFormField :label="t('booking.personInCharge')">
|
|
||||||
<USelect
|
|
||||||
v-model="selectedPersonInCharge"
|
|
||||||
size="xl"
|
|
||||||
class="w-full"
|
|
||||||
:items="personInCharge"
|
|
||||||
:disabled="!personInCharge.length"
|
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
|
|
||||||
<UButton id="getTicketBtn" type="submit" :label="t('booking.bookNow')" size="xl"
|
<div class="grid gap-5 sm:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]">
|
||||||
class="w-full justify-center" :disabled="!selectedPersonInCharge || !selectedBookingMode || !selectedTicket" :loading="submittingBooking" />
|
<UFormField :label="quantityLabel" name="quantity">
|
||||||
|
<UInputNumber v-model="form.quantity" size="xl" class="w-full" :min="1" :step="1" />
|
||||||
|
<template #help>
|
||||||
|
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
|
||||||
|
</template>
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
|
<UFormField :label="t('booking.personInCharge')">
|
||||||
|
<USelect
|
||||||
|
v-model="selectedPersonInCharge"
|
||||||
|
size="xl"
|
||||||
|
class="w-full"
|
||||||
|
:items="personInCharge"
|
||||||
|
:disabled="!personInCharge.length"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UFormField :label="t('booking.ticketCategory')" name="ticketType">
|
||||||
|
<URadioGroup
|
||||||
|
v-model="form.ticketType"
|
||||||
|
orientation="horizontal"
|
||||||
|
variant="card"
|
||||||
|
indicator="hidden"
|
||||||
|
:items="ticketCatalogOptions"
|
||||||
|
:ui="{
|
||||||
|
fieldset: 'grid grid-cols-1 gap-3 sm:grid-cols-2',
|
||||||
|
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
|
<div class="surface-panel rounded-lg px-4 py-4">
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<span class="text-sm font-medium text-muted">{{ t('common.totalPrice') }}</span>
|
||||||
|
<span class="text-2xl font-bold tabular-nums text-highlighted">{{ totalFormatted }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
id="getTicketBtn"
|
||||||
|
type="submit"
|
||||||
|
:label="t('booking.bookNow')"
|
||||||
|
size="xl"
|
||||||
|
class="w-full justify-center"
|
||||||
|
:disabled="!selectedPersonInCharge || !selectedBookingMode || !selectedTicket"
|
||||||
|
:loading="submittingBooking"
|
||||||
|
/>
|
||||||
</UForm>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<UContainer class="py-10 lg:py-16">
|
<UContainer class="page-shell-narrow">
|
||||||
<div class="mx-auto max-w-md">
|
<div class="grid gap-8 lg:grid-cols-[minmax(0,1fr)_27rem] lg:items-center">
|
||||||
<UCard class="border border-default bg-default shadow-sm">
|
<section class="page-header">
|
||||||
|
<UBadge :label="t('login.badge')" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
|
<h1 class="page-title">
|
||||||
|
{{ t('login.title') }}
|
||||||
|
</h1>
|
||||||
|
<p class="page-description">
|
||||||
|
{{ t('layout.brand') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
|
||||||
|
<div class="surface-card rounded-lg p-4">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||||
|
<UIcon name="i-lucide-lock-keyhole" class="size-5" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-highlighted">
|
||||||
|
{{ t('login.signIn') }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted">
|
||||||
|
{{ t('login.remember') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="surface-card rounded-lg p-4">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||||
|
<UIcon name="i-lucide-fingerprint" class="size-5" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-highlighted">
|
||||||
|
{{ t('login.passkey') }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted">
|
||||||
|
{{ t('login.or') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<UCard class="surface-card overflow-hidden rounded-lg">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="space-y-2">
|
<div class="space-y-1">
|
||||||
<UBadge :label="t('login.badge')" color="primary" variant="soft" class="rounded-full" />
|
<p class="text-sm font-semibold text-primary">
|
||||||
<h1 class="text-3xl font-bold text-highlighted">
|
{{ t('login.badge') }}
|
||||||
{{ t('login.title') }}
|
</p>
|
||||||
</h1>
|
<h2 class="text-xl font-semibold text-highlighted">
|
||||||
|
{{ t('login.signIn') }}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,22 +91,20 @@
|
|||||||
|
|
||||||
<div class="my-6 flex items-center gap-3">
|
<div class="my-6 flex items-center gap-3">
|
||||||
<div class="h-px flex-1 bg-default" />
|
<div class="h-px flex-1 bg-default" />
|
||||||
<span class="text-xs font-semibold uppercase tracking-[0.2em] text-muted">{{ t('login.or') }}</span>
|
<span class="text-xs font-semibold uppercase text-muted">{{ t('login.or') }}</span>
|
||||||
<div class="h-px flex-1 bg-default" />
|
<div class="h-px flex-1 bg-default" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<UButton
|
||||||
<UButton
|
:label="t('login.passkey')"
|
||||||
:label="t('login.passkey')"
|
color="neutral"
|
||||||
color="neutral"
|
variant="outline"
|
||||||
variant="outline"
|
size="xl"
|
||||||
size="xl"
|
class="w-full justify-center"
|
||||||
class="w-full justify-center"
|
icon="i-lucide-fingerprint"
|
||||||
icon="i-lucide-fingerprint"
|
:loading="passkeyPending"
|
||||||
:loading="passkeyPending"
|
@click="loginWithPasskey"
|
||||||
@click="loginWithPasskey"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</UCard>
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
</UContainer>
|
</UContainer>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<UContainer class="py-8">
|
<UContainer class="page-shell">
|
||||||
<div class="mx-auto max-w-6xl space-y-4">
|
<div class="space-y-5">
|
||||||
<div class="space-y-1.5">
|
<div class="page-header">
|
||||||
<UBadge label="Super Admin" color="primary" variant="soft" class="rounded-full" />
|
<UBadge label="Super Admin" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
<h1 class="text-2xl font-bold text-highlighted sm:text-3xl">
|
<h1 class="page-title">
|
||||||
User management
|
User management
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
icon="i-lucide-key-round"
|
icon="i-lucide-key-round"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'p-0 sm:p-0' }">
|
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'p-0 sm:p-0' }">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex flex-col gap-2.5 lg:flex-row lg:items-center lg:justify-between">
|
<div class="flex flex-col gap-2.5 lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<div class="min-w-[900px]" aria-label="Users">
|
<div class="compact-table min-w-[900px]" aria-label="Users">
|
||||||
<div class="grid grid-cols-[56px_minmax(190px,1.4fr)_minmax(150px,1fr)_120px_minmax(190px,1.2fr)_150px_minmax(230px,auto)] items-center gap-3 border-b border-default bg-muted px-4 py-2 text-xs font-semibold uppercase text-muted">
|
<div class="grid grid-cols-[56px_minmax(190px,1.4fr)_minmax(150px,1fr)_120px_minmax(190px,1.2fr)_150px_minmax(230px,auto)] items-center gap-3 border-b border-default bg-muted px-4 py-3 text-xs font-semibold uppercase text-muted">
|
||||||
<div>Order</div>
|
<div>Order</div>
|
||||||
<div>Display Name</div>
|
<div>Display Name</div>
|
||||||
<div>PIC Phone</div>
|
<div>PIC Phone</div>
|
||||||
|
|||||||
@@ -365,23 +365,23 @@ async function openBatchShare() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UContainer class="py-6 sm:py-8">
|
<UContainer class="page-shell-narrow">
|
||||||
<div class="mx-auto max-w-5xl space-y-5">
|
<div class="space-y-6">
|
||||||
<div class="space-y-1 text-center">
|
<div class="page-header text-center sm:items-center">
|
||||||
<UBadge :label="t('receipt.badge')" color="primary" variant="soft" class="rounded-full" />
|
<UBadge :label="t('receipt.badge')" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
|
<h1 class="page-title">
|
||||||
{{ eventDetails.title }}
|
{{ eventDetails.title }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-default p-1.5 shadow-sm">
|
<div class="surface-card rounded-lg p-1.5">
|
||||||
<div class="flex gap-1.5">
|
<div class="flex gap-1.5">
|
||||||
<button
|
<button
|
||||||
v-for="tab in tabs"
|
v-for="tab in tabs"
|
||||||
:key="tab.id"
|
:key="tab.id"
|
||||||
type="button"
|
type="button"
|
||||||
class="flex min-h-10 flex-1 items-center justify-center gap-1.5 rounded-xl px-3 py-2 text-xs font-medium transition sm:text-sm"
|
class="flex min-h-10 flex-1 items-center justify-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium transition sm:text-sm"
|
||||||
:class="activeTab === tab.id
|
:class="activeTab === tab.id
|
||||||
? 'bg-primary text-inverted shadow-sm'
|
? 'bg-primary text-inverted shadow-sm'
|
||||||
: 'bg-elevated text-default hover:bg-muted'"
|
: 'bg-elevated text-default hover:bg-muted'"
|
||||||
@click="activeTab = tab.id"
|
@click="activeTab = tab.id"
|
||||||
@@ -394,17 +394,17 @@ async function openBatchShare() {
|
|||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
v-if="activeTab === 'main'"
|
v-if="activeTab === 'main'"
|
||||||
class="border border-default bg-default shadow-sm"
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
:ui="{ body: 'p-5 sm:p-8' }"
|
:ui="{ body: 'p-5 sm:p-8' }"
|
||||||
>
|
>
|
||||||
<div class="mx-auto flex max-w-sm flex-col items-center gap-5 text-center">
|
<div class="mx-auto flex max-w-sm flex-col items-center gap-5 text-center">
|
||||||
<div class="rounded-[1.75rem] border border-default bg-elevated p-5 shadow-sm">
|
<div class="surface-panel rounded-lg p-5 shadow-sm">
|
||||||
<QrCodeSvg :value="receipt.receiptUrl" :size="240" />
|
<QrCodeSvg :value="receipt.receiptUrl" :size="240" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid w-full gap-2 sm:grid-cols-3">
|
<div class="grid w-full gap-2 sm:grid-cols-3">
|
||||||
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
|
<div class="surface-panel rounded-lg px-4 py-3 text-left">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
{{ t('common.category') }}
|
{{ t('common.category') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-sm font-semibold text-highlighted">
|
<p class="mt-1 text-sm font-semibold text-highlighted">
|
||||||
@@ -412,8 +412,8 @@ async function openBatchShare() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
|
<div class="surface-panel rounded-lg px-4 py-3 text-left">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
{{ t('common.seats') }}
|
{{ t('common.seats') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-sm font-semibold text-highlighted">
|
<p class="mt-1 text-sm font-semibold text-highlighted">
|
||||||
@@ -421,8 +421,8 @@ async function openBatchShare() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
|
<div class="surface-panel rounded-lg px-4 py-3 text-left">
|
||||||
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
||||||
{{ t('common.totalPrice') }}
|
{{ t('common.totalPrice') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-sm font-semibold text-highlighted">
|
<p class="mt-1 text-sm font-semibold text-highlighted">
|
||||||
@@ -443,10 +443,10 @@ async function openBatchShare() {
|
|||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
v-else-if="activeTab === 'status'"
|
v-else-if="activeTab === 'status'"
|
||||||
class="border border-default bg-default shadow-sm"
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
:ui="{ body: 'p-0' }"
|
:ui="{ body: 'p-0' }"
|
||||||
>
|
>
|
||||||
<div class="overflow-hidden rounded-2xl">
|
<div class="overflow-hidden rounded-lg">
|
||||||
<div
|
<div
|
||||||
v-for="row in statusRows"
|
v-for="row in statusRows"
|
||||||
:key="row.label"
|
:key="row.label"
|
||||||
@@ -467,7 +467,7 @@ async function openBatchShare() {
|
|||||||
|
|
||||||
<UCard
|
<UCard
|
||||||
v-else
|
v-else
|
||||||
class="border border-default bg-default shadow-sm"
|
class="surface-card overflow-hidden rounded-lg"
|
||||||
:ui="{ header: 'px-4 py-3', body: 'p-0' }"
|
:ui="{ header: 'px-4 py-3', body: 'p-0' }"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -494,7 +494,7 @@ async function openBatchShare() {
|
|||||||
:data="receipt.seats"
|
:data="receipt.seats"
|
||||||
:columns="seatColumns"
|
:columns="seatColumns"
|
||||||
:caption="t('common.seats')"
|
:caption="t('common.seats')"
|
||||||
class="min-w-[560px] sm:min-w-[720px]"
|
class="compact-table min-w-[560px] sm:min-w-[720px]"
|
||||||
>
|
>
|
||||||
<template #seatNumber-cell="{ row }">
|
<template #seatNumber-cell="{ row }">
|
||||||
<div class="min-w-0 space-y-0.5 py-0.5">
|
<div class="min-w-0 space-y-0.5 py-0.5">
|
||||||
@@ -502,7 +502,7 @@ async function openBatchShare() {
|
|||||||
<p class="text-sm font-semibold leading-tight text-highlighted sm:text-base">
|
<p class="text-sm font-semibold leading-tight text-highlighted sm:text-base">
|
||||||
{{ getSeatLabel(row.original.seatNumber, locale) }}
|
{{ getSeatLabel(row.original.seatNumber, locale) }}
|
||||||
</p>
|
</p>
|
||||||
<UBadge
|
<UBadge
|
||||||
:label="row.original.sharedAt ? t('receipt.sharedStatus') : t('receipt.availableStatus')"
|
:label="row.original.sharedAt ? t('receipt.sharedStatus') : t('receipt.availableStatus')"
|
||||||
:color="row.original.sharedAt ? 'primary' : 'neutral'"
|
:color="row.original.sharedAt ? 'primary' : 'neutral'"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
@@ -586,7 +586,7 @@ async function openBatchShare() {
|
|||||||
<template #body>
|
<template #body>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="grid gap-3 sm:grid-cols-2">
|
<div class="grid gap-3 sm:grid-cols-2">
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
{{ t('receipt.seatsToShare') }}
|
{{ t('receipt.seatsToShare') }}
|
||||||
</p>
|
</p>
|
||||||
@@ -600,7 +600,7 @@ async function openBatchShare() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
{{ t('receipt.nextSeats') }}
|
{{ t('receipt.nextSeats') }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -32,20 +32,20 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UContainer class="py-8">
|
<UContainer class="page-shell-narrow">
|
||||||
<div class="mx-auto max-w-4xl space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="space-y-2 text-center">
|
<div class="page-header text-center sm:items-center">
|
||||||
<UBadge label="Seat Ticket" color="primary" variant="soft" class="rounded-full" />
|
<UBadge label="Seat Ticket" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
|
<h1 class="page-title">
|
||||||
{{ getSeatLabel(receipt.seat.seatNumber) }}
|
{{ getSeatLabel(receipt.seat.seatNumber) }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-sm text-muted">
|
<p class="page-description">
|
||||||
{{ eventDetails.title }}
|
{{ eventDetails.title }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-6 lg:grid-cols-[18rem_minmax(0,1fr)]">
|
<div class="grid gap-6 lg:grid-cols-[18rem_minmax(0,1fr)]">
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'space-y-4 p-4 sm:p-5' }">
|
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'space-y-4 p-4 sm:p-5' }">
|
||||||
<div class="space-y-1 text-center">
|
<div class="space-y-1 text-center">
|
||||||
<p class="text-sm font-semibold text-highlighted">
|
<p class="text-sm font-semibold text-highlighted">
|
||||||
QR Code
|
QR Code
|
||||||
@@ -59,7 +59,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
<QrCodeSvg :value="receipt.seat.seatUrl" :size="220" />
|
<QrCodeSvg :value="receipt.seat.seatUrl" :size="220" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4 text-sm text-default">
|
<div class="surface-panel rounded-lg p-4 text-sm text-default">
|
||||||
<p class="font-medium text-highlighted">
|
<p class="font-medium text-highlighted">
|
||||||
{{ receipt.seat.recipientName || receipt.booking.customerName }}
|
{{ receipt.seat.recipientName || receipt.booking.customerName }}
|
||||||
</p>
|
</p>
|
||||||
@@ -72,7 +72,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
</div>
|
</div>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'space-y-5 p-4 sm:p-5' }">
|
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'space-y-5 p-4 sm:p-5' }">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-xl font-semibold text-highlighted">
|
<h2 class="text-xl font-semibold text-highlighted">
|
||||||
Booking Details
|
Booking Details
|
||||||
@@ -83,7 +83,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-3 sm:grid-cols-2">
|
<div class="grid gap-3 sm:grid-cols-2">
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Guest / Organizer
|
Guest / Organizer
|
||||||
</p>
|
</p>
|
||||||
@@ -91,7 +91,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
{{ receipt.booking.customerName }}
|
{{ receipt.booking.customerName }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Ticket Category
|
Ticket Category
|
||||||
</p>
|
</p>
|
||||||
@@ -99,7 +99,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
{{ ticketLabel }}
|
{{ ticketLabel }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Total Seats
|
Total Seats
|
||||||
</p>
|
</p>
|
||||||
@@ -107,7 +107,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
{{ receipt.booking.seatCount }} seats
|
{{ receipt.booking.seatCount }} seats
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<p class="text-xs uppercase tracking-wide text-muted">
|
<p class="text-xs uppercase tracking-wide text-muted">
|
||||||
Total Booking Value
|
Total Booking Value
|
||||||
</p>
|
</p>
|
||||||
@@ -117,7 +117,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-3 rounded-2xl border border-default bg-elevated p-4">
|
<div class="surface-panel space-y-3 rounded-lg p-4">
|
||||||
<div class="flex items-center gap-3 text-sm text-default">
|
<div class="flex items-center gap-3 text-sm text-default">
|
||||||
<UIcon name="i-lucide-calendar-days" class="size-4 text-muted" />
|
<UIcon name="i-lucide-calendar-days" class="size-4 text-muted" />
|
||||||
<span>{{ eventDetails.dateLabel }}</span>
|
<span>{{ eventDetails.dateLabel }}</span>
|
||||||
@@ -132,7 +132,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-2xl border border-default bg-elevated p-4 text-sm text-default">
|
<div class="surface-panel rounded-lg p-4 text-sm text-default">
|
||||||
<p class="font-medium text-highlighted">
|
<p class="font-medium text-highlighted">
|
||||||
Seat shared {{ receipt.seat.sharedAt ? formatDateTime(receipt.seat.sharedAt) : 'recently' }}
|
Seat shared {{ receipt.seat.sharedAt ? formatDateTime(receipt.seat.sharedAt) : 'recently' }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<UContainer class="py-8">
|
<UContainer class="page-shell-narrow">
|
||||||
<div class="mx-auto max-w-5xl space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="space-y-2">
|
<div class="page-header">
|
||||||
<UBadge label="Security" color="primary" variant="soft" class="rounded-full" />
|
<UBadge label="Security" color="primary" variant="soft" class="page-eyebrow" />
|
||||||
<h1 class="text-3xl font-bold text-highlighted">
|
<h1 class="page-title">
|
||||||
Password and passkey settings
|
Password and passkey settings
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
|
<div class="grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
|
||||||
<UCard class="border border-default bg-default">
|
<UCard class="surface-card overflow-hidden rounded-lg">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h2 class="text-xl font-semibold text-highlighted">
|
<h2 class="text-lg font-semibold text-highlighted">
|
||||||
Change password
|
Change password
|
||||||
</h2>
|
</h2>
|
||||||
</template>
|
</template>
|
||||||
@@ -39,15 +39,15 @@
|
|||||||
</UForm>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard class="border border-default bg-default">
|
<UCard class="surface-card overflow-hidden rounded-lg">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h2 class="text-xl font-semibold text-highlighted">
|
<h2 class="text-lg font-semibold text-highlighted">
|
||||||
Passkeys
|
Passkeys
|
||||||
</h2>
|
</h2>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="space-y-5">
|
<div class="space-y-5">
|
||||||
<div class="rounded-2xl border border-default bg-muted/40 p-4">
|
<div class="surface-panel rounded-lg p-4">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-sm font-semibold text-highlighted">
|
<div class="text-sm font-semibold text-highlighted">
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="passkey in passkeys"
|
v-for="passkey in passkeys"
|
||||||
:key="passkey.id"
|
:key="passkey.id"
|
||||||
class="rounded-2xl border border-default bg-default px-4 py-4"
|
class="surface-panel rounded-lg px-4 py-4"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user