Pages -> App (#265)

* pages -> app

* add meta test

* fix gosuslugi
This commit is contained in:
tmshv authored and GitHub committed 2026-02-09 00:25:06 +07:00
1 parent 0c8d74b21d
commit a1e51e78f0
23 files changed
+522 -516

No files matched your search

+90 -89
View File
@@ -1,12 +1,15 @@
"use client"
import s from "./app.module.css"
import { useContext } from "react"
import { Wrapper } from "../Wrapper"
import { MenuContext } from "@/context/MenuContext"
import { Footer } from "../Footer"
import { Content } from "../Content"
import { Breadcrumbs } from "../Breadcrumbs"
import type { BreadcrumbPart, MenuItem } from "@/types"
import { useRouter } from "next/router"
import type { BreadcrumbPart } from "@/types"
import { usePathname } from "next/navigation"
import { useMobile } from "@/hooks/useMobile"
import { Navigation } from "@/components/Navigation"
import { Box } from "@/ui/Box"
@@ -24,7 +27,7 @@ import Link from "next/link"
import AccessibilityPanel from "../AccessibilityPanel"
import useAccessibility from "@/hooks/useAccessibility"
const Gosuslugi = dynamic(import("../Gosuslugi").then(module => module.Gosuslugi), {
const Gosuslugi = dynamic(() => import("../Gosuslugi").then(module => ({ default: module.Gosuslugi })), {
ssr: false,
})
@@ -32,103 +35,101 @@ export type AppProps = {
showAuthor?: boolean
contentStyle?: React.CSSProperties
breadcrumbs?: BreadcrumbPart[]
menu: MenuItem[]
children?: React.ReactNode
}
export function App({ showAuthor = false, menu, ...props }: AppProps) {
export function App({ showAuthor = false, ...props }: AppProps) {
const hideBreadcrumbs = useMobile()
const accessibility = useAccessibility()
const router = useRouter()
const pathname = usePathname()
const menu = useContext(MenuContext)
return (
<MenuContext.Provider value={menu}>
<Wrapper
header={(
<header>
{!accessibility ? null : (
<AccessibilityPanel/>
)}
<Wrapper
header={(
<header>
{!accessibility ? null : (
<AccessibilityPanel/>
)}
<div className={s.navigation}>
<Navigation />
</div>
{hideBreadcrumbs || !props.breadcrumbs ? null : (
<Box center>
<Content>
<Breadcrumbs
items={props.breadcrumbs}
path={router.asPath}
/>
</Content>
</Box>
)}
</header>
)}
footer={(
<Footer>
<Box wrap gap={40} align={false} className={s.footerLinks}>
<Panel ghost>
<Contacts />
</Panel>
<Panel ghost className={s.footerPanel}>
<Title level={3}>
Версия для слабовидящих
</Title>
<AccessibilityButton />
</Panel>
<Panel ghost className={s.footerPanel}>
<Title level={3}>
Анкеты
</Title>
<ul>
<li>
<Link href={"https://bus.gov.ru/qrcode/rate/351942"}>
Независимая оценка качества оказания услуг
</Link>
</li>
<li>
<Link href={"https://forms.mkrf.ru/e/2579/xTPLeBU7/?ap_orgcode=600340071"}>
Анкета Министерства культуры РФ
</Link>
</li>
<li>
<Link href={"https://forms.yandex.ru/u/68ef8e9c90fa7b0bacf46e9d/"}>
Анкета получателей образовательных услуг ДОП Кировского муниципального района Ленинградской области 2025
</Link>
</li>
</ul>
</Panel>
<div className={s.navigation}>
<Navigation />
</div>
{hideBreadcrumbs || !props.breadcrumbs ? null : (
<Box center>
<Content>
<Breadcrumbs
items={props.breadcrumbs}
path={pathname}
/>
</Content>
</Box>
)}
</header>
)}
footer={(
<Footer>
<Box wrap gap={40} align={false} className={s.footerLinks}>
<Panel ghost>
<Contacts />
</Panel>
<div className={s.gosuslugi}>
<Gosuslugi />
</div>
<Panel ghost className={s.footerPanel}>
<Title level={3}>
Версия для слабовидящих
</Title>
<AccessibilityButton />
</Panel>
<Box>
<Copyright years={yearRange} />
<Panel ghost className={s.footerPanel}>
<Title level={3}>
Анкеты
</Title>
{!showAuthor ? null : (
<>
<Spacer as={"span"} />
<Author />
</>
)}
</Box>
</Footer>
)}
>
<Box center>
<Content style={props.contentStyle}>
{props.children}
</Content>
</Box>
</Wrapper>
</MenuContext.Provider>
<ul>
<li>
<Link href={"https://bus.gov.ru/qrcode/rate/351942"}>
Независимая оценка качества оказания услуг
</Link>
</li>
<li>
<Link href={"https://forms.mkrf.ru/e/2579/xTPLeBU7/?ap_orgcode=600340071"}>
Анкета Министерства культуры РФ
</Link>
</li>
<li>
<Link href={"https://forms.yandex.ru/u/68ef8e9c90fa7b0bacf46e9d/"}>
Анкета получателей образовательных услуг ДОП Кировского муниципального района Ленинградской области 2025
</Link>
</li>
</ul>
</Panel>
</Box>
<div className={s.gosuslugi}>
<Gosuslugi />
</div>
<Box>
<Copyright years={yearRange} />
{!showAuthor ? null : (
<>
<Spacer as={"span"} />
<Author />
</>
)}
</Box>
</Footer>
)}
>
<Box center>
<Content style={props.contentStyle}>
{props.children}
</Content>
</Box>
</Wrapper>
)
}
+1 -1
View File
@@ -8,7 +8,7 @@ import { getResizedUrl } from "@/lib/image"
import Link from "next/link"
import { Box } from "@/ui/Box"
const Signature = dynamic(import("./Signature").then(m => m.Signature), {
const Signature = dynamic(() => import("./Signature").then(m => ({ default: m.Signature })), {
ssr: false,
})
+29
View File
@@ -0,0 +1,29 @@
"use client"
import { HudozkaTitle } from "@/components/HudozkaTitle"
import { PageGrid } from "@/components/PageGrid"
import { useMobile } from "@/hooks/useMobile"
import type { PageCardDto } from "@/types"
export type HomeContentProps = {
items: PageCardDto[]
}
export function HomeContent({ items }: HomeContentProps) {
const mobile = useMobile()
return (
<>
{mobile ? null : (
<HudozkaTitle
style={{
marginTop: "var(--size-m)",
marginBottom: "var(--size-m)",
}}
/>
)}
<PageGrid items={items} />
</>
)
}
+5 -3
View File
@@ -1,9 +1,11 @@
"use client"
import s from "./styles.module.css"
import type { MenuItem } from "@/types"
import * as NavigationMenu from "@radix-ui/react-navigation-menu"
import classnames from "classnames/bind"
import { useRouter } from "next/router"
import { usePathname } from "next/navigation"
import { ActiveLink } from "./ActiveLink"
let cx = classnames.bind(s)
@@ -15,13 +17,13 @@ export type MenuProps = {
}
export const Menu: React.FC<MenuProps> = ({ vertical = false, onItemClick, items }) => {
const router = useRouter()
const pathname = usePathname()
return (
<NavigationMenu.Root>
<NavigationMenu.List className={cx(s.menu, { vertical })}>
{
items.map(item => {
const current = router.asPath === item.href
const current = pathname === item.href
const href = current
? undefined
: item.href
@@ -1,3 +1,5 @@
"use client"
import s from "./nav.module.css"
import { Menu } from "../Menu"
@@ -8,7 +10,7 @@ import { Spacer } from "../Spacer"
import HudozkaLogo from "../HudozkaLogo"
import { HudozkaTitle } from "../HudozkaTitle"
import Link from "next/link"
import { useRouter } from "next/router"
import { usePathname } from "next/navigation"
import { HiOutlineMenuAlt4, HiOutlineX } from "react-icons/hi"
export type MobileNavigationProps = {
@@ -19,7 +21,7 @@ export type MobileNavigationProps = {
export function MobileNavigation({ className, style }: MobileNavigationProps) {
const menu = useContext(MenuContext)
const detailsRef = useRef<HTMLDetailsElement>(null)
const router = useRouter()
const pathname = usePathname()
const close = useCallback(() => {
if (detailsRef.current) {
@@ -28,11 +30,8 @@ export function MobileNavigation({ className, style }: MobileNavigationProps) {
}, [])
useEffect(() => {
router.events.on("routeChangeStart", close)
return () => {
router.events.off("routeChangeStart", close)
}
}, [router.events, close])
close()
}, [pathname, close])
return (
<nav className={className}>
+93
View File
@@ -0,0 +1,93 @@
"use client"
import { Page } from "@/components/Page"
import { Html } from "@/components/Html"
import { Youtube } from "@/components/Youtube"
import { Picture } from "@/ui/Picture"
import { FileCard } from "@/components/FileCard"
import { PageGrid } from "@/components/PageGrid"
import type { Tag, Token, Sign } from "@/types"
export type PageContentProps = {
tags: Tag[]
date?: string
tokens: Token[]
documentSignature: Sign
}
export function PageContent({ tags, date, tokens, documentSignature }: PageContentProps) {
return (
<Page
tags={tags}
date={date ? new Date(date) : undefined}
>
<article className={"article"}>
{tokens.map((x, i) => {
switch (x.token) {
case "html":
return (
<Html
key={i}
html={x.data}
/>
)
case "instagram":
return (
<Html
key={i}
html={x.data.embed}
/>
)
case "youtube":
return (
<Youtube
key={i}
url={x.data.url}
/>
)
case "image":
return (
<Picture
key={i}
src={x.data.src}
alt={x.data.alt}
width={x.data.width}
height={x.data.height}
caption={x.data.caption}
blur={x.data.blur}
wide={x.wide}
/>
)
case "file":
return (
<FileCard
key={i}
sign={documentSignature}
{...x.data}
/>
)
case "grid":
return (
<PageGrid
key={i}
items={x.data.items}
/>
)
default:
return (
<pre key={i}>
{JSON.stringify(x)}
</pre>
)
}
})}
</article>
</Page>
)
}
+17
View File
@@ -0,0 +1,17 @@
"use client"
import { MenuContext } from "@/context/MenuContext"
import type { MenuItem } from "@/types"
export type ProvidersProps = {
menu: MenuItem[]
children: React.ReactNode
}
export function Providers({ menu, children }: ProvidersProps) {
return (
<MenuContext.Provider value={menu}>
{children}
</MenuContext.Provider>
)
}
+2
View File
@@ -1,3 +1,5 @@
"use client"
import s from "./wrapper.module.css"
import cx from "classnames"