Align admin mobile colors with Tamagui v2 tokens
This commit is contained in:
@@ -316,6 +316,7 @@ function LifecycleHero({
|
||||
const isPostEvent = phase === 'post';
|
||||
const showQuickControls = canManage && !isPostEvent;
|
||||
const displayStatus = event.status === 'archived' ? 'archived' : published ? 'published' : 'draft';
|
||||
const liveGradient = `linear-gradient(135deg, ${theme.primary} 0%, ${theme.accent} 100%)`;
|
||||
|
||||
// Header Row
|
||||
const Header = () => (
|
||||
@@ -338,13 +339,13 @@ function LifecycleHero({
|
||||
padding={cardPadding}
|
||||
backgroundColor={theme.primary}
|
||||
borderColor="transparent"
|
||||
style={{ backgroundImage: 'linear-gradient(135deg, #4F46E5 0%, #4338CA 100%)' }}
|
||||
style={{ backgroundImage: liveGradient }}
|
||||
>
|
||||
<YStack gap={isEmbedded ? '$2.5' : '$3'}>
|
||||
<XStack alignItems="center" justifyContent="space-between">
|
||||
<YStack gap="$1">
|
||||
<XStack alignItems="center" gap="$2">
|
||||
<YStack width={8} height={8} borderRadius={4} backgroundColor="#22C55E" />
|
||||
<YStack width={8} height={8} borderRadius={4} backgroundColor={theme.successText} />
|
||||
<Text color="white" fontWeight="700" fontSize="$xs" textTransform="uppercase" letterSpacing={1}>
|
||||
{t('dashboard:liveNow.status', 'Happening Now')}
|
||||
</Text>
|
||||
|
||||
@@ -148,9 +148,9 @@ function PhotoGridTile({
|
||||
badges: string[];
|
||||
isBusy: boolean;
|
||||
}) {
|
||||
const { border, muted, surfaceMuted } = useAdminTheme();
|
||||
const overlayBg = withAlpha('#0f172a', 0.55);
|
||||
const actionBg = withAlpha('#0f172a', 0.55);
|
||||
const { border, muted, surfaceMuted, overlay } = useAdminTheme();
|
||||
const overlayBg = overlay;
|
||||
const actionBg = overlay;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -48,10 +48,14 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
const backgroundColor = theme.background;
|
||||
const [isCompactHeader, setIsCompactHeader] = React.useState(false);
|
||||
|
||||
// --- DARK HEADER ---
|
||||
const headerSurface = '#0F172A'; // Slate 900
|
||||
const headerSurface = theme.surface;
|
||||
const headerText = theme.text;
|
||||
const headerMuted = theme.muted;
|
||||
const actionSurface = theme.primary;
|
||||
const actionBorder = 'rgba(255, 255, 255, 0.1)';
|
||||
const actionBorder = theme.glassBorder;
|
||||
const pillSurface = theme.glassSurface;
|
||||
const pillSurfacePress = theme.glassSurfaceStrong;
|
||||
const pillBorder = theme.glassBorder;
|
||||
|
||||
const [fallbackEvents, setFallbackEvents] = React.useState<TenantEvent[]>([]);
|
||||
const [loadingEvents, setLoadingEvents] = React.useState(false);
|
||||
@@ -144,7 +148,7 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
const EventContextPill = () => {
|
||||
if (!effectiveActive || isEventsIndex || isCompactHeader) {
|
||||
return (
|
||||
<Text fontSize="$md" fontWeight="700" fontFamily="$display" color="white">
|
||||
<Text fontSize="$md" fontWeight="700" fontFamily="$display" color={headerText}>
|
||||
{pageTitle}
|
||||
</Text>
|
||||
);
|
||||
@@ -158,7 +162,7 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
fontSize="$lg"
|
||||
fontWeight="700"
|
||||
fontFamily="$display"
|
||||
color="white"
|
||||
color={headerText}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
@@ -170,7 +174,7 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
return (
|
||||
<Pressable onPress={() => setSwitcherOpen(true)} aria-label={t('header.eventSwitcher', 'Switch event')}>
|
||||
<XStack
|
||||
backgroundColor="rgba(255, 255, 255, 0.12)"
|
||||
backgroundColor={pillSurface}
|
||||
paddingHorizontal="$3"
|
||||
paddingVertical="$1.5"
|
||||
borderRadius={999}
|
||||
@@ -178,20 +182,20 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
gap="$1.5"
|
||||
maxWidth={220}
|
||||
borderWidth={1}
|
||||
borderColor="rgba(255, 255, 255, 0.08)"
|
||||
pressStyle={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
|
||||
borderColor={pillBorder}
|
||||
pressStyle={{ backgroundColor: pillSurfacePress }}
|
||||
>
|
||||
<Text
|
||||
fontSize="$sm"
|
||||
fontWeight="700"
|
||||
color="white"
|
||||
color={headerText}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
flexShrink={1}
|
||||
>
|
||||
{displayName}
|
||||
</Text>
|
||||
<ChevronsUpDown size={14} color="rgba(255,255,255,0.6)" />
|
||||
<ChevronsUpDown size={14} color={headerMuted} />
|
||||
</XStack>
|
||||
</Pressable>
|
||||
);
|
||||
@@ -200,7 +204,7 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
|
||||
const headerBackButton = onBack ? (
|
||||
<HeaderActionButton onPress={onBack} ariaLabel={t('actions.back', 'Back')}>
|
||||
<XStack alignItems="center" gap="$1.5">
|
||||
<ChevronLeft size={28} color="white" strokeWidth={2.5} />
|
||||
<ChevronLeft size={28} color={headerText} strokeWidth={2.5} />
|
||||
</XStack>
|
||||
</HeaderActionButton>
|
||||
) : (
|
||||
|
||||
@@ -40,6 +40,10 @@ vi.mock('@tamagui/react-native-web-lite', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('tamagui', () => ({
|
||||
YStack: ({ children, ...props }: { children: React.ReactNode }) => <div {...props}>{children}</div>,
|
||||
XStack: ({ children, ...props }: { children: React.ReactNode }) => <div {...props}>{children}</div>,
|
||||
SizableText: ({ children, ...props }: { children: React.ReactNode }) => <span {...props}>{children}</span>,
|
||||
Image: ({ src, ...props }: { src?: string }) => <img src={src} alt="" {...props} />,
|
||||
Separator: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
|
||||
Tabs: Object.assign(({ children }: { children: React.ReactNode }) => <div>{children}</div>, {
|
||||
List: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
@@ -50,6 +54,7 @@ vi.mock('tamagui', () => ({
|
||||
|
||||
vi.mock('../BottomNav', () => ({
|
||||
BottomNav: () => <div data-testid="bottom-nav" />,
|
||||
BOTTOM_NAV_HEIGHT: 70,
|
||||
NavKey: {},
|
||||
}));
|
||||
|
||||
@@ -57,6 +62,10 @@ vi.mock('../UserMenuSheet', () => ({
|
||||
UserMenuSheet: () => <div data-testid="user-menu-sheet" />,
|
||||
}));
|
||||
|
||||
vi.mock('../EventSwitcherSheet', () => ({
|
||||
EventSwitcherSheet: () => <div data-testid="event-switcher-sheet" />,
|
||||
}));
|
||||
|
||||
const baseEvent: TenantEvent = {
|
||||
id: 1,
|
||||
name: 'Test Event',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useTheme, useThemeName } from '@tamagui/core';
|
||||
|
||||
export const ADMIN_COLORS = {
|
||||
primary: '#4F46E5', // Indigo 600
|
||||
primaryStrong: '#4338CA', // Indigo 700
|
||||
primary: '#FF5A5F', // Brand Rose
|
||||
primaryStrong: '#F43F5E', // Rose 500
|
||||
accent: '#F43F5E', // Rose 500
|
||||
accentSoft: '#E0E7FF', // Indigo 100
|
||||
accentWarm: '#FFE4E6', // Rose 100
|
||||
|
||||
@@ -10,11 +10,12 @@ import { MobileCard, CTAButton } from '../components/Primitives';
|
||||
import { ADMIN_HOME_PATH, ADMIN_WELCOME_BASE_PATH, ADMIN_WELCOME_PACKAGES_PATH, ADMIN_WELCOME_SUMMARY_PATH, adminPath } from '../../constants';
|
||||
import { getTenantPackagesOverview, trackOnboarding } from '../../api';
|
||||
import { getSelectedPackageId } from '../lib/onboardingSelection';
|
||||
import { ADMIN_COLORS } from '../theme';
|
||||
import { useAdminTheme } from '../theme';
|
||||
|
||||
export default function WelcomeEventPage() {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('onboarding');
|
||||
const theme = useAdminTheme();
|
||||
const selectedId = getSelectedPackageId();
|
||||
|
||||
const { data: overview } = useQuery({
|
||||
@@ -54,7 +55,7 @@ export default function WelcomeEventPage() {
|
||||
<Text fontSize="$sm" fontWeight="800">
|
||||
{t('eventSetup.step.title', 'Event setup in minutes')}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{t(
|
||||
'eventSetup.step.description',
|
||||
'We guide you through name, date, mood, and tasks. Afterwards you can moderate photos and support guests live.',
|
||||
@@ -83,7 +84,7 @@ export default function WelcomeEventPage() {
|
||||
<Text fontSize="$sm" fontWeight="800">
|
||||
{t('eventSetup.cta.heading', 'Ready for your first event?')}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{t(
|
||||
'eventSetup.cta.description',
|
||||
"You're switching to the event manager. Assign tasks, invite members, and test the gallery. You can always return to the welcome journey.",
|
||||
@@ -120,23 +121,24 @@ function FeatureRow({
|
||||
title: string;
|
||||
body: string;
|
||||
}) {
|
||||
const theme = useAdminTheme();
|
||||
return (
|
||||
<XStack alignItems="center" gap="$2">
|
||||
<XStack
|
||||
width={34}
|
||||
height={34}
|
||||
borderRadius={12}
|
||||
backgroundColor={ADMIN_COLORS.accentSoft}
|
||||
backgroundColor={theme.accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Icon size={16} color={ADMIN_COLORS.primary} />
|
||||
<Icon size={16} color={theme.primary} />
|
||||
</XStack>
|
||||
<YStack>
|
||||
<Text fontSize="$sm" fontWeight="700">
|
||||
{title}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
{body}
|
||||
</Text>
|
||||
</YStack>
|
||||
|
||||
@@ -15,12 +15,13 @@ import {
|
||||
ADMIN_WELCOME_PACKAGES_PATH,
|
||||
adminPath,
|
||||
} from '../../constants';
|
||||
import { ADMIN_COLORS } from '../theme';
|
||||
import { useAdminTheme } from '../theme';
|
||||
|
||||
export default function WelcomeLandingPage() {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('onboarding');
|
||||
const { hasEvents } = useEventContext();
|
||||
const theme = useAdminTheme();
|
||||
|
||||
const { data: packagesData } = useQuery({
|
||||
queryKey: ['mobile', 'onboarding', 'packages-overview'],
|
||||
@@ -53,7 +54,7 @@ export default function WelcomeLandingPage() {
|
||||
<Text fontSize="$lg" fontWeight="900">
|
||||
{t('hero.title', 'Design the next Fotospiel experience')}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{t(
|
||||
'hero.description',
|
||||
'In just a few steps you guide guests through a magical photo journey – complete with storytelling, tasks, and a moderated gallery.',
|
||||
@@ -124,11 +125,11 @@ function FeatureCard({
|
||||
width={36}
|
||||
height={36}
|
||||
borderRadius={12}
|
||||
backgroundColor={ADMIN_COLORS.accentSoft}
|
||||
backgroundColor={theme.accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Icon size={18} color={ADMIN_COLORS.primary} />
|
||||
<Icon size={18} color={theme.primary} />
|
||||
</XStack>
|
||||
<Text fontSize="$sm" fontWeight="800">
|
||||
{title}
|
||||
@@ -136,7 +137,7 @@ function FeatureCard({
|
||||
</XStack>
|
||||
{badge ? <PillBadge tone="muted">{badge}</PillBadge> : null}
|
||||
</XStack>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{body}
|
||||
</Text>
|
||||
</MobileCard>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { MobileCard, CTAButton, PillBadge } from '../components/Primitives';
|
||||
import { getPackages, getTenantPackagesOverview } from '../../api';
|
||||
import { ADMIN_WELCOME_BASE_PATH, ADMIN_WELCOME_EVENT_PATH, ADMIN_WELCOME_PACKAGES_PATH, adminPath } from '../../constants';
|
||||
import { getSelectedPackageId } from '../lib/onboardingSelection';
|
||||
import { ADMIN_COLORS } from '../theme';
|
||||
import { useAdminTheme } from '../theme';
|
||||
|
||||
type SummaryPackage = {
|
||||
id: number;
|
||||
@@ -25,6 +25,7 @@ type SummaryPackage = {
|
||||
export default function WelcomeSummaryPage() {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('onboarding');
|
||||
const theme = useAdminTheme();
|
||||
const selectedId = getSelectedPackageId();
|
||||
|
||||
const { data: catalog, isLoading: catalogLoading } = useQuery({
|
||||
@@ -79,7 +80,7 @@ export default function WelcomeSummaryPage() {
|
||||
>
|
||||
{loading ? (
|
||||
<MobileCard>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{t('summary.state.loading', 'Checking available packages …')}
|
||||
</Text>
|
||||
</MobileCard>
|
||||
@@ -88,7 +89,7 @@ export default function WelcomeSummaryPage() {
|
||||
<Text fontSize="$sm" fontWeight="800">
|
||||
{t('summary.state.missingTitle', 'No package selected')}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{t('summary.state.missingDescription', 'Select a package first or refresh if data changed.')}
|
||||
</Text>
|
||||
<CTAButton label={t('summary.footer.back', 'Back to package selection')} onPress={() => navigate(ADMIN_WELCOME_PACKAGES_PATH)} />
|
||||
@@ -101,17 +102,17 @@ export default function WelcomeSummaryPage() {
|
||||
width={36}
|
||||
height={36}
|
||||
borderRadius={12}
|
||||
backgroundColor={ADMIN_COLORS.accentSoft}
|
||||
backgroundColor={theme.accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<PackageIcon size={18} color={ADMIN_COLORS.primary} />
|
||||
<PackageIcon size={18} color={theme.primary} />
|
||||
</XStack>
|
||||
<YStack>
|
||||
<Text fontSize="$sm" fontWeight="800">
|
||||
{resolvedPackage.name}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
{resolvedPackage.active
|
||||
? t('summary.details.section.statusActive', 'Already purchased')
|
||||
: t('summary.details.section.statusInactive', 'Not purchased yet')}
|
||||
@@ -149,8 +150,8 @@ export default function WelcomeSummaryPage() {
|
||||
|
||||
{resolvedPackage.active ? (
|
||||
<XStack alignItems="center" gap="$2">
|
||||
<CheckCircle2 size={18} color={ADMIN_COLORS.success} />
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.success} fontWeight="700">
|
||||
<CheckCircle2 size={18} color={theme.successText} />
|
||||
<Text fontSize="$sm" color={theme.successText} fontWeight="700">
|
||||
{t('summary.details.section.statusActive', 'Already purchased')}
|
||||
</Text>
|
||||
</XStack>
|
||||
@@ -172,10 +173,10 @@ export default function WelcomeSummaryPage() {
|
||||
],
|
||||
}) as string[]).map((item) => (
|
||||
<XStack key={item} gap="$2">
|
||||
<Text fontSize="$xs" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
•
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{item}
|
||||
</Text>
|
||||
</XStack>
|
||||
@@ -202,12 +203,13 @@ export default function WelcomeSummaryPage() {
|
||||
}
|
||||
|
||||
function SummaryRow({ label, value }: { label: string; value: string }) {
|
||||
const theme = useAdminTheme();
|
||||
return (
|
||||
<XStack alignItems="center" justifyContent="space-between">
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.text}>
|
||||
<Text fontSize="$sm" color={theme.text}>
|
||||
{label}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||||
<Text fontSize="$sm" color={theme.muted}>
|
||||
{value}
|
||||
</Text>
|
||||
</XStack>
|
||||
|
||||
Reference in New Issue
Block a user