Files
landing/src/pages/SupportPage.tsx
2026-07-31 06:06:07 -07:00

243 lines
11 KiB
TypeScript

import {
ActionIcon,
Anchor,
Box,
Button,
Card,
Container,
Group,
Paper,
SimpleGrid,
Stack,
Text,
TextInput,
ThemeIcon,
Title,
} from '@mantine/core';
import {
IconBook2,
IconHelpCircle,
IconLifebuoy,
IconMail,
IconMessageQuestion,
IconSearch,
IconShieldQuestion,
IconX,
} from '@tabler/icons-react';
import { useMemo, useState } from 'react';
import knownIssues from '../../known-issues.json';
import { SEO } from '../components/SEO/SEO';
import { SiteFooter } from '../components/SiteFooter/SiteFooter';
import { SiteHeader } from '../components/SiteHeader/SiteHeader';
import classes from './SupportPage.module.css';
export function SupportPage() {
const [query, setQuery] = useState('');
const normalizedQuery = query.trim().toLowerCase();
const suggestions = useMemo(() => {
if (!normalizedQuery) {
return [];
}
const terms = normalizedQuery
.split(/\s+/)
.map((term) => term.replace(/[^a-z0-9-]/g, ''))
.filter((term) => term.length > 2);
return knownIssues
.map((issue) => {
const searchable = [issue.title, issue.summary, issue.solution, ...issue.keywords]
.join(' ')
.toLowerCase();
const score = terms.filter((term) => searchable.includes(term)).length;
return { issue, score };
})
.filter(({ score }) => score > 0)
.sort((left, right) => right.score - left.score)
.slice(0, 5);
}, [normalizedQuery]);
return (
<>
<SEO
title="Rosetta Support"
description="Find known Rosetta issues or contact support for help with account access, messaging, media, privacy, and security."
canonical="https://rosetta.im/support/"
keywords="Rosetta support, known issues, account recovery, messaging help"
/>
<SiteHeader />
<main className={classes.page}>
<Box component="section" className={classes.hero}>
<Container className={classes.heroInner}>
<ThemeIcon
className={`${classes.decorativeIcon} ${classes.iconOne}`}
size={50}
radius="xl"
variant="white"
aria-hidden="true"
>
<IconHelpCircle size={26} />
</ThemeIcon>
<ThemeIcon
className={`${classes.decorativeIcon} ${classes.iconTwo}`}
size={44}
radius="xl"
variant="white"
aria-hidden="true"
>
<IconMessageQuestion size={22} />
</ThemeIcon>
<ThemeIcon
className={`${classes.decorativeIcon} ${classes.iconThree}`}
size={54}
radius="xl"
variant="white"
aria-hidden="true"
>
<IconLifebuoy size={27} />
</ThemeIcon>
<ThemeIcon
className={`${classes.decorativeIcon} ${classes.iconFour}`}
size={42}
radius="xl"
variant="white"
aria-hidden="true"
>
<IconShieldQuestion size={21} />
</ThemeIcon>
<Stack align="center" gap="sm" className={classes.heroContent}>
<Title order={1}>How can we help?</Title>
<Text c="blue.9" ta="center" className={classes.heroDescription}>
Search account access, messaging, media, privacy, and security help.
</Text>
<Box className={classes.searchWrap}>
<TextInput
value={query}
onChange={(event) => setQuery(event.currentTarget.value)}
leftSection={<IconSearch size={21} />}
rightSection={
query ? (
<ActionIcon
variant="subtle"
color="gray"
onClick={() => setQuery('')}
aria-label="Clear support search"
>
<IconX size={18} />
</ActionIcon>
) : null
}
placeholder="Describe your problem"
aria-label="Search Rosetta support"
size="lg"
radius="sm"
className={classes.searchInput}
/>
{normalizedQuery && (
<Paper
withBorder
shadow="lg"
radius="md"
className={classes.suggestions}
>
{suggestions.length > 0 ? (
<Stack gap={0}>
{suggestions.map(({ issue }) => (
<Anchor
key={issue.id}
href={`#${issue.id}`}
onClick={() => setQuery('')}
className={classes.suggestion}
>
<IconHelpCircle size={18} />
<Box>
<Text fw={600} size="sm">
{issue.title}
</Text>
<Text c="dimmed" size="xs" mt={3}>
{issue.summary}
</Text>
</Box>
</Anchor>
))}
</Stack>
) : (
<Box p="md">
<Text fw={600} size="sm">
No matching known issue
</Text>
<Text c="dimmed" size="xs" mt={4}>
Try fewer words or contact the support team below.
</Text>
</Box>
)}
</Paper>
)}
</Box>
</Stack>
</Container>
</Box>
<Container className={classes.content}>
<Box component="section">
<Group gap="sm">
<ThemeIcon variant="light" radius="xl">
<IconBook2 size={18} />
</ThemeIcon>
<Title order={2}>Known issues</Title>
</Group>
<Text c="dimmed" mt="xs" className={classes.sectionDescription}>
Common situations and the information needed to resolve them.
</Text>
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg" mt="xl">
{knownIssues.map((issue) => (
<Card
component="article"
id={issue.id}
key={issue.id}
withBorder
radius="md"
padding="xl"
className={classes.issue}
>
<Title order={3}>{issue.title}</Title>
<Text c="dimmed" size="sm" mt="sm" lh={1.65}>
{issue.summary}
</Text>
<Text size="sm" mt="lg" lh={1.7}>
{issue.solution}
</Text>
</Card>
))}
</SimpleGrid>
</Box>
<Paper component="section" radius="lg" className={classes.contact}>
<Box>
<Title order={2}>Still need help?</Title>
<Text c="dimmed" size="sm" mt="xs" lh={1.7} maw={620}>
Include your operating system, Rosetta version, approximate time,
and reproduction steps. Never send a recovery phrase or private key.
</Text>
</Box>
<Button
component="a"
href="mailto:support@rosetta.im?subject=Rosetta%20Support%20Request"
leftSection={<IconMail size={17} />}
variant="default"
>
support@rosetta.im
</Button>
</Paper>
</Container>
</main>
<SiteFooter />
</>
);
}