diff --git a/src/app/accessibility/page.tsx b/src/app/accessibility/page.tsx index 0efdc88..76aafc5 100644 --- a/src/app/accessibility/page.tsx +++ b/src/app/accessibility/page.tsx @@ -20,16 +20,15 @@ export const metadata: Metadata = { } export default async function AccessibilityPage() { - let communityName = '' + let publicSettings = null try { - const settings = await getPublicSettings() - communityName = settings.communityName + publicSettings = await getPublicSettings() } catch { // silently degrade } return ( - +
diff --git a/src/app/admin/design/page.test.tsx b/src/app/admin/design/page.test.tsx index 58f117e..a30f9e2 100644 --- a/src/app/admin/design/page.test.tsx +++ b/src/app/admin/design/page.test.tsx @@ -56,6 +56,14 @@ describe('AdminDesignPage', () => { expect(screen.getByRole('heading', { name: /design/i })).toBeInTheDocument() }) + it('renders header logo upload with help text', async () => { + render() + await waitFor(() => { + expect(screen.getByText('Header Logo')).toBeInTheDocument() + }) + expect(screen.getByText(/Wide logo or wordmark for the forum header/)).toBeInTheDocument() + }) + it('renders logo upload section with help text', async () => { render() await waitFor(() => { @@ -72,6 +80,15 @@ describe('AdminDesignPage', () => { expect(screen.getByText(/256×256px.*JPEG, PNG, WebP, GIF/)).toBeInTheDocument() }) + it('renders "Show community name" toggle, defaults to checked', async () => { + render() + await waitFor(() => { + const toggle = screen.getByRole('checkbox', { name: /show community name/i }) + expect(toggle).toBeInTheDocument() + expect(toggle).toBeChecked() + }) + }) + it('renders primary color input', async () => { render() await waitFor(() => { diff --git a/src/app/admin/design/page.tsx b/src/app/admin/design/page.tsx index c708f69..5a8d715 100644 --- a/src/app/admin/design/page.tsx +++ b/src/app/admin/design/page.tsx @@ -1,7 +1,7 @@ /** * Admin design page. * URL: /admin/design - * Logo upload, favicon upload, primary/accent color configuration. + * Header logo upload, community logo upload, favicon upload, primary/accent color configuration. */ 'use client' @@ -16,6 +16,7 @@ import { updateCommunitySettings, uploadCommunityLogo, uploadCommunityFavicon, + uploadHeaderLogo, } from '@/lib/api/client' import type { CommunitySettings } from '@/lib/api/types' import { useAuth } from '@/hooks/use-auth' @@ -44,6 +45,39 @@ export default function AdminDesignPage() { void fetchSettings() }, [fetchSettings]) + const handleHeaderLogoUpload = useCallback( + async (file: File) => { + const result = await uploadHeaderLogo(file, getAccessToken() ?? '') + setSettings((prev) => (prev ? { ...prev, headerLogoUrl: result.url } : prev)) + return result + }, + [getAccessToken] + ) + + const handleHeaderLogoRemove = useCallback(async () => { + try { + const updated = await updateCommunitySettings({ headerLogoUrl: null }, getAccessToken() ?? '') + setSettings(updated) + } catch { + setSaveError('Failed to remove header logo.') + } + }, [getAccessToken]) + + const handleShowCommunityNameChange = useCallback( + async (value: boolean) => { + try { + const updated = await updateCommunitySettings( + { showCommunityName: value }, + getAccessToken() ?? '' + ) + setSettings(updated) + } catch { + setSaveError('Failed to update community name visibility.') + } + }, + [getAccessToken] + ) + const handleLogoUpload = useCallback( async (file: File) => { const result = await uploadCommunityLogo(file, getAccessToken() ?? '') @@ -118,6 +152,9 @@ export default function AdminDesignPage() {
void handleHeaderLogoRemove()} + onShowCommunityNameChange={(v) => void handleShowCommunityNameChange(v)} onLogoUpload={handleLogoUpload} onLogoRemove={() => void handleLogoRemove()} onFaviconUpload={handleFaviconUpload} diff --git a/src/app/c/[slug]/page.tsx b/src/app/c/[slug]/page.tsx index a90f022..11a7060 100644 --- a/src/app/c/[slug]/page.tsx +++ b/src/app/c/[slug]/page.tsx @@ -91,8 +91,6 @@ export default async function CategoryPage({ params, searchParams }: CategoryPag getPublicSettings().catch(() => null), ]) - const communityName = publicSettings?.communityName ?? '' - const totalPages = Math.max(1, Math.ceil(category.topicCount / TOPICS_PER_PAGE)) const breadcrumbItems = [ @@ -102,7 +100,7 @@ export default async function CategoryPage({ params, searchParams }: CategoryPag return ( } > {/* Breadcrumbs (includes JSON-LD BreadcrumbList) */} diff --git a/src/app/new/page.tsx b/src/app/new/page.tsx index fd2774f..30809f5 100644 --- a/src/app/new/page.tsx +++ b/src/app/new/page.tsx @@ -9,7 +9,7 @@ import { useState, useEffect } from 'react' import { useRouter, useSearchParams } from 'next/navigation' -import type { CreateTopicInput } from '@/lib/api/types' +import type { CreateTopicInput, PublicSettings } from '@/lib/api/types' import { createTopic, getPublicSettings } from '@/lib/api/client' import { getTopicUrl } from '@/lib/format' import { ForumLayout } from '@/components/layout/forum-layout' @@ -26,11 +26,11 @@ export default function NewTopicPage() { const { ensureOnboarded } = useOnboardingContext() const [submitting, setSubmitting] = useState(false) const [error, setError] = useState(null) - const [communityName, setCommunityName] = useState('') + const [publicSettings, setPublicSettings] = useState(null) useEffect(() => { getPublicSettings() - .then((settings) => setCommunityName(settings.communityName)) + .then((settings) => setPublicSettings(settings)) .catch(() => {}) }, []) @@ -51,7 +51,7 @@ export default function NewTopicPage() { } return ( - +
diff --git a/src/app/notifications/page.tsx b/src/app/notifications/page.tsx index 37e2533..660c4a6 100644 --- a/src/app/notifications/page.tsx +++ b/src/app/notifications/page.tsx @@ -15,7 +15,7 @@ import { Breadcrumbs } from '@/components/breadcrumbs' import { ErrorAlert } from '@/components/error-alert' import { getNotifications, markNotificationsRead, getPublicSettings } from '@/lib/api/client' import { cn } from '@/lib/utils' -import type { Notification, NotificationType } from '@/lib/api/types' +import type { Notification, NotificationType, PublicSettings } from '@/lib/api/types' import { useAuth } from '@/hooks/use-auth' import { ProtectedRoute } from '@/components/auth/protected-route' @@ -40,11 +40,11 @@ function NotificationsContent() { const [loading, setLoading] = useState(true) const [loadError, setLoadError] = useState(null) const [actionError, setActionError] = useState(null) - const [communityName, setCommunityName] = useState('') + const [publicSettings, setPublicSettings] = useState(null) useEffect(() => { getPublicSettings() - .then((settings) => setCommunityName(settings.communityName)) + .then((settings) => setPublicSettings(settings)) .catch(() => {}) }, []) @@ -91,7 +91,7 @@ function NotificationsContent() { const hasUnread = notifications.some((n) => !n.read) return ( - +
diff --git a/src/app/p/[slug]/page.tsx b/src/app/p/[slug]/page.tsx index 546b6ab..585922d 100644 --- a/src/app/p/[slug]/page.tsx +++ b/src/app/p/[slug]/page.tsx @@ -60,10 +60,9 @@ export default async function PublicPage({ params }: PublicPageProps) { throw error } - let communityName = '' + let publicSettings = null try { - const settings = await getPublicSettings() - communityName = settings.communityName + publicSettings = await getPublicSettings() } catch { // silently degrade } @@ -78,7 +77,7 @@ export default async function PublicPage({ params }: PublicPageProps) { } return ( - +