Add admin help center entry points
This commit is contained in:
158
resources/js/admin/mobile/HelpCenterPage.tsx
Normal file
158
resources/js/admin/mobile/HelpCenterPage.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
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(['admin-issue-resolution']);
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user