Skip to content

Commit 93f8823

Browse files
committed
feat: 등록 QR 코드 링크 추가
1 parent d52e653 commit 93f8823

File tree

8 files changed

+95
-21
lines changed

8 files changed

+95
-21
lines changed

apps/pyconkr/src/components/layout/Header/Mobile/MobileLanguageToggle.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,24 @@ interface MobileLanguageToggleProps {
1111
export const MobileLanguageToggle: React.FC<MobileLanguageToggleProps> = ({ isMainPath = true }) => {
1212
const { language, setAppContext } = useAppContext();
1313

14-
const handleLanguageChange = (newLanguage: "ko" | "en") => {
14+
const toggleLanguage = () => {
15+
const newLanguage = language === "ko" ? "en" : "ko";
1516
localStorage.setItem(LOCAL_STORAGE_LANGUAGE_KEY, newLanguage);
1617
setAppContext((ps) => ({ ...ps, language: newLanguage }));
1718
};
19+
1820
return (
19-
<ToggleContainer isMainPath={isMainPath}>
20-
<LanguageButton isActive={language === "ko"} isMainPath={isMainPath} onClick={() => handleLanguageChange("ko")}>
21-
KO
22-
</LanguageButton>
23-
<LanguageButton isActive={language === "en"} isMainPath={isMainPath} onClick={() => handleLanguageChange("en")}>
24-
EN
25-
</LanguageButton>
21+
<ToggleContainer isMainPath={isMainPath} onClick={toggleLanguage}>
22+
<LanguageButton isActive={language === "ko"} isMainPath={isMainPath} children="KO" />
23+
<LanguageButton isActive={language === "en"} isMainPath={isMainPath} children="EN" />
2624
</ToggleContainer>
2725
);
2826
};
2927

3028
const ToggleContainer = styled("div")<{ isMainPath: boolean }>(({ theme, isMainPath }) => ({
3129
display: "flex",
32-
width: 94,
33-
height: 29,
30+
width: "4rem",
31+
height: "1.5rem",
3432
border: "1px solid white",
3533
borderRadius: 15,
3634
padding: 2,

apps/pyconkr/src/components/layout/Header/Mobile/MobileNavigation.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as R from "remeda";
99
import { HamburgerButton } from "./HamburgerButton";
1010
import { MobileLanguageToggle } from "./MobileLanguageToggle";
1111
import { SignInButton } from "../../SignInButton";
12+
import { ScanCodeButton } from "../../UserScanCodeButton";
1213

1314
type MenuType = BackendAPISchemas.NestedSiteMapSchema;
1415

@@ -195,12 +196,15 @@ export const MobileNavigation: React.FC<MobileNavigationProps> = ({ isOpen, onCl
195196
{navState.level === "depth3" && renderDepth3Menu()}
196197
</NavigationContent>
197198

198-
<BottomActions isMainPath={isMainPath}>
199-
<Stack direction="row" alignItems="center" spacing={6.25}>
199+
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ px: 2, py: 4, flexGrow: 0, width: "100%" }}>
200+
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1 }}>
200201
<MobileLanguageToggle isMainPath={isMainPath} />
201-
<SignInButton isMobile={true} isMainPath={isMainPath} onClose={handleClose} />
202202
</Stack>
203-
</BottomActions>
203+
<ScanCodeButton />
204+
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1 }}>
205+
<SignInButton isMobile isMainPath={isMainPath} onClose={handleClose} />
206+
</Stack>
207+
</Stack>
204208
</DrawerContent>
205209
</StyledDrawer>
206210
);
@@ -300,12 +304,6 @@ const MenuChip = styled(Chip)<{ isMainPath?: boolean }>(({ theme, isMainPath = t
300304
},
301305
}));
302306

