Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions apps/web/app/i18n/staticSiteText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ export const staticSiteText = {
'signin.title': 'Welcome back',
'signin.subtitle': 'Sign in to see your entries, rewards and live draws.',
'signin.subtitle.business': 'Sign in to manage your campaigns.',
'signin.badge': 'Secure access',
'signin.badge.business': 'Business portal',
'signin.switch.business': 'Running campaigns? Business sign in',
'signin.switch.participant': 'Joining campaigns? Participant sign in',
'signin.email': 'Email address',
Expand All @@ -51,7 +49,6 @@ export const staticSiteText = {
// registration (one form, two portals: ADR-P001)
'register.participant.title': 'Create your account',
'register.participant.subtitle': 'Join campaigns, track your entries and claim rewards.',
'register.participant.badge': 'Free for participants',
'register.participant.name': 'Full name',
'register.participant.email': 'Email address',
'register.participant.agreement': 'I accept the Terms of Service and the Privacy Notice',
Expand Down Expand Up @@ -91,7 +88,6 @@ export const staticSiteText = {

// check email
'checkEmail.title': 'Check your email',
'checkEmail.badge': 'Verification link sent',
'checkEmail.body':
'If {email} is not registered yet, we sent it a verification link. Open the link to activate your account. If you already have an account, sign in instead.',
'checkEmail.bodyNoEmail':
Expand Down
14 changes: 6 additions & 8 deletions apps/web/app/modules/auth/components/RegisterForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { t, type AppTextKey } from '../../../i18n';
import { useAuth } from '../composables/useAuth';
import { useSignupDraft } from '../composables/useSignupDraft';
import AuthFailureNotice from './AuthFailureNotice.vue';
import { AGREEMENT_VERSION, BRAND_NAME, HOME } from '../constants';
import { AGREEMENT_VERSION, HOME } from '../constants';
import { classifyAuthError, toApiError, type AuthFailure } from '../errors';
import {
businessNameProblem,
Expand Down Expand Up @@ -122,21 +122,19 @@ async function submit() {
loading.value = false; // input is kept so nothing is lost on a recoverable failure
}
}
const crumbs = [
{ label: t('nav.discover'), to: '/discover' },
{ label: isBusiness ? t('footer.business.register') : t('nav.signup') },
];
</script>

<template>
<UiAuthShell
:size="isBusiness ? 'wide' : 'narrow'"
:brand-name="BRAND_NAME"
:breadcrumbs="crumbs"
:title="k('title')"
:subtitle="k('subtitle')"
>
<template v-if="!isBusiness" #badge>
<UiBadge shape="pill" tone="info" icon="shield-check">{{
t('register.participant.badge')
}}</UiBadge>
</template>

<form class="form" novalidate @submit.prevent="submit">
<UiErrorSummary :errors="summary" />
<AuthFailureNotice
Expand Down
14 changes: 6 additions & 8 deletions apps/web/app/modules/auth/components/SigninForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { computed, onMounted, ref } from 'vue';
import { t } from '../../../i18n';
import { useAuth } from '../composables/useAuth';
import AuthFailureNotice from './AuthFailureNotice.vue';
import { BRAND_NAME, HOME } from '../constants';
import { HOME } from '../constants';
import type { Portal } from '../types';
import { classifyAuthError, toApiError, type AuthFailure } from '../errors';
import {
Expand Down Expand Up @@ -70,20 +70,18 @@ async function submit() {
loading.value = false; // input is kept so nothing is lost on a recoverable failure
}
}
const crumbs = [
{ label: t('nav.discover'), to: '/discover' },
{ label: isBusiness ? t('footer.business.signin') : t('nav.signin') },
];
</script>

<template>
<UiAuthShell
:brand-name="BRAND_NAME"
:breadcrumbs="crumbs"
:title="t('signin.title')"
:subtitle="isBusiness ? t('signin.subtitle.business') : t('signin.subtitle')"
>
<template #badge>
<UiBadge shape="pill" tone="info" icon="shield-check">{{
isBusiness ? t('signin.badge.business') : t('signin.badge')
}}</UiBadge>
</template>

<form class="form" novalidate @submit.prevent="submit">
<UiErrorSummary :errors="summary" />
<AuthFailureNotice
Expand Down
10 changes: 5 additions & 5 deletions apps/web/app/pages/check-email.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { t } from '../i18n';
import { useSignupDraft } from '../modules/auth/composables/useSignupDraft';
import { BRAND_NAME, SIGNIN } from '../modules/auth/constants';
import { SIGNIN } from '../modules/auth/constants';

useHead({ title: t('checkEmail.title') });

Expand All @@ -10,10 +10,10 @@ const isDev = import.meta.dev;
</script>

<template>
<UiAuthShell :brand-name="BRAND_NAME" :title="t('checkEmail.title')">
<template #badge>
<UiBadge shape="pill" tone="success" icon="mail">{{ t('checkEmail.badge') }}</UiBadge>
</template>
<UiAuthShell
:breadcrumbs="[{ label: t('nav.discover'), to: '/discover' }, { label: t('checkEmail.title') }]"
:title="t('checkEmail.title')"
>
<p class="ui-text-body">
{{
draft.submittedEmail
Expand Down
20 changes: 10 additions & 10 deletions apps/web/app/pages/verify-email.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { t } from '../i18n';
import { useAuth } from '../modules/auth/composables/useAuth';
import { BRAND_NAME } from '../modules/auth/constants';

/**
* Landing page for the link in the verification email. The token is redeemed by a POST from the
Expand All @@ -28,18 +27,19 @@ onMounted(async () => {
state.value = 'bad';
}
});
const title = computed(() =>
state.value === 'ok'
? t('verify.ok.title')
: state.value === 'bad'
? t('verify.bad.title')
: t('verify.working'),
);
</script>

<template>
<UiAuthShell
:brand-name="BRAND_NAME"
:title="
state === 'ok'
? t('verify.ok.title')
: state === 'bad'
? t('verify.bad.title')
: t('verify.working')
"
:breadcrumbs="[{ label: t('nav.discover'), to: '/discover' }, { label: title }]"
:title="title"
>
<UiSkeleton v-if="state === 'working'" variant="text" :lines="2" />
<template v-else-if="state === 'ok'">
Expand Down
4 changes: 2 additions & 2 deletions docs/frontend/component-catalogue.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@
| `UiStepper` | Builder stages (with status) and participant "Step n of N" progress. | `UiIcon`, `UiProgress` | - | UI-BLD-01, UI-PUB-05 |
| `UiTabs` | Tabs with arrow-key navigation. | - | - | Spec 5 |
| `UiAccordion` | Disclosure list for how-it-works and FAQ. | `UiIcon` | - | Spec 5, UI-PUB-01 |
| `UiBreadcrumbs` | Breadcrumb trail with the current page marked. | `UiIcon`, `UiLink` | - | UI-DSH-01 |
| `UiBreadcrumbs` | Breadcrumb trail with the current page marked. | `UiIcon`, `UiLink` | `UiAuthShell` | UI-DSH-01 |
| `UiSidebarNav` | Primary navigation filtered by role by the caller. | `UiIcon`, `UiLink` | `UiConsoleShell`, `UiDashboardShell` | UI-DSH-01, Spec 2.2 |
| `UiTopBar` | Business name and role, search, help, notifications and user menu. | `UiButton`, `UiIcon`, `UiLink` | `UiDashboardShell` | UI-DSH-01, UI-A11Y-13, UI-DSH-14 |
| `UiSkipLink` | Skip-to-content link, first focusable element on every page. | - | `UiAuthShell`, `UiConsoleShell`, `UiDashboardShell`, `UiPortalShell`, `UiPublicShell` | UI-A11Y-06 |
| `UiDashboardShell` | Dashboard frame: sidebar or drawer, top bar, banners, breadcrumbs, main landmark. | `UiSidebarNav`, `UiSkipLink`, `UiTopBar` | - | UI-DSH-01, UI-FND-05 |
| `UiPublicShell` | Public page frame: brand, optional navigation and actions, language switcher, reading column or wide listing width, required footer links. | `UiLink`, `UiSelect`, `UiSkipLink` | - | UI-PUB-01, UI-PUB-23, UI-A11Y-13 |
| `UiAuthShell` | Frame for sign-in, sign-up and onboarding: brand, one card, account-routing footer, optional side column. | `UiSkipLink` | - | UI-PUB-01, UI-A11Y-06 |
| `UiAuthShell` | Frame for sign-in, sign-up and onboarding: brand, one card, account-routing footer, optional side column. | `UiBreadcrumbs`, `UiSkipLink` | - | UI-PUB-01, UI-A11Y-06 |
| `UiConsoleShell` | Business console frame: sidebar (brand, navigation, account) and a slim header with search and actions. | `UiAvatar`, `UiButton`, `UiSidebarNav`, `UiSkipLink` | - | UI-DSH-01, UI-A11Y-05 |
| `UiPortalShell` | Participant frame: brand, tabs, signed-in person, one main landmark and a footer. | `UiAvatar`, `UiButton`, `UiLink`, `UiSkipLink` | - | UI-PRT-01, UI-A11Y-05 |

Expand Down
18 changes: 12 additions & 6 deletions packages/ui/app/components/UiAuthShell.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<script setup lang="ts">
import UiBreadcrumbs from './UiBreadcrumbs.vue';
import UiSkipLink from './UiSkipLink.vue';

/**
* Frame for sign-in, sign-up and onboarding screens: brand, one centred card (title, subtitle,
* Frame for sign-in, sign-up and onboarding screens: breadcrumbs back to where the person came
* from (or a brand line when none are given), one centred card (title, subtitle,
* body, account-routing footer strip) and optional text under it. `wide` adds a side column for
* supporting content on large screens. It holds no per-user data, so it is safe to server-render.
*/
withDefaults(
defineProps<{
title: string;
subtitle?: string;
brandName: string;
/** Shown only when there are no breadcrumbs. */
brandName?: string;
/** Trail back to the public site, e.g. Discover > Sign in. The last item is the current page. */
breadcrumbs?: Array<{ label: string; to?: string }>;
size?: 'narrow' | 'wide';
}>(),
{ size: 'narrow' },
Expand All @@ -20,8 +25,9 @@ withDefaults(
<template>
<div class="ui-auth">
<UiSkipLink />
<header class="ui-auth__brand">
<slot name="brand">
<header v-if="breadcrumbs?.length || brandName || $slots.brand" class="ui-auth__brand">
<UiBreadcrumbs v-if="breadcrumbs?.length" :items="breadcrumbs" />
<slot v-else name="brand">
<strong class="ui-text-h3">{{ brandName }}</strong>
</slot>
</header>
Expand Down Expand Up @@ -56,8 +62,8 @@ withDefaults(
}
.ui-auth__brand {
display: flex;
justify-content: center;
padding: var(--ui-space-8) var(--ui-space-4) 0;
justify-content: flex-start;
padding: var(--ui-space-5) var(--ui-space-6) 0;
}
.ui-auth__main {
display: flex;
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/app/gallery/composition.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const composition: Record<string, { composes: string[]; usedBy: string[]
},
"UiAuthShell": {
"composes": [
"UiBreadcrumbs",
"UiSkipLink"
],
"usedBy": []
Expand Down Expand Up @@ -45,7 +46,9 @@ export const composition: Record<string, { composes: string[]; usedBy: string[]
"UiIcon",
"UiLink"
],
"usedBy": []
"usedBy": [
"UiAuthShell"
]
},
"UiButton": {
"composes": [
Expand Down
Loading