159 lines
5.3 KiB
TypeScript
159 lines
5.3 KiB
TypeScript
import React from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { ChevronRight, HelpCircle } from 'lucide-react';
|
|
import { YStack, XStack } from '@tamagui/stacks';
|
|
import { SizableText as Text } from '@tamagui/text';
|
|
import { YGroup } from '@tamagui/group';
|
|
import { ListItem } from '@tamagui/list-item';
|
|
|
|
import { MobileShell } from './components/MobileShell';
|
|
import { MobileCard, CTAButton, SkeletonCard } from './components/Primitives';
|
|
import { useAdminTheme } from './theme';
|
|
import { useBackNavigation } from './hooks/useBackNavigation';
|
|
import { adminPath, ADMIN_PROFILE_PATH } from '../constants';
|
|
import { fetchHelpCenterArticles, type HelpCenterArticleSummary } from '../api';
|
|
|
|
const FAQ_SLUGS = new Set(['faq-admin']);
|
|
|
|
function isFaqArticle(article: HelpCenterArticleSummary): boolean {
|
|
const title = article.title?.toLowerCase() ?? '';
|
|
return FAQ_SLUGS.has(article.slug) || article.slug.startsWith('faq-') || title.includes('faq');
|
|
}
|
|
|
|
export default function MobileHelpCenterPage() {
|
|
const navigate = useNavigate();
|
|
const { t, i18n } = useTranslation(['management', 'dashboard']);
|
|
const theme = useAdminTheme();
|
|
const back = useBackNavigation(ADMIN_PROFILE_PATH);
|
|
const locale = i18n.language;
|
|
|
|
const { data, isLoading, isError, refetch } = useQuery({
|
|
queryKey: ['mobile', 'help-center', locale],
|
|
queryFn: () => fetchHelpCenterArticles(locale),
|
|
});
|
|
|
|
const articles = Array.isArray(data) ? data : [];
|
|
const faqArticles = articles.filter(isFaqArticle);
|
|
const guideArticles = articles.filter((article) => !isFaqArticle(article));
|
|
|
|
return (
|
|
<MobileShell activeTab="profile" title={t('common.help', 'Help')} onBack={back}>
|
|
{isLoading ? (
|
|
<YStack space="$2">
|
|
<SkeletonCard height={120} />
|
|
<SkeletonCard height={120} />
|
|
</YStack>
|
|
) : null}
|
|
|
|
{isError ? (
|
|
<MobileCard>
|
|
<YStack space="$2">
|
|
<Text fontSize="$sm" fontWeight="700" color={theme.textStrong}>
|
|
{t('dashboard:help.error', 'Help could not be loaded.')}
|
|
</Text>
|
|
<CTAButton
|
|
label={t('common.retry', 'Erneut versuchen')}
|
|
onPress={() => refetch()}
|
|
fullWidth={false}
|
|
/>
|
|
</YStack>
|
|
</MobileCard>
|
|
) : null}
|
|
|
|
{!isLoading && !isError ? (
|
|
<YStack space="$3">
|
|
<HelpSection
|
|
title={t('dashboard:help.title', 'FAQ')}
|
|
icon={HelpCircle}
|
|
items={faqArticles}
|
|
emptyLabel={t('dashboard:help.description', 'Noch keine FAQ-Artikel verfügbar.')}
|
|
onSelect={(slug) => navigate(adminPath(`/mobile/help/${encodeURIComponent(slug)}`))}
|
|
/>
|
|
<HelpSection
|
|
title={t('dashboard:help.documentationTitle', 'Guides & Dokus')}
|
|
items={guideArticles}
|
|
emptyLabel={t('dashboard:help.description', 'Noch keine Help-Artikel verfügbar.')}
|
|
onSelect={(slug) => navigate(adminPath(`/mobile/help/${encodeURIComponent(slug)}`))}
|
|
/>
|
|
</YStack>
|
|
) : null}
|
|
</MobileShell>
|
|
);
|
|
}
|
|
|
|
function HelpSection({
|
|
title,
|
|
items,
|
|
emptyLabel,
|
|
icon: IconCmp,
|
|
onSelect,
|
|
}: {
|
|
title: string;
|
|
items: HelpCenterArticleSummary[];
|
|
emptyLabel: string;
|
|
icon?: React.ComponentType<{ size?: number; color?: string }>;
|
|
onSelect: (slug: string) => void;
|
|
}) {
|
|
const theme = useAdminTheme();
|
|
|
|
return (
|
|
<MobileCard padding="$0">
|
|
<YStack padding="$3" space="$2">
|
|
<XStack alignItems="center" space="$2">
|
|
{IconCmp ? (
|
|
<XStack
|
|
width={28}
|
|
height={28}
|
|
borderRadius={10}
|
|
alignItems="center"
|
|
justifyContent="center"
|
|
backgroundColor={theme.surfaceMuted}
|
|
borderWidth={1}
|
|
borderColor={theme.border}
|
|
>
|
|
<IconCmp size={14} color={theme.textStrong} />
|
|
</XStack>
|
|
) : null}
|
|
<Text fontSize="$md" fontWeight="800" color={theme.textStrong}>
|
|
{title}
|
|
</Text>
|
|
</XStack>
|
|
{items.length === 0 ? (
|
|
<Text fontSize="$sm" color={theme.muted}>
|
|
{emptyLabel}
|
|
</Text>
|
|
) : (
|
|
<YGroup {...({ borderRadius: '$4', borderWidth: 1, borderColor: theme.border, overflow: 'hidden' } as any)}>
|
|
{items.map((item) => (
|
|
<YGroup.Item key={item.slug}>
|
|
<ListItem
|
|
hoverTheme
|
|
pressTheme
|
|
paddingVertical="$2"
|
|
paddingHorizontal="$3"
|
|
onPress={() => onSelect(item.slug)}
|
|
title={
|
|
<Text fontSize="$sm" fontWeight="700" color={theme.textStrong}>
|
|
{item.title}
|
|
</Text>
|
|
}
|
|
subTitle={
|
|
item.summary ? (
|
|
<Text fontSize="$xs" color={theme.muted}>
|
|
{item.summary}
|
|
</Text>
|
|
) : undefined
|
|
}
|
|
iconAfter={<ChevronRight size={16} color={theme.muted} />}
|
|
/>
|
|
</YGroup.Item>
|
|
))}
|
|
</YGroup>
|
|
)}
|
|
</YStack>
|
|
</MobileCard>
|
|
);
|
|
}
|