303-
const BottomActions = styled(Stack)<{ isMainPath: boolean }>({
304-
padding: "20px 23px",
305-
gap: 50,
306-
alignItems: "center",
307-
});
308-
309307
const HeaderTitle = styled(Typography)<{ isMainPath: boolean }>(({ theme, isMainPath }) => ({
310308
color: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
311309
fontSize: 18,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useAppContext } from "../../../contexts/app_context";
1111
import { CartBadgeButton } from "../CartBadgeButton";
1212
import LanguageSelector from "../LanguageSelector";
1313
import { SignInButton } from "../SignInButton";
14+
import { ScanCodeIconButton } from "../UserScanCodeButton";
1415
import { MobileHeader } from "./Mobile/MobileHeader";
1516

1617
type MenuType = BackendAPISchemas.NestedSiteMapSchema;
@@ -148,6 +149,7 @@ const Header: React.FC = () => {
148149
<Stack direction="row" alignItems="center" gap={1} sx={{ marginLeft: "auto" }}>
149150
<LanguageSelector />
150151
<CartBadgeButton />
152+
<ScanCodeIconButton />
151153
<SignInButton />
152154
</Stack>
153155
</NavSideElementContainer>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const InnerSignInButtonImpl: React.FC<InnerSignInButtonImplPropType> = ({
4949
fontWeight: 500,
5050
textTransform: "none",
5151
minWidth: "auto",
52-
padding: "0 13px",
52+
padding: 0,
5353

5454
"&:hover": {
5555
backgroundColor: isMainPath ? "rgba(255, 255, 255, 0.1)" : "rgba(18, 109, 127, 0.1)",
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as Shop from "@frontend/shop";
2+
import { QrCode2 } from "@mui/icons-material";
3+
import { Button, IconButton, IconButtonProps } from "@mui/material";
4+
import { ErrorBoundary, Suspense } from "@suspensive/react";
5+
import * as React from "react";
6+
7+
import { useAppContext } from "../../../contexts/app_context";
8+
9+
export const ScanCodeIconButton: React.FC<{ sx?: IconButtonProps["sx"] }> = Suspense.with(
10+
{ fallback: <React.Fragment /> },
11+
ErrorBoundary.with({ fallback: <React.Fragment /> }, ({ sx }) => {
12+
const shopAPIClient = Shop.Hooks.useShopClient();
13+
const { data } = Shop.Hooks.useUserInfo(shopAPIClient);
14+
15+
const iconBtnStyle: IconButtonProps["sx"] = (theme) => ({
16+
color: theme.palette.primary.nonFocus,
17+
"&:hover": { color: theme.palette.primary.dark },
18+
"&:active": { color: theme.palette.primary.main },
19+
transition: "color 0.4s ease, background-color 0.4s ease",
20+
});
21+
22+
return (
23+
<a href={data.scancode_url} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none" }}>
24+
<IconButton children={<QrCode2 />} sx={sx ?? iconBtnStyle} />
25+
</a>
26+
);
27+
})
28+
);
29+
30+
export const ScanCodeButton: React.FC = Suspense.with(
31+
{ fallback: null },
32+
ErrorBoundary.with({ fallback: null }, () => {
33+
const { language } = useAppContext();
34+
const shopAPIClient = Shop.Hooks.useShopClient();
35+
const { data } = Shop.Hooks.useUserInfo(shopAPIClient);
36+
37+
const buttonText = language === "ko" ? "등록 코드" : "Entrance QR Code";
38+
39+
return (
40+
<a
41+
href={data.scancode_url}
42+
target="_blank"
43+
rel="noopener noreferrer"
44+
style={{
45+
textDecoration: "none",
46+
display: "flex",
47+
alignItems: "center",
48+
justifyContent: "center",
49+
flex: 1,
50+
}}
51+
>
52+
<Button startIcon={<QrCode2 />} sx={{ color: "white", p: 0, textTransform: "none", lineHeight: 1 }} children={buttonText} />
53+
</a>
54+
);
55+
})
56+
);

packages/shop/src/apis/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ namespace ShopAPIs {
6767
}
6868
};
6969

70+
/**
71+
* 현재 사용자의 row 정보를 가져옵니다.
72+
* @returns 로그인 정보
73+
*/
74+
export const retrieveUserRowInfo = (client: ShopAPIClient) => async () => client.get<ShopSchemas.UserRow>("v1/users/");
75+
7076
/**
7177
* 노출 중인 모든 상품의 목록을 가져옵니다.
7278
* @returns 노출 중인 모든 상품의 목록

packages/shop/src/hooks/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import ShopSchemas from "../schemas";
99
const QUERY_KEYS = {
1010
BASE: ["query", "shop"],
1111
USER: ["query", "shop", "user"],
12+
USER_ROW: ["query", "shop", "user_row"],
1213
PRODUCT_LIST: ["query", "shop", "products"],
1314
CART_INFO: ["query", "shop", "cart"],
1415
ORDER_LIST: ["query", "shop", "orders"],
@@ -48,6 +49,13 @@ namespace ShopHooks {
4849
retry: 3,
4950
});
5051

52+
export const useUserInfo = (client: ShopAPIClient) =>
53+
useSuspenseQuery({
54+
queryKey: [...QUERY_KEYS.USER_ROW, client.language],
55+
queryFn: ShopAPIs.retrieveUserRowInfo(client),
56+
retry: 3,
57+
});
58+
5159
export const useSignInWithEmailMutation = (client: ShopAPIClient) =>
5260
useMutation({
5361
mutationKey: MUTATION_KEYS.USER_SIGN_IN_EMAIL,

packages/shop/src/schemas/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ namespace ShopSchemas {
7474
};
7575
};
7676

77+
export type UserRow = {
78+
id: string;
79+
email: string;
80+
scancode_url: string;
81+
};
82+
7783
export type Option = {
7884
id: string;
7985
name: string;

0 commit comments

Comments
 (0)