Skip to content

Commit e74c6ae

Browse files
authored
Merge pull request #32 from pythonkr/feature/connect-gnb-footer-with-api
feat: GnB / Footer 노출 여부 및 GnB 메뉴 API 연동
2 parents f27e4cf + 8ac0149 commit e74c6ae

File tree

29 files changed

+709
-841
lines changed

29 files changed

+709
-841
lines changed

apps/pyconkr-admin/src/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const queryClient = new QueryClient({
3232

3333
const CommonOptions: Common.Contexts.ContextOptions = {
3434
debug: true,
35+
language: "ko",
3536
baseUrl: ".",
3637
frontendDomain: import.meta.env.VITE_PYCONKR_FRONTEND_DOMAIN,
3738
backendApiDomain: import.meta.env.VITE_PYCONKR_BACKEND_API_DOMAIN,

apps/pyconkr/src/App.tsx

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
11
import * as Common from "@frontend/common";
2-
import React from "react";
3-
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
2+
import * as React from "react";
3+
import { Route, Routes, useLocation } from "react-router-dom";
4+
import * as R from "remeda";
45

5-
import MainLayout from "./components/layout";
6-
import { Test } from "./components/pages/test";
7-
import { IS_DEBUG_ENV } from "./consts/index.ts";
8-
import { SponsorProvider } from "./contexts/SponsorContext";
6+
import MainLayout from "./components/layout/index.tsx";
7+
import { PageIdParamRenderer, RouteRenderer } from "./components/pages/dynamic_route.tsx";
8+
import { Test } from "./components/pages/test.tsx";
9+
import { IS_DEBUG_ENV } from "./consts";
10+
import { useAppContext } from "./contexts/app_context";
11+
import BackendAPISchemas from "../../../packages/common/src/schemas/backendAPI";
912

10-
// 스폰서를 표시할 페이지 경로 설정
11-
const SPONSOR_VISIBLE_PATHS = ["/"];
13+
export const App: React.FC = () => {
14+
const backendAPIClient = Common.Hooks.BackendAPI.useBackendClient();
15+
const { data: flatSiteMap } = Common.Hooks.BackendAPI.useFlattenSiteMapQuery(backendAPIClient);
16+
const siteMapNode = Common.Utils.buildNestedSiteMap(flatSiteMap)?.[""];
1217

13-
const AppContent = () => {
1418
const location = useLocation();
15-
const shouldShowSponsor = SPONSOR_VISIBLE_PATHS.includes(location.pathname);
19+
const { setAppContext, language } = useAppContext();
1620

17-
return (
18-
<SponsorProvider initialVisibility={shouldShowSponsor}>
19-
<Routes>
20-
<Route element={<MainLayout />}>
21-
{IS_DEBUG_ENV && <Route path="/debug" element={<Test />} />}
22-
<Route path="/pages/:id" element={<Common.Components.PageIdParamRenderer />} />
23-
<Route path="*" element={<Common.Components.RouteRenderer />} />
24-
</Route>
25-
</Routes>
26-
</SponsorProvider>
27-
);
28-
};
21+
React.useEffect(() => {
22+
(async () => {
23+
const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !R.isEmpty(code))];
24+
const currentSiteMapDepth: (BackendAPISchemas.NestedSiteMapSchema | undefined)[] = [siteMapNode];
25+
26+
for (const routeCode of currentRouteCodes.splice(1)) {
27+
currentSiteMapDepth.push(currentSiteMapDepth.at(-1)?.children[routeCode]);
28+
if (R.isNullish(currentSiteMapDepth.at(-1))) {
29+
console.warn(`Route not found in site map: ${routeCode}`);
30+
break;
31+
}
32+
}
33+
34+
setAppContext((ps) => ({ ...ps, siteMapNode, currentSiteMapDepth }));
35+
})();
36+
// eslint-disable-next-line react-hooks/exhaustive-deps
37+
}, [location, language, flatSiteMap]);
2938

30-
export const App: React.FC = () => {
3139
return (
32-
<BrowserRouter>
33-
<AppContent />
34-
</BrowserRouter>
40+
<Routes>
41+
<Route element={<MainLayout />}>
42+
{IS_DEBUG_ENV && <Route path="/debug" element={<Test />} />}
43+
<Route path="/pages/:id" element={<PageIdParamRenderer />} />
44+
<Route path="*" element={<RouteRenderer />} />
45+
</Route>
46+
</Routes>
3547
);
3648
};

apps/pyconkr/src/components/layout/BreadCrumb/index.tsx

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,71 @@
1-
import styled from "@emotion/styled";
2-
import { useEffect, useState } from "react";
1+
import { Stack, styled } from "@mui/material";
2+
import * as React from "react";
3+
import { Link } from "react-router-dom";
4+
import * as R from "remeda";
35

4-
export default function BreadCrumb() {
5-
const [breadcrumbInfo, setBreadcrumbInfo] = useState({
6-
paths: [{ text: "홈", href: "/" }],
7-
title: "파이콘 한국 행동강령(CoC)",
8-
});
6+
import BackendAPISchemas from "../../../../../../packages/common/src/schemas/backendAPI";
97

10-
useEffect(() => {
11-
const mockPathInfo = {
12-
paths: [
13-
{ text: "홈", href: "/" },
14-
{ text: "파이콘 한국", href: "/about" },
15-
],
16-
title: "파이콘 한국 행동강령(CoC)",
17-
};
18-
setBreadcrumbInfo(mockPathInfo);
19-
}, []);
8+
type BreadCrumbPropType = {
9+
title: string;
10+
parentSiteMaps: (BackendAPISchemas.NestedSiteMapSchema | undefined)[];
11+
};
2012

13+
export const BreadCrumb: React.FC<BreadCrumbPropType> = ({ title, parentSiteMaps }) => {
14+
let route = "/";
2115
return (
2216
<BreadCrumbContainer>
23-
<BreadcrumbPathContainer>
24-
{breadcrumbInfo.paths.map((item, index) => (
25-
<span key={index}>
26-
{index > 0 && <span className="separator">&gt;</span>}
27-
<a href={item.href}>{item.text}</a>
28-
</span>
29-
))}
17+
<BreadcrumbPathContainer direction="row" alignItems="center">
18+
{parentSiteMaps
19+
.slice(1, -1)
20+
.filter((routeInfo) => R.isNonNullish(routeInfo))
21+
.map(({ route_code, name }, index) => {
22+
route += `${route_code}/`;
23+
return (
24+
<span key={index}>
25+
{index > 0 && <span className="separator">&gt;</span>}
26+
<Link to={route} children={name} />
27+
</span>
28+
);
29+
})}
3030
</BreadcrumbPathContainer>
31-
<PageTitle>{breadcrumbInfo.title}</PageTitle>
31+
<PageTitle>{title}</PageTitle>
3232
</BreadCrumbContainer>
3333
);
34-
}
34+
};
3535

36-
const BreadCrumbContainer = styled.div`
37-
width: 100%;
38-
padding: 14px 117px;
39-
background-color: rgba(255, 255, 255, 0.7);
40-
background-image: linear-gradient(rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.45));
41-
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
42-
display: flex;
43-
flex-direction: column;
44-
gap: 5px;
45-
`;
36+
const BreadCrumbContainer = styled(Stack)(({ theme }) => ({
37+
position: "fixed",
38+
39+
top: "3.625rem",
40+
width: "100%",
41+
height: "4.5rem",
42+
background: "linear-gradient(rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.45))",
43+
boxShadow: "0 1px 10px rgba(0, 0, 0, 0.1)",
44+
backdropFilter: "blur(10px)",
45+
46+
gap: "0.25rem",
47+
justifyContent: "center",
48+
alignItems: "flex-start",
49+
50+
zIndex: theme.zIndex.appBar - 1,
51+
52+
paddingRight: "8rem",
53+
paddingLeft: "8rem",
54+
55+
[theme.breakpoints.down("lg")]: {
56+
paddingRight: "2rem",
57+
paddingLeft: "2rem",
58+
},
59+
[theme.breakpoints.down("sm")]: {
60+
paddingRight: "1rem",
61+
paddingLeft: "1rem",
62+
},
63+
}));
4664

47-
const BreadcrumbPathContainer = styled.div`
65+
const BreadcrumbPathContainer = styled(Stack)`
4866
font-size: 9.75px;
4967
font-weight: 300;
5068
color: #000000;
51-
display: flex;
52-
align-items: center;
53-
gap: 0;
5469
5570
a {
5671
color: #000000;
@@ -67,7 +82,7 @@ const BreadcrumbPathContainer = styled.div`
6782
}
6883
`;
6984

70-
const PageTitle = styled.h1`
85+
const PageTitle = styled("h1")`
7186
font-size: 27px;
7287
font-weight: 600;
7388
color: #000000;

0 commit comments

Comments
 (0)