diff --git a/.gitignore b/.gitignore index a547bf3..cf970ef 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ dist dist-ssr *.local +# TypeScript incremental build cache +*.tsbuildinfo + # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/apps/pyconkr-2025/src/App.tsx b/apps/pyconkr-2025/src/App.tsx index 7ce3a4c..81bdf8f 100644 --- a/apps/pyconkr-2025/src/App.tsx +++ b/apps/pyconkr-2025/src/App.tsx @@ -1,10 +1,10 @@ -import { useBackendClient, useFlattenSiteMapQuery, useSponsorQuery } from "@frontend/common/src/hooks/useAPI"; -import { buildNestedSiteMap } from "@frontend/common/src/utils"; -import * as React from "react"; +import { useBackendClient, useFlattenSiteMapQuery, useSponsorQuery } from "@frontend/common/hooks/useAPI"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; +import { buildNestedSiteMap } from "@frontend/common/utils"; +import { FC, useEffect } from "react"; import { Route, Routes, useLocation } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty, isNullish } from "remeda"; -import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI"; import MainLayout from "./components/layout/index.tsx"; import { PageIdParamRenderer, RouteRenderer } from "./components/pages/dynamic_route.tsx"; import { PresentationDetailPage } from "./components/pages/presentation_detail.tsx"; @@ -14,7 +14,7 @@ import { Test } from "./components/pages/test.tsx"; import { IS_DEBUG_ENV } from "./consts"; import { useAppContext } from "./contexts/app_context"; -export const App: React.FC = () => { +export const App: FC = () => { const backendAPIClient = useBackendClient(); const { data: sponsorTiers } = useSponsorQuery(backendAPIClient); const { data: flatSiteMap } = useFlattenSiteMapQuery(backendAPIClient); @@ -23,17 +23,17 @@ export const App: React.FC = () => { const location = useLocation(); const { setAppContext, language } = useAppContext(); - React.useEffect(() => { + useEffect(() => { (async () => { - const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !R.isEmpty(code))]; - const currentSiteMapDepth: (BackendAPISchemas.NestedSiteMapSchema | undefined)[] = [siteMapNode]; + const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !isEmpty(code))]; + const currentSiteMapDepth: (NestedSiteMapSchema | undefined)[] = [siteMapNode]; for (const routeCode of currentRouteCodes.splice(1)) { const childrenMap = currentSiteMapDepth .at(-1) - ?.children?.reduce((acc, child) => ({ ...acc, [child.route_code]: child }), {} as Record); + ?.children?.reduce((acc, child) => ({ ...acc, [child.route_code]: child }), {} as Record); currentSiteMapDepth.push(childrenMap?.[routeCode]); - if (R.isNullish(currentSiteMapDepth.at(-1))) { + if (isNullish(currentSiteMapDepth.at(-1))) { console.warn(`Route not found in site map: ${routeCode}`); break; } diff --git a/apps/pyconkr-2025/src/components/layout/BreadCrumb/index.tsx b/apps/pyconkr-2025/src/components/layout/BreadCrumb/index.tsx index 483b786..caa7198 100644 --- a/apps/pyconkr-2025/src/components/layout/BreadCrumb/index.tsx +++ b/apps/pyconkr-2025/src/components/layout/BreadCrumb/index.tsx @@ -1,23 +1,21 @@ +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { Stack, styled } from "@mui/material"; -import * as React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; -import * as R from "remeda"; - -import { NestedSiteMapSchema } from "../../../../../../packages/common/src/schemas/backendAPI"; - +import { isNonNullish } from "remeda"; type BreadCrumbPropType = { title: string; parentSiteMaps: (NestedSiteMapSchema | undefined)[]; }; -export const BreadCrumb: React.FC = ({ title, parentSiteMaps }) => { +export const BreadCrumb: FC = ({ title, parentSiteMaps }) => { let route = "/"; return ( {parentSiteMaps .slice(1, -1) - .filter((routeInfo) => R.isNonNullish(routeInfo)) + .filter((routeInfo) => isNonNullish(routeInfo)) .map(({ route_code, name }, index) => { route += `${route_code}/`; return ( diff --git a/apps/pyconkr-2025/src/components/layout/CartBadgeButton/index.tsx b/apps/pyconkr-2025/src/components/layout/CartBadgeButton/index.tsx index c18e20c..b3ac586 100644 --- a/apps/pyconkr-2025/src/components/layout/CartBadgeButton/index.tsx +++ b/apps/pyconkr-2025/src/components/layout/CartBadgeButton/index.tsx @@ -1,8 +1,8 @@ -import * as Shop from "@frontend/shop"; +import { useCart, useShopClient } from "@frontend/shop/hooks"; import { ShoppingCart } from "@mui/icons-material"; import { Badge, badgeClasses, IconButton, styled } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC } from "react"; import { useNavigate } from "react-router-dom"; type InnerCartBadgeButtonPropType = { @@ -19,7 +19,7 @@ const ColoredIconButton = styled(IconButton)(({ theme }) => ({ const InnerCartBadge = styled(Badge)({ [`& .${badgeClasses.badge}`]: { top: "-12px", right: "-3px" } }); -const InnerCartBadgeButton: React.FC = ({ loading, count }) => { +const InnerCartBadgeButton: FC = ({ loading, count }) => { const navigate = useNavigate(); return ( @@ -30,11 +30,11 @@ const InnerCartBadgeButton: React.FC = ({ loading, ); }; -export const CartBadgeButton: React.FC = Suspense.with( +export const CartBadgeButton: FC = Suspense.with( { fallback: }, ErrorBoundary.with({ fallback: }, () => { - const shopAPIClient = Shop.Hooks.useShopClient(); - const { data: cart } = Shop.Hooks.useCart(shopAPIClient); + const shopAPIClient = useShopClient(); + const { data: cart } = useCart(shopAPIClient); return ; }) ); diff --git a/apps/pyconkr-2025/src/components/layout/Footer/Mobile/MobileFooter.tsx b/apps/pyconkr-2025/src/components/layout/Footer/Mobile/MobileFooter.tsx index 79efc63..d32cfe0 100644 --- a/apps/pyconkr-2025/src/components/layout/Footer/Mobile/MobileFooter.tsx +++ b/apps/pyconkr-2025/src/components/layout/Footer/Mobile/MobileFooter.tsx @@ -1,14 +1,13 @@ import styled from "@emotion/styled"; -import { useEmail } from "@frontend/common/src/hooks/useEmail"; +import { useEmail } from "@frontend/common/hooks/useEmail"; import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, X, YouTube } from "@mui/icons-material"; -import * as React from "react"; +import { FC } from "react"; import FlickrIcon from "@apps/pyconkr-2025/assets/thirdparty/flickr.svg?react"; - -import { useAppContext } from "../../../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2025/contexts/app_context"; interface IconItem { - icon: React.FC<{ width?: number; height?: number }>; + icon: FC<{ width?: number; height?: number }>; alt: string; href: string; } diff --git a/apps/pyconkr-2025/src/components/layout/Footer/index.tsx b/apps/pyconkr-2025/src/components/layout/Footer/index.tsx index e5d313d..159aed1 100644 --- a/apps/pyconkr-2025/src/components/layout/Footer/index.tsx +++ b/apps/pyconkr-2025/src/components/layout/Footer/index.tsx @@ -1,17 +1,16 @@ import styled from "@emotion/styled"; -import { useEmail } from "@frontend/common/src/hooks/useEmail"; +import { useEmail } from "@frontend/common/hooks/useEmail"; import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, OpenInNew, X, YouTube } from "@mui/icons-material"; import { Button, useMediaQuery, useTheme } from "@mui/material"; -import * as React from "react"; +import { FC, Fragment } from "react"; import FlickrIcon from "@apps/pyconkr-2025/assets/thirdparty/flickr.svg?react"; +import { useAppContext } from "@apps/pyconkr-2025/contexts/app_context"; -// import MobileFooter from "./Mobile/MobileFooter"; -import { useAppContext } from "../../../contexts/app_context"; import MobileFooter from "./Mobile/MobileFooter"; interface IconItem { - icon: React.FC<{ width?: number; height?: number }>; + icon: FC<{ width?: number; height?: number }>; alt: string; href: string; } @@ -47,7 +46,7 @@ const defaultIcons: IconItem[] = [ }, ]; -const Bar: React.FC = () =>
|
; +const Bar: FC = () =>
|
; export default function Footer() { const { sendEmail } = useEmail(); @@ -119,12 +118,12 @@ export default function Footer() { {links.map((link, index) => ( - + {link.text} {index < links.length - 1 && |} - + ))} diff --git a/apps/pyconkr-2025/src/components/layout/Header/Mobile/HamburgerButton.tsx b/apps/pyconkr-2025/src/components/layout/Header/Mobile/HamburgerButton.tsx index c859d12..1f811bd 100644 --- a/apps/pyconkr-2025/src/components/layout/Header/Mobile/HamburgerButton.tsx +++ b/apps/pyconkr-2025/src/components/layout/Header/Mobile/HamburgerButton.tsx @@ -1,13 +1,12 @@ import { IconButton, styled } from "@mui/material"; -import * as React from "react"; - +import { FC } from "react"; interface HamburgerButtonProps { isOpen: boolean; onClick: () => void; isMainPath?: boolean; } -export const HamburgerButton: React.FC = ({ isOpen, onClick, isMainPath = true }) => { +export const HamburgerButton: FC = ({ isOpen, onClick, isMainPath = true }) => { return ( diff --git a/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileHeader.tsx b/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileHeader.tsx index c93d8e9..34b6ca7 100644 --- a/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileHeader.tsx +++ b/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileHeader.tsx @@ -1,22 +1,23 @@ -import { Components } from "@frontend/common"; +import { PythonKorea } from "@frontend/common/components"; import { Box, Stack, styled, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, useState } from "react"; import { Link, useLocation } from "react-router-dom"; +import { useAppContext } from "@apps/pyconkr-2025/contexts/app_context"; + import { HamburgerButton } from "./HamburgerButton"; import { MobileLanguageToggle } from "./MobileLanguageToggle"; import { MobileNavigation } from "./MobileNavigation"; -import { useAppContext } from "../../../../contexts/app_context"; interface MobileHeaderProps { isNavigationOpen?: boolean; onToggleNavigation?: () => void; } -export const MobileHeader: React.FC = ({ isNavigationOpen = false, onToggleNavigation }) => { +export const MobileHeader: FC = ({ isNavigationOpen = false, onToggleNavigation }) => { const { siteMapNode } = useAppContext(); const location = useLocation(); - const [internalNavigationOpen, setInternalNavigationOpen] = React.useState(false); + const [internalNavigationOpen, setInternalNavigationOpen] = useState(false); const navigationOpen = onToggleNavigation ? isNavigationOpen : internalNavigationOpen; const toggleNavigation = onToggleNavigation || (() => setInternalNavigationOpen(!internalNavigationOpen)); @@ -31,7 +32,7 @@ export const MobileHeader: React.FC = ({ isNavigationOpen = f - + = ({ isMainPath = true }) => { +export const MobileLanguageToggle: FC = ({ isMainPath = true }) => { const { language, setAppContext } = useAppContext(); const toggleLanguage = () => { diff --git a/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileNavigation.tsx b/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileNavigation.tsx index 86d54d3..2bed3ec 100644 --- a/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileNavigation.tsx +++ b/apps/pyconkr-2025/src/components/layout/Header/Mobile/MobileNavigation.tsx @@ -1,17 +1,18 @@ -import { Components } from "@frontend/common"; -import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI"; +import { PythonKorea } from "@frontend/common/components"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { ArrowBack, ArrowForward } from "@mui/icons-material"; import { Box, Button, Chip, Drawer, IconButton, Stack, styled, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, useState } from "react"; import { Link, useLocation } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty } from "remeda"; + +import { SignInButton } from "@apps/pyconkr-2025/components/layout/SignInButton"; import { HamburgerButton } from "./HamburgerButton"; import { MobileLanguageToggle } from "./MobileLanguageToggle"; -import { SignInButton } from "../../SignInButton"; -// import { ScanCodeButton } from "../../UserScanCodeButton"; +// import { ScanCodeButton } from "@apps/pyconkr-2025/components/layout/UserScanCodeButton"; -type MenuType = BackendAPISchemas.NestedSiteMapSchema; +type MenuType = NestedSiteMapSchema; interface MobileNavigationProps { isOpen: boolean; @@ -28,9 +29,9 @@ interface NavigationState { breadcrumbs: { name: string; level: NavigationLevel }[]; } -export const MobileNavigation: React.FC = ({ isOpen, onClose, siteMapNode }) => { +export const MobileNavigation: FC = ({ isOpen, onClose, siteMapNode }) => { const location = useLocation(); - const [navState, setNavState] = React.useState({ + const [navState, setNavState] = useState({ level: "depth1", breadcrumbs: [], }); @@ -88,7 +89,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl .filter((s) => !s.hide) .map((menu) => ( - {!R.isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) ? ( + {!isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) ? ( navigateToDepth2(menu)}> {menu.name} @@ -97,7 +98,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl {menu.name} )} - {!R.isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) && ( + {!isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) && ( navigateToDepth2(menu)}> @@ -130,7 +131,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl - {!R.isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) && ( + {!isEmpty(menu.children) && Object.values(menu.children).some((child) => !child.hide) && ( navigateToDepth3(menu)}> @@ -183,7 +184,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl - + 파이콘 한국 2025 diff --git a/apps/pyconkr-2025/src/components/layout/Header/index.tsx b/apps/pyconkr-2025/src/components/layout/Header/index.tsx index 0ecbe1b..a91e50d 100644 --- a/apps/pyconkr-2025/src/components/layout/Header/index.tsx +++ b/apps/pyconkr-2025/src/components/layout/Header/index.tsx @@ -1,17 +1,18 @@ -import { Components } from "@frontend/common"; +import { PythonKorea } from "@frontend/common/components"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { ArrowForwardIos } from "@mui/icons-material"; import { Box, Button, CircularProgress, Divider, Stack, styled, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"; import { MUIStyledCommonProps } from "@mui/system"; -import * as React from "react"; +import { CSSProperties, FC, Fragment, useEffect, useState } from "react"; import { Link } from "react-router-dom"; -import * as R from "remeda"; - -import { NestedSiteMapSchema } from "../../../../../../packages/common/src/schemas/backendAPI"; -import { useAppContext } from "../../../contexts/app_context"; -import { CartBadgeButton } from "../CartBadgeButton"; -import LanguageSelector from "../LanguageSelector"; -import { SignInButton } from "../SignInButton"; -// import { ScanCodeIconButton } from "../UserScanCodeButton"; +import { isEmpty, isNonNullish, isString } from "remeda"; + +import { CartBadgeButton } from "@apps/pyconkr-2025/components/layout/CartBadgeButton"; +import LanguageSelector from "@apps/pyconkr-2025/components/layout/LanguageSelector"; +import { SignInButton } from "@apps/pyconkr-2025/components/layout/SignInButton"; +import { useAppContext } from "@apps/pyconkr-2025/contexts/app_context"; + +// import { ScanCodeIconButton } from "@apps/pyconkr-2025/components/layout/UserScanCodeButton"; import { MobileHeader } from "./Mobile/MobileHeader"; type MenuType = NestedSiteMapSchema; @@ -23,14 +24,14 @@ type NavigationStateType = { depth3?: MenuType; }; -const HeaderHeight: React.CSSProperties["height"] = "3.625rem"; -const BreadCrumbHeight: React.CSSProperties["height"] = "4.5rem"; +const HeaderHeight: CSSProperties["height"] = "3.625rem"; +const BreadCrumbHeight: CSSProperties["height"] = "4.5rem"; -const Header: React.FC = () => { +const Header: FC = () => { const { title, language, siteMapNode, currentSiteMapDepth, shouldShowTitleBanner } = useAppContext(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("md")); - const [navState, setNavState] = React.useState({}); + const [navState, setNavState] = useState({}); const resetDepths = () => setNavState({}); const setDepth1 = (depth1: MenuOrUndefinedType) => setNavState({ depth1 }); @@ -40,7 +41,7 @@ const Header: React.FC = () => { const getDepth2Route = (nextRoute?: string) => (navState.depth1?.route_code || "") + `/${nextRoute || ""}`; const getDepth3Route = (nextRoute?: string) => getDepth2Route(navState.depth2?.route_code) + `/${nextRoute || ""}`; - React.useEffect(resetDepths, [language]); + useEffect(resetDepths, [language]); if (isMobile) { return ; @@ -48,7 +49,7 @@ const Header: React.FC = () => { let breadCrumbRoute = ""; let breadCrumbArray = currentSiteMapDepth.slice(1, -1); - if (R.isEmpty(breadCrumbArray)) breadCrumbArray = currentSiteMapDepth.slice(0, -1); + if (isEmpty(breadCrumbArray)) breadCrumbArray = currentSiteMapDepth.slice(0, -1); const headerContainerStyle: SxProps = shouldShowTitleBanner ? {} @@ -63,7 +64,7 @@ const Header: React.FC = () => { - + @@ -76,8 +77,8 @@ const Header: React.FC = () => { diff --git a/apps/pyconkr-2025/src/consts/mdx_components.ts b/apps/pyconkr-2025/src/consts/mdx_components.ts index 3276981..051477e 100644 --- a/apps/pyconkr-2025/src/consts/mdx_components.ts +++ b/apps/pyconkr-2025/src/consts/mdx_components.ts @@ -1,143 +1,281 @@ // 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. -import { Components, Schemas } from "@frontend/common"; -import * as Shop from "@frontend/shop"; -import * as mui from "@mui/material"; +import PyCon2025HostLogoBig from "@frontend/common/assets/pyconkr2025_hostlogo_big.png"; +import PyCon2025HostLogoSmall from "@frontend/common/assets/pyconkr2025_hostlogo_small.png"; +import PyCon2025MobileLogoImage from "@frontend/common/assets/pyconkr2025_main_cover_image.png"; +import PyCon2025MobileLogoTitle from "@frontend/common/assets/pyconkr2025_main_cover_title.png"; +import { LottiePlayer, NetworkLottiePlayer } from "@frontend/common/components"; +import { + Confetti, + FAQAccordion, + Map as MDXMap, + MobileAccordion, + MobileCover, + PrimaryStyledDetails, + SecondaryStyledDetails, + SessionList, + SessionTimeTable, + StyledFullWidthButton, +} from "@frontend/common/components/mdx_components"; +import type { SessionSchema } from "@frontend/common/schemas/backendAPI"; +import { PriceDisplay, ShopContextProvider, SignInGuard, UserSignInAccount, UserSignInMethod } from "@frontend/shop/components/common"; +import { CartStatus, OrderList, PatronList, ProductImageCardList, ProductList, UserInfo } from "@frontend/shop/components/features"; +import { + Accordion, + AccordionActions, + AccordionDetails, + AccordionSummary, + Alert, + AlertTitle, + AppBar, + Autocomplete, + Avatar, + AvatarGroup, + Backdrop, + Badge, + BottomNavigation, + BottomNavigationAction, + Box, + Breadcrumbs, + Button, + ButtonBase, + ButtonGroup, + Card, + CardActionArea, + CardActions, + CardContent, + CardHeader, + CardMedia, + Checkbox, + Chip, + CircularProgress, + Collapse, + Container, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Divider, + Drawer, + Fab, + Fade, + FilledInput, + FormControl, + FormControlLabel, + FormGroup, + FormHelperText, + FormLabel, + Grid, + Grow, + Icon, + IconButton, + ImageList, + ImageListItem, + ImageListItemBar, + Input, + InputAdornment, + InputBase, + InputLabel, + LinearProgress, + Link, + List, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemIcon, + ListItemSecondaryAction, + ListItemText, + ListSubheader, + Menu, + MenuItem, + MenuList, + MobileStepper, + Modal, + NativeSelect, + NoSsr, + OutlinedInput, + Pagination, + PaginationItem, + Paper, + Popover, + Popper, + Portal, + Radio, + RadioGroup, + Rating, + Select, + Skeleton, + Slide, + Slider, + Snackbar, + SnackbarContent, + SpeedDial, + SpeedDialAction, + SpeedDialIcon, + Stack, + Step, + StepButton, + StepConnector, + StepContent, + StepIcon, + StepLabel, + Stepper, + SvgIcon, + SwipeableDrawer, + Switch, + Tab, + TabScrollButton, + Table, + TableBody, + TableCell, + TableContainer, + TableFooter, + TableHead, + TablePagination, + TableRow, + TableSortLabel, + Tabs, + TextField, + TextareaAutosize, + ToggleButton, + ToggleButtonGroup, + Toolbar, + Tooltip, + Typography, + Zoom, +} from "@mui/material"; import type { MDXComponents } from "mdx/types.js"; -import * as React from "react"; +import { ComponentProps, FC, createElement } from "react"; -import PyCon2025HostLogoBig from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_big.png"; -import PyCon2025HostLogoSmall from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_small.png"; -import PyCon2025MobileLogoImage from "../../../../packages/common/src/assets/pyconkr2025_main_cover_image.png"; -import PyCon2025MobileLogoTitle from "../../../../packages/common/src/assets/pyconkr2025_main_cover_title.png"; -import PyCon2025Logo from "../assets/pyconkr2025_logo.png"; +import PyCon2025Logo from "@apps/pyconkr-2025/assets/pyconkr2025_logo.png"; const MUIMDXComponents: MDXComponents = { - Mui__material__Accordion: mui.Accordion, - Mui__material__AccordionActions: mui.AccordionActions, - Mui__material__AccordionDetails: mui.AccordionDetails, - Mui__material__AccordionSummary: mui.AccordionSummary, - Mui__material__Alert: mui.Alert, - Mui__material__AlertTitle: mui.AlertTitle, - Mui__material__AppBar: mui.AppBar, - Mui__material__Autocomplete: mui.Autocomplete, - Mui__material__Avatar: mui.Avatar, - Mui__material__AvatarGroup: mui.AvatarGroup, - Mui__material__Backdrop: mui.Backdrop, - Mui__material__Badge: mui.Badge, - Mui__material__BottomNavigation: mui.BottomNavigation, - Mui__material__BottomNavigationAction: mui.BottomNavigationAction, - Mui__material__Box: mui.Box, - Mui__material__Breadcrumbs: mui.Breadcrumbs, - Mui__material__Button: mui.Button, - Mui__material__ButtonBase: mui.ButtonBase, - Mui__material__ButtonGroup: mui.ButtonGroup, - Mui__material__Card: mui.Card, - Mui__material__CardActionArea: mui.CardActionArea, - Mui__material__CardActions: mui.CardActions, - Mui__material__CardContent: mui.CardContent, - Mui__material__CardHeader: mui.CardHeader, - Mui__material__CardMedia: mui.CardMedia, - Mui__material__Checkbox: mui.Checkbox, - Mui__material__Chip: mui.Chip, - Mui__material__CircularProgress: mui.CircularProgress, - Mui__material__Collapse: mui.Collapse, - Mui__material__Container: mui.Container, - Mui__material__Dialog: mui.Dialog, - Mui__material__DialogActions: mui.DialogActions, - Mui__material__DialogContent: mui.DialogContent, - Mui__material__DialogContentText: mui.DialogContentText, - Mui__material__DialogTitle: mui.DialogTitle, - Mui__material__Divider: mui.Divider, - Mui__material__Drawer: mui.Drawer, - Mui__material__Fab: mui.Fab, - Mui__material__Fade: mui.Fade, - Mui__material__FilledInput: mui.FilledInput, - Mui__material__FormControl: mui.FormControl, - Mui__material__FormControlLabel: mui.FormControlLabel, - Mui__material__FormGroup: mui.FormGroup, - Mui__material__FormHelperText: mui.FormHelperText, - Mui__material__FormLabel: mui.FormLabel, - Mui__material__Grid: mui.Grid, - Mui__material__Grow: mui.Grow, - Mui__material__Icon: mui.Icon, - Mui__material__IconButton: mui.IconButton, - Mui__material__ImageList: mui.ImageList, - Mui__material__ImageListItem: mui.ImageListItem, - Mui__material__ImageListItemBar: mui.ImageListItemBar, - Mui__material__Input: mui.Input, - Mui__material__InputAdornment: mui.InputAdornment, - Mui__material__InputBase: mui.InputBase, - Mui__material__InputLabel: mui.InputLabel, - Mui__material__LinearProgress: mui.LinearProgress, - Mui__material__Link: mui.Link, - Mui__material__List: mui.List, - Mui__material__ListItem: mui.ListItem, - Mui__material__ListItemAvatar: mui.ListItemAvatar, - Mui__material__ListItemButton: mui.ListItemButton, - Mui__material__ListItemIcon: mui.ListItemIcon, - Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, - Mui__material__ListItemText: mui.ListItemText, - Mui__material__ListSubheader: mui.ListSubheader, - Mui__material__Menu: mui.Menu, - Mui__material__MenuItem: mui.MenuItem, - Mui__material__MenuList: mui.MenuList, - Mui__material__MobileStepper: mui.MobileStepper, - Mui__material__Modal: mui.Modal, - Mui__material__NativeSelect: mui.NativeSelect, - Mui__material__NoSsr: mui.NoSsr, - Mui__material__OutlinedInput: mui.OutlinedInput, - Mui__material__Pagination: mui.Pagination, - Mui__material__PaginationItem: mui.PaginationItem, - Mui__material__Paper: mui.Paper, - Mui__material__Popover: mui.Popover, - Mui__material__Popper: mui.Popper, - Mui__material__Portal: mui.Portal, - Mui__material__Radio: mui.Radio, - Mui__material__RadioGroup: mui.RadioGroup, - Mui__material__Rating: mui.Rating, - Mui__material__Select: mui.Select, - Mui__material__Skeleton: mui.Skeleton, - Mui__material__Slide: mui.Slide, - Mui__material__Slider: mui.Slider, - Mui__material__Snackbar: mui.Snackbar, - Mui__material__SnackbarContent: mui.SnackbarContent, - Mui__material__SpeedDial: mui.SpeedDial, - Mui__material__SpeedDialAction: mui.SpeedDialAction, - Mui__material__SpeedDialIcon: mui.SpeedDialIcon, - Mui__material__Stack: mui.Stack, - Mui__material__Step: mui.Step, - Mui__material__StepButton: mui.StepButton, - Mui__material__StepConnector: mui.StepConnector, - Mui__material__StepContent: mui.StepContent, - Mui__material__StepIcon: mui.StepIcon, - Mui__material__StepLabel: mui.StepLabel, - Mui__material__Stepper: mui.Stepper, - Mui__material__SvgIcon: mui.SvgIcon, - Mui__material__SwipeableDrawer: mui.SwipeableDrawer, - Mui__material__Switch: mui.Switch, - Mui__material__Tab: mui.Tab, - Mui__material__Table: mui.Table, - Mui__material__TableBody: mui.TableBody, - Mui__material__TableCell: mui.TableCell, - Mui__material__TableContainer: mui.TableContainer, - Mui__material__TableFooter: mui.TableFooter, - Mui__material__TableHead: mui.TableHead, - Mui__material__TablePagination: mui.TablePagination, - Mui__material__TableRow: mui.TableRow, - Mui__material__TableSortLabel: mui.TableSortLabel, - Mui__material__Tabs: mui.Tabs, - Mui__material__TabScrollButton: mui.TabScrollButton, - Mui__material__TextField: mui.TextField, - Mui__material__TextareaAutosize: mui.TextareaAutosize, - Mui__material__ToggleButton: mui.ToggleButton, - Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, - Mui__material__Toolbar: mui.Toolbar, - Mui__material__Tooltip: mui.Tooltip, - Mui__material__Typography: mui.Typography, - Mui__material__Zoom: mui.Zoom, + Mui__material__Accordion: Accordion, + Mui__material__AccordionActions: AccordionActions, + Mui__material__AccordionDetails: AccordionDetails, + Mui__material__AccordionSummary: AccordionSummary, + Mui__material__Alert: Alert, + Mui__material__AlertTitle: AlertTitle, + Mui__material__AppBar: AppBar, + Mui__material__Autocomplete: Autocomplete, + Mui__material__Avatar: Avatar, + Mui__material__AvatarGroup: AvatarGroup, + Mui__material__Backdrop: Backdrop, + Mui__material__Badge: Badge, + Mui__material__BottomNavigation: BottomNavigation, + Mui__material__BottomNavigationAction: BottomNavigationAction, + Mui__material__Box: Box, + Mui__material__Breadcrumbs: Breadcrumbs, + Mui__material__Button: Button, + Mui__material__ButtonBase: ButtonBase, + Mui__material__ButtonGroup: ButtonGroup, + Mui__material__Card: Card, + Mui__material__CardActionArea: CardActionArea, + Mui__material__CardActions: CardActions, + Mui__material__CardContent: CardContent, + Mui__material__CardHeader: CardHeader, + Mui__material__CardMedia: CardMedia, + Mui__material__Checkbox: Checkbox, + Mui__material__Chip: Chip, + Mui__material__CircularProgress: CircularProgress, + Mui__material__Collapse: Collapse, + Mui__material__Container: Container, + Mui__material__Dialog: Dialog, + Mui__material__DialogActions: DialogActions, + Mui__material__DialogContent: DialogContent, + Mui__material__DialogContentText: DialogContentText, + Mui__material__DialogTitle: DialogTitle, + Mui__material__Divider: Divider, + Mui__material__Drawer: Drawer, + Mui__material__Fab: Fab, + Mui__material__Fade: Fade, + Mui__material__FilledInput: FilledInput, + Mui__material__FormControl: FormControl, + Mui__material__FormControlLabel: FormControlLabel, + Mui__material__FormGroup: FormGroup, + Mui__material__FormHelperText: FormHelperText, + Mui__material__FormLabel: FormLabel, + Mui__material__Grid: Grid, + Mui__material__Grow: Grow, + Mui__material__Icon: Icon, + Mui__material__IconButton: IconButton, + Mui__material__ImageList: ImageList, + Mui__material__ImageListItem: ImageListItem, + Mui__material__ImageListItemBar: ImageListItemBar, + Mui__material__Input: Input, + Mui__material__InputAdornment: InputAdornment, + Mui__material__InputBase: InputBase, + Mui__material__InputLabel: InputLabel, + Mui__material__LinearProgress: LinearProgress, + Mui__material__Link: Link, + Mui__material__List: List, + Mui__material__ListItem: ListItem, + Mui__material__ListItemAvatar: ListItemAvatar, + Mui__material__ListItemButton: ListItemButton, + Mui__material__ListItemIcon: ListItemIcon, + Mui__material__ListItemSecondaryAction: ListItemSecondaryAction, + Mui__material__ListItemText: ListItemText, + Mui__material__ListSubheader: ListSubheader, + Mui__material__Menu: Menu, + Mui__material__MenuItem: MenuItem, + Mui__material__MenuList: MenuList, + Mui__material__MobileStepper: MobileStepper, + Mui__material__Modal: Modal, + Mui__material__NativeSelect: NativeSelect, + Mui__material__NoSsr: NoSsr, + Mui__material__OutlinedInput: OutlinedInput, + Mui__material__Pagination: Pagination, + Mui__material__PaginationItem: PaginationItem, + Mui__material__Paper: Paper, + Mui__material__Popover: Popover, + Mui__material__Popper: Popper, + Mui__material__Portal: Portal, + Mui__material__Radio: Radio, + Mui__material__RadioGroup: RadioGroup, + Mui__material__Rating: Rating, + Mui__material__Select: Select, + Mui__material__Skeleton: Skeleton, + Mui__material__Slide: Slide, + Mui__material__Slider: Slider, + Mui__material__Snackbar: Snackbar, + Mui__material__SnackbarContent: SnackbarContent, + Mui__material__SpeedDial: SpeedDial, + Mui__material__SpeedDialAction: SpeedDialAction, + Mui__material__SpeedDialIcon: SpeedDialIcon, + Mui__material__Stack: Stack, + Mui__material__Step: Step, + Mui__material__StepButton: StepButton, + Mui__material__StepConnector: StepConnector, + Mui__material__StepContent: StepContent, + Mui__material__StepIcon: StepIcon, + Mui__material__StepLabel: StepLabel, + Mui__material__Stepper: Stepper, + Mui__material__SvgIcon: SvgIcon, + Mui__material__SwipeableDrawer: SwipeableDrawer, + Mui__material__Switch: Switch, + Mui__material__Tab: Tab, + Mui__material__Table: Table, + Mui__material__TableBody: TableBody, + Mui__material__TableCell: TableCell, + Mui__material__TableContainer: TableContainer, + Mui__material__TableFooter: TableFooter, + Mui__material__TableHead: TableHead, + Mui__material__TablePagination: TablePagination, + Mui__material__TableRow: TableRow, + Mui__material__TableSortLabel: TableSortLabel, + Mui__material__Tabs: Tabs, + Mui__material__TabScrollButton: TabScrollButton, + Mui__material__TextField: TextField, + Mui__material__TextareaAutosize: TextareaAutosize, + Mui__material__ToggleButton: ToggleButton, + Mui__material__ToggleButtonGroup: ToggleButtonGroup, + Mui__material__Toolbar: Toolbar, + Mui__material__Tooltip: Tooltip, + Mui__material__Typography: Typography, + Mui__material__Zoom: Zoom, }; -const getPyConKR2025SessionUrl = (session: Schemas.BackendAPI.SessionSchema): string => { +const getPyConKR2025SessionUrl = (session: SessionSchema): string => { const urlSafeTitle = session.title .replace(/ /g, "-") .replace(/([.])/g, "_") @@ -145,27 +283,27 @@ const getPyConKR2025SessionUrl = (session: Schemas.BackendAPI.SessionSchema): st return `/presentations/${session.id}#${urlSafeTitle}`; }; -const PyConKR2025FallbackImage = React.createElement("img", { +const PyConKR2025FallbackImage = createElement("img", { src: PyCon2025Logo, alt: "PyCon 2025 Logo", style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }, }); -const PyConKR2025SessionList: React.FC> = (props) => - React.createElement(Components.MDX.SessionList, { +const PyConKR2025SessionList: FC> = (props) => + createElement(SessionList, { ...props, fallbackImage: PyConKR2025FallbackImage, getSessionUrl: getPyConKR2025SessionUrl, }); -const PyConKR2025SessionTimeTable: React.FC> = (props) => - React.createElement(Components.MDX.SessionTimeTable, { +const PyConKR2025SessionTimeTable: FC> = (props) => + createElement(SessionTimeTable, { ...props, getSessionUrl: getPyConKR2025SessionUrl, }); -const PyConKR2025MobileAccordion: React.FC = () => - React.createElement(Components.MDX.MobileAccordion, { +const PyConKR2025MobileAccordion: FC = () => + createElement(MobileAccordion, { marqueeText: "AUG 15 - 17", marqueeLogoSrc: PyCon2025HostLogoSmall, hostLogoBigSrc: PyCon2025HostLogoBig, @@ -173,21 +311,21 @@ const PyConKR2025MobileAccordion: React.FC = () => venueEnLines: ["New Engineering Building, Dongguk University", "Pildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea"], }); -const PyConKR2025MobileCover: React.FC = () => - React.createElement(Components.MDX.MobileCover, { +const PyConKR2025MobileCover: FC = () => + createElement(MobileCover, { coverImageSrc: PyCon2025MobileLogoImage, coverTitleSrc: PyCon2025MobileLogoTitle, }); const PyConKRCommonMDXComponents: MDXComponents = { - Common__Components__Lottie: Components.LottiePlayer, - Common__Components__NetworkLottie: Components.NetworkLottiePlayer, - Common__Components__MDX__Confetti: Components.MDX.Confetti, - Common__Components__MDX__PrimaryStyledDetails: Components.MDX.PrimaryStyledDetails, - Common__Components__MDX__SecondaryStyledDetails: Components.MDX.SecondaryStyledDetails, - Common__Components__MDX__Map: Components.MDX.Map, - Common__Components__MDX__FAQAccordion: Components.MDX.FAQAccordion, - Common__Components__MDX__FullWidthStyledButton: Components.MDX.StyledFullWidthButton, + Common__Components__Lottie: LottiePlayer, + Common__Components__NetworkLottie: NetworkLottiePlayer, + Common__Components__MDX__Confetti: Confetti, + Common__Components__MDX__PrimaryStyledDetails: PrimaryStyledDetails, + Common__Components__MDX__SecondaryStyledDetails: SecondaryStyledDetails, + Common__Components__MDX__Map: MDXMap, + Common__Components__MDX__FAQAccordion: FAQAccordion, + Common__Components__MDX__FullWidthStyledButton: StyledFullWidthButton, Common__Components__Session__List: PyConKR2025SessionList, Common__Components__Session__TimeTable: PyConKR2025SessionTimeTable, Common__Components__MDX__MobileAccordion: PyConKR2025MobileAccordion, @@ -195,17 +333,17 @@ const PyConKRCommonMDXComponents: MDXComponents = { }; const PythonKRShopMDXComponents: MDXComponents = { - Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, - Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, - Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, - Shop__Common__UserSignInMethod: Shop.Components.Common.UserSignInMethod, - Shop__Common__UserSignInAccount: Shop.Components.Common.UserSignInAccount, - Shop__Feature__CartStatus: Shop.Components.Features.CartStatus, - Shop__Feature__ProductList: Shop.Components.Features.ProductList, - Shop__Feature__ProductImageCardList: Shop.Components.Features.ProductImageCardList, - Shop__Feature__OrderList: Shop.Components.Features.OrderList, - Shop__Feature__UserInfo: Shop.Components.Features.UserInfo, - Shop__Feature__PatronList: Shop.Components.Features.PatronList, + Shop__Common__PriceDisplay: PriceDisplay, + Shop__Common__SignInGuard: SignInGuard, + Shop__Common__ContextProvider: ShopContextProvider, + Shop__Common__UserSignInMethod: UserSignInMethod, + Shop__Common__UserSignInAccount: UserSignInAccount, + Shop__Feature__CartStatus: CartStatus, + Shop__Feature__ProductList: ProductList, + Shop__Feature__ProductImageCardList: ProductImageCardList, + Shop__Feature__OrderList: OrderList, + Shop__Feature__UserInfo: UserInfo, + Shop__Feature__PatronList: PatronList, }; export const PyConKRMDXComponents = { diff --git a/apps/pyconkr-2025/src/contexts/app_context.tsx b/apps/pyconkr-2025/src/contexts/app_context.tsx index 3c56bb8..3a20055 100644 --- a/apps/pyconkr-2025/src/contexts/app_context.tsx +++ b/apps/pyconkr-2025/src/contexts/app_context.tsx @@ -1,7 +1,5 @@ -import * as React from "react"; - -import { NestedSiteMapSchema, SponsorTierSchema } from "../../../../packages/common/src/schemas/backendAPI"; - +import { NestedSiteMapSchema, SponsorTierSchema } from "@frontend/common/schemas/backendAPI"; +import { Dispatch, SetStateAction, createContext, useContext } from "react"; type LanguageType = "ko" | "en"; export type AppContextType = { @@ -14,13 +12,13 @@ export type AppContextType = { title: string; currentSiteMapDepth: (NestedSiteMapSchema | undefined)[]; - setAppContext: React.Dispatch>>; + setAppContext: Dispatch>>; }; -export const AppContext = React.createContext(undefined); +export const AppContext = createContext(undefined); export const useAppContext = (): AppContextType => { - const context = React.useContext(AppContext); + const context = useContext(AppContext); if (!context) { throw new Error("useAppContext must be used within an AppContextProvider"); } diff --git a/apps/pyconkr-2025/src/debug/page/component_test.tsx b/apps/pyconkr-2025/src/debug/page/component_test.tsx index b406c61..0eb0e3b 100644 --- a/apps/pyconkr-2025/src/debug/page/component_test.tsx +++ b/apps/pyconkr-2025/src/debug/page/component_test.tsx @@ -1,8 +1,7 @@ -import { Components } from "@frontend/common"; +import { PrimaryStyledDetails, SecondaryStyledDetails } from "@frontend/common/components/mdx_components"; import { Chip, Stack, Table, TableBody, TableCell, TableRow } from "@mui/material"; -import * as React from "react"; - -const HighlightedChip: React.FC<{ label: string }> = ({ label }) => ( +import { FC } from "react"; +const HighlightedChip: FC<{ label: string }> = ({ label }) => ( ({ @@ -14,10 +13,10 @@ const HighlightedChip: React.FC<{ label: string }> = ({ label }) => ( /> ); -export const ComponentTestPage: React.FC = () => { +export const ComponentTestPage: FC = () => { return ( - + 모든 자동차의 출입은 동국대 정문으로만 가능 @@ -47,9 +46,9 @@ export const ComponentTestPage: React.FC = () => {
-
+ - + 모든 자동차의 출입은 동국대 정문으로만 가능 @@ -79,7 +78,7 @@ export const ComponentTestPage: React.FC = () => {
-
+
); }; diff --git a/apps/pyconkr-2025/src/debug/page/map_test.tsx b/apps/pyconkr-2025/src/debug/page/map_test.tsx index 9c6bace..a67ed28 100644 --- a/apps/pyconkr-2025/src/debug/page/map_test.tsx +++ b/apps/pyconkr-2025/src/debug/page/map_test.tsx @@ -1,14 +1,13 @@ -import { Components } from "@frontend/common"; -import { getFormValue, isFormValid } from "@frontend/common/src/utils"; +import { Map as MDXMap, MapPropType } from "@frontend/common/components/mdx_components"; +import { getFormValue, isFormValid } from "@frontend/common/utils"; import { Box, Button, FormControlLabel, Stack, Switch, TextField } from "@mui/material"; -import * as React from "react"; - +import { FC, useRef, useState } from "react"; type MapTestPageStateType = { checked: boolean; - mapProps: Components.MDX.MapPropType; + mapProps: MapPropType; }; -const INITIAL_DATA: Components.MDX.MapPropType = { +const INITIAL_DATA: MapPropType = { geo: { lat: 37.5580918, lng: 126.9982178 }, placeName: { ko: "동국대학교 신공학관", @@ -23,13 +22,13 @@ const INITIAL_DATA: Components.MDX.MapPropType = { "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3162.871473157695!2d126.99821779999999!3d37.5580918!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x357ca302befa0c31%3A0xbc66c66731962172!2z64-Z6rWt64yA7ZWZ6rWQIOyLoOqzte2Vmeq0gA!5e0!3m2!1sko!2sen!4v1748768615566!5m2!1sko!2sen", }; -export const MapTestPage: React.FC = () => { - const geoFormRef = React.useRef(null); - const placeNameFormRef = React.useRef(null); - const placeCodeFormRef = React.useRef(null); - const gMapIframeUrlInputRef = React.useRef(null); +export const MapTestPage: FC = () => { + const geoFormRef = useRef(null); + const placeNameFormRef = useRef(null); + const placeCodeFormRef = useRef(null); + const gMapIframeUrlInputRef = useRef(null); - const [state, setState] = React.useState({ checked: false, mapProps: INITIAL_DATA }); + const [state, setState] = useState({ checked: false, mapProps: INITIAL_DATA }); const setChecked = (checked: boolean) => setState((ps) => ({ ...ps, checked })); const language = state.checked ? "en" : "ko"; @@ -90,7 +89,7 @@ export const MapTestPage: React.FC = () => { - + ); diff --git a/apps/pyconkr-2025/src/debug/page/mdi_test.tsx b/apps/pyconkr-2025/src/debug/page/mdi_test.tsx index ad7e376..2ff46fd 100644 --- a/apps/pyconkr-2025/src/debug/page/mdi_test.tsx +++ b/apps/pyconkr-2025/src/debug/page/mdi_test.tsx @@ -1,5 +1,5 @@ -import { Components } from "@frontend/common"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +import { MDXEditor, MDXRenderer } from "@frontend/common/components"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { Box, Stack } from "@mui/material"; import React from "react"; @@ -27,10 +27,10 @@ export const MdiTestPage: React.FC = () => { }} > - + - + ); diff --git a/apps/pyconkr-2025/src/debug/page/shop_test.tsx b/apps/pyconkr-2025/src/debug/page/shop_test.tsx index bb5beee..5bfbe1a 100644 --- a/apps/pyconkr-2025/src/debug/page/shop_test.tsx +++ b/apps/pyconkr-2025/src/debug/page/shop_test.tsx @@ -1,4 +1,4 @@ -import * as Shop from "@frontend/shop"; +import { CartStatus, OrderList, ProductImageCardList, ProductList, UserInfo } from "@frontend/shop/components/features"; import { Divider, Stack, Typography } from "@mui/material"; import React from "react"; @@ -11,26 +11,26 @@ export const ShopTestPage: React.FC = () => ( 계정 상태 - + 상품 목록 - + 상품 목록 (이미지 카드) - + 장바구니 - + 주문 내역 - + ); diff --git a/apps/pyconkr-2025/src/main.tsx b/apps/pyconkr-2025/src/main.tsx index eef13e6..c0c0d3b 100644 --- a/apps/pyconkr-2025/src/main.tsx +++ b/apps/pyconkr-2025/src/main.tsx @@ -1,14 +1,16 @@ import { Global } from "@emotion/react"; -import { Components, Utils } from "@frontend/common"; -import type { ContextOptions } from "@frontend/common/src/contexts"; -import * as Shop from "@frontend/shop"; +import { CenteredPage, CommonContextProvider, ErrorFallback } from "@frontend/common/components"; +import type { ContextOptions } from "@frontend/common/contexts"; +import { registerChunkLoadErrorReloadHandler } from "@frontend/common/utils"; +import { ShopContextProvider } from "@frontend/shop/components/common"; +import { ContextOptions as ShopContextOptions } from "@frontend/shop/contexts"; import { CircularProgress, CssBaseline, ThemeProvider } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { matchQuery, MutationCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { SnackbarProvider } from "notistack"; -import * as React from "react"; -import * as ReactDom from "react-dom/client"; +import { FC, StrictMode, useState } from "react"; +import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { App } from "./App.tsx"; @@ -55,7 +57,7 @@ const CommonOptions: ContextOptions = { mdxComponents: PyConKRMDXComponents, }; -const ShopOptions: Shop.Contexts.ContextOptions = { +const ShopOptions: ShopContextOptions = { language: "ko", shopApiDomain: import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN, shopApiCSRFCookieName: import.meta.env.VITE_PYCONKR_SHOP_CSRF_COOKIE_NAME, @@ -64,13 +66,13 @@ const ShopOptions: Shop.Contexts.ContextOptions = { }; const SuspenseFallback = ( - + - + ); -const MainApp: React.FC = () => { - const [appState, setAppContext] = React.useState>({ +export const MainApp: FC = () => { + const [appState, setAppContext] = useState>({ language: (localStorage.getItem(LOCAL_STORAGE_LANGUAGE_KEY) as "ko" | "en" | null) ?? "ko", shouldShowTitleBanner: true, shouldShowSponsorBanner: false, @@ -81,15 +83,15 @@ const MainApp: React.FC = () => { }); return ( - + - - - + + + @@ -98,16 +100,16 @@ const MainApp: React.FC = () => { - - + + - + ); }; -Utils.registerChunkLoadErrorReloadHandler(); +registerChunkLoadErrorReloadHandler(); -ReactDom.createRoot(document.getElementById("root")!).render(); +createRoot(document.getElementById("root")!).render(); diff --git a/apps/pyconkr-2025/tsconfig.json b/apps/pyconkr-2025/tsconfig.json index 2eacc5e..ff99f6e 100644 --- a/apps/pyconkr-2025/tsconfig.json +++ b/apps/pyconkr-2025/tsconfig.json @@ -1,32 +1,4 @@ { - "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true, - "forceConsistentCasingInFileNames": false, - - /* Paths */ - "baseUrl": ".", - "paths": { - "@apps/pyconkr-2025/*": ["apps/pyconkr-2025/src/*"], - } - }, - "include": ["src", "vite.config.mts", "vite-env.d.ts", "../../types"], + "extends": "../../tsconfig.base.json", + "include": ["src", "vite.config.mts", "vite-env.d.ts", "../../types"] } diff --git a/apps/pyconkr-2025/vite.config.mts b/apps/pyconkr-2025/vite.config.mts index 0be52a7..0026113 100644 --- a/apps/pyconkr-2025/vite.config.mts +++ b/apps/pyconkr-2025/vite.config.mts @@ -20,9 +20,8 @@ export default defineConfig(({ mode }) => { plugins: [react(), mdx(), ...(isLocalHttpBackend ? [] : [mkcert({ hosts: [host] })]), svgr()], resolve: { alias: { - "@frontend/common/src": path.resolve(__dirname, "../../packages/common/src"), - "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), - "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/common": path.resolve(__dirname, "../../packages/common/src"), + "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src"), "@apps/pyconkr-2025": path.resolve(__dirname, "./src"), }, }, diff --git a/apps/pyconkr-2026/src/App.tsx b/apps/pyconkr-2026/src/App.tsx index 051811e..ad96358 100644 --- a/apps/pyconkr-2026/src/App.tsx +++ b/apps/pyconkr-2026/src/App.tsx @@ -1,9 +1,9 @@ -import { useBackendClient, useFlattenSiteMapQuery, useSponsorQuery } from "@frontend/common/src/hooks/useAPI"; -import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI"; -import { buildNestedSiteMap } from "@frontend/common/src/utils"; -import * as React from "react"; +import { useBackendClient, useFlattenSiteMapQuery, useSponsorQuery } from "@frontend/common/hooks/useAPI"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; +import { buildNestedSiteMap } from "@frontend/common/utils"; +import { FC, useEffect } from "react"; import { Route, Routes, useLocation } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty, isNullish } from "remeda"; import MainLayout from "./components/layout/index.tsx"; import { PageIdParamRenderer, RouteRenderer } from "./components/pages/dynamic_route.tsx"; @@ -12,7 +12,7 @@ import { ShopSignInPage } from "./components/pages/sign_in.tsx"; import { SponsorDetailPage } from "./components/pages/sponsor_detail.tsx"; import { useAppContext } from "./contexts/app_context"; -export const App: React.FC = () => { +export const App: FC = () => { const backendAPIClient = useBackendClient(); const { data: sponsorTiers } = useSponsorQuery(backendAPIClient); const { data: flatSiteMap } = useFlattenSiteMapQuery(backendAPIClient); @@ -21,17 +21,17 @@ export const App: React.FC = () => { const location = useLocation(); const { setAppContext, language } = useAppContext(); - React.useEffect(() => { + useEffect(() => { (async () => { - const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !R.isEmpty(code))]; - const currentSiteMapDepth: (BackendAPISchemas.NestedSiteMapSchema | undefined)[] = [siteMapNode]; + const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !isEmpty(code))]; + const currentSiteMapDepth: (NestedSiteMapSchema | undefined)[] = [siteMapNode]; for (const routeCode of currentRouteCodes.splice(1)) { const childrenMap = currentSiteMapDepth .at(-1) - ?.children?.reduce((acc, child) => ({ ...acc, [child.route_code]: child }), {} as Record); + ?.children?.reduce((acc, child) => ({ ...acc, [child.route_code]: child }), {} as Record); currentSiteMapDepth.push(childrenMap?.[routeCode]); - if (R.isNullish(currentSiteMapDepth.at(-1))) { + if (isNullish(currentSiteMapDepth.at(-1))) { console.warn(`Route not found in site map: ${routeCode}`); break; } diff --git a/apps/pyconkr-2026/src/components/layout/BreadCrumb/index.tsx b/apps/pyconkr-2026/src/components/layout/BreadCrumb/index.tsx index 483b786..caa7198 100644 --- a/apps/pyconkr-2026/src/components/layout/BreadCrumb/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/BreadCrumb/index.tsx @@ -1,23 +1,21 @@ +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { Stack, styled } from "@mui/material"; -import * as React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; -import * as R from "remeda"; - -import { NestedSiteMapSchema } from "../../../../../../packages/common/src/schemas/backendAPI"; - +import { isNonNullish } from "remeda"; type BreadCrumbPropType = { title: string; parentSiteMaps: (NestedSiteMapSchema | undefined)[]; }; -export const BreadCrumb: React.FC = ({ title, parentSiteMaps }) => { +export const BreadCrumb: FC = ({ title, parentSiteMaps }) => { let route = "/"; return ( {parentSiteMaps .slice(1, -1) - .filter((routeInfo) => R.isNonNullish(routeInfo)) + .filter((routeInfo) => isNonNullish(routeInfo)) .map(({ route_code, name }, index) => { route += `${route_code}/`; return ( diff --git a/apps/pyconkr-2026/src/components/layout/CartBadgeButton/index.tsx b/apps/pyconkr-2026/src/components/layout/CartBadgeButton/index.tsx index c18e20c..b3ac586 100644 --- a/apps/pyconkr-2026/src/components/layout/CartBadgeButton/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/CartBadgeButton/index.tsx @@ -1,8 +1,8 @@ -import * as Shop from "@frontend/shop"; +import { useCart, useShopClient } from "@frontend/shop/hooks"; import { ShoppingCart } from "@mui/icons-material"; import { Badge, badgeClasses, IconButton, styled } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC } from "react"; import { useNavigate } from "react-router-dom"; type InnerCartBadgeButtonPropType = { @@ -19,7 +19,7 @@ const ColoredIconButton = styled(IconButton)(({ theme }) => ({ const InnerCartBadge = styled(Badge)({ [`& .${badgeClasses.badge}`]: { top: "-12px", right: "-3px" } }); -const InnerCartBadgeButton: React.FC = ({ loading, count }) => { +const InnerCartBadgeButton: FC = ({ loading, count }) => { const navigate = useNavigate(); return ( @@ -30,11 +30,11 @@ const InnerCartBadgeButton: React.FC = ({ loading, ); }; -export const CartBadgeButton: React.FC = Suspense.with( +export const CartBadgeButton: FC = Suspense.with( { fallback: }, ErrorBoundary.with({ fallback: }, () => { - const shopAPIClient = Shop.Hooks.useShopClient(); - const { data: cart } = Shop.Hooks.useCart(shopAPIClient); + const shopAPIClient = useShopClient(); + const { data: cart } = useCart(shopAPIClient); return ; }) ); diff --git a/apps/pyconkr-2026/src/components/layout/Footer/Mobile/MobileFooter.tsx b/apps/pyconkr-2026/src/components/layout/Footer/Mobile/MobileFooter.tsx index 2eb9368..39ac2b7 100644 --- a/apps/pyconkr-2026/src/components/layout/Footer/Mobile/MobileFooter.tsx +++ b/apps/pyconkr-2026/src/components/layout/Footer/Mobile/MobileFooter.tsx @@ -1,14 +1,13 @@ import styled from "@emotion/styled"; -import { useEmail } from "@frontend/common/src/hooks/useEmail"; +import { useEmail } from "@frontend/common/hooks/useEmail"; import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, X, YouTube } from "@mui/icons-material"; -import * as React from "react"; +import { FC } from "react"; import FlickrIcon from "@apps/pyconkr-2026/assets/thirdparty/flickr.svg?react"; - -import { useAppContext } from "../../../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; interface IconItem { - icon: React.FC<{ width?: number; height?: number }>; + icon: FC<{ width?: number; height?: number }>; alt: string; href: string; } diff --git a/apps/pyconkr-2026/src/components/layout/Footer/index.tsx b/apps/pyconkr-2026/src/components/layout/Footer/index.tsx index a010177..65538fa 100644 --- a/apps/pyconkr-2026/src/components/layout/Footer/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/Footer/index.tsx @@ -1,16 +1,16 @@ import styled from "@emotion/styled"; -import { useEmail } from "@frontend/common/src/hooks/useEmail"; +import { useEmail } from "@frontend/common/hooks/useEmail"; import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, OpenInNew, X, YouTube } from "@mui/icons-material"; import { Button, useMediaQuery, useTheme } from "@mui/material"; -import * as React from "react"; +import { FC, Fragment } from "react"; import FlickrIcon from "@apps/pyconkr-2026/assets/thirdparty/flickr.svg?react"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; -import { useAppContext } from "../../../contexts/app_context"; import MobileFooter from "./Mobile/MobileFooter"; interface IconItem { - icon: React.FC<{ width?: number; height?: number }>; + icon: FC<{ width?: number; height?: number }>; alt: string; href: string; } @@ -46,7 +46,7 @@ const defaultIcons: IconItem[] = [ }, ]; -const Bar: React.FC = () =>
|
; +const Bar: FC = () =>
|
; export default function Footer() { const { sendEmail } = useEmail(); @@ -117,12 +117,12 @@ export default function Footer() { {links.map((link, index) => ( - + {link.text} {index < links.length - 1 && |} - + ))} diff --git a/apps/pyconkr-2026/src/components/layout/Header/Mobile/HamburgerButton.tsx b/apps/pyconkr-2026/src/components/layout/Header/Mobile/HamburgerButton.tsx index 18f90b5..39ea37b 100644 --- a/apps/pyconkr-2026/src/components/layout/Header/Mobile/HamburgerButton.tsx +++ b/apps/pyconkr-2026/src/components/layout/Header/Mobile/HamburgerButton.tsx @@ -1,12 +1,11 @@ import { IconButton, styled } from "@mui/material"; -import * as React from "react"; - +import { FC } from "react"; interface HamburgerButtonProps { isOpen: boolean; onClick: () => void; } -export const HamburgerButton: React.FC = ({ isOpen, onClick }) => { +export const HamburgerButton: FC = ({ isOpen, onClick }) => { return ( diff --git a/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileHeader.tsx b/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileHeader.tsx index 98c5f1c..ff6a501 100644 --- a/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileHeader.tsx +++ b/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileHeader.tsx @@ -1,16 +1,17 @@ -import { Components } from "@frontend/common"; +import { PythonKorea } from "@frontend/common/components"; import { Box, Stack, styled, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, useState } from "react"; import { Link } from "react-router-dom"; +import LanguageSelector from "@apps/pyconkr-2026/components/layout/LanguageSelector"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; + import { HamburgerButton } from "./HamburgerButton"; import { MobileNavigation } from "./MobileNavigation"; -import LanguageSelector from "../../LanguageSelector"; -import { useAppContext } from "../../../../contexts/app_context"; -export const MobileHeader: React.FC = () => { +export const MobileHeader: FC = () => { const { siteMapNode } = useAppContext(); - const [isOpen, setIsOpen] = React.useState(false); + const [isOpen, setIsOpen] = useState(false); return ( <> @@ -19,7 +20,7 @@ export const MobileHeader: React.FC = () => { setIsOpen(!isOpen)} /> - + 파이콘 한국 2026 diff --git a/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileNavigation.tsx b/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileNavigation.tsx index 7848de4..f398446 100644 --- a/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileNavigation.tsx +++ b/apps/pyconkr-2026/src/components/layout/Header/Mobile/MobileNavigation.tsx @@ -1,15 +1,16 @@ -import { Components } from "@frontend/common"; -import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI"; +import { PythonKorea } from "@frontend/common/components"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { ArrowBack, ArrowForward } from "@mui/icons-material"; import { Box, Button, Chip, Drawer, IconButton, Stack, styled } from "@mui/material"; -import * as React from "react"; +import { FC, useState } from "react"; import { Link } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty } from "remeda"; + +import LanguageSelector from "@apps/pyconkr-2026/components/layout/LanguageSelector"; import { HamburgerButton } from "./HamburgerButton"; -import LanguageSelector from "../../LanguageSelector"; -type MenuType = BackendAPISchemas.NestedSiteMapSchema; +type MenuType = NestedSiteMapSchema; interface MobileNavigationProps { isOpen: boolean; @@ -26,8 +27,8 @@ interface NavigationState { breadcrumbs: { name: string; level: NavigationLevel }[]; } -export const MobileNavigation: React.FC = ({ isOpen, onClose, siteMapNode }) => { - const [navState, setNavState] = React.useState({ level: "depth1", breadcrumbs: [] }); +export const MobileNavigation: FC = ({ isOpen, onClose, siteMapNode }) => { + const [navState, setNavState] = useState({ level: "depth1", breadcrumbs: [] }); const resetNavigation = () => setNavState({ level: "depth1", breadcrumbs: [] }); @@ -65,14 +66,14 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl .filter((s) => !s.hide) .map((menu) => ( - {!R.isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) ? ( + {!isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) ? ( navigateToDepth2(menu)}>{menu.name} ) : ( {menu.name} )} - {!R.isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) && ( + {!isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) && ( navigateToDepth2(menu)}> @@ -102,7 +103,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl - {!R.isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) && ( + {!isEmpty(menu.children) && Object.values(menu.children).some((c) => !c.hide) && ( navigateToDepth3(menu)}> @@ -150,7 +151,7 @@ export const MobileNavigation: React.FC = ({ isOpen, onCl - + 파이콘 한국 2026 diff --git a/apps/pyconkr-2026/src/components/layout/Header/index.tsx b/apps/pyconkr-2026/src/components/layout/Header/index.tsx index b9da072..7d274d3 100644 --- a/apps/pyconkr-2026/src/components/layout/Header/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/Header/index.tsx @@ -1,14 +1,15 @@ -import { Components } from "@frontend/common"; +import { PythonKorea } from "@frontend/common/components"; +import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI"; import { ArrowForwardIos } from "@mui/icons-material"; import { Box, Button, CircularProgress, Divider, Stack, styled, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"; import { MUIStyledCommonProps } from "@mui/system"; -import * as React from "react"; +import { CSSProperties, Fragment, useEffect, useState } from "react"; import { Link } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty, isNonNullish, isString } from "remeda"; + +import LanguageSelector from "@apps/pyconkr-2026/components/layout/LanguageSelector"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; -import { NestedSiteMapSchema } from "../../../../../../packages/common/src/schemas/backendAPI"; -import { useAppContext } from "../../../contexts/app_context"; -import LanguageSelector from "../LanguageSelector"; import { MobileHeader } from "./Mobile/MobileHeader"; type MenuType = NestedSiteMapSchema; @@ -20,14 +21,14 @@ type NavigationStateType = { depth3?: MenuType; }; -const HeaderHeight: React.CSSProperties["height"] = "3.625rem"; -const BreadCrumbHeight: React.CSSProperties["height"] = "4.5rem"; +const HeaderHeight: CSSProperties["height"] = "3.625rem"; +const BreadCrumbHeight: CSSProperties["height"] = "4.5rem"; export default function Header() { const { title, language, siteMapNode, currentSiteMapDepth, shouldShowTitleBanner } = useAppContext(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("md")); - const [navState, setNavState] = React.useState({}); + const [navState, setNavState] = useState({}); const resetDepths = () => setNavState({}); const setDepth1 = (depth1: MenuOrUndefinedType) => setNavState({ depth1 }); @@ -37,13 +38,13 @@ export default function Header() { const getDepth2Route = (nextRoute?: string) => (navState.depth1?.route_code || "") + `/${nextRoute || ""}`; const getDepth3Route = (nextRoute?: string) => getDepth2Route(navState.depth2?.route_code) + `/${nextRoute || ""}`; - React.useEffect(resetDepths, [language]); + useEffect(resetDepths, [language]); if (isMobile) return ; let breadCrumbRoute = ""; let breadCrumbArray = currentSiteMapDepth.slice(1, -1); - if (R.isEmpty(breadCrumbArray)) breadCrumbArray = currentSiteMapDepth.slice(0, -1); + if (isEmpty(breadCrumbArray)) breadCrumbArray = currentSiteMapDepth.slice(0, -1); const headerStyle: SxProps = shouldShowTitleBanner ? {} : { backgroundColor: "transparent" }; @@ -53,7 +54,7 @@ export default function Header() { - + PyCon Korea 2026 @@ -69,8 +70,8 @@ export default function Header() { setDepth1(r)} isActive={navState.depth1?.id === r.id}> @@ -105,9 +106,9 @@ export default function Header() { className={r.id === navState.depth2?.id ? "active" : ""} onClick={resetDepths} onMouseEnter={() => setDepth2(r)} - onMouseLeave={() => R.isEmpty(navState.depth2?.children ?? {}) && setDepth2(undefined)} - target={R.isString(r.external_link) ? "_blank" : undefined} - rel={R.isString(r.external_link) ? "noopener noreferrer" : undefined} + onMouseLeave={() => isEmpty(navState.depth2?.children ?? {}) && setDepth2(undefined)} + target={isString(r.external_link) ? "_blank" : undefined} + rel={isString(r.external_link) ? "noopener noreferrer" : undefined} to={r.external_link || getDepth2Route(r.route_code)} > {r.name} @@ -115,7 +116,7 @@ export default function Header() { ))} - {navState.depth2 && !R.isEmpty(navState.depth2.children) && ( + {navState.depth2 && !isEmpty(navState.depth2.children) && ( <> @@ -128,8 +129,8 @@ export default function Header() { onClick={resetDepths} onMouseEnter={() => setDepth3(r)} onMouseLeave={() => setDepth3(undefined)} - target={R.isString(r.external_link) ? "_blank" : undefined} - rel={R.isString(r.external_link) ? "noopener noreferrer" : undefined} + target={isString(r.external_link) ? "_blank" : undefined} + rel={isString(r.external_link) ? "noopener noreferrer" : undefined} to={r.external_link || getDepth3Route(r?.route_code)} > {r.name} @@ -148,14 +149,14 @@ export default function Header() { {breadCrumbArray - .filter((routeInfo) => R.isNonNullish(routeInfo)) + .filter((routeInfo) => isNonNullish(routeInfo)) .map(({ route_code, name }, index) => { breadCrumbRoute += `${route_code}/`; return ( - + {index > 0 && } - + ); })} diff --git a/apps/pyconkr-2026/src/components/layout/LanguageSelector/index.tsx b/apps/pyconkr-2026/src/components/layout/LanguageSelector/index.tsx index 5535802..92b4f55 100644 --- a/apps/pyconkr-2026/src/components/layout/LanguageSelector/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/LanguageSelector/index.tsx @@ -1,8 +1,8 @@ import { Language } from "@mui/icons-material"; import { Button, Stack, styled } from "@mui/material"; -import { LOCAL_STORAGE_LANGUAGE_KEY } from "../../../consts/local_stroage"; -import { useAppContext } from "../../../contexts/app_context"; +import { LOCAL_STORAGE_LANGUAGE_KEY } from "@apps/pyconkr-2026/consts/local_stroage"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; export default function LanguageSelector() { const { language, setAppContext } = useAppContext(); diff --git a/apps/pyconkr-2026/src/components/layout/SignInButton/index.tsx b/apps/pyconkr-2026/src/components/layout/SignInButton/index.tsx index 7996129..b965837 100644 --- a/apps/pyconkr-2026/src/components/layout/SignInButton/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/SignInButton/index.tsx @@ -1,10 +1,10 @@ -import * as Shop from "@frontend/shop"; +import { useShopClient, useSignOutMutation, useUserStatus } from "@frontend/shop/hooks"; import { Login, Logout } from "@mui/icons-material"; import { Button, Stack } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { useNavigate } from "react-router-dom"; -import { useAppContext } from "../../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; type InnerSignInButtonImplPropType = { loading?: boolean; @@ -77,29 +77,28 @@ const InnerSignInButtonImpl: React.FC = ({ ); }; -export const SignInButton: React.FC<{ isMobile?: boolean; isMainPath?: boolean; onClose?: () => void }> = ({ - isMobile = false, - isMainPath = true, - onClose, -}) => { - const SignInWithErrorBoundary = ErrorBoundary.with( - { fallback: }, - Suspense.with({ fallback: }, () => { - const shopAPIClient = Shop.Hooks.useShopClient(); - const signOutMutation = Shop.Hooks.useSignOutMutation(shopAPIClient); - const { data } = Shop.Hooks.useUserStatus(shopAPIClient); +type SignInButtonProps = { isMobile?: boolean; isMainPath?: boolean; onClose?: () => void }; - return ( - - ); - }) - ); +const SignInButtonContent: React.FC = ({ isMobile, isMainPath, onClose }) => { + const shopAPIClient = useShopClient(); + const signOutMutation = useSignOutMutation(shopAPIClient); + const { data } = useUserStatus(shopAPIClient); - return ; + return ( + + ); }; + +export const SignInButton: React.FC = ({ isMobile = false, isMainPath = true, onClose }) => ( + }> + }> + + + +); diff --git a/apps/pyconkr-2026/src/components/layout/Sponsor/index.tsx b/apps/pyconkr-2026/src/components/layout/Sponsor/index.tsx index 547c2ba..f999319 100644 --- a/apps/pyconkr-2026/src/components/layout/Sponsor/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/Sponsor/index.tsx @@ -2,7 +2,7 @@ import { Badge, CircularProgress, Divider, Stack, Tooltip, Typography, Typograph import { ErrorBoundary, Suspense } from "@suspensive/react"; import { Link } from "react-router-dom"; -import { useAppContext } from "../../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; const LogoHeight: React.CSSProperties["height"] = "8rem"; const LogoWidth: React.CSSProperties["width"] = "15rem"; diff --git a/apps/pyconkr-2026/src/components/layout/UserScanCodeButton/index.tsx b/apps/pyconkr-2026/src/components/layout/UserScanCodeButton/index.tsx index 3a26bf0..83af57f 100644 --- a/apps/pyconkr-2026/src/components/layout/UserScanCodeButton/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/UserScanCodeButton/index.tsx @@ -1,16 +1,16 @@ -import * as Shop from "@frontend/shop"; +import { useShopClient, useUserInfo } from "@frontend/shop/hooks"; import { QrCode2 } from "@mui/icons-material"; import { Button, IconButton, IconButtonProps } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, Fragment } from "react"; -import { useAppContext } from "../../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; -export const ScanCodeIconButton: React.FC<{ sx?: IconButtonProps["sx"] }> = Suspense.with( - { fallback: }, - ErrorBoundary.with({ fallback: }, ({ sx }) => { - const shopAPIClient = Shop.Hooks.useShopClient(); - const { data } = Shop.Hooks.useUserInfo(shopAPIClient); +export const ScanCodeIconButton: FC<{ sx?: IconButtonProps["sx"] }> = Suspense.with( + { fallback: }, + ErrorBoundary.with({ fallback: }, ({ sx }) => { + const shopAPIClient = useShopClient(); + const { data } = useUserInfo(shopAPIClient); const iconBtnStyle: IconButtonProps["sx"] = (theme) => ({ color: theme.palette.primary.nonFocus, @@ -27,12 +27,12 @@ export const ScanCodeIconButton: React.FC<{ sx?: IconButtonProps["sx"] }> = Susp }) ); -export const ScanCodeButton: React.FC = Suspense.with( +export const ScanCodeButton: FC = Suspense.with( { fallback: null }, ErrorBoundary.with({ fallback: null }, () => { const { language } = useAppContext(); - const shopAPIClient = Shop.Hooks.useShopClient(); - const { data } = Shop.Hooks.useUserInfo(shopAPIClient); + const shopAPIClient = useShopClient(); + const { data } = useUserInfo(shopAPIClient); const buttonText = language === "ko" ? "등록 코드" : "Entrance QR Code"; diff --git a/apps/pyconkr-2026/src/components/layout/index.tsx b/apps/pyconkr-2026/src/components/layout/index.tsx index f86462c..017190c 100644 --- a/apps/pyconkr-2026/src/components/layout/index.tsx +++ b/apps/pyconkr-2026/src/components/layout/index.tsx @@ -2,7 +2,8 @@ import styled from "@emotion/styled"; import { Stack } from "@mui/material"; import { Outlet } from "react-router-dom"; -import { useAppContext } from "../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; + import Footer from "./Footer"; import Header from "./Header"; import { Sponsor } from "./Sponsor"; diff --git a/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx b/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx index 1fd2246..7d3c3dd 100644 --- a/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx +++ b/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx @@ -1,19 +1,19 @@ -import { Components } from "@frontend/common"; -import { useBackendClient, usePageQuery } from "@frontend/common/src/hooks/useAPI"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; -import { parseCss } from "@frontend/common/src/utils"; -import { BackendAPIClientError } from "@frontend/common/src/apis"; +import { BackendAPIClientError } from "@frontend/common/apis"; +import { CenteredPage, ErrorFallback, MDXRenderer } from "@frontend/common/components"; +import { useBackendClient, usePageQuery } from "@frontend/common/hooks/useAPI"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; +import { parseCss } from "@frontend/common/utils"; import { CircularProgress, Stack, Theme } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { useQueryClient } from "@tanstack/react-query"; import { AxiosError, AxiosResponse } from "axios"; -import * as React from "react"; +import { CSSProperties, FC, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import * as R from "remeda"; +import { isEmpty, isString } from "remeda"; -import { useAppContext } from "../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; -const initialPageStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) => React.CSSProperties = (additionalStyle) => (theme) => ({ +const initialPageStyle: (additionalStyle: CSSProperties) => (theme: Theme) => CSSProperties = (additionalStyle) => (theme) => ({ width: "100%", display: "flex", justifyContent: "flex-start", @@ -22,7 +22,7 @@ const initialPageStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) marginTop: theme.spacing(4), - ...(!R.isEmpty(additionalStyle) + ...(!isEmpty(additionalStyle) ? additionalStyle : { [theme.breakpoints.down("lg")]: { @@ -34,7 +34,7 @@ const initialPageStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) }), }); -const initialSectionStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) => React.CSSProperties = (additionalStyle) => (theme) => ({ +const initialSectionStyle: (additionalStyle: CSSProperties) => (theme: Theme) => CSSProperties = (additionalStyle) => (theme) => ({ width: "100%", maxWidth: "1200px", display: "flex", @@ -44,7 +44,7 @@ const initialSectionStyle: (additionalStyle: React.CSSProperties) => (theme: The paddingLeft: theme.spacing(16), "& .markdown-body": { width: "100%" }, - ...(!R.isEmpty(additionalStyle) + ...(!isEmpty(additionalStyle) ? additionalStyle : { [theme.breakpoints.down("lg")]: { @@ -58,13 +58,13 @@ const initialSectionStyle: (additionalStyle: React.CSSProperties) => (theme: The }), }); -const LoginRequired: React.FC = () => <>401 Login Required; -const PermissionDenied: React.FC = () => <>403 Permission Denied; -const PageNotFound: React.FC = () => <>404 Not Found; -const CenteredLoadingPage: React.FC = () => ( - +const LoginRequired: FC = () => <>401 Login Required; +const PermissionDenied: FC = () => <>403 Permission Denied; +const PageNotFound: FC = () => <>404 Not Found; +const CenteredLoadingPage: FC = () => ( + - + ); const throwPageNotFound: (message: string) => never = (message) => { @@ -75,7 +75,7 @@ const throwPageNotFound: (message: string) => never = (message) => { throw new BackendAPIClientError(axiosError); }; -const RouteErrorFallback: React.FC<{ error: Error; reset: () => void }> = ({ error, reset }) => { +const RouteErrorFallback: FC<{ error: Error; reset: () => void }> = ({ error, reset }) => { if (error instanceof BackendAPIClientError) { switch (error.status) { case 401: @@ -85,17 +85,17 @@ const RouteErrorFallback: React.FC<{ error: Error; reset: () => void }> = ({ err case 404: return ; default: - return ; + return ; } } - return ; + return ; }; -const WaitedCenteredLoadingPage: React.FC = Suspense.with({ fallback: }, () => { - const [isFetching, setIsFetching] = React.useState(true); +const WaitedCenteredLoadingPage: FC = Suspense.with({ fallback: }, () => { + const [isFetching, setIsFetching] = useState(true); const qClient = useQueryClient(); - React.useEffect(() => { + useEffect(() => { const unsubscribe = qClient.getQueryCache().subscribe(() => setIsFetching(qClient.isFetching() > 0)); return () => unsubscribe(); }, [qClient]); @@ -103,13 +103,13 @@ const WaitedCenteredLoadingPage: React.FC = Suspense.with({ fallback: : ; }); -const InnerPageRenderer: React.FC<{ id: string }> = Suspense.with({ fallback: }, ({ id }) => { +const InnerPageRenderer: FC<{ id: string }> = Suspense.with({ fallback: }, ({ id }) => { const { setAppContext } = useAppContext(); const { baseUrl, mdxComponents } = useCommonContext(); const backendClient = useBackendClient(); const { data } = usePageQuery(backendClient, id); - React.useEffect(() => { + useEffect(() => { setAppContext((prev) => ({ ...prev, title: data.title, @@ -122,33 +122,33 @@ const InnerPageRenderer: React.FC<{ id: string }> = Suspense.with({ fallback: {data.sections.map((s) => ( - + ))} ); }); -export const PageRenderer: React.FC<{ id: string }> = ({ id }) => ( +export const PageRenderer: FC<{ id: string }> = ({ id }) => ( ); -export const RouteRenderer: React.FC = ErrorBoundary.with( +export const RouteRenderer: FC = ErrorBoundary.with( { fallback: RouteErrorFallback }, Suspense.with({ fallback: }, () => { const { siteMapNode, currentSiteMapDepth } = useAppContext(); - const routeInfo = !R.isEmpty(currentSiteMapDepth) && currentSiteMapDepth[currentSiteMapDepth.length - 1]; + const routeInfo = !isEmpty(currentSiteMapDepth) && currentSiteMapDepth[currentSiteMapDepth.length - 1]; if (!(siteMapNode && routeInfo)) return ; - if (R.isString(routeInfo.page)) return ; - if (R.isString(routeInfo.external_link)) window.location.replace(routeInfo.external_link); + if (isString(routeInfo.page)) return ; + if (isString(routeInfo.external_link)) window.location.replace(routeInfo.external_link); return ; }) ); -export const PageIdParamRenderer: React.FC = Suspense.with({ fallback: }, () => { +export const PageIdParamRenderer: FC = Suspense.with({ fallback: }, () => { const { id } = useParams(); if (!id) throwPageNotFound("Page ID is required"); return ; diff --git a/apps/pyconkr-2026/src/components/pages/presentation_detail.tsx b/apps/pyconkr-2026/src/components/pages/presentation_detail.tsx index eea07a4..60bb7d5 100644 --- a/apps/pyconkr-2026/src/components/pages/presentation_detail.tsx +++ b/apps/pyconkr-2026/src/components/pages/presentation_detail.tsx @@ -1,16 +1,16 @@ -import { Components } from "@frontend/common"; -import { useBackendClient, useSessionQuery } from "@frontend/common/src/hooks/useAPI"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +import PyCon2025Logo from "@frontend/common/assets/pyconkr2025_logo.png"; +import { CenteredPage, ErrorFallback, FallbackImage, LinkHandler, MDXRenderer } from "@frontend/common/components"; +import { useBackendClient, useSessionQuery } from "@frontend/common/hooks/useAPI"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { Box, Chip, CircularProgress, Divider, Stack, styled, Table, TableBody, TableCell, TableRow, Typography } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { DateTime } from "luxon"; -import * as React from "react"; +import { CSSProperties, FC, useEffect } from "react"; import { Navigate, useParams } from "react-router-dom"; -import * as R from "remeda"; +import { isString } from "remeda"; -import PyCon2025Logo from "../../../../../packages/common/src/assets/pyconkr2025_logo.png"; -import { useAppContext } from "../../contexts/app_context"; -import { PageLayout } from "../layout/PageLayout"; +import { PageLayout } from "@apps/pyconkr-2026/components/layout/PageLayout"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; const PROFILE_IMAGE_SIZE = "7rem"; @@ -21,13 +21,13 @@ type SimplifiedSpeakerSchema = { biography: string; }; -const CenteredLoadingPage: React.FC = () => ( - +const CenteredLoadingPage: FC = () => ( + - + ); -const StyledPresentationImage = styled(Components.FallbackImage)(({ theme }) => ({ +const StyledPresentationImage = styled(FallbackImage)(({ theme }) => ({ maxWidth: "75%", maxHeight: "480px", aspectRatio: "1", @@ -86,21 +86,21 @@ const ProfileImageContainer = styled(Stack)({ border: `1px solid rgba(0, 0, 0, 0.12)`, }); -const ProfileImageStyle: React.CSSProperties = { +const ProfileImageStyle: CSSProperties = { width: "100%", height: "100%", objectFit: "cover", }; -const ProfileImage = styled(Components.FallbackImage)(ProfileImageStyle); +const ProfileImage = styled(FallbackImage)(ProfileImageStyle); -const ProfileImageErrorFallback: React.FC = () => ( +const ProfileImageErrorFallback: FC = () => ( PyCon 2025 Logo ); -const PresentationSpeakerItem: React.FC<{ speaker: SimplifiedSpeakerSchema }> = ({ speaker }) => { +const PresentationSpeakerItem: FC<{ speaker: SimplifiedSpeakerSchema }> = ({ speaker }) => { const { baseUrl, mdxComponents } = useCommonContext(); return ( <> @@ -111,9 +111,7 @@ const PresentationSpeakerItem: React.FC<{ speaker: SimplifiedSpeakerSchema }> = {speaker.biography ? ( - } - /> + } /> ) : ( <>
@@ -127,7 +125,7 @@ const PresentationSpeakerItem: React.FC<{ speaker: SimplifiedSpeakerSchema }> = ); }; -const PresentationImageFallback: React.FC<{ language: "ko" | "en" }> = ({ language }) => { +const PresentationImageFallback: FC<{ language: "ko" | "en" }> = ({ language }) => { const message = language === "ko" ? ( <> @@ -146,8 +144,8 @@ const PresentationImageFallback: React.FC<{ language: "ko" | "en" }> = ({ langua return ; }; -export const PresentationDetailPage: React.FC = ErrorBoundary.with( - { fallback: Components.ErrorFallback }, +export const PresentationDetailPage: FC = ErrorBoundary.with( + { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams(); const { language, setAppContext } = useAppContext(); @@ -188,7 +186,7 @@ export const PresentationDetailPage: React.FC = ErrorBoundary.with( {} as Record ); - React.useEffect(() => { + useEffect(() => { setAppContext((prev) => ({ ...prev, title: language === "ko" ? "발표 상세" : "Presentation Detail", @@ -246,11 +244,11 @@ export const PresentationDetailPage: React.FC = ErrorBoundary.with( ) : null} - {R.isString(presentation.public_slideshow_file) ? ( + {isString(presentation.public_slideshow_file) ? ( } /> ({ color: theme.palette.primary.main, textDecoration: "underline" })}> - + ) : null} @@ -264,12 +262,7 @@ export const PresentationDetailPage: React.FC = ErrorBoundary.with( /> )} - + {presentation.speakers && ( diff --git a/apps/pyconkr-2026/src/components/pages/sign_in.tsx b/apps/pyconkr-2026/src/components/pages/sign_in.tsx index 905b2ad..b9712d3 100644 --- a/apps/pyconkr-2026/src/components/pages/sign_in.tsx +++ b/apps/pyconkr-2026/src/components/pages/sign_in.tsx @@ -1,29 +1,29 @@ -import * as Shop from "@frontend/shop"; +import { useShopClient, useSignInWithSNSMutation, useUserStatus } from "@frontend/shop/hooks"; import { AccountCircleOutlined, Google } from "@mui/icons-material"; import { Backdrop, Button, ButtonProps, CircularProgress, Stack, Typography } from "@mui/material"; import { Suspense } from "@suspensive/react"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; +import { FC, ReactNode, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { useAppContext } from "../../contexts/app_context"; -import { PageLayout } from "../layout/PageLayout"; +import { PageLayout } from "@apps/pyconkr-2026/components/layout/PageLayout"; +import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context"; type PageeStateType = { openBackdrop: boolean; }; -export const ShopSignInPage: React.FC = Suspense.with({ fallback: }, () => { +export const ShopSignInPage: FC = Suspense.with({ fallback: }, () => { const { setAppContext, language } = useAppContext(); - const [state, setState] = React.useState({ openBackdrop: false }); + const [state, setState] = useState({ openBackdrop: false }); const navigate = useNavigate(); - const shopAPIClient = Shop.Hooks.useShopClient(); - const SignInMutation = Shop.Hooks.useSignInWithSNSMutation(shopAPIClient); - const { data } = Shop.Hooks.useUserStatus(shopAPIClient); + const shopAPIClient = useShopClient(); + const SignInMutation = useSignInWithSNSMutation(shopAPIClient); + const { data } = useUserStatus(shopAPIClient); const shouldOpenBackdrop = SignInMutation.isPending || state.openBackdrop; - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const triggerSignIn = (provider: "google" | "kakao" | "naver") => { @@ -39,7 +39,7 @@ export const ShopSignInPage: React.FC = Suspense.with({ fallback: { + useEffect(() => { if (data && data.meta.is_authenticated) { addSnackbar( language === "ko" ? `이미 ${data.data.user.username}님으로 로그인되어 있습니다!` : `You are already signed in as ${data.data.user.username}!`, @@ -55,7 +55,7 @@ export const ShopSignInPage: React.FC = Suspense.with({ fallback: <>404 Not Found; -const CenteredLoadingPage: React.FC = () => ( - +const PageNotFound: FC = () => <>404 Not Found; +const CenteredLoadingPage: FC = () => ( + - + ); const LogoImage = styled("img")(({ theme }) => ({ @@ -50,8 +50,8 @@ const DescriptionBox = styled(Box)(({ theme }) => ({ }, })); -export const SponsorDetailPage: React.FC = ErrorBoundary.with( - { fallback: Components.ErrorFallback }, +export const SponsorDetailPage: FC = ErrorBoundary.with( + { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams(); const { language, sponsorTiers, setAppContext } = useAppContext(); @@ -62,12 +62,12 @@ export const SponsorDetailPage: React.FC = ErrorBoundary.with( const title = language === "ko" ? "후원사" : "Sponsor"; const descriptionFallback = language === "ko" ? "해당 후원사의 설명은 준비 중이에요!" : "This sponsor's description is under preparation!"; - React.useEffect(() => { + useEffect(() => { setAppContext((prev) => ({ ...prev, title: `${title} - ${sponsor?.name || "Detail"}`, shouldShowTitleBanner: true, - shouldShowSponsorBanner: !R.isNonNullish(sponsor), + shouldShowSponsorBanner: !isNonNullish(sponsor), })); }, [sponsor, title, setAppContext]); @@ -90,7 +90,7 @@ export const SponsorDetailPage: React.FC = ErrorBoundary.with( - + ); diff --git a/apps/pyconkr-2026/src/consts/mdx_components.ts b/apps/pyconkr-2026/src/consts/mdx_components.ts index 974feee..82b447b 100644 --- a/apps/pyconkr-2026/src/consts/mdx_components.ts +++ b/apps/pyconkr-2026/src/consts/mdx_components.ts @@ -1,143 +1,279 @@ // 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. -import { Components, Schemas } from "@frontend/common"; -import * as Shop from "@frontend/shop"; -import * as mui from "@mui/material"; +import PyCon2025HostLogoBig from "@frontend/common/assets/pyconkr2025_hostlogo_big.png"; +import PyCon2025HostLogoSmall from "@frontend/common/assets/pyconkr2025_hostlogo_small.png"; +import PyCon2025Logo from "@frontend/common/assets/pyconkr2025_logo.png"; +import PyCon2025MobileLogoImage from "@frontend/common/assets/pyconkr2025_main_cover_image.png"; +import PyCon2025MobileLogoTitle from "@frontend/common/assets/pyconkr2025_main_cover_title.png"; +import { LottiePlayer, NetworkLottiePlayer } from "@frontend/common/components"; +import { + Confetti, + FAQAccordion, + Map as MDXMap, + MobileAccordion, + MobileCover, + PrimaryStyledDetails, + SecondaryStyledDetails, + SessionList, + SessionTimeTable, + StyledFullWidthButton, +} from "@frontend/common/components/mdx_components"; +import type { SessionSchema } from "@frontend/common/schemas/backendAPI"; +import { PriceDisplay, ShopContextProvider, SignInGuard, UserSignInAccount, UserSignInMethod } from "@frontend/shop/components/common"; +import { CartStatus, OrderList, PatronList, ProductImageCardList, ProductList, UserInfo } from "@frontend/shop/components/features"; +import { + Accordion, + AccordionActions, + AccordionDetails, + AccordionSummary, + Alert, + AlertTitle, + AppBar, + Autocomplete, + Avatar, + AvatarGroup, + Backdrop, + Badge, + BottomNavigation, + BottomNavigationAction, + Box, + Breadcrumbs, + Button, + ButtonBase, + ButtonGroup, + Card, + CardActionArea, + CardActions, + CardContent, + CardHeader, + CardMedia, + Checkbox, + Chip, + CircularProgress, + Collapse, + Container, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Divider, + Drawer, + Fab, + Fade, + FilledInput, + FormControl, + FormControlLabel, + FormGroup, + FormHelperText, + FormLabel, + Grid, + Grow, + Icon, + IconButton, + ImageList, + ImageListItem, + ImageListItemBar, + Input, + InputAdornment, + InputBase, + InputLabel, + LinearProgress, + Link, + List, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemIcon, + ListItemSecondaryAction, + ListItemText, + ListSubheader, + Menu, + MenuItem, + MenuList, + MobileStepper, + Modal, + NativeSelect, + NoSsr, + OutlinedInput, + Pagination, + PaginationItem, + Paper, + Popover, + Popper, + Portal, + Radio, + RadioGroup, + Rating, + Select, + Skeleton, + Slide, + Slider, + Snackbar, + SnackbarContent, + SpeedDial, + SpeedDialAction, + SpeedDialIcon, + Stack, + Step, + StepButton, + StepConnector, + StepContent, + StepIcon, + StepLabel, + Stepper, + SvgIcon, + SwipeableDrawer, + Switch, + Tab, + TabScrollButton, + Table, + TableBody, + TableCell, + TableContainer, + TableFooter, + TableHead, + TablePagination, + TableRow, + TableSortLabel, + Tabs, + TextField, + TextareaAutosize, + ToggleButton, + ToggleButtonGroup, + Toolbar, + Tooltip, + Typography, + Zoom, +} from "@mui/material"; import type { MDXComponents } from "mdx/types.js"; -import * as React from "react"; - -import PyCon2025HostLogoBig from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_big.png"; -import PyCon2025HostLogoSmall from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_small.png"; -import PyCon2025Logo from "../../../../packages/common/src/assets/pyconkr2025_logo.png"; -import PyCon2025MobileLogoImage from "../../../../packages/common/src/assets/pyconkr2025_main_cover_image.png"; -import PyCon2025MobileLogoTitle from "../../../../packages/common/src/assets/pyconkr2025_main_cover_title.png"; - +import { ComponentProps, FC, createElement } from "react"; const MUIMDXComponents: MDXComponents = { - Mui__material__Accordion: mui.Accordion, - Mui__material__AccordionActions: mui.AccordionActions, - Mui__material__AccordionDetails: mui.AccordionDetails, - Mui__material__AccordionSummary: mui.AccordionSummary, - Mui__material__Alert: mui.Alert, - Mui__material__AlertTitle: mui.AlertTitle, - Mui__material__AppBar: mui.AppBar, - Mui__material__Autocomplete: mui.Autocomplete, - Mui__material__Avatar: mui.Avatar, - Mui__material__AvatarGroup: mui.AvatarGroup, - Mui__material__Backdrop: mui.Backdrop, - Mui__material__Badge: mui.Badge, - Mui__material__BottomNavigation: mui.BottomNavigation, - Mui__material__BottomNavigationAction: mui.BottomNavigationAction, - Mui__material__Box: mui.Box, - Mui__material__Breadcrumbs: mui.Breadcrumbs, - Mui__material__Button: mui.Button, - Mui__material__ButtonBase: mui.ButtonBase, - Mui__material__ButtonGroup: mui.ButtonGroup, - Mui__material__Card: mui.Card, - Mui__material__CardActionArea: mui.CardActionArea, - Mui__material__CardActions: mui.CardActions, - Mui__material__CardContent: mui.CardContent, - Mui__material__CardHeader: mui.CardHeader, - Mui__material__CardMedia: mui.CardMedia, - Mui__material__Checkbox: mui.Checkbox, - Mui__material__Chip: mui.Chip, - Mui__material__CircularProgress: mui.CircularProgress, - Mui__material__Collapse: mui.Collapse, - Mui__material__Container: mui.Container, - Mui__material__Dialog: mui.Dialog, - Mui__material__DialogActions: mui.DialogActions, - Mui__material__DialogContent: mui.DialogContent, - Mui__material__DialogContentText: mui.DialogContentText, - Mui__material__DialogTitle: mui.DialogTitle, - Mui__material__Divider: mui.Divider, - Mui__material__Drawer: mui.Drawer, - Mui__material__Fab: mui.Fab, - Mui__material__Fade: mui.Fade, - Mui__material__FilledInput: mui.FilledInput, - Mui__material__FormControl: mui.FormControl, - Mui__material__FormControlLabel: mui.FormControlLabel, - Mui__material__FormGroup: mui.FormGroup, - Mui__material__FormHelperText: mui.FormHelperText, - Mui__material__FormLabel: mui.FormLabel, - Mui__material__Grid: mui.Grid, - Mui__material__Grow: mui.Grow, - Mui__material__Icon: mui.Icon, - Mui__material__IconButton: mui.IconButton, - Mui__material__ImageList: mui.ImageList, - Mui__material__ImageListItem: mui.ImageListItem, - Mui__material__ImageListItemBar: mui.ImageListItemBar, - Mui__material__Input: mui.Input, - Mui__material__InputAdornment: mui.InputAdornment, - Mui__material__InputBase: mui.InputBase, - Mui__material__InputLabel: mui.InputLabel, - Mui__material__LinearProgress: mui.LinearProgress, - Mui__material__Link: mui.Link, - Mui__material__List: mui.List, - Mui__material__ListItem: mui.ListItem, - Mui__material__ListItemAvatar: mui.ListItemAvatar, - Mui__material__ListItemButton: mui.ListItemButton, - Mui__material__ListItemIcon: mui.ListItemIcon, - Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, - Mui__material__ListItemText: mui.ListItemText, - Mui__material__ListSubheader: mui.ListSubheader, - Mui__material__Menu: mui.Menu, - Mui__material__MenuItem: mui.MenuItem, - Mui__material__MenuList: mui.MenuList, - Mui__material__MobileStepper: mui.MobileStepper, - Mui__material__Modal: mui.Modal, - Mui__material__NativeSelect: mui.NativeSelect, - Mui__material__NoSsr: mui.NoSsr, - Mui__material__OutlinedInput: mui.OutlinedInput, - Mui__material__Pagination: mui.Pagination, - Mui__material__PaginationItem: mui.PaginationItem, - Mui__material__Paper: mui.Paper, - Mui__material__Popover: mui.Popover, - Mui__material__Popper: mui.Popper, - Mui__material__Portal: mui.Portal, - Mui__material__Radio: mui.Radio, - Mui__material__RadioGroup: mui.RadioGroup, - Mui__material__Rating: mui.Rating, - Mui__material__Select: mui.Select, - Mui__material__Skeleton: mui.Skeleton, - Mui__material__Slide: mui.Slide, - Mui__material__Slider: mui.Slider, - Mui__material__Snackbar: mui.Snackbar, - Mui__material__SnackbarContent: mui.SnackbarContent, - Mui__material__SpeedDial: mui.SpeedDial, - Mui__material__SpeedDialAction: mui.SpeedDialAction, - Mui__material__SpeedDialIcon: mui.SpeedDialIcon, - Mui__material__Stack: mui.Stack, - Mui__material__Step: mui.Step, - Mui__material__StepButton: mui.StepButton, - Mui__material__StepConnector: mui.StepConnector, - Mui__material__StepContent: mui.StepContent, - Mui__material__StepIcon: mui.StepIcon, - Mui__material__StepLabel: mui.StepLabel, - Mui__material__Stepper: mui.Stepper, - Mui__material__SvgIcon: mui.SvgIcon, - Mui__material__SwipeableDrawer: mui.SwipeableDrawer, - Mui__material__Switch: mui.Switch, - Mui__material__Tab: mui.Tab, - Mui__material__Table: mui.Table, - Mui__material__TableBody: mui.TableBody, - Mui__material__TableCell: mui.TableCell, - Mui__material__TableContainer: mui.TableContainer, - Mui__material__TableFooter: mui.TableFooter, - Mui__material__TableHead: mui.TableHead, - Mui__material__TablePagination: mui.TablePagination, - Mui__material__TableRow: mui.TableRow, - Mui__material__TableSortLabel: mui.TableSortLabel, - Mui__material__Tabs: mui.Tabs, - Mui__material__TabScrollButton: mui.TabScrollButton, - Mui__material__TextField: mui.TextField, - Mui__material__TextareaAutosize: mui.TextareaAutosize, - Mui__material__ToggleButton: mui.ToggleButton, - Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, - Mui__material__Toolbar: mui.Toolbar, - Mui__material__Tooltip: mui.Tooltip, - Mui__material__Typography: mui.Typography, - Mui__material__Zoom: mui.Zoom, + Mui__material__Accordion: Accordion, + Mui__material__AccordionActions: AccordionActions, + Mui__material__AccordionDetails: AccordionDetails, + Mui__material__AccordionSummary: AccordionSummary, + Mui__material__Alert: Alert, + Mui__material__AlertTitle: AlertTitle, + Mui__material__AppBar: AppBar, + Mui__material__Autocomplete: Autocomplete, + Mui__material__Avatar: Avatar, + Mui__material__AvatarGroup: AvatarGroup, + Mui__material__Backdrop: Backdrop, + Mui__material__Badge: Badge, + Mui__material__BottomNavigation: BottomNavigation, + Mui__material__BottomNavigationAction: BottomNavigationAction, + Mui__material__Box: Box, + Mui__material__Breadcrumbs: Breadcrumbs, + Mui__material__Button: Button, + Mui__material__ButtonBase: ButtonBase, + Mui__material__ButtonGroup: ButtonGroup, + Mui__material__Card: Card, + Mui__material__CardActionArea: CardActionArea, + Mui__material__CardActions: CardActions, + Mui__material__CardContent: CardContent, + Mui__material__CardHeader: CardHeader, + Mui__material__CardMedia: CardMedia, + Mui__material__Checkbox: Checkbox, + Mui__material__Chip: Chip, + Mui__material__CircularProgress: CircularProgress, + Mui__material__Collapse: Collapse, + Mui__material__Container: Container, + Mui__material__Dialog: Dialog, + Mui__material__DialogActions: DialogActions, + Mui__material__DialogContent: DialogContent, + Mui__material__DialogContentText: DialogContentText, + Mui__material__DialogTitle: DialogTitle, + Mui__material__Divider: Divider, + Mui__material__Drawer: Drawer, + Mui__material__Fab: Fab, + Mui__material__Fade: Fade, + Mui__material__FilledInput: FilledInput, + Mui__material__FormControl: FormControl, + Mui__material__FormControlLabel: FormControlLabel, + Mui__material__FormGroup: FormGroup, + Mui__material__FormHelperText: FormHelperText, + Mui__material__FormLabel: FormLabel, + Mui__material__Grid: Grid, + Mui__material__Grow: Grow, + Mui__material__Icon: Icon, + Mui__material__IconButton: IconButton, + Mui__material__ImageList: ImageList, + Mui__material__ImageListItem: ImageListItem, + Mui__material__ImageListItemBar: ImageListItemBar, + Mui__material__Input: Input, + Mui__material__InputAdornment: InputAdornment, + Mui__material__InputBase: InputBase, + Mui__material__InputLabel: InputLabel, + Mui__material__LinearProgress: LinearProgress, + Mui__material__Link: Link, + Mui__material__List: List, + Mui__material__ListItem: ListItem, + Mui__material__ListItemAvatar: ListItemAvatar, + Mui__material__ListItemButton: ListItemButton, + Mui__material__ListItemIcon: ListItemIcon, + Mui__material__ListItemSecondaryAction: ListItemSecondaryAction, + Mui__material__ListItemText: ListItemText, + Mui__material__ListSubheader: ListSubheader, + Mui__material__Menu: Menu, + Mui__material__MenuItem: MenuItem, + Mui__material__MenuList: MenuList, + Mui__material__MobileStepper: MobileStepper, + Mui__material__Modal: Modal, + Mui__material__NativeSelect: NativeSelect, + Mui__material__NoSsr: NoSsr, + Mui__material__OutlinedInput: OutlinedInput, + Mui__material__Pagination: Pagination, + Mui__material__PaginationItem: PaginationItem, + Mui__material__Paper: Paper, + Mui__material__Popover: Popover, + Mui__material__Popper: Popper, + Mui__material__Portal: Portal, + Mui__material__Radio: Radio, + Mui__material__RadioGroup: RadioGroup, + Mui__material__Rating: Rating, + Mui__material__Select: Select, + Mui__material__Skeleton: Skeleton, + Mui__material__Slide: Slide, + Mui__material__Slider: Slider, + Mui__material__Snackbar: Snackbar, + Mui__material__SnackbarContent: SnackbarContent, + Mui__material__SpeedDial: SpeedDial, + Mui__material__SpeedDialAction: SpeedDialAction, + Mui__material__SpeedDialIcon: SpeedDialIcon, + Mui__material__Stack: Stack, + Mui__material__Step: Step, + Mui__material__StepButton: StepButton, + Mui__material__StepConnector: StepConnector, + Mui__material__StepContent: StepContent, + Mui__material__StepIcon: StepIcon, + Mui__material__StepLabel: StepLabel, + Mui__material__Stepper: Stepper, + Mui__material__SvgIcon: SvgIcon, + Mui__material__SwipeableDrawer: SwipeableDrawer, + Mui__material__Switch: Switch, + Mui__material__Tab: Tab, + Mui__material__Table: Table, + Mui__material__TableBody: TableBody, + Mui__material__TableCell: TableCell, + Mui__material__TableContainer: TableContainer, + Mui__material__TableFooter: TableFooter, + Mui__material__TableHead: TableHead, + Mui__material__TablePagination: TablePagination, + Mui__material__TableRow: TableRow, + Mui__material__TableSortLabel: TableSortLabel, + Mui__material__Tabs: Tabs, + Mui__material__TabScrollButton: TabScrollButton, + Mui__material__TextField: TextField, + Mui__material__TextareaAutosize: TextareaAutosize, + Mui__material__ToggleButton: ToggleButton, + Mui__material__ToggleButtonGroup: ToggleButtonGroup, + Mui__material__Toolbar: Toolbar, + Mui__material__Tooltip: Tooltip, + Mui__material__Typography: Typography, + Mui__material__Zoom: Zoom, }; -const getPyConKR2025SessionUrl = (session: Schemas.BackendAPI.SessionSchema): string => { +const getPyConKR2025SessionUrl = (session: SessionSchema): string => { const urlSafeTitle = session.title .replace(/ /g, "-") .replace(/([.])/g, "_") @@ -145,27 +281,27 @@ const getPyConKR2025SessionUrl = (session: Schemas.BackendAPI.SessionSchema): st return `/presentations/${session.id}#${urlSafeTitle}`; }; -const PyConKR2025FallbackImage = React.createElement("img", { +const PyConKR2025FallbackImage = createElement("img", { src: PyCon2025Logo, alt: "PyCon 2025 Logo", style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }, }); -const PyConKR2025SessionList: React.FC> = (props) => - React.createElement(Components.MDX.SessionList, { +const PyConKR2025SessionList: FC> = (props) => + createElement(SessionList, { ...props, fallbackImage: PyConKR2025FallbackImage, getSessionUrl: getPyConKR2025SessionUrl, }); -const PyConKR2025SessionTimeTable: React.FC> = (props) => - React.createElement(Components.MDX.SessionTimeTable, { +const PyConKR2025SessionTimeTable: FC> = (props) => + createElement(SessionTimeTable, { ...props, getSessionUrl: getPyConKR2025SessionUrl, }); -const PyConKR2025MobileAccordion: React.FC = () => - React.createElement(Components.MDX.MobileAccordion, { +const PyConKR2025MobileAccordion: FC = () => + createElement(MobileAccordion, { marqueeText: "AUG 15 - 17", marqueeLogoSrc: PyCon2025HostLogoSmall, hostLogoBigSrc: PyCon2025HostLogoBig, @@ -173,21 +309,21 @@ const PyConKR2025MobileAccordion: React.FC = () => venueEnLines: ["New Engineering Building, Dongguk University", "Pildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea"], }); -const PyConKR2025MobileCover: React.FC = () => - React.createElement(Components.MDX.MobileCover, { +const PyConKR2025MobileCover: FC = () => + createElement(MobileCover, { coverImageSrc: PyCon2025MobileLogoImage, coverTitleSrc: PyCon2025MobileLogoTitle, }); const PyConKRCommonMDXComponents: MDXComponents = { - Common__Components__Lottie: Components.LottiePlayer, - Common__Components__NetworkLottie: Components.NetworkLottiePlayer, - Common__Components__MDX__Confetti: Components.MDX.Confetti, - Common__Components__MDX__PrimaryStyledDetails: Components.MDX.PrimaryStyledDetails, - Common__Components__MDX__SecondaryStyledDetails: Components.MDX.SecondaryStyledDetails, - Common__Components__MDX__Map: Components.MDX.Map, - Common__Components__MDX__FAQAccordion: Components.MDX.FAQAccordion, - Common__Components__MDX__FullWidthStyledButton: Components.MDX.StyledFullWidthButton, + Common__Components__Lottie: LottiePlayer, + Common__Components__NetworkLottie: NetworkLottiePlayer, + Common__Components__MDX__Confetti: Confetti, + Common__Components__MDX__PrimaryStyledDetails: PrimaryStyledDetails, + Common__Components__MDX__SecondaryStyledDetails: SecondaryStyledDetails, + Common__Components__MDX__Map: MDXMap, + Common__Components__MDX__FAQAccordion: FAQAccordion, + Common__Components__MDX__FullWidthStyledButton: StyledFullWidthButton, Common__Components__Session__List: PyConKR2025SessionList, Common__Components__Session__TimeTable: PyConKR2025SessionTimeTable, Common__Components__MDX__MobileAccordion: PyConKR2025MobileAccordion, @@ -195,17 +331,17 @@ const PyConKRCommonMDXComponents: MDXComponents = { }; const PythonKRShopMDXComponents: MDXComponents = { - Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, - Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, - Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, - Shop__Common__UserSignInMethod: Shop.Components.Common.UserSignInMethod, - Shop__Common__UserSignInAccount: Shop.Components.Common.UserSignInAccount, - Shop__Feature__CartStatus: Shop.Components.Features.CartStatus, - Shop__Feature__ProductList: Shop.Components.Features.ProductList, - Shop__Feature__ProductImageCardList: Shop.Components.Features.ProductImageCardList, - Shop__Feature__OrderList: Shop.Components.Features.OrderList, - Shop__Feature__UserInfo: Shop.Components.Features.UserInfo, - Shop__Feature__PatronList: Shop.Components.Features.PatronList, + Shop__Common__PriceDisplay: PriceDisplay, + Shop__Common__SignInGuard: SignInGuard, + Shop__Common__ContextProvider: ShopContextProvider, + Shop__Common__UserSignInMethod: UserSignInMethod, + Shop__Common__UserSignInAccount: UserSignInAccount, + Shop__Feature__CartStatus: CartStatus, + Shop__Feature__ProductList: ProductList, + Shop__Feature__ProductImageCardList: ProductImageCardList, + Shop__Feature__OrderList: OrderList, + Shop__Feature__UserInfo: UserInfo, + Shop__Feature__PatronList: PatronList, }; export const PyConKRMDXComponents = { diff --git a/apps/pyconkr-2026/src/contexts/app_context.tsx b/apps/pyconkr-2026/src/contexts/app_context.tsx index 3c56bb8..3a20055 100644 --- a/apps/pyconkr-2026/src/contexts/app_context.tsx +++ b/apps/pyconkr-2026/src/contexts/app_context.tsx @@ -1,7 +1,5 @@ -import * as React from "react"; - -import { NestedSiteMapSchema, SponsorTierSchema } from "../../../../packages/common/src/schemas/backendAPI"; - +import { NestedSiteMapSchema, SponsorTierSchema } from "@frontend/common/schemas/backendAPI"; +import { Dispatch, SetStateAction, createContext, useContext } from "react"; type LanguageType = "ko" | "en"; export type AppContextType = { @@ -14,13 +12,13 @@ export type AppContextType = { title: string; currentSiteMapDepth: (NestedSiteMapSchema | undefined)[]; - setAppContext: React.Dispatch>>; + setAppContext: Dispatch>>; }; -export const AppContext = React.createContext(undefined); +export const AppContext = createContext(undefined); export const useAppContext = (): AppContextType => { - const context = React.useContext(AppContext); + const context = useContext(AppContext); if (!context) { throw new Error("useAppContext must be used within an AppContextProvider"); } diff --git a/apps/pyconkr-2026/src/main.tsx b/apps/pyconkr-2026/src/main.tsx index cf24b1f..0703388 100644 --- a/apps/pyconkr-2026/src/main.tsx +++ b/apps/pyconkr-2026/src/main.tsx @@ -1,14 +1,16 @@ import { Global } from "@emotion/react"; -import { Components, Utils } from "@frontend/common"; -import type { ContextOptions } from "@frontend/common/src/contexts"; -import * as Shop from "@frontend/shop"; +import { CenteredPage, CommonContextProvider, ErrorFallback } from "@frontend/common/components"; +import type { ContextOptions } from "@frontend/common/contexts"; +import { registerChunkLoadErrorReloadHandler } from "@frontend/common/utils"; +import { ShopContextProvider } from "@frontend/shop/components/common"; +import { ContextOptions as ShopContextOptions } from "@frontend/shop/contexts"; import { CircularProgress, CssBaseline, ThemeProvider } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { matchQuery, MutationCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { SnackbarProvider } from "notistack"; -import * as React from "react"; -import * as ReactDom from "react-dom/client"; +import { FC, StrictMode, useState } from "react"; +import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { App } from "./App.tsx"; @@ -55,7 +57,7 @@ const CommonOptions: ContextOptions = { mdxComponents: PyConKRMDXComponents, }; -const ShopOptions: Shop.Contexts.ContextOptions = { +const ShopOptions: ShopContextOptions = { language: "ko", shopApiDomain: import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN, shopApiCSRFCookieName: import.meta.env.VITE_PYCONKR_SHOP_CSRF_COOKIE_NAME, @@ -64,13 +66,13 @@ const ShopOptions: Shop.Contexts.ContextOptions = { }; const SuspenseFallback = ( - + - + ); -const MainApp: React.FC = () => { - const [appState, setAppContext] = React.useState>({ +export const MainApp: FC = () => { + const [appState, setAppContext] = useState>({ language: (localStorage.getItem(LOCAL_STORAGE_LANGUAGE_KEY) as "ko" | "en" | null) ?? "ko", shouldShowTitleBanner: true, shouldShowSponsorBanner: false, @@ -81,15 +83,15 @@ const MainApp: React.FC = () => { }); return ( - + - - - + + + @@ -98,16 +100,16 @@ const MainApp: React.FC = () => { - - + + - + ); }; -Utils.registerChunkLoadErrorReloadHandler(); +registerChunkLoadErrorReloadHandler(); -ReactDom.createRoot(document.getElementById("root")!).render(); +createRoot(document.getElementById("root")!).render(); diff --git a/apps/pyconkr-2026/tsconfig.json b/apps/pyconkr-2026/tsconfig.json index e9694fd..ff99f6e 100644 --- a/apps/pyconkr-2026/tsconfig.json +++ b/apps/pyconkr-2026/tsconfig.json @@ -1,32 +1,4 @@ { - "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true, - "forceConsistentCasingInFileNames": false, - - /* Paths */ - "baseUrl": ".", - "paths": { - "@apps/pyconkr-2026/*": ["apps/pyconkr-2026/src/*"] - } - }, + "extends": "../../tsconfig.base.json", "include": ["src", "vite.config.mts", "vite-env.d.ts", "../../types"] } diff --git a/apps/pyconkr-2026/vite.config.mts b/apps/pyconkr-2026/vite.config.mts index 1dbfc41..1f9cfbd 100644 --- a/apps/pyconkr-2026/vite.config.mts +++ b/apps/pyconkr-2026/vite.config.mts @@ -20,9 +20,8 @@ export default defineConfig(({ mode }) => { plugins: [react(), mdx(), ...(isLocalHttpBackend ? [] : [mkcert({ hosts: [host] })]), svgr()], resolve: { alias: { - "@frontend/common/src": path.resolve(__dirname, "../../packages/common/src"), - "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), - "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/common": path.resolve(__dirname, "../../packages/common/src"), + "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src"), "@apps/pyconkr-2026": path.resolve(__dirname, "./src"), }, }, diff --git a/apps/pyconkr-admin/src/components/elements/admin_filter_fieldset.tsx b/apps/pyconkr-admin/src/components/elements/admin_filter_fieldset.tsx index 9477fc3..f9bf07f 100644 --- a/apps/pyconkr-admin/src/components/elements/admin_filter_fieldset.tsx +++ b/apps/pyconkr-admin/src/components/elements/admin_filter_fieldset.tsx @@ -1,6 +1,5 @@ import { Stack, styled } from "@mui/material"; -import * as React from "react"; - +import { FC, ReactNode } from "react"; const StyledFieldset = styled("fieldset")(({ theme }) => ({ margin: 0, padding: theme.spacing(1, 2, 2), @@ -19,10 +18,10 @@ const StyledLegend = styled("legend")(({ theme }) => ({ type Props = { label: string; - children: React.ReactNode; + children: ReactNode; }; -export const AdminFilterFieldset: React.FC = ({ label, children }) => ( +export const AdminFilterFieldset: FC = ({ label, children }) => ( {label} diff --git a/apps/pyconkr-admin/src/components/elements/admin_list_filter.tsx b/apps/pyconkr-admin/src/components/elements/admin_list_filter.tsx index 142a0da..e24ad6c 100644 --- a/apps/pyconkr-admin/src/components/elements/admin_list_filter.tsx +++ b/apps/pyconkr-admin/src/components/elements/admin_list_filter.tsx @@ -1,9 +1,7 @@ +import { ChoicesResponse, OpenAPIParameterSchema } from "@frontend/common/schemas/backendAdminAPI"; import { Add, Clear, FilterList, RestartAlt } from "@mui/icons-material"; import { Box, Button, Chip, FormControl, IconButton, InputLabel, MenuItem, Select, Stack, TextField } from "@mui/material"; -import * as React from "react"; - -import { ChoicesResponse, OpenAPIParameterSchema } from "../../../../../packages/common/src/schemas/backendAdminAPI"; - +import { FC, useEffect, useState } from "react"; type AdminListFilterProps = { parameters: OpenAPIParameterSchema[]; values: Record; @@ -11,10 +9,10 @@ type AdminListFilterProps = { onApply: (values: Record) => void; }; -export const AdminListFilter: React.FC = ({ parameters, values, choices, onApply }) => { - const [localValues, setLocalValues] = React.useState>(values); +export const AdminListFilter: FC = ({ parameters, values, choices, onApply }) => { + const [localValues, setLocalValues] = useState>(values); - React.useEffect(() => { + useEffect(() => { setLocalValues(values); }, [values]); @@ -74,7 +72,7 @@ type FilterFieldProps = { onChange: (name: string, value: string) => void; }; -const FilterField: React.FC = ({ param, value, choices, onChange }) => { +const FilterField: FC = ({ param, value, choices, onChange }) => { const { name, schema, description } = param; if (schema?.type === "array") return ; @@ -121,7 +119,7 @@ type EnumFilterFieldProps = { onChange: (name: string, value: string) => void; }; -const EnumFilterField: React.FC = ({ name, options, value, onChange }) => { +const EnumFilterField: FC = ({ name, options, value, onChange }) => { const selectedValues = value ? value.split(",") : []; const handleChange = (newValues: string | string[]) => { @@ -162,7 +160,7 @@ type ArrayFilterFieldProps = { onChange: (name: string, value: string) => void; }; -const ArrayFilterField: React.FC = ({ name, items, value, onChange }) => { +const ArrayFilterField: FC = ({ name, items, value, onChange }) => { const values = value ? value.split(",") : []; const updateValues = (newValues: string[]) => onChange(name, newValues.filter((v) => v !== "").join(",")); diff --git a/apps/pyconkr-admin/src/components/elements/admin_pagination.tsx b/apps/pyconkr-admin/src/components/elements/admin_pagination.tsx index 4c50d7d..a6140d7 100644 --- a/apps/pyconkr-admin/src/components/elements/admin_pagination.tsx +++ b/apps/pyconkr-admin/src/components/elements/admin_pagination.tsx @@ -1,6 +1,5 @@ import { FormControl, InputLabel, MenuItem, Pagination, Select, Stack, Typography } from "@mui/material"; -import * as React from "react"; - +import { FC } from "react"; type Props = { count: number; page: number; @@ -15,7 +14,7 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [25, 50, 100, 200]; const scrollToTop = () => window.scrollTo({ top: 0, behavior: "smooth" }); -export const AdminPagination: React.FC = ({ +export const AdminPagination: FC = ({ count, page, pageSize, diff --git a/apps/pyconkr-admin/src/components/elements/admin_signin_guard.tsx b/apps/pyconkr-admin/src/components/elements/admin_signin_guard.tsx index 52e73e8..cae6a23 100644 --- a/apps/pyconkr-admin/src/components/elements/admin_signin_guard.tsx +++ b/apps/pyconkr-admin/src/components/elements/admin_signin_guard.tsx @@ -1,12 +1,12 @@ -import { useBackendAdminClient, useSignedInUserQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useSignedInUserQuery } from "@frontend/common/hooks/useAdminAPI"; import { CircularProgress } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, ReactNode } from "react"; import { Navigate } from "react-router-dom"; -import { addSnackbar } from "../../utils/snackbar"; +import { addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; -export const BackendAdminSignInGuard: React.FC<{ children: React.ReactNode }> = ErrorBoundary.with( +export const BackendAdminSignInGuard: FC<{ children: ReactNode }> = ErrorBoundary.with( { fallback: <>로그인 정보를 불러오는 중 문제가 발생했습니다. }, Suspense.with({ fallback: }, ({ children }) => { const backendAdminAPIClient = useBackendAdminClient(); diff --git a/apps/pyconkr-admin/src/components/elements/error_fallback.tsx b/apps/pyconkr-admin/src/components/elements/error_fallback.tsx index 50b6dda..9988f02 100644 --- a/apps/pyconkr-admin/src/components/elements/error_fallback.tsx +++ b/apps/pyconkr-admin/src/components/elements/error_fallback.tsx @@ -1,8 +1,7 @@ -import { Components } from "@frontend/common"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; -import * as React from "react"; - -export const ErrorFallback: React.FC<{ error: Error; reset: () => void }> = ({ error, reset }) => { +import { ErrorFallback as BaseErrorFallback } from "@frontend/common/components"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; +import { FC } from "react"; +export const ErrorFallback: FC<{ error: Error; reset: () => void }> = ({ error, reset }) => { const { debug } = useCommonContext(); - return ; + return ; }; diff --git a/apps/pyconkr-admin/src/components/elements/public_file_picker.tsx b/apps/pyconkr-admin/src/components/elements/public_file_picker.tsx index 626c154..acde042 100644 --- a/apps/pyconkr-admin/src/components/elements/public_file_picker.tsx +++ b/apps/pyconkr-admin/src/components/elements/public_file_picker.tsx @@ -1,8 +1,8 @@ -import { useBackendAdminClient, useChoicesQuery, usePublicFileQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useChoicesQuery, usePublicFileQuery } from "@frontend/common/hooks/useAdminAPI"; import { OpenInNew } from "@mui/icons-material"; import { Autocomplete, Box, Button, CircularProgress, Stack, TextField } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, useMemo } from "react"; import { Link as RouterLink } from "react-router-dom"; type Option = { value: string; label: string }; @@ -35,7 +35,7 @@ const previewBoxSx = { overflow: "hidden", }; -const ImagePreview: React.FC<{ id: string }> = ErrorBoundary.with( +const ImagePreview: FC<{ id: string }> = ErrorBoundary.with( { fallback: () => }, Suspense.with( { @@ -72,18 +72,10 @@ const ImagePreview: React.FC<{ id: string }> = ErrorBoundary.with( ) ); -export const PublicFilePicker: React.FC = ({ - label = "이미지", - value, - onChange, - choicesApp, - choicesResource, - choicesField, - acceptExtensions, -}) => { +export const PublicFilePicker: FC = ({ label = "이미지", value, onChange, choicesApp, choicesResource, choicesField, acceptExtensions }) => { const client = useBackendAdminClient(); const choicesQuery = useChoicesQuery(client, choicesApp, choicesResource); - const options: Option[] = React.useMemo(() => { + const options: Option[] = useMemo(() => { const all = (choicesQuery.data?.[choicesField] ?? []).map((item) => ({ value: item.const ?? "", label: item.title })); if (!acceptExtensions || acceptExtensions.length === 0) return all; const allowed = acceptExtensions.map((e) => e.toLowerCase()); diff --git a/apps/pyconkr-admin/src/components/layouts/admin_editor.tsx b/apps/pyconkr-admin/src/components/layouts/admin_editor.tsx index c7d0b03..d125136 100644 --- a/apps/pyconkr-admin/src/components/layouts/admin_editor.tsx +++ b/apps/pyconkr-admin/src/components/layouts/admin_editor.tsx @@ -1,5 +1,5 @@ -import { Components } from "@frontend/common"; -import { retrieve } from "@frontend/common/src/apis/admin_api"; +import { retrieve } from "@frontend/common/apis/admin_api"; +import { LottieDebugPanel, MDXRenderer, MarkdownEditor } from "@frontend/common/components"; import { useBackendAdminClient, useChoicesQuery, @@ -7,13 +7,13 @@ import { useRemoveMutation, useSchemaQuery, useUpdateMutation, -} from "@frontend/common/src/hooks/useAdminAPI"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +} from "@frontend/common/hooks/useAdminAPI"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { filterPropertiesByLanguageInJsonSchema, filterReadOnlyPropertiesInJsonSchema, filterWritablePropertiesInJsonSchema, -} from "@frontend/common/src/utils"; +} from "@frontend/common/utils"; import { Add, Close, Delete, Edit } from "@mui/icons-material"; import { Box, @@ -45,20 +45,32 @@ import { customizeValidator } from "@rjsf/validator-ajv8"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import AjvDraft04 from "ajv-draft-04"; import { JSONSchema7 } from "json-schema"; -import * as React from "react"; +import { + ChangeEvent, + FC, + FocusEvent, + FormEvent, + MouseEventHandler, + PropsWithChildren, + SyntheticEvent, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { useNavigate, useParams } from "react-router-dom"; -import * as R from "remeda"; +import { addProp, isArray, isNonNullish, isObjectType, isString } from "remeda"; -import { addErrorSnackbar, addSnackbar } from "../../utils/snackbar"; -import { BackendAdminSignInGuard } from "../elements/admin_signin_guard"; -import { ErrorFallback } from "../elements/error_fallback"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type EditorFormDataEventType = IChangeEvent, RJSFSchema, { [k in string]: unknown }>; -type onSubmitType = (data: Record, event: React.FormEvent) => void; +type onSubmitType = (data: Record, event: FormEvent) => void; type AppResourceType = { app: string; resource: string }; type AppResourceIdType = AppResourceType & { id?: string }; -type AdminEditorPropsType = React.PropsWithChildren<{ +type AdminEditorPropsType = PropsWithChildren<{ hidingFields?: string[]; context?: Record; onCreated?: (data: Record) => void; @@ -70,7 +82,7 @@ type AdminEditorPropsType = React.PropsWithChildren<{ extraActions?: ButtonProps[]; }>; -const processFile = (event: React.ChangeEvent) => { +const processFile = (event: ChangeEvent) => { if (!event.target.files || event.target.files.length === 0) return Promise.resolve(""); const f = event.target.files[0]; @@ -94,7 +106,7 @@ const FileField: Field = (p) => ( type DescriptedEnum = { const: string; title: string }; type DescriptedEnumObject = Record; -const SelectdChipRenderer: React.FC<{ selectable: DescriptedEnumObject; selected: string[] }> = ({ selectable, selected }) => { +const SelectdChipRenderer: FC<{ selectable: DescriptedEnumObject; selected: string[] }> = ({ selectable, selected }) => { const children = selected.map((v) => ); return ; }; @@ -139,12 +151,12 @@ const fieldPropsToSelectedProps = (props: FieldProps): OutlinedSelectProps & { d idPrefix, idSeparator, }; - const onFocus = (event: React.FocusEvent) => rawOnFocus(event.currentTarget.name, event.currentTarget.value); - const onBlur = (event: React.FocusEvent) => rawOnBlur(event.currentTarget.name, event.currentTarget.value); - const onChange = (event: React.ChangeEvent) => rawOnChange(event.target.value, undefined, event.target.name); + const onFocus = (event: FocusEvent) => rawOnFocus(event.currentTarget.name, event.currentTarget.value); + const onBlur = (event: FocusEvent) => rawOnBlur(event.currentTarget.name, event.currentTarget.value); + const onChange = (event: ChangeEvent) => rawOnChange(event.target.value, undefined, event.target.name); const sx: OutlinedSelectProps["sx"] = color ? { color, borderColor: color } : {}; - const defaultValue = (formData ? (R.isArray(formData) ? formData : [formData.toString()]) : []) as string[]; - return R.addProp({ ...rest, name, label: name, defaultValue, autoFocus, readOnly, onFocus, onBlur, onChange, sx }, "data-rjsf", data); + const defaultValue = (formData ? (isArray(formData) ? formData : [formData.toString()]) : []) as string[]; + return addProp({ ...rest, name, label: name, defaultValue, autoFocus, readOnly, onFocus, onBlur, onChange, sx }, "data-rjsf", data); }; const M2MSelect: Field = ErrorBoundary.with( @@ -185,7 +197,7 @@ const MDRendererContainer = styled(Box)(({ theme }) => ({ const MDEditorField: Field = ErrorBoundary.with({ fallback: ErrorFallback }, ({ disabled, formData, name, onChange: rawOnChange }) => { const { baseUrl, mdxComponents } = useCommonContext(); - const [valueState, setValueState] = React.useState(formData?.toString() || ""); + const [valueState, setValueState] = useState(formData?.toString() || ""); const onChange = (value?: string) => { setValueState(value); rawOnChange(value, undefined, name); @@ -195,10 +207,10 @@ const MDEditorField: Field = ErrorBoundary.with({ fallback: ErrorFallback }, ({ - + - + @@ -212,21 +224,21 @@ type ReadOnlyValueFieldStateType = { objectUrl: string | null; }; -const ReadOnlyValueField: React.FC<{ +const ReadOnlyValueField: FC<{ name: string; value: unknown; uiSchema: UiSchema; }> = Suspense.with({ fallback: }, ({ name, value, uiSchema }) => { - const [fieldState, setFieldState] = React.useState({ + const [fieldState, setFieldState] = useState({ loading: true, blob: null, blobText: null, objectUrl: null, }); - React.useEffect(() => { + useEffect(() => { (async () => { - if (!(R.isString(value) && value.startsWith("http") && uiSchema?.[name]["ui:field"] === "file")) { + if (!(isString(value) && value.startsWith("http") && uiSchema?.[name]["ui:field"] === "file")) { setFieldState((ps) => ({ ...ps, loading: false })); return; } @@ -248,7 +260,7 @@ const ReadOnlyValueField: React.FC<{ )} {fieldState.blob.type.startsWith("application/json") && fieldState.blobText && ( - + )} 링크 @@ -264,7 +276,7 @@ type InnerAdminEditorStateType = { formData: Record | undefined; }; -const InnerAdminEditor: React.FC = ErrorBoundary.with( +const InnerAdminEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with( { fallback: }, @@ -284,8 +296,8 @@ const InnerAdminEditor: React.FC = Err children, }) => { const navigate = useNavigate(); - const formRef = React.useRef, RJSFSchema, { [k in string]: unknown }> | null>(null); - const [editorState, setEditorState] = React.useState({ + const formRef = useRef, RJSFSchema, { [k in string]: unknown }> | null>(null); + const [editorState, setEditorState] = useState({ tab: 0, formData: undefined, }); @@ -295,7 +307,7 @@ const InnerAdminEditor: React.FC = Err const { data: choicesData } = useChoicesQuery(backendAdminClient, app, resource); // Merge choices into schema for FK/M2M fields - React.useMemo(() => { + useMemo(() => { if (!choicesData || !schemaInfo.schema.properties) return; for (const [fieldName, items] of Object.entries(choicesData)) { const prop = (schemaInfo.schema.properties as Record)[fieldName]; @@ -308,7 +320,7 @@ const InnerAdminEditor: React.FC = Err } }, [choicesData, schemaInfo.schema]); - const setTab = (_: React.SyntheticEvent, tab: number) => setEditorState((ps) => ({ ...ps, tab })); + const setTab = (_: SyntheticEvent, tab: number) => setEditorState((ps) => ({ ...ps, tab })); const setFormData = (formData?: Record) => setEditorState((ps) => ({ ...ps, formData })); const appendFormDataState = (data?: Record) => setEditorState((ps) => ({ ...ps, formData: { ...ps.formData, ...data } })); const selectedLanguage = editorState.tab === 0 ? "ko" : "en"; @@ -319,7 +331,7 @@ const InnerAdminEditor: React.FC = Err const deleteMutation = useRemoveMutation(backendAdminClient, app, resource, id || "undefined"); const submitMutation = id ? modifyMutation : createMutation; - React.useEffect(() => { + useEffect(() => { (async () => { if (!id) { setFormData(context || {}); @@ -332,9 +344,9 @@ const InnerAdminEditor: React.FC = Err // eslint-disable-next-line react-hooks/exhaustive-deps }, [app, resource, id, context]); - const onSubmitButtonClick: React.MouseEventHandler = () => formRef.current && formRef.current.submit(); + const onSubmitButtonClick: MouseEventHandler = () => formRef.current && formRef.current.submit(); - const onSubmitFunc = (data: EditorFormDataEventType, event: React.FormEvent) => { + const onSubmitFunc = (data: EditorFormDataEventType, event: FormEvent) => { // react-jsonschema-form에서 주는 formData에는 translation_fields로 필터링된 필드가 빠져있어, // 사용자가 특정 탭에서 수정한 후 다른 탭으로 이동해서 수정하게 되면 이전 탭의 수정 내용이 사라지는 문제가 발생함. // 따라서, onChange로 항상 값이 추적되는 editorState.formData를 가장 우선적으로 사용함. @@ -368,7 +380,7 @@ const InnerAdminEditor: React.FC = Err const goToCreateNew = () => navigate(`/${app}/${resource}/create`); - if (R.isNonNullish(hidingFields) && R.isObjectType(schemaInfo.schema.properties)) { + if (isNonNullish(hidingFields) && isObjectType(schemaInfo.schema.properties)) { schemaInfo.schema.properties = Object.entries(schemaInfo.schema.properties || {}) .filter(([key]) => !hidingFields.includes(key)) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {} as RJSFSchema); @@ -404,7 +416,7 @@ const InnerAdminEditor: React.FC = Err } }; - React.useEffect(() => { + useEffect(() => { document.addEventListener("keydown", handleCtrlSAction); return () => { console.log("Removing event listener for Ctrl+S action"); @@ -502,18 +514,15 @@ const InnerAdminEditor: React.FC = Err ) ); -export const AdminEditor: React.FC = (props) => ( +export const AdminEditor: FC = (props) => ( ); -export const AdminEditorCreateRoutePage: React.FC = (props) => ; +export const AdminEditorCreateRoutePage: FC = (props) => ; -export const AdminEditorModifyRoutePage: React.FC = Suspense.with( - { fallback: }, - (props) => { - const { id } = useParams<{ id?: string }>(); - return ; - } -); +export const AdminEditorModifyRoutePage: FC = Suspense.with({ fallback: }, (props) => { + const { id } = useParams<{ id?: string }>(); + return ; +}); diff --git a/apps/pyconkr-admin/src/components/layouts/admin_list.tsx b/apps/pyconkr-admin/src/components/layouts/admin_list.tsx index ad8325d..fed1b82 100644 --- a/apps/pyconkr-admin/src/components/layouts/admin_list.tsx +++ b/apps/pyconkr-admin/src/components/layouts/admin_list.tsx @@ -4,18 +4,18 @@ import { useListQuery, useOpenApiSchemaQuery, useRemovePreparedMutation, -} from "@frontend/common/src/hooks/useAdminAPI"; -import { extractQueryParameters } from "@frontend/common/src/utils"; +} from "@frontend/common/hooks/useAdminAPI"; +import { extractQueryParameters } from "@frontend/common/utils"; import { Add, Delete, Edit } from "@mui/icons-material"; import { Box, Button, CircularProgress, IconButton, Stack, Table, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, type ReactNode, useMemo } from "react"; import { Link, useNavigate, useSearchParams } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../utils/snackbar"; -import { AdminListFilter } from "../elements/admin_list_filter"; -import { BackendAdminSignInGuard } from "../elements/admin_signin_guard"; -import { ErrorFallback } from "../elements/error_fallback"; +import { AdminListFilter } from "@apps/pyconkr-admin/components/elements/admin_list_filter"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type ListRowType = { id: string; @@ -29,7 +29,7 @@ export type AdminListColumn = { header: string; width?: string | number; align?: "left" | "right" | "center"; - render?: (row: Record) => React.ReactNode; + render?: (row: Record) => ReactNode; }; type AdminListProps = { @@ -43,7 +43,7 @@ type AdminListProps = { enableRowActions?: boolean; }; -const InnerAdminList: React.FC = ErrorBoundary.with( +const InnerAdminList: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with( { fallback: }, @@ -57,10 +57,7 @@ const InnerAdminList: React.FC = ErrorBoundary.with( const listQuery = useListQuery>(backendAdminClient, app, resource, filterParams); const openApiSchemaQuery = useOpenApiSchemaQuery(backendAdminClient); - const queryParameters = React.useMemo( - () => extractQueryParameters(openApiSchemaQuery.data, app, resource), - [openApiSchemaQuery.data, app, resource] - ); + const queryParameters = useMemo(() => extractQueryParameters(openApiSchemaQuery.data, app, resource), [openApiSchemaQuery.data, app, resource]); const choicesQuery = useChoicesQuery(backendAdminClient, app, resource); @@ -178,7 +175,7 @@ const InnerAdminList: React.FC = ErrorBoundary.with( ) ); -export const AdminList: React.FC = (props) => ( +export const AdminList: FC = (props) => ( diff --git a/apps/pyconkr-admin/src/components/layouts/global.tsx b/apps/pyconkr-admin/src/components/layouts/global.tsx index ff8e52e..6ec6910 100644 --- a/apps/pyconkr-admin/src/components/layouts/global.tsx +++ b/apps/pyconkr-admin/src/components/layouts/global.tsx @@ -18,7 +18,7 @@ import { Tooltip, Typography, } from "@mui/material"; -import * as React from "react"; +import { CSSProperties, FC, useState } from "react"; import { Outlet, useNavigate } from "react-router-dom"; import { MiniVariantAppBar, MiniVariantDrawer } from "./sidebar"; @@ -76,12 +76,12 @@ const PageInnerContainer = styled(Box)(({ theme }) => ({ flexGrow: 1, })); -export const Layout: React.FC<{ routes: RouteDef[] }> = ({ routes }) => { +export const Layout: FC<{ routes: RouteDef[] }> = ({ routes }) => { const navigate = useNavigate(); - const [state, dispatch] = React.useState({ showDrawer: false }); + const [state, dispatch] = useState({ showDrawer: false }); const toggleDrawer = () => dispatch((ps) => ({ ...ps, showDrawer: !ps.showDrawer })); - const SidebarItem: React.FC<{ routeInfo: RouteDef }> = ({ routeInfo }) => { + const SidebarItem: FC<{ routeInfo: RouteDef }> = ({ routeInfo }) => { switch (routeInfo.type) { case "separator": return ( @@ -133,7 +133,7 @@ export const Layout: React.FC<{ routes: RouteDef[] }> = ({ routes }) => { } }; - const menuButtonStyle: (t: Theme) => React.CSSProperties = (t) => ({ + const menuButtonStyle: (t: Theme) => CSSProperties = (t) => ({ width: `calc(${t.spacing(7)} + 1px)`, [t.breakpoints.up("sm")]: { width: `calc(${t.spacing(8)} + 1px)`, diff --git a/apps/pyconkr-admin/src/components/pages/account/account.tsx b/apps/pyconkr-admin/src/components/pages/account/account.tsx index ca6db0f..31fbc4b 100644 --- a/apps/pyconkr-admin/src/components/pages/account/account.tsx +++ b/apps/pyconkr-admin/src/components/pages/account/account.tsx @@ -1,12 +1,12 @@ -import { useBackendAdminClient, useSignedInUserQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useSignedInUserQuery } from "@frontend/common/hooks/useAdminAPI"; import { CircularProgress } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC } from "react"; import { Navigate } from "react-router-dom"; -import { ErrorFallback } from "../../elements/error_fallback"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; -export const AccountRedirectPage: React.FC = ErrorBoundary.with( +export const AccountRedirectPage: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const backendAdminAPIClient = useBackendAdminClient(); diff --git a/apps/pyconkr-admin/src/components/pages/account/manage.tsx b/apps/pyconkr-admin/src/components/pages/account/manage.tsx index dbbdb03..0f82dd9 100644 --- a/apps/pyconkr-admin/src/components/pages/account/manage.tsx +++ b/apps/pyconkr-admin/src/components/pages/account/manage.tsx @@ -1,12 +1,12 @@ -import { me } from "@frontend/common/src/apis/admin_api"; -import { useBackendAdminClient, useChangePasswordMutation, useSignOutMutation } from "@frontend/common/src/hooks/useAdminAPI"; -import { getFormValue, isFormValid } from "@frontend/common/src/utils"; +import { me } from "@frontend/common/apis/admin_api"; +import { useBackendAdminClient, useChangePasswordMutation, useSignOutMutation } from "@frontend/common/hooks/useAdminAPI"; +import { getFormValue, isFormValid } from "@frontend/common/utils"; import { Logout } from "@mui/icons-material"; import { Button, Stack, Tab, Tabs, TextField, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, FormEvent, SyntheticEvent, useEffect, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../../utils/snackbar"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type ChangePasswordFormType = { old_password: string; @@ -14,15 +14,15 @@ type ChangePasswordFormType = { new_password_confirm: string; }; -export const AccountManagementPage: React.FC = () => { - const changePasswordFormRef = React.useRef(null); - const [pageState, setPageState] = React.useState<{ tab: number }>({ tab: 0 }); +export const AccountManagementPage: FC = () => { + const changePasswordFormRef = useRef(null); + const [pageState, setPageState] = useState<{ tab: number }>({ tab: 0 }); const navigate = useNavigate(); const backendAdminAPIClient = useBackendAdminClient(); const signOutMutation = useSignOutMutation(backendAdminAPIClient); const changePasswordMutation = useChangePasswordMutation(backendAdminAPIClient); - const setTab = (_: React.SyntheticEvent, tab: number) => setPageState((ps) => ({ ...ps, tab })); + const setTab = (_: SyntheticEvent, tab: number) => setPageState((ps) => ({ ...ps, tab })); const handleSignOut = () => { signOutMutation.mutate(undefined, { @@ -34,7 +34,7 @@ export const AccountManagementPage: React.FC = () => { }); }; - const handleChangePassword = (event: React.FormEvent) => { + const handleChangePassword = (event: FormEvent) => { event.preventDefault(); event.stopPropagation(); @@ -59,7 +59,7 @@ export const AccountManagementPage: React.FC = () => { }); }; - React.useEffect(() => { + useEffect(() => { (async () => { const userInfo = await me(backendAdminAPIClient)(); if (!userInfo) { diff --git a/apps/pyconkr-admin/src/components/pages/account/sign_in.tsx b/apps/pyconkr-admin/src/components/pages/account/sign_in.tsx index 82e4d1c..8f3799e 100644 --- a/apps/pyconkr-admin/src/components/pages/account/sign_in.tsx +++ b/apps/pyconkr-admin/src/components/pages/account/sign_in.tsx @@ -1,27 +1,27 @@ -import { useBackendAdminClient, useSignInMutation } from "@frontend/common/src/hooks/useAdminAPI"; -import { getFormValue } from "@frontend/common/src/utils"; -import { me } from "@frontend/common/src/apis/admin_api"; +import { me } from "@frontend/common/apis/admin_api"; +import { useBackendAdminClient, useSignInMutation } from "@frontend/common/hooks/useAdminAPI"; +import { getFormValue } from "@frontend/common/utils"; import { Login } from "@mui/icons-material"; import { Button, Stack, TextField, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, FormEvent, useEffect, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../../utils/snackbar"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type PageStateType = { userJustSignedIn: boolean; }; -export const SignInPage: React.FC = () => { +export const SignInPage: FC = () => { const navigate = useNavigate(); - const formRef = React.useRef(null); - const [pageState, setPageState] = React.useState({ userJustSignedIn: false }); + const formRef = useRef(null); + const [pageState, setPageState] = useState({ userJustSignedIn: false }); const setUserJustSignedIn = () => setPageState((ps) => ({ ...ps, userJustSignedIn: true })); const backendAdminAPIClient = useBackendAdminClient(); const signInMutation = useSignInMutation(backendAdminAPIClient); - const handleSignIn = (event: React.FormEvent) => { + const handleSignIn = (event: FormEvent) => { event.preventDefault(); if (!formRef.current) return; @@ -39,7 +39,7 @@ export const SignInPage: React.FC = () => { }); }; - React.useEffect(() => { + useEffect(() => { (async () => { if (pageState.userJustSignedIn) return; diff --git a/apps/pyconkr-admin/src/components/pages/external_api/google_oauth2_editor.tsx b/apps/pyconkr-admin/src/components/pages/external_api/google_oauth2_editor.tsx index 5358720..31fcc74 100644 --- a/apps/pyconkr-admin/src/components/pages/external_api/google_oauth2_editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/external_api/google_oauth2_editor.tsx @@ -1,12 +1,12 @@ -import { useBackendAdminClient, useIssueGoogleOAuth2AccessTokenMutation } from "@frontend/common/src/hooks/useAdminAPI"; -import { GoogleOAuth2AccessTokenResponseSchema } from "@frontend/common/src/schemas/backendAdminAPI"; +import { useBackendAdminClient, useIssueGoogleOAuth2AccessTokenMutation } from "@frontend/common/hooks/useAdminAPI"; +import { GoogleOAuth2AccessTokenResponseSchema } from "@frontend/common/schemas/backendAdminAPI"; import { VpnKey } from "@mui/icons-material"; import { Box, Button, Stack, Table, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material"; -import * as React from "react"; +import { FC, ReactNode, useState } from "react"; import { useParams } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../../utils/snackbar"; -import { AdminEditor } from "../../layouts/admin_editor"; +import { AdminEditor } from "@apps/pyconkr-admin/components/layouts/admin_editor"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type CachedToken = { issuedAt: number; response: GoogleOAuth2AccessTokenResponseSchema }; type TokenState = { issuedAt: Date; response: GoogleOAuth2AccessTokenResponseSchema }; @@ -31,10 +31,10 @@ const loadCachedToken = (id: string): TokenState | null => { } }; -export const AdminGoogleOAuth2Editor: React.FC = () => { +export const AdminGoogleOAuth2Editor: FC = () => { const { id } = useParams<{ id?: string }>(); const backendAdminClient = useBackendAdminClient(); - const [tokenState, setTokenState] = React.useState(() => (id ? loadCachedToken(id) : null)); + const [tokenState, setTokenState] = useState(() => (id ? loadCachedToken(id) : null)); const accessTokenMutation = useIssueGoogleOAuth2AccessTokenMutation(backendAdminClient, id ?? ""); @@ -51,7 +51,7 @@ export const AdminGoogleOAuth2Editor: React.FC = () => { }); }; - const renderValue = (key: string, value: unknown): React.ReactNode => { + const renderValue = (key: string, value: unknown): ReactNode => { if (Array.isArray(value)) { return ( diff --git a/apps/pyconkr-admin/src/components/pages/file/upload.tsx b/apps/pyconkr-admin/src/components/pages/file/upload.tsx index 251a60d..79f59c6 100644 --- a/apps/pyconkr-admin/src/components/pages/file/upload.tsx +++ b/apps/pyconkr-admin/src/components/pages/file/upload.tsx @@ -1,34 +1,34 @@ -import { useBackendAdminClient, useUploadPublicFileMutation } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useUploadPublicFileMutation } from "@frontend/common/hooks/useAdminAPI"; import { CloudUpload, PermMedia } from "@mui/icons-material"; import { Box, Button, Input, Stack, Typography } from "@mui/material"; -import * as React from "react"; +import { BaseSyntheticEvent, ChangeEvent, DragEvent, DragEventHandler, FC, MouseEventHandler, useCallback, useEffect, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../../utils/snackbar"; -import { BackendAdminSignInGuard } from "../../elements/admin_signin_guard"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type PublicFileUploadPageStateType = { isMouseOnDragBox: boolean; _forceRerender: number; }; -const ignoreEvent = (e: React.BaseSyntheticEvent | Event) => { +const ignoreEvent = (e: BaseSyntheticEvent | Event) => { e.preventDefault(); e.stopPropagation(); }; -const InnerPublicFileUploadPage: React.FC = () => { +const InnerPublicFileUploadPage: FC = () => { const navigate = useNavigate(); - const [state, setState] = React.useState({ + const [state, setState] = useState({ isMouseOnDragBox: false, _forceRerender: 0, // 강제 리렌더링을 위한 상태 }); - const fileInputRef = React.useRef(null); - const fileDragBoxRef = React.useRef(null); + const fileInputRef = useRef(null); + const fileDragBoxRef = useRef(null); const backendAdminClient = useBackendAdminClient(); const uploadPublicFileMutation = useUploadPublicFileMutation(backendAdminClient); - const forceRerender = React.useCallback( + const forceRerender = useCallback( () => setState((prev) => { let newValue = Math.random(); @@ -41,7 +41,7 @@ const InnerPublicFileUploadPage: React.FC = () => { [] ); - const onFileSelectButtonClick: React.MouseEventHandler = () => { + const onFileSelectButtonClick: MouseEventHandler = () => { if (fileInputRef.current) { fileInputRef.current.click(); } else { @@ -49,11 +49,11 @@ const InnerPublicFileUploadPage: React.FC = () => { } }; - const onDragEnter = (e: React.DragEvent) => { + const onDragEnter = (e: DragEvent) => { ignoreEvent(e); setState((prev) => ({ ...prev, isMouseOnDragBox: true })); }; - const onDragLeave = (e: React.DragEvent) => { + const onDragLeave = (e: DragEvent) => { // onDragLeave 이벤트는 자식 요소에 마우스가 들어갈 때도 발생합니다. // 따라서, 드래그 박스에 마우스가 있는지 확인하기 위해 마우스 위치를 확인하여 실제 onDragLeave 이벤트가 트리거되어야 하는지 확인합니다. // (e.relatedTarget는 Safari에서 지원되지 않아 사용할 수 없습니다.) @@ -66,7 +66,7 @@ const InnerPublicFileUploadPage: React.FC = () => { if (!fileDragBoxRef.current.contains(currentHoveredElement) || (x === 0 && y === 0)) setState((prev) => ({ ...prev, isMouseOnDragBox: false })); }; - const handleFile = React.useCallback( + const handleFile = useCallback( (file: File) => { if (!file || file.size === 0) { addSnackbar("파일을 찾을 수 없거나, 파일 크기가 0입니다.", "error"); @@ -99,7 +99,7 @@ const InnerPublicFileUploadPage: React.FC = () => { [forceRerender] ); - const resetFileSelect = React.useCallback(() => { + const resetFileSelect = useCallback(() => { if (fileInputRef.current) { fileInputRef.current.value = ""; // 파일 선택 초기화 fileInputRef.current.files = null; // 파일 목록 초기화 @@ -107,7 +107,7 @@ const InnerPublicFileUploadPage: React.FC = () => { } }, [forceRerender]); - const onFileSelect = (e: React.ChangeEvent) => { + const onFileSelect = (e: ChangeEvent) => { ignoreEvent(e); setState((prev) => ({ ...prev, isMouseOnDragBox: false })); @@ -132,7 +132,7 @@ const InnerPublicFileUploadPage: React.FC = () => { handleFile(file); }; - const onClipboardPaste = React.useCallback( + const onClipboardPaste = useCallback( (event: DocumentEventMap["paste"]) => { ignoreEvent(event); setState((prev) => ({ ...prev, isMouseOnDragBox: false })); @@ -159,7 +159,7 @@ const InnerPublicFileUploadPage: React.FC = () => { [handleFile] ); - const onDrop: React.DragEventHandler = (event) => { + const onDrop: DragEventHandler = (event) => { ignoreEvent(event); setState((prev) => ({ ...prev, isMouseOnDragBox: false })); @@ -187,7 +187,7 @@ const InnerPublicFileUploadPage: React.FC = () => { }); }; - React.useEffect(() => { + useEffect(() => { document.addEventListener("paste", onClipboardPaste); return () => document.removeEventListener("paste", onClipboardPaste); }, [onClipboardPaste, state.isMouseOnDragBox]); @@ -240,7 +240,7 @@ const InnerPublicFileUploadPage: React.FC = () => { ); }; -export const PublicFileUploadPage: React.FC = () => ( +export const PublicFileUploadPage: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/home.tsx b/apps/pyconkr-admin/src/components/pages/home.tsx index fab4a35..ed2b558 100644 --- a/apps/pyconkr-admin/src/components/pages/home.tsx +++ b/apps/pyconkr-admin/src/components/pages/home.tsx @@ -1,6 +1,5 @@ -import * as React from "react"; - -export const LandingPage: React.FC = () => ( +import { FC } from "react"; +export const LandingPage: FC = () => (

PyCon Korea Admin

diff --git a/apps/pyconkr-admin/src/components/pages/modification_audit/components.tsx b/apps/pyconkr-admin/src/components/pages/modification_audit/components.tsx index 3fbfaa1..02447b7 100644 --- a/apps/pyconkr-admin/src/components/pages/modification_audit/components.tsx +++ b/apps/pyconkr-admin/src/components/pages/modification_audit/components.tsx @@ -1,6 +1,6 @@ -import { Components } from "@frontend/common"; -import { useBackendAdminClient, usePublicFileQuery } from "@frontend/common/src/hooks/useAdminAPI"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +import { FallbackImage, Fieldset, MDXRenderer } from "@frontend/common/components"; +import { useBackendAdminClient, usePublicFileQuery } from "@frontend/common/hooks/useAdminAPI"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { Accordion, AccordionDetails, @@ -17,9 +17,8 @@ import { TextFieldProps, Typography, } from "@mui/material"; -import * as React from "react"; -import * as R from "remeda"; - +import { FC, ReactNode } from "react"; +import { isArray, isEmpty } from "remeda"; type SharedPreviewFieldProps = { originalDataset: Record; previewDataset: Record; @@ -39,7 +38,7 @@ const MarkdownContainerBox = styled(Box)(({ theme }) => ({ }, })); -export const PreviewTextField: React.FC = ({ originalDataset, previewDataset, name, ...props }) => { +export const PreviewTextField: FC = ({ originalDataset, previewDataset, name, ...props }) => { const textFieldSx: TextFieldProps["sx"] = { "& .MuiInputBase-input, & .Mui-disabled": { color: "black", @@ -78,61 +77,51 @@ export const PreviewTextField: React.FC = ({ originalDataset, ); }; -export const PreviewMarkdownField: React.FC = ({ originalDataset, previewDataset, name, label }) => { +export const PreviewMarkdownField: FC = ({ originalDataset, previewDataset, name, label }) => { const { baseUrl, mdxComponents } = useCommonContext(); return originalDataset[name] === previewDataset[name] ? ( - +

- + - +
) : ( - +
- + - +
기존 값을 보려면 여기를 클릭해주세요.
- +
- + - +
); }; -const ImageFallback: React.FC = () => ( +const ImageFallback: FC = () => ( ); -const WidthSpecifiedFallbackImage = styled(Components.FallbackImage)({ +const WidthSpecifiedFallbackImage = styled(FallbackImage)({ maxWidth: "20rem", objectFit: "cover", }); -export const PreviewImageField: React.FC = ({ originalDataset, previewDataset, name, label }) => { +export const PreviewImageField: FC = ({ originalDataset, previewDataset, name, label }) => { const backendAdminClient = useBackendAdminClient(); const oldImgId = (originalDataset[name] as string) || ""; const newImgId = (previewDataset[name] as string) || ""; @@ -141,7 +130,7 @@ export const PreviewImageField: React.FC = ({ originalD const { data: previewImage } = usePublicFileQuery(backendAdminClient, newImgId); return originalImage?.id === previewImage?.id ? ( - +
{previewImage?.file ? ( } /> @@ -149,13 +138,13 @@ export const PreviewImageField: React.FC = ({ originalD )} - +
) : ( - +
{previewImage?.file ? ( } /> @@ -163,12 +152,12 @@ export const PreviewImageField: React.FC = ({ originalD )} - +
기존 이미지를 보려면 여기를 클릭해주세요.
- +
{originalImage?.file ? ( } /> @@ -176,7 +165,7 @@ export const PreviewImageField: React.FC = ({ originalD )} - +
@@ -194,9 +183,9 @@ type SimplifiedModificationAudit = { }[]; }; -export const ModificationAuditProperties: React.FC<{ audit: SimplifiedModificationAudit }> = ({ audit }) => { - let rejectReason: React.ReactNode = null; - if (R.isArray(audit.comments) && !R.isEmpty(audit.comments.filter((c) => c.created_by.is_superuser))) { +export const ModificationAuditProperties: FC<{ audit: SimplifiedModificationAudit }> = ({ audit }) => { + let rejectReason: ReactNode = null; + if (isArray(audit.comments) && !isEmpty(audit.comments.filter((c) => c.created_by.is_superuser))) { const comment = audit.comments.filter((c) => c.created_by.is_superuser)[0]; rejectReason = ( <> diff --git a/apps/pyconkr-admin/src/components/pages/modification_audit/dialogs.tsx b/apps/pyconkr-admin/src/components/pages/modification_audit/dialogs.tsx index 10da0cc..3a72f07 100644 --- a/apps/pyconkr-admin/src/components/pages/modification_audit/dialogs.tsx +++ b/apps/pyconkr-admin/src/components/pages/modification_audit/dialogs.tsx @@ -1,21 +1,20 @@ -import { Components } from "@frontend/common"; -import { useApproveModificationAuditMutation, useBackendAdminClient, useRejectModificationAuditMutation } from "@frontend/common/src/hooks/useAdminAPI"; -import { BackendAPIClientError } from "@frontend/common/src/apis"; +import { BackendAPIClientError } from "@frontend/common/apis"; +import { Fieldset } from "@frontend/common/components"; +import { useApproveModificationAuditMutation, useBackendAdminClient, useRejectModificationAuditMutation } from "@frontend/common/hooks/useAdminAPI"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, Typography } from "@mui/material"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; - +import { FC, ReactNode, useRef } from "react"; type SubmitConfirmDialogProps = { open: boolean; onClose: () => void; modificationAuditId: string; }; -export const ApproveSubmitConfirmDialog: React.FC = ({ open, onClose, modificationAuditId }) => { +export const ApproveSubmitConfirmDialog: FC = ({ open, onClose, modificationAuditId }) => { const backendAdminClient = useBackendAdminClient(); const approveModificationAuditMutation = useApproveModificationAuditMutation(backendAdminClient, modificationAuditId); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const onApproveClick = () => { @@ -51,12 +50,12 @@ export const ApproveSubmitConfirmDialog: React.FC = ({ ); }; -export const RejectSubmitConfirmDialog: React.FC = ({ open, onClose, modificationAuditId }) => { - const inputRef = React.useRef(null); +export const RejectSubmitConfirmDialog: FC = ({ open, onClose, modificationAuditId }) => { + const inputRef = useRef(null); const backendAdminClient = useBackendAdminClient(); const rejectModificationAuditMutation = useRejectModificationAuditMutation(backendAdminClient, modificationAuditId); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const onRejectClick = () => { @@ -85,9 +84,9 @@ export const RejectSubmitConfirmDialog: React.FC = ({
반려 후에는 다시 승인할 수 없습니다! - +
- +
- + ); }; -const SectionEditorField: React.FC = ({ language, disabled, defaultValue, onInsertNewSection, onChange, onDelete }) => { +const SectionEditorField: FC = ({ language, disabled, defaultValue, onInsertNewSection, onChange, onDelete }) => { const onFieldChange = (key: "body_ko" | "body_en", value?: string) => onChange({ ...defaultValue, [key]: value }); return ( @@ -81,20 +81,20 @@ type AdminCMSPageEditorStateType = { sections?: SectionType[]; }; -export const AdminCMSPageEditor: React.FC = ErrorBoundary.with( +export const AdminCMSPageEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams<{ id?: string }>(); const { frontendDomain } = useCommonContext(); const backendAdminClient = useBackendAdminClient(); const { data: initialSections } = useListPageSectionsQuery(backendAdminClient, id || ""); - const [editorState, setEditorState] = React.useState({ + const [editorState, setEditorState] = useState({ sections: initialSections, tab: 0, }); const bulkUpdateSectionsMutation = useBulkUpdatePageSectionsMutation(backendAdminClient, id || ""); - const setTab = (_: React.SyntheticEvent, selectedTab: number) => setEditorState((ps) => ({ ...ps, tab: selectedTab })); + const setTab = (_: SyntheticEvent, selectedTab: number) => setEditorState((ps) => ({ ...ps, tab: selectedTab })); const openOnSiteButton: ButtonProps = { variant: "outlined", diff --git a/apps/pyconkr-admin/src/components/pages/presentation/editor.tsx b/apps/pyconkr-admin/src/components/pages/presentation/editor.tsx index 0e39ec3..65bcf23 100644 --- a/apps/pyconkr-admin/src/components/pages/presentation/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/presentation/editor.tsx @@ -1,4 +1,4 @@ -import { Components } from "@frontend/common"; +import { Fieldset, MDXRenderer, MarkdownEditor } from "@frontend/common/components"; import { useBackendAdminClient, useChoicesQuery, @@ -7,8 +7,8 @@ import { useRemovePreparedMutation, useSchemaQuery, useUpdatePreparedMutation, -} from "@frontend/common/src/hooks/useAdminAPI"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +} from "@frontend/common/hooks/useAdminAPI"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { Autocomplete, Box, Button, Card, CardContent, CircularProgress, Stack, styled, Tab, Tabs, TextField, Typography } from "@mui/material"; import { DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon"; @@ -16,11 +16,11 @@ import { PickerValue } from "@mui/x-date-pickers/internals"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { DateTime } from "luxon"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; +import { FC, ReactNode, SyntheticEvent, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { ErrorFallback } from "../../elements/error_fallback"; -import { AdminEditor } from "../../layouts/admin_editor"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { AdminEditor } from "@apps/pyconkr-admin/components/layouts/admin_editor"; const DUMMY_UUID = "00000000-0000-4000-8000-000000000000"; @@ -96,10 +96,10 @@ type AutoCompleteType = { label: string; }; -const PresentationSpeakerForm: React.FC = ({ disabled, schema, speaker, onChange, onRemove }) => { +const PresentationSpeakerForm: FC = ({ disabled, schema, speaker, onChange, onRemove }) => { const { baseUrl, mdxComponents } = useCommonContext(); - const [formState, setFormState] = React.useState({ tab: "ko" }); - const setLanguage = (_: React.SyntheticEvent, tab: "ko" | "en") => setFormState((ps) => ({ ...ps, tab })); + const [formState, setFormState] = useState({ tab: "ko" }); + const setLanguage = (_: SyntheticEvent, tab: "ko" | "en") => setFormState((ps) => ({ ...ps, tab })); const userOptions: AutoCompleteType[] = schema.schema.properties.user.oneOf.map((item) => ({ name: "user", @@ -116,7 +116,7 @@ const PresentationSpeakerForm: React.FC = ({ di const bioField = formState.tab === "ko" ? "biography_ko" : "biography_en"; const onSpeakerBioChange = (value?: string) => onChange({ ...speaker, [bioField]: value || "" }); - const onSpeakerChange = (fieldName: string) => (_: React.SyntheticEvent, selected: AutoCompleteType | null) => { + const onSpeakerChange = (fieldName: string) => (_: SyntheticEvent, selected: AutoCompleteType | null) => { onChange({ ...speaker, [fieldName]: selected?.value || "" }); }; const onSpeakerRemove = () => { @@ -155,10 +155,10 @@ const PresentationSpeakerForm: React.FC = ({ di - + - + @@ -206,7 +206,7 @@ type ScheduleFormPropType = { onRemove: (schedule: OnMemorySchedule) => void; }; -const PresentationScheduleForm: React.FC = ({ schema, disabled, schedule, onChange, onRemove }) => { +const PresentationScheduleForm: FC = ({ schema, disabled, schedule, onChange, onRemove }) => { const roomOptions: AutoCompleteType[] = schema.schema.properties.room.oneOf.map((item) => ({ name: "room", value: item.const || "", @@ -214,7 +214,7 @@ const PresentationScheduleForm: React.FC = ({ schema, disa })); const currentSelectedRoom = roomOptions.find((r) => r.value === schedule.room?.toString()); - const onSelectChange = (fieldName: string) => (_: React.SyntheticEvent, selected: AutoCompleteType | null) => { + const onSelectChange = (fieldName: string) => (_: SyntheticEvent, selected: AutoCompleteType | null) => { onChange({ ...schedule, [fieldName]: selected?.value || "" }); }; @@ -274,12 +274,12 @@ type PresentationEditorStateType = { schedules: OnMemorySchedule[]; }; -export const AdminPresentationEditor: React.FC = ErrorBoundary.with( +export const AdminPresentationEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams<{ id?: string }>(); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const backendAdminAPIClient = useBackendAdminClient(); @@ -302,8 +302,11 @@ export const AdminPresentationEditor: React.FC = ErrorBoundary.with( const { data: scheduleInitialData } = useListQuery(...scheduleQueryParams, { presentation }); const schedules = scheduleInitialData.map((s) => ({ ...s, trackId: s.id || Math.random().toString(36).substring(2, 15) })); - React.useMemo(() => { - const mergeChoices = (schema: { schema?: { properties?: Record } }, choices: Record) => { + useMemo(() => { + const mergeChoices = ( + schema: { schema?: { properties?: Record } }, + choices: Record + ) => { if (!schema?.schema?.properties || !choices) return; for (const [fieldName, items] of Object.entries(choices)) { const prop = schema.schema.properties[fieldName]; @@ -332,7 +335,7 @@ export const AdminPresentationEditor: React.FC = ErrorBoundary.with( end_at: DateTime.now().plus({ hours: 1 }).toISO({ includeOffset: false }), }); - const [editorState, setEditorState] = React.useState({ speakers, schedules }); + const [editorState, setEditorState] = useState({ speakers, schedules }); const onSpeakerCreate = () => setEditorState((ps) => ({ ...ps, speakers: [...ps.speakers, createEmptySpeaker()] })); const onSpeakerRemove = (oldSpeaker: OnMemoeryPresentationSpeaker) => setEditorState((ps) => ({ ...ps, speakers: ps.speakers.filter((s) => s.trackId !== oldSpeaker.trackId) })); @@ -386,7 +389,7 @@ export const AdminPresentationEditor: React.FC = ErrorBoundary.with( {id ? ( - +
스케줄 정보 {editorState.schedules.map((s) => ( @@ -400,8 +403,8 @@ export const AdminPresentationEditor: React.FC = ErrorBoundary.with( ))}
+
발표자 정보 {editorState.speakers.map((s) => ( @@ -415,7 +418,7 @@ export const AdminPresentationEditor: React.FC = ErrorBoundary.with( ))}
) : ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/_common/status_labels.ts b/apps/pyconkr-admin/src/components/pages/shop/_common/status_labels.ts index dc1e038..322995e 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/_common/status_labels.ts +++ b/apps/pyconkr-admin/src/components/pages/shop/_common/status_labels.ts @@ -1,5 +1,5 @@ -import { OrderProductStatus, PaymentStatus } from "../order/types"; -import { ProductCurrentStatus } from "../product/types"; +import { OrderProductStatus, PaymentStatus } from "@apps/pyconkr-admin/components/pages/shop/order/types"; +import { ProductCurrentStatus } from "@apps/pyconkr-admin/components/pages/shop/product/types"; type ChipColor = "default" | "primary" | "success" | "warning" | "error" | "info"; diff --git a/apps/pyconkr-admin/src/components/pages/shop/category_group/editor.tsx b/apps/pyconkr-admin/src/components/pages/shop/category_group/editor.tsx index bed4e01..3f246be 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/category_group/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/category_group/editor.tsx @@ -1,4 +1,4 @@ -import { useBackendAdminClient, useRetrieveQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useRetrieveQuery } from "@frontend/common/hooks/useAdminAPI"; import { Add, Delete, Edit } from "@mui/icons-material"; import { Button, @@ -20,11 +20,11 @@ import { } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { useMutation } from "@tanstack/react-query"; -import * as React from "react"; +import { FC, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { addErrorSnackbar, addSnackbar } from "../../../../utils/snackbar"; -import { AdminEditor } from "../../../layouts/admin_editor"; +import { AdminEditor } from "@apps/pyconkr-admin/components/layouts/admin_editor"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type Category = { id?: string; @@ -54,15 +54,15 @@ type CategoryDialogProps = { category?: Category; }; -const CategoryDialog: React.FC = ({ open, onClose, group, category }) => { +const CategoryDialog: FC = ({ open, onClose, group, category }) => { const client = useBackendAdminClient(); - const [values, setValues] = React.useState({ + const [values, setValues] = useState({ name: category?.name ?? "", priority: category ? String(category.priority) : "0", }); - React.useEffect(() => { + useEffect(() => { if (open) { setValues({ name: category?.name ?? "", @@ -137,13 +137,13 @@ const CategoryDialog: React.FC = ({ open, onClose, group, c ); }; -const InnerChildCategoryList: React.FC<{ groupId: string }> = ErrorBoundary.with( +const InnerChildCategoryList: FC<{ groupId: string }> = ErrorBoundary.with( { fallback: () => null }, Suspense.with({ fallback: }, ({ groupId }) => { const client = useBackendAdminClient(); const groupQuery = useRetrieveQuery(client, "shop", "category-groups", groupId); const group = groupQuery.data; - const [dialogState, setDialogState] = React.useState<{ open: boolean; category?: Category }>({ open: false }); + const [dialogState, setDialogState] = useState<{ open: boolean; category?: Category }>({ open: false }); const deleteMutation = useMutation({ mutationFn: async (categoryId: string) => { @@ -215,7 +215,7 @@ const InnerChildCategoryList: React.FC<{ groupId: string }> = ErrorBoundary.with }) ); -export const ShopCategoryGroupEditorPage: React.FC = () => { +export const ShopCategoryGroupEditorPage: FC = () => { const { id } = useParams<{ id?: string }>(); return ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/category_group/list.tsx b/apps/pyconkr-admin/src/components/pages/shop/category_group/list.tsx index 81d44a7..f590004 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/category_group/list.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/category_group/list.tsx @@ -1,7 +1,7 @@ -import * as React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; -import { AdminList, AdminListColumn } from "../../../layouts/admin_list"; +import { AdminList, AdminListColumn } from "@apps/pyconkr-admin/components/layouts/admin_list"; const columns: AdminListColumn[] = [ { @@ -31,4 +31,4 @@ const columns: AdminListColumn[] = [ }, ]; -export const ShopCategoryGroupListPage: React.FC = () => ; +export const ShopCategoryGroupListPage: FC = () => ; diff --git a/apps/pyconkr-admin/src/components/pages/shop/order/editor.tsx b/apps/pyconkr-admin/src/components/pages/shop/order/editor.tsx index c0ae856..ca55e46 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/order/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/order/editor.tsx @@ -1,4 +1,4 @@ -import { useBackendAdminClient, useRetrieveQuery, useUpdateMutation } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useRetrieveQuery, useUpdateMutation } from "@frontend/common/hooks/useAdminAPI"; import { CurrencyExchange, NotificationsActive, Save } from "@mui/icons-material"; import { Alert, @@ -19,36 +19,37 @@ import { Typography, } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, FormEvent, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { ORDER_PRODUCT_STATUS_LABEL, PAYMENT_STATUS_LABEL } from "@apps/pyconkr-admin/components/pages/shop/_common/status_labels"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; + import { RefundDialog } from "./refund_dialog"; import { OrderAdmin, SimpleCustomerInfo, SimpleOrderProductRelation } from "./types"; -import { addErrorSnackbar, addSnackbar } from "../../../../utils/snackbar"; -import { BackendAdminSignInGuard } from "../../../elements/admin_signin_guard"; -import { ErrorFallback } from "../../../elements/error_fallback"; -import { ORDER_PRODUCT_STATUS_LABEL, PAYMENT_STATUS_LABEL } from "../_common/status_labels"; const formatPrice = (price: number) => `₩${price.toLocaleString()}`; // ----------------- Customer Info Tab (editable) ----------------- -const CustomerInfoTab: React.FC<{ order: OrderAdmin }> = ({ order }) => { +const CustomerInfoTab: FC<{ order: OrderAdmin }> = ({ order }) => { const client = useBackendAdminClient(); const updateMutation = useUpdateMutation<{ customer_info: SimpleCustomerInfo }>(client, "shop", "orders", order.id); - const [name, setName] = React.useState(order.customer_info?.name ?? ""); - const [phone, setPhone] = React.useState(order.customer_info?.phone ?? ""); - const [email, setEmail] = React.useState(order.customer_info?.email ?? ""); - const [organization, setOrganization] = React.useState(order.customer_info?.organization ?? ""); + const [name, setName] = useState(order.customer_info?.name ?? ""); + const [phone, setPhone] = useState(order.customer_info?.phone ?? ""); + const [email, setEmail] = useState(order.customer_info?.email ?? ""); + const [organization, setOrganization] = useState(order.customer_info?.organization ?? ""); - React.useEffect(() => { + useEffect(() => { setName(order.customer_info?.name ?? ""); setPhone(order.customer_info?.phone ?? ""); setEmail(order.customer_info?.email ?? ""); setOrganization(order.customer_info?.organization ?? ""); }, [order.customer_info]); - const onSubmit = (e: React.FormEvent) => { + const onSubmit = (e: FormEvent) => { e.preventDefault(); if (!name.trim() || !phone.trim() || !email.trim()) { addSnackbar("이름, 연락처, 이메일은 필수입니다.", "error"); @@ -89,7 +90,7 @@ const CustomerInfoTab: React.FC<{ order: OrderAdmin }> = ({ order }) => { ); }; -const OrderProductRow: React.FC<{ relation: SimpleOrderProductRelation }> = ({ relation }) => { +const OrderProductRow: FC<{ relation: SimpleOrderProductRelation }> = ({ relation }) => { const status = ORDER_PRODUCT_STATUS_LABEL[relation.status]; return ( <> @@ -142,7 +143,7 @@ const OrderProductRow: React.FC<{ relation: SimpleOrderProductRelation }> = ({ r ); }; -const OrderProductsTab: React.FC<{ order: OrderAdmin }> = ({ order }) => ( +const OrderProductsTab: FC<{ order: OrderAdmin }> = ({ order }) => ( @@ -168,7 +169,7 @@ const OrderProductsTab: React.FC<{ order: OrderAdmin }> = ({ order }) => (
); -const PaymentHistoryTab: React.FC<{ order: OrderAdmin; onRefund: () => void }> = ({ order, onRefund }) => { +const PaymentHistoryTab: FC<{ order: OrderAdmin; onRefund: () => void }> = ({ order, onRefund }) => { const histories = [...order.payment_histories].sort((a, b) => (a.created_at < b.created_at ? -1 : 1)); const canRefund = order.current_paid_price > 0 && (order.current_status === "completed" || order.current_status === "partial_refunded"); @@ -221,14 +222,14 @@ const PaymentHistoryTab: React.FC<{ order: OrderAdmin; onRefund: () => void }> = ); }; -const InnerOrderEditor: React.FC = ErrorBoundary.with( +const InnerOrderEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams<{ id?: string }>(); const navigate = useNavigate(); const client = useBackendAdminClient(); - const [tab, setTab] = React.useState(0); - const [refundOpen, setRefundOpen] = React.useState(false); + const [tab, setTab] = useState(0); + const [refundOpen, setRefundOpen] = useState(false); const orderQuery = useRetrieveQuery(client, "shop", "orders", id ?? ""); const order = orderQuery.data; @@ -345,7 +346,7 @@ const InnerOrderEditor: React.FC = ErrorBoundary.with( }) ); -export const ShopOrderEditorPage: React.FC = () => ( +export const ShopOrderEditorPage: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/order/list.tsx b/apps/pyconkr-admin/src/components/pages/shop/order/list.tsx index 56af830..1e56eaa 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/order/list.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/order/list.tsx @@ -1,4 +1,4 @@ -import { useBackendAdminClient, useListPaginatedQuery, useListQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useListPaginatedQuery, useListQuery } from "@frontend/common/hooks/useAdminAPI"; import { Chip, CircularProgress, @@ -14,16 +14,17 @@ import { Typography, } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, useMemo } from "react"; import { Link, useSearchParams } from "react-router-dom"; +import { AdminFilterFieldset } from "@apps/pyconkr-admin/components/elements/admin_filter_fieldset"; +import { AdminPagination } from "@apps/pyconkr-admin/components/elements/admin_pagination"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { PAYMENT_STATUS_LABEL } from "@apps/pyconkr-admin/components/pages/shop/_common/status_labels"; +import { CategoryGroupAdminWithCategories } from "@apps/pyconkr-admin/components/pages/shop/product/types"; + import { OrderAdmin, PaymentStatus } from "./types"; -import { AdminFilterFieldset } from "../../../elements/admin_filter_fieldset"; -import { AdminPagination } from "../../../elements/admin_pagination"; -import { BackendAdminSignInGuard } from "../../../elements/admin_signin_guard"; -import { ErrorFallback } from "../../../elements/error_fallback"; -import { PAYMENT_STATUS_LABEL } from "../_common/status_labels"; -import { CategoryGroupAdminWithCategories } from "../product/types"; const formatPrice = (price: number) => `₩${price.toLocaleString()}`; @@ -31,7 +32,7 @@ const DEFAULT_PAGE_SIZE = 50; type StatusFilter = "all" | PaymentStatus; -const InnerOrderList: React.FC = ErrorBoundary.with( +const InnerOrderList: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const client = useBackendAdminClient(); @@ -65,7 +66,7 @@ const InnerOrderList: React.FC = ErrorBoundary.with( const ordersQuery = useListPaginatedQuery(client, "shop", "orders", apiParams); const groupsQuery = useListQuery(client, "shop", "category-groups", {}); const { count = 0, results: orders = [] } = ordersQuery.data ?? {}; - const groups = React.useMemo(() => groupsQuery.data ?? [], [groupsQuery.data]); + const groups = useMemo(() => groupsQuery.data ?? [], [groupsQuery.data]); const updateFilterParam = (key: string, value: string) => { const next = new URLSearchParams(searchParams); @@ -261,7 +262,7 @@ const InnerOrderList: React.FC = ErrorBoundary.with( }) ); -export const ShopOrderListPage: React.FC = () => ( +export const ShopOrderListPage: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/order/refund_dialog.tsx b/apps/pyconkr-admin/src/components/pages/shop/order/refund_dialog.tsx index 6822646..4219e0a 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/order/refund_dialog.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/order/refund_dialog.tsx @@ -1,10 +1,11 @@ -import { useBackendAdminClient } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient } from "@frontend/common/hooks/useAdminAPI"; import { Alert, Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField, Typography } from "@mui/material"; import { useMutation } from "@tanstack/react-query"; -import * as React from "react"; +import { FC, useEffect, useState } from "react"; + +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; import { OrderAdmin } from "./types"; -import { addErrorSnackbar, addSnackbar } from "../../../../utils/snackbar"; type RefundDialogProps = { open: boolean; @@ -14,12 +15,12 @@ type RefundDialogProps = { const formatPrice = (price: number) => `₩${price.toLocaleString()}`; -export const RefundDialog: React.FC = ({ open, onClose, order }) => { +export const RefundDialog: FC = ({ open, onClose, order }) => { const client = useBackendAdminClient(); - const [totp, setTotp] = React.useState(""); - const [touched, setTouched] = React.useState(false); + const [totp, setTotp] = useState(""); + const [touched, setTouched] = useState(false); - React.useEffect(() => { + useEffect(() => { if (open) { setTotp(""); setTouched(false); diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/editor.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/editor.tsx index d537752..a1f6a71 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/editor.tsx @@ -5,7 +5,7 @@ import { useRemoveMutation, useRetrieveQuery, useUpdateMutation, -} from "@frontend/common/src/hooks/useAdminAPI"; +} from "@frontend/common/hooks/useAdminAPI"; import { Add, Delete, Edit } from "@mui/icons-material"; import { Box, @@ -23,20 +23,21 @@ import { Typography, } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, ReactNode, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; + import { buildDefaultFormValues, ProductFormValues, toPayload } from "./form"; import { BasicInfoTab } from "./tabs/basic_info_tab"; import { OptionGroupsTab } from "./tabs/option_groups_tab"; import { PriceOptionsTab } from "./tabs/price_options_tab"; import { TimeSettingsTab } from "./tabs/time_settings_tab"; import { CategoryGroupAdminWithCategories, ProductAdmin, TagAdmin } from "./types"; -import { addErrorSnackbar, addSnackbar } from "../../../../utils/snackbar"; -import { BackendAdminSignInGuard } from "../../../elements/admin_signin_guard"; -import { ErrorFallback } from "../../../elements/error_fallback"; -const formatLeftover = (v: number | null | undefined): React.ReactNode => +const formatLeftover = (v: number | null | undefined): ReactNode => v === undefined ? ( — @@ -47,7 +48,7 @@ const formatLeftover = (v: number | null | undefined): React.ReactNode => v.toLocaleString() ); -const InnerProductEditor: React.FC = ErrorBoundary.with( +const InnerProductEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams<{ id?: string }>(); @@ -63,10 +64,10 @@ const InnerProductEditor: React.FC = ErrorBoundary.with( const groups = groupsQuery.data ?? []; const tags = tagsQuery.data ?? []; - const [tab, setTab] = React.useState(0); - const [values, setValues] = React.useState(() => buildDefaultFormValues(existing)); + const [tab, setTab] = useState(0); + const [values, setValues] = useState(() => buildDefaultFormValues(existing)); - React.useEffect(() => { + useEffect(() => { if (existing) setValues(buildDefaultFormValues(existing)); }, [existing]); @@ -249,7 +250,7 @@ const InnerProductEditor: React.FC = ErrorBoundary.with( }) ); -export const ShopProductEditorPage: React.FC = () => ( +export const ShopProductEditorPage: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/list.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/list.tsx index ac137d3..8847537 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/list.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/list.tsx @@ -1,4 +1,4 @@ -import { useBackendAdminClient, useListQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useListQuery } from "@frontend/common/hooks/useAdminAPI"; import { Add, Delete, Edit } from "@mui/icons-material"; import { Button, @@ -18,14 +18,15 @@ import { } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { useMutation } from "@tanstack/react-query"; -import * as React from "react"; +import { FC, useMemo } from "react"; import { Link, useNavigate, useSearchParams } from "react-router-dom"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { PRODUCT_STATUS_LABEL } from "@apps/pyconkr-admin/components/pages/shop/_common/status_labels"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; + import { CategoryGroupAdminWithCategories, ProductAdmin, ProductCurrentStatus } from "./types"; -import { addErrorSnackbar, addSnackbar } from "../../../../utils/snackbar"; -import { BackendAdminSignInGuard } from "../../../elements/admin_signin_guard"; -import { ErrorFallback } from "../../../elements/error_fallback"; -import { PRODUCT_STATUS_LABEL } from "../_common/status_labels"; const formatPrice = (price: number) => `₩${price.toLocaleString()}`; const formatLeftoverStock = (leftover: number | null | undefined) => { @@ -35,7 +36,7 @@ const formatLeftoverStock = (leftover: number | null | undefined) => { type StatusFilter = "all" | ProductCurrentStatus; -const InnerProductList: React.FC = ErrorBoundary.with( +const InnerProductList: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const client = useBackendAdminClient(); @@ -57,9 +58,9 @@ const InnerProductList: React.FC = ErrorBoundary.with( const groupsQuery = useListQuery(client, "shop", "category-groups", {}); const products = productsQuery.data ?? []; - const groups = React.useMemo(() => groupsQuery.data ?? [], [groupsQuery.data]); + const groups = useMemo(() => groupsQuery.data ?? [], [groupsQuery.data]); - const categoryToGroup: Record = React.useMemo(() => { + const categoryToGroup: Record = useMemo(() => { const map: Record = {}; for (const g of groups) { for (const c of g.categories ?? []) { @@ -206,7 +207,7 @@ const InnerProductList: React.FC = ErrorBoundary.with( }) ); -export const ShopProductListPage: React.FC = () => ( +export const ShopProductListPage: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/basic_info_tab.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/basic_info_tab.tsx index 40e0799..a6eed90 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/basic_info_tab.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/basic_info_tab.tsx @@ -1,5 +1,5 @@ -import { Components } from "@frontend/common"; -import { useCommonContext } from "@frontend/common/src/hooks/useCommonContext"; +import { MarkdownEditor, MDXRenderer } from "@frontend/common/components"; +import { useCommonContext } from "@frontend/common/hooks/useCommonContext"; import { Autocomplete, Box, @@ -15,12 +15,12 @@ import { TextField, Typography, } from "@mui/material"; -import * as React from "react"; +import { FC, useState } from "react"; -import { IMAGE_FILE_EXTENSIONS } from "../../../../../consts/file_extensions"; -import { PublicFilePicker } from "../../../../elements/public_file_picker"; -import { ProductFormValues, SetField } from "../form"; -import { CategoryGroupAdminWithCategories, TagAdmin } from "../types"; +import { PublicFilePicker } from "@apps/pyconkr-admin/components/elements/public_file_picker"; +import { ProductFormValues, SetField } from "@apps/pyconkr-admin/components/pages/shop/product/form"; +import { CategoryGroupAdminWithCategories, TagAdmin } from "@apps/pyconkr-admin/components/pages/shop/product/types"; +import { IMAGE_FILE_EXTENSIONS } from "@apps/pyconkr-admin/consts/file_extensions"; const MUIStyledFieldset = styled("fieldset")(({ theme }) => ({ color: theme.palette.text.secondary, @@ -47,8 +47,8 @@ type Props = { tags: TagAdmin[]; }; -export const BasicInfoTab: React.FC = ({ values, setField, disabled, groups, tags }) => { - const [langTab, setLangTab] = React.useState<"ko" | "en">("ko"); +export const BasicInfoTab: FC = ({ values, setField, disabled, groups, tags }) => { + const [langTab, setLangTab] = useState<"ko" | "en">("ko"); const { baseUrl, mdxComponents } = useCommonContext(); const selectedTags = tags.filter((t) => values.tag_set.includes(t.id)); const isKo = langTab === "ko"; @@ -128,15 +128,10 @@ export const BasicInfoTab: React.FC = ({ values, setField, disabled, grou - setField(descKey, value ?? "")} - /> + setField(descKey, value ?? "")} /> - + diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/option_groups_tab.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/option_groups_tab.tsx index 40f05e1..930a5b9 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/option_groups_tab.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/option_groups_tab.tsx @@ -1,4 +1,4 @@ -import { useBackendAdminClient, useCreateMutation, useUpdateMutation } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useCreateMutation, useUpdateMutation } from "@frontend/common/hooks/useAdminAPI"; import { Add, Delete, Edit, ExpandMore } from "@mui/icons-material"; import { Accordion, @@ -25,10 +25,10 @@ import { Typography, } from "@mui/material"; import { useMutation } from "@tanstack/react-query"; -import * as React from "react"; +import { FC, useEffect, useState } from "react"; -import { addErrorSnackbar, addSnackbar } from "../../../../../utils/snackbar"; -import { OptionAdmin, OptionGroupAdmin } from "../types"; +import { OptionAdmin, OptionGroupAdmin } from "@apps/pyconkr-admin/components/pages/shop/product/types"; +import { addErrorSnackbar, addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; // ----------------- Option dialog ----------------- type OptionFormValues = { @@ -47,10 +47,10 @@ type OptionDialogProps = { option?: OptionAdmin; }; -const OptionDialog: React.FC = ({ open, onClose, optionGroup, option }) => { +const OptionDialog: FC = ({ open, onClose, optionGroup, option }) => { const client = useBackendAdminClient(); const updateMutation = useUpdateMutation(client, "shop", "option-groups", optionGroup.id); - const [values, setValues] = React.useState({ + const [values, setValues] = useState({ name_ko: "", name_en: "", additional_price: "0", @@ -59,7 +59,7 @@ const OptionDialog: React.FC = ({ open, onClose, optionGroup, priority: "0", }); - React.useEffect(() => { + useEffect(() => { if (open) { setValues({ name_ko: option?.name_ko ?? "", @@ -190,12 +190,12 @@ type OptionGroupDialogProps = { existingGroupCount: number; }; -const OptionGroupDialog: React.FC = ({ open, onClose, productId, group, existingGroupCount }) => { +const OptionGroupDialog: FC = ({ open, onClose, productId, group, existingGroupCount }) => { const client = useBackendAdminClient(); const createMutation = useCreateMutation(client, "shop", "option-groups"); const updateMutation = useUpdateMutation(client, "shop", "option-groups", group?.id ?? ""); - const [values, setValues] = React.useState({ + const [values, setValues] = useState({ name_ko: "", name_en: "", min_quantity_per_product: "0", @@ -206,7 +206,7 @@ const OptionGroupDialog: React.FC = ({ open, onClose, pr response_modifiable_ends_at: "", }); - React.useEffect(() => { + useEffect(() => { if (open) { setValues({ name_ko: group?.name_ko ?? "", @@ -347,10 +347,10 @@ type Props = { optionGroups: OptionGroupAdmin[]; }; -export const OptionGroupsTab: React.FC = ({ productId, optionGroups }) => { +export const OptionGroupsTab: FC = ({ productId, optionGroups }) => { const client = useBackendAdminClient(); - const [groupDialog, setGroupDialog] = React.useState<{ open: boolean; group?: OptionGroupAdmin }>({ open: false }); - const [optionDialog, setOptionDialog] = React.useState<{ open: boolean; optionGroup?: OptionGroupAdmin; option?: OptionAdmin }>({ open: false }); + const [groupDialog, setGroupDialog] = useState<{ open: boolean; group?: OptionGroupAdmin }>({ open: false }); + const [optionDialog, setOptionDialog] = useState<{ open: boolean; optionGroup?: OptionGroupAdmin; option?: OptionAdmin }>({ open: false }); const deleteGroupMutation = useMutation({ mutationFn: async (groupId: string) => client.delete(`v1/admin-api/shop/option-groups/${groupId}/`), diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/price_options_tab.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/price_options_tab.tsx index bb0c29f..295f935 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/price_options_tab.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/price_options_tab.tsx @@ -1,14 +1,14 @@ import { Checkbox, Divider, FormControlLabel, Stack, TextField } from "@mui/material"; -import * as React from "react"; +import { FC } from "react"; -import { ProductFormValues, SetField } from "../form"; +import { ProductFormValues, SetField } from "@apps/pyconkr-admin/components/pages/shop/product/form"; type Props = { values: ProductFormValues; setField: SetField; }; -export const PriceOptionsTab: React.FC = ({ values, setField }) => ( +export const PriceOptionsTab: FC = ({ values, setField }) => ( setField("price", e.target.value)} fullWidth /> diff --git a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/time_settings_tab.tsx b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/time_settings_tab.tsx index fe7900c..89273aa 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/product/tabs/time_settings_tab.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/product/tabs/time_settings_tab.tsx @@ -1,7 +1,7 @@ import { Stack, TextField, Typography } from "@mui/material"; -import * as React from "react"; +import { ChangeEvent, FC } from "react"; -import { ProductFormValues, SetField } from "../form"; +import { ProductFormValues, SetField } from "@apps/pyconkr-admin/components/pages/shop/product/form"; type Props = { values: ProductFormValues; @@ -14,12 +14,12 @@ const dateTimeFieldProps = (values: ProductFormValues, setField: SetField, key: label, type: "datetime-local" as const, value: values[key]?.slice(0, 16) ?? "", - onChange: (e: React.ChangeEvent) => setField(key, e.target.value), + onChange: (e: ChangeEvent) => setField(key, e.target.value), fullWidth: true, slotProps: { inputLabel: { shrink: true } }, }); -export const TimeSettingsTab: React.FC = ({ values, setField }) => ( +export const TimeSettingsTab: FC = ({ values, setField }) => ( 비워두면 항상 활성으로 처리됩니다. diff --git a/apps/pyconkr-admin/src/components/pages/shop/tag/list.tsx b/apps/pyconkr-admin/src/components/pages/shop/tag/list.tsx index 655146b..578e726 100644 --- a/apps/pyconkr-admin/src/components/pages/shop/tag/list.tsx +++ b/apps/pyconkr-admin/src/components/pages/shop/tag/list.tsx @@ -1,8 +1,8 @@ import { Chip } from "@mui/material"; -import * as React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; -import { AdminList, AdminListColumn } from "../../../layouts/admin_list"; +import { AdminList, AdminListColumn } from "@apps/pyconkr-admin/components/layouts/admin_list"; const formatStock = (stock: number) => (stock === 0 ? "무한대" : stock.toLocaleString()); const formatMaxPerUser = (qty: number) => (qty === 0 ? "제한 없음" : qty.toLocaleString()); @@ -48,4 +48,4 @@ const columns: AdminListColumn[] = [ }, ]; -export const ShopTagListPage: React.FC = () => ; +export const ShopTagListPage: FC = () => ; diff --git a/apps/pyconkr-admin/src/components/pages/sitemap/list.tsx b/apps/pyconkr-admin/src/components/pages/sitemap/list.tsx index b341c00..007c975 100644 --- a/apps/pyconkr-admin/src/components/pages/sitemap/list.tsx +++ b/apps/pyconkr-admin/src/components/pages/sitemap/list.tsx @@ -4,8 +4,9 @@ import { useListQuery, useRemovePreparedMutation, useUpdatePreparedMutation, -} from "@frontend/common/src/hooks/useAdminAPI"; -import { buildFlatSiteMap, buildNestedSiteMap } from "@frontend/common/src/utils"; +} from "@frontend/common/hooks/useAdminAPI"; +import { FlattenedSiteMapSchema, NestedSiteMapSchema } from "@frontend/common/schemas/backendAdminAPI"; +import { buildFlatSiteMap, buildNestedSiteMap } from "@frontend/common/utils"; import { Add, Delete, Edit, Save } from "@mui/icons-material"; import { Box, @@ -28,20 +29,19 @@ import { } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; +import { CSSProperties, FC, ReactElement, ReactNode, cloneElement, useEffect, useState } from "react"; import { GroupOptions, ReactSortable, SortableEvent, SortableOptions } from "react-sortablejs"; -import { FlattenedSiteMapSchema, NestedSiteMapSchema } from "../../../../../../packages/common/src/schemas/backendAdminAPI"; -import { BackendAdminSignInGuard } from "../../elements/admin_signin_guard"; -import { ErrorFallback } from "../../elements/error_fallback"; -import { AdminEditor } from "../../layouts/admin_editor"; +import { BackendAdminSignInGuard } from "@apps/pyconkr-admin/components/elements/admin_signin_guard"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { AdminEditor } from "@apps/pyconkr-admin/components/layouts/admin_editor"; type FlatSiteMap = FlattenedSiteMapSchema; type FlatSiteMapObj = Record; type NestedSiteMap = NestedSiteMapSchema; type FlatNestedSiteMap = Record; -const DepthColorMap: React.CSSProperties["backgroundColor"][] = [ +const DepthColorMap: CSSProperties["backgroundColor"][] = [ "rgba(255, 229, 204, 1)", "rgba(255, 255, 204, 1)", "rgba(204, 255, 204, 1)", @@ -88,11 +88,11 @@ type NodePropType = { type TooltipBtnPropType = IconButtonProps & { tooltip: string; - icon: React.ReactElement<{ fontSize: string }>; + icon: ReactElement<{ fontSize: string }>; }; -const TooltipBtn: React.FC = ({ tooltip, icon, ...props }) => ( - } /> +const TooltipBtn: FC = ({ tooltip, icon, ...props }) => ( + } /> ); type InnerSiteMapStateType = { @@ -105,9 +105,9 @@ type InnerSiteMapStateType = { const ModifyDetectionFields: (keyof FlatSiteMap)[] = ["order", "parent_sitemap"]; -type InnerSiteMapListProps = { domainGroupId: string; headerSlot: React.ReactNode }; +type InnerSiteMapListProps = { domainGroupId: string; headerSlot: ReactNode }; -const InnerSiteMapList: React.FC = ErrorBoundary.with( +const InnerSiteMapList: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, ({ domainGroupId, headerSlot }: InnerSiteMapListProps) => { const backendAdminAPIClient = useBackendAdminClient(); @@ -115,17 +115,17 @@ const InnerSiteMapList: React.FC = ErrorBoundary.with( const deleteMutation = useRemovePreparedMutation(backendAdminAPIClient, "cms", "sitemap"); const { mutateAsync: updateMutationAsync } = useUpdatePreparedMutation(backendAdminAPIClient, "cms", "sitemap"); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const originalFlatSiteMapObj = Object.values(data).reduce((acc, item) => ({ ...acc, [item.id]: item }), {} as FlatSiteMapObj); - const [state, setState] = React.useState({ flatSiteMap: data }); + const [state, setState] = useState({ flatSiteMap: data }); const nestedSiteMap = buildNestedSiteMap(state.flatSiteMap)[""]; const childrenFlatSiteMap = buildFlatSiteMap(nestedSiteMap); const childrenFlatSiteMapObj = Object.values(childrenFlatSiteMap).reduce((acc, item) => ({ ...acc, [item.id]: item }), {} as FlatNestedSiteMap); - React.useEffect(() => setState((ps) => ({ ...ps, flatSiteMap: data })), [data]); + useEffect(() => setState((ps) => ({ ...ps, flatSiteMap: data })), [data]); const setEditorSiteMapId = (editorSiteMapId: string | undefined) => setState((ps) => ({ ...ps, editorSiteMapId })); const setDeleteSiteMapId = (deleteSiteMapId: string | undefined) => setState((ps) => ({ ...ps, deleteSiteMapId })); @@ -197,7 +197,7 @@ const InnerSiteMapList: React.FC = ErrorBoundary.with( onAdd, }; - const Node: React.FC = ({ node, index, parentRoute, depth }) => { + const Node: FC = ({ node, index, parentRoute, depth }) => { const isSelected = state.editorSiteMapId === node.id; const route = parentRoute || node.route_code ? `${parentRoute}/${node.route_code}` : ""; const group: GroupOptions = { pull: depth !== 0, put: depth !== 0, name: node.id }; @@ -261,14 +261,14 @@ const InnerSiteMapList: React.FC = ErrorBoundary.with( }) ); -const DomainGroupSelector: React.FC = ErrorBoundary.with( +const DomainGroupSelector: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const backendAdminAPIClient = useBackendAdminClient(); const { data: choices } = useChoicesQuery(backendAdminAPIClient, "cms", "sitemap"); const domainGroupChoices = (choices["domain_group"] ?? []).filter((c): c is { const: string; title: string } => c.const !== null); - const [domainGroupId, setDomainGroupId] = React.useState(() => domainGroupChoices[0]?.const ?? ""); + const [domainGroupId, setDomainGroupId] = useState(() => domainGroupChoices[0]?.const ?? ""); if (domainGroupChoices.length === 0) { return ( @@ -295,7 +295,7 @@ const DomainGroupSelector: React.FC = ErrorBoundary.with( }) ); -export const SiteMapList: React.FC = () => ( +export const SiteMapList: FC = () => ( diff --git a/apps/pyconkr-admin/src/components/pages/user/editor.tsx b/apps/pyconkr-admin/src/components/pages/user/editor.tsx index b718036..493a84d 100644 --- a/apps/pyconkr-admin/src/components/pages/user/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/user/editor.tsx @@ -1,15 +1,16 @@ -import { useBackendAdminClient, useResetUserPasswordMutation } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useResetUserPasswordMutation } from "@frontend/common/hooks/useAdminAPI"; import { KeyOff } from "@mui/icons-material"; import { Button, ButtonProps, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { AdminEditor } from "@apps/pyconkr-admin/components/layouts/admin_editor"; +import { addErrorSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; + import { PasswordResultDialog } from "./password_result_dialog"; import { ShopOrderSection } from "./shop_order_section"; -import { addErrorSnackbar } from "../../../utils/snackbar"; -import { ErrorFallback } from "../../elements/error_fallback"; -import { AdminEditor } from "../../layouts/admin_editor"; type PageStateType = { isConfirmDialogOpen: boolean; @@ -18,12 +19,12 @@ type PageStateType = { createdUserId: string | null; }; -export const AdminUserExtEditor: React.FC = ErrorBoundary.with( +export const AdminUserExtEditor: FC = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, () => { const { id } = useParams<{ id?: string }>(); const navigate = useNavigate(); - const [pageState, setPageState] = React.useState({ + const [pageState, setPageState] = useState({ isConfirmDialogOpen: false, isResultDialogOpen: false, newPassword: null, diff --git a/apps/pyconkr-admin/src/components/pages/user/password_result_dialog.tsx b/apps/pyconkr-admin/src/components/pages/user/password_result_dialog.tsx index f25dc3c..d03cf74 100644 --- a/apps/pyconkr-admin/src/components/pages/user/password_result_dialog.tsx +++ b/apps/pyconkr-admin/src/components/pages/user/password_result_dialog.tsx @@ -1,8 +1,8 @@ import { ContentCopy } from "@mui/icons-material"; import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, InputAdornment, TextField } from "@mui/material"; -import * as React from "react"; +import { FC } from "react"; -import { addSnackbar } from "../../../utils/snackbar"; +import { addSnackbar } from "@apps/pyconkr-admin/utils/snackbar"; type PasswordResultDialogProps = { open: boolean; @@ -10,7 +10,7 @@ type PasswordResultDialogProps = { onClose: () => void; }; -export const PasswordResultDialog: React.FC = ({ open, password, onClose }) => { +export const PasswordResultDialog: FC = ({ open, password, onClose }) => { const copyPasswordToClipboard = () => { if (password) { navigator.clipboard.writeText(password).then( diff --git a/apps/pyconkr-admin/src/components/pages/user/shop_order_section.tsx b/apps/pyconkr-admin/src/components/pages/user/shop_order_section.tsx index cf0f9c9..2060c85 100644 --- a/apps/pyconkr-admin/src/components/pages/user/shop_order_section.tsx +++ b/apps/pyconkr-admin/src/components/pages/user/shop_order_section.tsx @@ -1,12 +1,12 @@ -import { useBackendAdminClient, useListQuery } from "@frontend/common/src/hooks/useAdminAPI"; +import { useBackendAdminClient, useListQuery } from "@frontend/common/hooks/useAdminAPI"; import { Alert, Chip, CircularProgress, Divider, Stack, Table, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; -import * as React from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; -import { ErrorFallback } from "../../elements/error_fallback"; -import { PAYMENT_STATUS_LABEL } from "../shop/_common/status_labels"; -import { PaymentStatus } from "../shop/order/types"; +import { ErrorFallback } from "@apps/pyconkr-admin/components/elements/error_fallback"; +import { PAYMENT_STATUS_LABEL } from "@apps/pyconkr-admin/components/pages/shop/_common/status_labels"; +import { PaymentStatus } from "@apps/pyconkr-admin/components/pages/shop/order/types"; type OrderListRow = { id: string; @@ -20,7 +20,7 @@ type OrderListRow = { const formatPrice = (price: number) => `₩${price.toLocaleString()}`; -const InnerShopOrderSection: React.FC<{ userId: string }> = ErrorBoundary.with( +const InnerShopOrderSection: FC<{ userId: string }> = ErrorBoundary.with( { fallback: ErrorFallback }, Suspense.with({ fallback: }, ({ userId }) => { const client = useBackendAdminClient(); @@ -81,4 +81,4 @@ const InnerShopOrderSection: React.FC<{ userId: string }> = ErrorBoundary.with( }) ); -export const ShopOrderSection: React.FC<{ userId: string }> = (props) => ; +export const ShopOrderSection: FC<{ userId: string }> = (props) => ; diff --git a/apps/pyconkr-admin/src/consts/mdx_components.ts b/apps/pyconkr-admin/src/consts/mdx_components.ts index 8b2b767..f9b421e 100644 --- a/apps/pyconkr-admin/src/consts/mdx_components.ts +++ b/apps/pyconkr-admin/src/consts/mdx_components.ts @@ -1,160 +1,295 @@ // 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. -import { Components } from "@frontend/common"; -import * as Shop from "@frontend/shop"; -import * as mui from "@mui/material"; +import { LottiePlayer, NetworkLottiePlayer } from "@frontend/common/components"; +import { + Confetti, + FAQAccordion, + Map as MDXMap, + PrimaryStyledDetails, + SecondaryStyledDetails, + SessionList, + SessionTimeTable, + StyledFullWidthButton, +} from "@frontend/common/components/mdx_components"; +import { PriceDisplay, ShopContextProvider, SignInGuard, UserSignInAccount, UserSignInMethod } from "@frontend/shop/components/common"; +import { CartStatus, OrderList, PatronList, ProductImageCardList, ProductList, UserInfo } from "@frontend/shop/components/features"; +import { + Accordion, + AccordionActions, + AccordionDetails, + AccordionSummary, + Alert, + AlertTitle, + AppBar, + Autocomplete, + Avatar, + AvatarGroup, + Backdrop, + Badge, + BottomNavigation, + BottomNavigationAction, + Box, + Breadcrumbs, + Button, + ButtonBase, + ButtonGroup, + Card, + CardActionArea, + CardActions, + CardContent, + CardHeader, + CardMedia, + Checkbox, + Chip, + CircularProgress, + Collapse, + Container, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Divider, + Drawer, + Fab, + Fade, + FilledInput, + FormControl, + FormControlLabel, + FormGroup, + FormHelperText, + FormLabel, + Grid, + Grow, + Icon, + IconButton, + ImageList, + ImageListItem, + ImageListItemBar, + Input, + InputAdornment, + InputBase, + InputLabel, + LinearProgress, + Link, + List, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemIcon, + ListItemSecondaryAction, + ListItemText, + ListSubheader, + Menu, + MenuItem, + MenuList, + MobileStepper, + Modal, + NativeSelect, + NoSsr, + OutlinedInput, + Pagination, + PaginationItem, + Paper, + Popover, + Popper, + Portal, + Radio, + RadioGroup, + Rating, + Select, + Skeleton, + Slide, + Slider, + Snackbar, + SnackbarContent, + SpeedDial, + SpeedDialAction, + SpeedDialIcon, + Stack, + Step, + StepButton, + StepConnector, + StepContent, + StepIcon, + StepLabel, + Stepper, + SvgIcon, + SwipeableDrawer, + Switch, + Tab, + TabScrollButton, + Table, + TableBody, + TableCell, + TableContainer, + TableFooter, + TableHead, + TablePagination, + TableRow, + TableSortLabel, + Tabs, + TextField, + TextareaAutosize, + ToggleButton, + ToggleButtonGroup, + Toolbar, + Tooltip, + Typography, + Zoom, +} from "@mui/material"; import type { MDXComponents } from "mdx/types.js"; const MUIMDXComponents: MDXComponents = { - Mui__material__Accordion: mui.Accordion, - Mui__material__AccordionActions: mui.AccordionActions, - Mui__material__AccordionDetails: mui.AccordionDetails, - Mui__material__AccordionSummary: mui.AccordionSummary, - Mui__material__Alert: mui.Alert, - Mui__material__AlertTitle: mui.AlertTitle, - Mui__material__AppBar: mui.AppBar, - Mui__material__Autocomplete: mui.Autocomplete, - Mui__material__Avatar: mui.Avatar, - Mui__material__AvatarGroup: mui.AvatarGroup, - Mui__material__Backdrop: mui.Backdrop, - Mui__material__Badge: mui.Badge, - Mui__material__BottomNavigation: mui.BottomNavigation, - Mui__material__BottomNavigationAction: mui.BottomNavigationAction, - Mui__material__Box: mui.Box, - Mui__material__Breadcrumbs: mui.Breadcrumbs, - Mui__material__Button: mui.Button, - Mui__material__ButtonBase: mui.ButtonBase, - Mui__material__ButtonGroup: mui.ButtonGroup, - Mui__material__Card: mui.Card, - Mui__material__CardActionArea: mui.CardActionArea, - Mui__material__CardActions: mui.CardActions, - Mui__material__CardContent: mui.CardContent, - Mui__material__CardHeader: mui.CardHeader, - Mui__material__CardMedia: mui.CardMedia, - Mui__material__Checkbox: mui.Checkbox, - Mui__material__Chip: mui.Chip, - Mui__material__CircularProgress: mui.CircularProgress, - Mui__material__Collapse: mui.Collapse, - Mui__material__Container: mui.Container, - Mui__material__Dialog: mui.Dialog, - Mui__material__DialogActions: mui.DialogActions, - Mui__material__DialogContent: mui.DialogContent, - Mui__material__DialogContentText: mui.DialogContentText, - Mui__material__DialogTitle: mui.DialogTitle, - Mui__material__Divider: mui.Divider, - Mui__material__Drawer: mui.Drawer, - Mui__material__Fab: mui.Fab, - Mui__material__Fade: mui.Fade, - Mui__material__FilledInput: mui.FilledInput, - Mui__material__FormControl: mui.FormControl, - Mui__material__FormControlLabel: mui.FormControlLabel, - Mui__material__FormGroup: mui.FormGroup, - Mui__material__FormHelperText: mui.FormHelperText, - Mui__material__FormLabel: mui.FormLabel, - Mui__material__Grid: mui.Grid, - Mui__material__Grow: mui.Grow, - Mui__material__Icon: mui.Icon, - Mui__material__IconButton: mui.IconButton, - Mui__material__ImageList: mui.ImageList, - Mui__material__ImageListItem: mui.ImageListItem, - Mui__material__ImageListItemBar: mui.ImageListItemBar, - Mui__material__Input: mui.Input, - Mui__material__InputAdornment: mui.InputAdornment, - Mui__material__InputBase: mui.InputBase, - Mui__material__InputLabel: mui.InputLabel, - Mui__material__LinearProgress: mui.LinearProgress, - Mui__material__Link: mui.Link, - Mui__material__List: mui.List, - Mui__material__ListItem: mui.ListItem, - Mui__material__ListItemAvatar: mui.ListItemAvatar, - Mui__material__ListItemButton: mui.ListItemButton, - Mui__material__ListItemIcon: mui.ListItemIcon, - Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, - Mui__material__ListItemText: mui.ListItemText, - Mui__material__ListSubheader: mui.ListSubheader, - Mui__material__Menu: mui.Menu, - Mui__material__MenuItem: mui.MenuItem, - Mui__material__MenuList: mui.MenuList, - Mui__material__MobileStepper: mui.MobileStepper, - Mui__material__Modal: mui.Modal, - Mui__material__NativeSelect: mui.NativeSelect, - Mui__material__NoSsr: mui.NoSsr, - Mui__material__OutlinedInput: mui.OutlinedInput, - Mui__material__Pagination: mui.Pagination, - Mui__material__PaginationItem: mui.PaginationItem, - Mui__material__Paper: mui.Paper, - Mui__material__Popover: mui.Popover, - Mui__material__Popper: mui.Popper, - Mui__material__Portal: mui.Portal, - Mui__material__Radio: mui.Radio, - Mui__material__RadioGroup: mui.RadioGroup, - Mui__material__Rating: mui.Rating, - Mui__material__Select: mui.Select, - Mui__material__Skeleton: mui.Skeleton, - Mui__material__Slide: mui.Slide, - Mui__material__Slider: mui.Slider, - Mui__material__Snackbar: mui.Snackbar, - Mui__material__SnackbarContent: mui.SnackbarContent, - Mui__material__SpeedDial: mui.SpeedDial, - Mui__material__SpeedDialAction: mui.SpeedDialAction, - Mui__material__SpeedDialIcon: mui.SpeedDialIcon, - Mui__material__Stack: mui.Stack, - Mui__material__Step: mui.Step, - Mui__material__StepButton: mui.StepButton, - Mui__material__StepConnector: mui.StepConnector, - Mui__material__StepContent: mui.StepContent, - Mui__material__StepIcon: mui.StepIcon, - Mui__material__StepLabel: mui.StepLabel, - Mui__material__Stepper: mui.Stepper, - Mui__material__SvgIcon: mui.SvgIcon, - Mui__material__SwipeableDrawer: mui.SwipeableDrawer, - Mui__material__Switch: mui.Switch, - Mui__material__Tab: mui.Tab, - Mui__material__Table: mui.Table, - Mui__material__TableBody: mui.TableBody, - Mui__material__TableCell: mui.TableCell, - Mui__material__TableContainer: mui.TableContainer, - Mui__material__TableFooter: mui.TableFooter, - Mui__material__TableHead: mui.TableHead, - Mui__material__TablePagination: mui.TablePagination, - Mui__material__TableRow: mui.TableRow, - Mui__material__TableSortLabel: mui.TableSortLabel, - Mui__material__Tabs: mui.Tabs, - Mui__material__TabScrollButton: mui.TabScrollButton, - Mui__material__TextField: mui.TextField, - Mui__material__TextareaAutosize: mui.TextareaAutosize, - Mui__material__ToggleButton: mui.ToggleButton, - Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, - Mui__material__Toolbar: mui.Toolbar, - Mui__material__Tooltip: mui.Tooltip, - Mui__material__Typography: mui.Typography, - Mui__material__Zoom: mui.Zoom, + Mui__material__Accordion: Accordion, + Mui__material__AccordionActions: AccordionActions, + Mui__material__AccordionDetails: AccordionDetails, + Mui__material__AccordionSummary: AccordionSummary, + Mui__material__Alert: Alert, + Mui__material__AlertTitle: AlertTitle, + Mui__material__AppBar: AppBar, + Mui__material__Autocomplete: Autocomplete, + Mui__material__Avatar: Avatar, + Mui__material__AvatarGroup: AvatarGroup, + Mui__material__Backdrop: Backdrop, + Mui__material__Badge: Badge, + Mui__material__BottomNavigation: BottomNavigation, + Mui__material__BottomNavigationAction: BottomNavigationAction, + Mui__material__Box: Box, + Mui__material__Breadcrumbs: Breadcrumbs, + Mui__material__Button: Button, + Mui__material__ButtonBase: ButtonBase, + Mui__material__ButtonGroup: ButtonGroup, + Mui__material__Card: Card, + Mui__material__CardActionArea: CardActionArea, + Mui__material__CardActions: CardActions, + Mui__material__CardContent: CardContent, + Mui__material__CardHeader: CardHeader, + Mui__material__CardMedia: CardMedia, + Mui__material__Checkbox: Checkbox, + Mui__material__Chip: Chip, + Mui__material__CircularProgress: CircularProgress, + Mui__material__Collapse: Collapse, + Mui__material__Container: Container, + Mui__material__Dialog: Dialog, + Mui__material__DialogActions: DialogActions, + Mui__material__DialogContent: DialogContent, + Mui__material__DialogContentText: DialogContentText, + Mui__material__DialogTitle: DialogTitle, + Mui__material__Divider: Divider, + Mui__material__Drawer: Drawer, + Mui__material__Fab: Fab, + Mui__material__Fade: Fade, + Mui__material__FilledInput: FilledInput, + Mui__material__FormControl: FormControl, + Mui__material__FormControlLabel: FormControlLabel, + Mui__material__FormGroup: FormGroup, + Mui__material__FormHelperText: FormHelperText, + Mui__material__FormLabel: FormLabel, + Mui__material__Grid: Grid, + Mui__material__Grow: Grow, + Mui__material__Icon: Icon, + Mui__material__IconButton: IconButton, + Mui__material__ImageList: ImageList, + Mui__material__ImageListItem: ImageListItem, + Mui__material__ImageListItemBar: ImageListItemBar, + Mui__material__Input: Input, + Mui__material__InputAdornment: InputAdornment, + Mui__material__InputBase: InputBase, + Mui__material__InputLabel: InputLabel, + Mui__material__LinearProgress: LinearProgress, + Mui__material__Link: Link, + Mui__material__List: List, + Mui__material__ListItem: ListItem, + Mui__material__ListItemAvatar: ListItemAvatar, + Mui__material__ListItemButton: ListItemButton, + Mui__material__ListItemIcon: ListItemIcon, + Mui__material__ListItemSecondaryAction: ListItemSecondaryAction, + Mui__material__ListItemText: ListItemText, + Mui__material__ListSubheader: ListSubheader, + Mui__material__Menu: Menu, + Mui__material__MenuItem: MenuItem, + Mui__material__MenuList: MenuList, + Mui__material__MobileStepper: MobileStepper, + Mui__material__Modal: Modal, + Mui__material__NativeSelect: NativeSelect, + Mui__material__NoSsr: NoSsr, + Mui__material__OutlinedInput: OutlinedInput, + Mui__material__Pagination: Pagination, + Mui__material__PaginationItem: PaginationItem, + Mui__material__Paper: Paper, + Mui__material__Popover: Popover, + Mui__material__Popper: Popper, + Mui__material__Portal: Portal, + Mui__material__Radio: Radio, + Mui__material__RadioGroup: RadioGroup, + Mui__material__Rating: Rating, + Mui__material__Select: Select, + Mui__material__Skeleton: Skeleton, + Mui__material__Slide: Slide, + Mui__material__Slider: Slider, + Mui__material__Snackbar: Snackbar, + Mui__material__SnackbarContent: SnackbarContent, + Mui__material__SpeedDial: SpeedDial, + Mui__material__SpeedDialAction: SpeedDialAction, + Mui__material__SpeedDialIcon: SpeedDialIcon, + Mui__material__Stack: Stack, + Mui__material__Step: Step, + Mui__material__StepButton: StepButton, + Mui__material__StepConnector: StepConnector, + Mui__material__StepContent: StepContent, + Mui__material__StepIcon: StepIcon, + Mui__material__StepLabel: StepLabel, + Mui__material__Stepper: Stepper, + Mui__material__SvgIcon: SvgIcon, + Mui__material__SwipeableDrawer: SwipeableDrawer, + Mui__material__Switch: Switch, + Mui__material__Tab: Tab, + Mui__material__Table: Table, + Mui__material__TableBody: TableBody, + Mui__material__TableCell: TableCell, + Mui__material__TableContainer: TableContainer, + Mui__material__TableFooter: TableFooter, + Mui__material__TableHead: TableHead, + Mui__material__TablePagination: TablePagination, + Mui__material__TableRow: TableRow, + Mui__material__TableSortLabel: TableSortLabel, + Mui__material__Tabs: Tabs, + Mui__material__TabScrollButton: TabScrollButton, + Mui__material__TextField: TextField, + Mui__material__TextareaAutosize: TextareaAutosize, + Mui__material__ToggleButton: ToggleButton, + Mui__material__ToggleButtonGroup: ToggleButtonGroup, + Mui__material__Toolbar: Toolbar, + Mui__material__Tooltip: Tooltip, + Mui__material__Typography: Typography, + Mui__material__Zoom: Zoom, }; const PyConKRCommonMDXComponents: MDXComponents = { - Common__Components__Lottie: Components.LottiePlayer, - Common__Components__NetworkLottie: Components.NetworkLottiePlayer, - Common__Components__MDX__Confetti: Components.MDX.Confetti, - Common__Components__MDX__PrimaryStyledDetails: Components.MDX.PrimaryStyledDetails, - Common__Components__MDX__SecondaryStyledDetails: Components.MDX.SecondaryStyledDetails, - Common__Components__MDX__Map: Components.MDX.Map, - Common__Components__MDX__FAQAccordion: Components.MDX.FAQAccordion, - Common__Components__MDX__FullWidthStyledButton: Components.MDX.StyledFullWidthButton, - Common__Components__Session__List: Components.MDX.SessionList, - Common__Components__Session__TimeTable: Components.MDX.SessionTimeTable, + Common__Components__Lottie: LottiePlayer, + Common__Components__NetworkLottie: NetworkLottiePlayer, + Common__Components__MDX__Confetti: Confetti, + Common__Components__MDX__PrimaryStyledDetails: PrimaryStyledDetails, + Common__Components__MDX__SecondaryStyledDetails: SecondaryStyledDetails, + Common__Components__MDX__Map: MDXMap, + Common__Components__MDX__FAQAccordion: FAQAccordion, + Common__Components__MDX__FullWidthStyledButton: StyledFullWidthButton, + Common__Components__Session__List: SessionList, + Common__Components__Session__TimeTable: SessionTimeTable, }; const PythonKRShopMDXComponents: MDXComponents = { - Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, - Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, - Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, - Shop__Common__UserSignInMethod: Shop.Components.Common.UserSignInMethod, - Shop__Common__UserSignInAccount: Shop.Components.Common.UserSignInAccount, - Shop__Feature__CartStatus: Shop.Components.Features.CartStatus, - Shop__Feature__ProductList: Shop.Components.Features.ProductList, - Shop__Feature__ProductImageCardList: Shop.Components.Features.ProductImageCardList, - Shop__Feature__OrderList: Shop.Components.Features.OrderList, - Shop__Feature__UserInfo: Shop.Components.Features.UserInfo, - Shop__Feature__PatronList: Shop.Components.Features.PatronList, + Shop__Common__PriceDisplay: PriceDisplay, + Shop__Common__SignInGuard: SignInGuard, + Shop__Common__ContextProvider: ShopContextProvider, + Shop__Common__UserSignInMethod: UserSignInMethod, + Shop__Common__UserSignInAccount: UserSignInAccount, + Shop__Feature__CartStatus: CartStatus, + Shop__Feature__ProductList: ProductList, + Shop__Feature__ProductImageCardList: ProductImageCardList, + Shop__Feature__OrderList: OrderList, + Shop__Feature__UserInfo: UserInfo, + Shop__Feature__PatronList: PatronList, }; export const PyConKRMDXComponents = { diff --git a/apps/pyconkr-admin/src/main.tsx b/apps/pyconkr-admin/src/main.tsx index a2857dc..4efe2c1 100644 --- a/apps/pyconkr-admin/src/main.tsx +++ b/apps/pyconkr-admin/src/main.tsx @@ -1,13 +1,15 @@ -import { Components, Utils } from "@frontend/common"; -import type { ContextOptions } from "@frontend/common/src/contexts"; -import * as Shop from "@frontend/shop"; +import { CenteredPage, CommonContextProvider } from "@frontend/common/components"; +import type { ContextOptions } from "@frontend/common/contexts"; +import { registerChunkLoadErrorReloadHandler } from "@frontend/common/utils"; +import { ShopContextProvider } from "@frontend/shop/components/common"; +import { ContextOptions as ShopContextOptions } from "@frontend/shop/contexts"; import { CircularProgress } from "@mui/material"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { matchQuery, MutationCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { SnackbarProvider } from "notistack"; -import * as React from "react"; -import * as ReactDom from "react-dom/client"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom"; import { Layout } from "./components/layouts/global"; @@ -49,7 +51,7 @@ const CommonOptions: ContextOptions = { mdxComponents: PyConKRMDXComponents, }; -const ShopOptions: Shop.Contexts.ContextOptions = { +const ShopOptions: ShopContextOptions = { language: "ko", shopApiDomain: import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN, shopApiCSRFCookieName: import.meta.env.VITE_PYCONKR_SHOP_CSRF_COOKIE_NAME, @@ -57,22 +59,22 @@ const ShopOptions: Shop.Contexts.ContextOptions = { shopImpAccountId: import.meta.env.VITE_PYCONKR_SHOP_IMP_ACCOUNT_ID, }; -Utils.registerChunkLoadErrorReloadHandler(); +registerChunkLoadErrorReloadHandler(); -ReactDom.createRoot(document.getElementById("root")!).render( - - 문제가 발생했습니다, 새로고침을 해주세요.}> +createRoot(document.getElementById("root")!).render( + + 문제가 발생했습니다, 새로고침을 해주세요.}> + - + } > - - + + @@ -86,10 +88,10 @@ ReactDom.createRoot(document.getElementById("root")!).render( - - + + - + ); diff --git a/apps/pyconkr-admin/src/vite-env.d.ts b/apps/pyconkr-admin/src/vite-env.d.ts index f42f232..1ba6712 100644 --- a/apps/pyconkr-admin/src/vite-env.d.ts +++ b/apps/pyconkr-admin/src/vite-env.d.ts @@ -1,8 +1,7 @@ /// -import * as React from "react"; - +import { FC, SVGProps } from "react"; declare module "*.svg?react" { - const component: React.FC>; + const component: FC>; export default component; } diff --git a/apps/pyconkr-admin/tsconfig.json b/apps/pyconkr-admin/tsconfig.json index ace5325..a487c70 100644 --- a/apps/pyconkr-admin/tsconfig.json +++ b/apps/pyconkr-admin/tsconfig.json @@ -1,32 +1,4 @@ { - "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true, - "forceConsistentCasingInFileNames": false, - - /* Paths */ - "baseUrl": ".", - "paths": { - "@apps/pyconkr-admin/*": ["apps/pyconkr-admin/src/*"], - } - }, - "include": ["src", "vite.config.ts", "vite-env.d.ts", "../../types"], + "extends": "../../tsconfig.base.json", + "include": ["src", "vite.config.ts", "vite-env.d.ts", "../../types"] } diff --git a/apps/pyconkr-admin/vite.config.ts b/apps/pyconkr-admin/vite.config.ts index 6b7e2dc..ca277d0 100644 --- a/apps/pyconkr-admin/vite.config.ts +++ b/apps/pyconkr-admin/vite.config.ts @@ -20,9 +20,8 @@ export default defineConfig(({ mode }) => { plugins: [react(), mdx(), ...(isLocalHttpBackend ? [] : [mkcert({ hosts: [host] })]), svgr()], resolve: { alias: { - "@frontend/common/src": path.resolve(__dirname, "../../packages/common/src"), - "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), - "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/common": path.resolve(__dirname, "../../packages/common/src"), + "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src"), "@apps/pyconkr-admin": path.resolve(__dirname, "./src"), }, }, diff --git a/apps/pyconkr-participant-portal/src/App.tsx b/apps/pyconkr-participant-portal/src/App.tsx index f53bae5..63888cf 100644 --- a/apps/pyconkr-participant-portal/src/App.tsx +++ b/apps/pyconkr-participant-portal/src/App.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import { FC } from "react"; import { Navigate, Route, Routes } from "react-router-dom"; import { Layout } from "./components/layout.tsx"; @@ -9,7 +9,7 @@ import { SessionEditor } from "./components/pages/session_editor"; import { SignInPage } from "./components/pages/signin.tsx"; import { SponsorEditor } from "./components/pages/sponsor_editor"; -export const App: React.FC = () => ( +export const App: FC = () => ( }> } /> diff --git a/apps/pyconkr-participant-portal/src/components/dialogs/change_password.tsx b/apps/pyconkr-participant-portal/src/components/dialogs/change_password.tsx index 52444ca..e802160 100644 --- a/apps/pyconkr-participant-portal/src/components/dialogs/change_password.tsx +++ b/apps/pyconkr-participant-portal/src/components/dialogs/change_password.tsx @@ -1,11 +1,11 @@ -import { useChangePasswordMutation, useParticipantPortalClient } from "@frontend/common/src/hooks/useParticipantPortalAPI"; -import { getFormValue, isFormValid } from "@frontend/common/src/utils"; -import { BackendAPIClientError } from "@frontend/common/src/apis"; +import { BackendAPIClientError } from "@frontend/common/apis"; +import { useChangePasswordMutation, useParticipantPortalClient } from "@frontend/common/hooks/useParticipantPortalAPI"; +import { getFormValue, isFormValid } from "@frontend/common/utils"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField } from "@mui/material"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; +import { FC, ReactNode, useRef } from "react"; -import { useAppContext } from "../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-participant-portal/contexts/app_context"; type ChangePasswordDialogProps = { open: boolean; @@ -18,13 +18,13 @@ type PasswordFormDataType = { new_password_confirm: string; }; -export const ChangePasswordDialog: React.FC = ({ open, onClose }) => { - const formRef = React.useRef(null); +export const ChangePasswordDialog: FC = ({ open, onClose }) => { + const formRef = useRef(null); const { language } = useAppContext(); const participantPortalClient = useParticipantPortalClient(); const changePasswordMutation = useChangePasswordMutation(participantPortalClient); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const titleStr = language === "ko" ? "비밀번호 변경" : "Change Password"; diff --git a/apps/pyconkr-participant-portal/src/components/dialogs/modification_audit_cancel_confirm.tsx b/apps/pyconkr-participant-portal/src/components/dialogs/modification_audit_cancel_confirm.tsx index 2e8ae85..9988b27 100644 --- a/apps/pyconkr-participant-portal/src/components/dialogs/modification_audit_cancel_confirm.tsx +++ b/apps/pyconkr-participant-portal/src/components/dialogs/modification_audit_cancel_confirm.tsx @@ -1,10 +1,10 @@ -import { useCancelModificationAuditMutation, useParticipantPortalClient } from "@frontend/common/src/hooks/useParticipantPortalAPI"; -import { BackendAPIClientError } from "@frontend/common/src/apis"; +import { BackendAPIClientError } from "@frontend/common/apis"; +import { useCancelModificationAuditMutation, useParticipantPortalClient } from "@frontend/common/hooks/useParticipantPortalAPI"; import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; +import { FC, ReactNode, useRef } from "react"; -import { useAppContext } from "../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-participant-portal/contexts/app_context"; type ModificationAuditCancelConfirmDialogProps = { modificationAuditId: string; @@ -12,13 +12,13 @@ type ModificationAuditCancelConfirmDialogProps = { onClose: () => void; }; -export const ModificationAuditCancelConfirmDialog: React.FC = ({ open, onClose, modificationAuditId }) => { - const reasonInputRef = React.useRef(null); +export const ModificationAuditCancelConfirmDialog: FC = ({ open, onClose, modificationAuditId }) => { + const reasonInputRef = useRef(null); const { language } = useAppContext(); const participantPortalClient = useParticipantPortalClient(); const cancelModificationAuditMutation = useCancelModificationAuditMutation(participantPortalClient); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const titleStr = language === "ko" ? "수정 요청 철회 확인" : "Confirm Withdrawal of Modification Request"; diff --git a/apps/pyconkr-participant-portal/src/components/dialogs/public_file_upload.tsx b/apps/pyconkr-participant-portal/src/components/dialogs/public_file_upload.tsx index 9794dc8..4cba2b5 100644 --- a/apps/pyconkr-participant-portal/src/components/dialogs/public_file_upload.tsx +++ b/apps/pyconkr-participant-portal/src/components/dialogs/public_file_upload.tsx @@ -1,12 +1,12 @@ -import { Components } from "@frontend/common"; -import { useParticipantPortalClient, useUploadPublicFileMutation } from "@frontend/common/src/hooks/useParticipantPortalAPI"; -import { BackendAPIClientError } from "@frontend/common/src/apis"; +import { BackendAPIClientError } from "@frontend/common/apis"; +import { DndFileInput } from "@frontend/common/components"; +import { useParticipantPortalClient, useUploadPublicFileMutation } from "@frontend/common/hooks/useParticipantPortalAPI"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; import { enqueueSnackbar, OptionsObject } from "notistack"; -import * as React from "react"; -import * as R from "remeda"; +import { FC, ReactNode, useState } from "react"; +import { isEmpty, isString } from "remeda"; -import { useAppContext } from "../../contexts/app_context"; +import { useAppContext } from "@apps/pyconkr-participant-portal/contexts/app_context"; type SetUploadedFileAsValueConfirmDialogProps = { language: "ko" | "en"; @@ -15,12 +15,7 @@ type SetUploadedFileAsValueConfirmDialogProps = { setValueAndCloseAll: () => void; }; -const SetUploadedFileAsValueConfirmDialog: React.FC = ({ - language, - open, - closeAll, - setValueAndCloseAll, -}) => { +const SetUploadedFileAsValueConfirmDialog: FC = ({ language, open, closeAll, setValueAndCloseAll }) => { const titleStr = language === "ko" ? "파일 업로드 완료" : "File Upload Completed"; const confirmStr = language === "ko" ? "업로드한 파일을 현재 값으로 설정하시겠습니까?" : "Do you want to set the uploaded file as the current value?"; @@ -50,13 +45,13 @@ type PublicFileUploadDialogState = { uploadedFileId: string | null; }; -export const PublicFileUploadDialog: React.FC = ({ open, onClose, setFileIdAsValue }) => { +export const PublicFileUploadDialog: FC = ({ open, onClose, setFileIdAsValue }) => { const { language } = useAppContext(); - const [dialogState, setDialogState] = React.useState({ selectedFile: null, uploadedFileId: null }); + const [dialogState, setDialogState] = useState({ selectedFile: null, uploadedFileId: null }); const participantPortalClient = useParticipantPortalClient(); const uploadPublicFileMutation = useUploadPublicFileMutation(participantPortalClient); - const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) => + const addSnackbar = (c: string | ReactNode, variant: OptionsObject["variant"]) => enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } }); const titleStr = language === "ko" ? "파일 업로드" : "Upload File"; @@ -101,14 +96,14 @@ export const PublicFileUploadDialog: React.FC = ({ <> - +