Skip to content

feat: 네이버 지도 연동 추가 #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 16, 2025
448 changes: 448 additions & 0 deletions .pnp.cjs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"react-hook-form": "^7.50.0",
"react-i18next": "^15.4.1",
"react-intersection-observer": "^9.8.0",
"react-naver-maps": "^0.1.3",
"react-pdf": "^9.0.0",
"react-router-dom": "^6.21.3",
"react-select": "^5.8.0",
Expand All @@ -55,6 +56,7 @@
"@boolti/typescript-config": "*",
"@emotion/babel-plugin": "^11.11.0",
"@types/js-cookie": "^3.0.6",
"@types/navermaps": "^3.7.9",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
13 changes: 10 additions & 3 deletions apps/admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'the-new-css-reset/css/reset.css';
import '@mdxeditor/editor/style.css';
import './index.css';
import './i18n';

import { NavermapsProvider } from 'react-naver-maps';
import { QueryClientProvider } from '@boolti/api';
import { BooltiUIProvider } from '@boolti/ui';
import { setDefaultOptions } from 'date-fns';
Expand Down Expand Up @@ -43,6 +43,7 @@ import ShowSettlementPage from './pages/ShowSettlementPage';
import ShowEnterancePage from './pages/ShowEnterancePage';
import { initVConsole } from './utils/vConsole';
import { checkIsWebView } from '@boolti/bridge';
import { X_NCP_APIGW_API_KEY_ID } from './constants/ncp';
import { IS_PRODUCTION_PHASE } from './constants/phase';
import WebView from './pages/WebView';

Expand Down Expand Up @@ -159,7 +160,9 @@ const routes: RouteObject[] = [
<QueryClientProvider>
<BooltiUIProvider>
<LazyMotion features={domAnimation}>
<Outlet />
<NavermapsProvider ncpClientId={X_NCP_APIGW_API_KEY_ID} submodules={['geocoder']}>
<Outlet />
</NavermapsProvider>
</LazyMotion>
</BooltiUIProvider>
</QueryClientProvider>
Expand All @@ -176,7 +179,11 @@ const routes: RouteObject[] = [
const router = createBrowserRouter(routes);

const App = () => {
return <RouterProvider router={router} />;
return (
<Suspense fallback={null}>
<RouterProvider router={router} />
</Suspense>
);
};

export default App;
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ImageFile } from '@boolti/api';
import { ImageFile, fetcher } from '@boolti/api';
import { CloseIcon, FileUpIcon } from '@boolti/icon';
import { Button, TextField, TimePicker, useDialog } from '@boolti/ui';
import { add, format } from 'date-fns';
import { useRef } from 'react';
import { useDropzone } from 'react-dropzone';
import { Controller, UseFormReturn } from 'react-hook-form';
import { useNavermaps } from 'react-naver-maps';
import DaumPostcode from 'react-daum-postcode';

import Styled from './ShowInfoFormContent.styles';
Expand All @@ -30,6 +31,7 @@ const ShowBasicInfoFormContent = ({
onDeleteImage,
}: ShowBasicInfoFormContentProps) => {
const { open, close, isOpen } = useDialog();
const naverMaps = useNavermaps();
const detailAddressInputRef = useRef<HTMLInputElement>(null);

const {
Expand All @@ -56,8 +58,31 @@ const ShowBasicInfoFormContent = ({
content: (
<DaumPostcode
style={{ maxWidth: 426, height: 470 }}
onComplete={(address) => {
onComplete={async (address) => {
const response = await fetcher.get<naver.maps.Service.GeocodeResponse['v2']>(
'web/v1/naver-maps/geocoding',
{
searchParams: {
query: address.address,
filter: `BCODE@${address.bcode};`,
},
},
);

if (
response.status === naverMaps.Service.GeocodeStatus.OK &&
response.meta.totalCount > 0
) {
const foundAddress = response.addresses.find(Boolean);

if (foundAddress) {
setValue('latitude', Number(foundAddress.y));
setValue('longitude', Number(foundAddress.x));
}
}

setValue('placeStreetAddress', address.roadAddress);

detailAddressInputRef.current?.focus();
}}
onClose={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent
rules={{
required: true,
}}
render={({ field: { onChange, onBlur, value } }) => {
if (value === undefined) return <></>;

render={({ field: { onChange, onBlur, value = '' } }) => {
return (
<Styled.TextAreaContainer>
<MarkdownEditor
Expand Down
2 changes: 2 additions & 0 deletions apps/admin/src/components/ShowInfoFormContent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface ShowBasicInfoFormInputs {
placeName: string;
placeStreetAddress: string;
placeDetailAddress: string;
latitude: number;
longitude: number;
}

export interface ShowDetailInfoFormInputs {
Expand Down
1 change: 1 addition & 0 deletions apps/admin/src/constants/ncp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const X_NCP_APIGW_API_KEY_ID = import.meta.env.VITE_X_NCP_APIGW_API_KEY_ID;
2 changes: 2 additions & 0 deletions apps/admin/src/pages/ShowAddPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => {
images: showImageInfo,
date: `${showBasicInfoForm.getValues('date')}T${showBasicInfoForm.getValues('startTime')}:00.000Z`,
runningTime: Number(showBasicInfoForm.getValues('runningTime')),
latitude: showBasicInfoForm.getValues('latitude'),
longitude: showBasicInfoForm.getValues('longitude'),
place: {
name: showBasicInfoForm.getValues('placeName'),
streetAddress: showBasicInfoForm.getValues('placeStreetAddress'),
Expand Down
25 changes: 16 additions & 9 deletions apps/admin/src/pages/ShowInfoPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ const ShowInfoPage = () => {
thumbnailPath: image.thumbnailPath,
path: image.path,
})),
latitude: showBasicInfoForm.getValues('latitude'),
longitude: showBasicInfoForm.getValues('longitude'),
date: `${showBasicInfoFormInputs.date}T${showBasicInfoFormInputs.startTime}:00.000Z`,
runningTime: +showBasicInfoFormInputs.runningTime,
place: {
Expand Down Expand Up @@ -163,17 +165,19 @@ const ShowInfoPage = () => {
]);

const confirmSaveShowInfo = useCallback(async () => {
const isDirty = Object.values(showBasicInfoForm.formState.dirtyFields).some((value) => value) ||
const isDirty =
Object.values(showBasicInfoForm.formState.dirtyFields).some((value) => value) ||
Object.values(showDetailInfoForm.formState.dirtyFields).some((value) => value) ||
isImageFilesDirty ||
isCastTeamListDraftDirty
isCastTeamListDraftDirty;

if (!isDirty) return true
if (!isDirty) return true;

const result = await confirm(
<Styled.ConfirmMessageContainer>
<Styled.ConfirmMessage>
저장하지 않고 이 페이지를 나가면 작성한 정보가 손실됩니다.<br />이 페이지를 나갈까요?
저장하지 않고 이 페이지를 나가면 작성한 정보가 손실됩니다.
<br />이 페이지를 나갈까요?
</Styled.ConfirmMessage>
<Styled.ConfirmSubMessage>
*페이지 하단의 [저장하기] 버튼을 눌러 정보를 저장할 수 있습니다.
Expand Down Expand Up @@ -326,15 +330,17 @@ const ShowInfoPage = () => {
: '',
startTime: showBasicInfoForm.watch('startTime'),
runningTime: showBasicInfoForm.watch('runningTime'),
latitude: showBasicInfoForm.watch('latitude'),
longitude: showBasicInfoForm.watch('longitude'),
salesStartTime: showSalesInfo
? format(showSalesInfo.salesStartTime, 'yyyy.MM.dd (E)')
: '',
salesEndTime: showSalesInfo
? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)')
: '',
placeName: showBasicInfoForm.watch('placeName'),
placeStreetAddress: showBasicInfoForm.watch('placeStreetAddress'),
placeDetailAddress: showBasicInfoForm.watch('placeDetailAddress'),
streetAddress: showBasicInfoForm.watch('placeStreetAddress'),
detailAddress: showBasicInfoForm.watch('placeDetailAddress'),
notice: showDetailInfoForm.watch('notice'),
hostName: showDetailInfoForm.watch('hostName'),
hostPhoneNumber: showDetailInfoForm.watch('hostPhoneNumber'),
Expand Down Expand Up @@ -391,8 +397,8 @@ const ShowInfoPage = () => {
? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)')
: '',
placeName: showBasicInfoForm.watch('placeName'),
placeStreetAddress: showBasicInfoForm.watch('placeStreetAddress'),
placeDetailAddress: showBasicInfoForm.watch('placeDetailAddress'),
streetAddress: showBasicInfoForm.watch('placeStreetAddress'),
detailAddress: showBasicInfoForm.watch('placeDetailAddress'),
notice: showDetailInfoForm.watch('notice'),
hostName: showDetailInfoForm.watch('hostName'),
hostPhoneNumber: showDetailInfoForm.watch('hostPhoneNumber'),
Expand All @@ -405,7 +411,8 @@ const ShowInfoPage = () => {
userNickname: member.userNickname ?? '',
userImgPath: member.userImgPath ?? '',
})),
})) ?? []}
})) ?? []
}
hasNoticePage
containerRef={showPreviewMobileRef}
/>
Expand Down
2 changes: 2 additions & 0 deletions apps/preview/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet-async": "^2.0.5",
"react-naver-maps": "^0.1.3",
"react-router-dom": "^6.22.3",
"swiper": "^11.1.14",
"the-new-css-reset": "^1.11.2"
Expand All @@ -30,6 +31,7 @@
"@boolti/eslint-config": "*",
"@boolti/typescript-config": "*",
"@emotion/babel-plugin": "^11.11.0",
"@types/navermaps": "^3.7.9",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/react-helmet": "^6.1.11",
Expand Down
2 changes: 1 addition & 1 deletion apps/preview/src/components/NotFound/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from 'react';

export default () => {
useEffect(() => {
window.location.href = 'https://boolti.in';
// window.location.href = 'https://boolti.in';
}, []);
return null;
};
29 changes: 21 additions & 8 deletions apps/preview/src/pages/ShowInfoDetailPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import { ShowPreviewResponse } from "@boolti/api";
import { ShowInfoDetail } from "@boolti/ui";
import { format } from "date-fns";
import { useLoaderData } from "react-router-dom";
import { ShowPreviewResponse } from '@boolti/api';
import { ShowInfoDetail } from '@boolti/ui';
import { format } from 'date-fns';
import { useLoaderData } from 'react-router-dom';
import Styled from './ShowInfoDetailPage.styles';

const ShowInfoDetailPage: React.FC = () => {
const [show, { count: soldTicketCount }] = useLoaderData() as [ShowPreviewResponse, { count: number }];
const [show, { count: soldTicketCount }] = useLoaderData() as [
ShowPreviewResponse,
{ count: number },
];

const {
date,
notice,
salesEndTime,
salesStartTime,
hostName,
placeName,
streetAddress,
detailAddress,
latitude,
longitude,
} = show;

const callLinkClickHandler = () => {
location.href = `tel:${show.hostPhoneNumber}`;
}
};

const messageLinkClickHandler = () => {
location.href = `sms:${show.hostPhoneNumber}`;
}
};

return (
<Styled.Container>
Expand All @@ -30,8 +38,13 @@ const ShowInfoDetailPage: React.FC = () => {
date,
salesStartTime: format(new Date(salesStartTime), 'yyyy.MM.dd (E)'),
salesEndTime: format(new Date(salesEndTime), 'yyyy.MM.dd (E)'),
placeName,
streetAddress,
detailAddress,
notice,
hostName,
latitude,
longitude,
}}
soldTicketCount={soldTicketCount}
onClickCallLink={callLinkClickHandler}
Expand All @@ -41,6 +54,6 @@ const ShowInfoDetailPage: React.FC = () => {
/>
</Styled.Container>
);
}
};

export default ShowInfoDetailPage;
27 changes: 17 additions & 10 deletions apps/preview/src/pages/ShowPreviewPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useLoaderData } from 'react-router-dom';
import Styled from './ShowPreviewPage.styles';
import { Meta } from '../../components/Meta';
import BooltiGrayLogo from '../../components/BooltiGrayLogo';
import { NavermapsProvider } from 'react-naver-maps';
import useBodyScrollLock from '../../hooks/useBodyScrollLock';
import { useState } from 'react';

Expand Down Expand Up @@ -41,6 +42,8 @@ const getShareText = (show: {
);
};

const X_NCP_APIGW_API_KEY_ID = import.meta.env.VITE_X_NCP_APIGW_API_KEY_ID;

const ShowPreviewPage = () => {
const [shareDialogOpen, setShareDialogOpen] = useState<boolean>(false);

Expand Down Expand Up @@ -72,6 +75,8 @@ const ShowPreviewPage = () => {
detailAddress,
hostName,
hostPhoneNumber,
latitude,
longitude,
} = previewData;

const shareShowPreviewLink = async () => {
Expand All @@ -93,15 +98,15 @@ const ShowPreviewPage = () => {
placeName,
streetAddress,
detailAddress,
})
});

if (navigator.share) {
await navigator.share({ text });
} else {
await navigator.clipboard.writeText(text);
alert('공연 링크가 복사되었어요');
}
}
};

const shareButtonClickHandler = async () => {
dialog.open({
Expand All @@ -118,11 +123,11 @@ const ShowPreviewPage = () => {
isAuto: true,
mobileType: 'darkBottomSheet',
onClose: () => {
setShareDialogOpen(false)
}
})
setShareDialogOpen(false);
},
});

setShareDialogOpen(true)
setShareDialogOpen(true);
};

const reservationButtonClickHandler = () => {
Expand Down Expand Up @@ -154,7 +159,7 @@ const ShowPreviewPage = () => {
};

return (
<>
<NavermapsProvider ncpClientId={X_NCP_APIGW_API_KEY_ID}>
<Meta title={title} showId={id.toString()} />
<Styled.ShowPreviewPage>
<Styled.ShowPreviewContainer>
Expand All @@ -168,8 +173,10 @@ const ShowPreviewPage = () => {
salesStartTime: format(new Date(salesStartTime), 'yyyy.MM.dd (E)'),
salesEndTime: format(new Date(salesEndTime), 'yyyy.MM.dd (E)'),
placeName: placeName,
placeStreetAddress: streetAddress,
placeDetailAddress: detailAddress,
streetAddress,
detailAddress,
latitude,
longitude,
notice: text,
hostName: hostName,
hostPhoneNumber: hostPhoneNumber,
Expand Down Expand Up @@ -203,7 +210,7 @@ const ShowPreviewPage = () => {
</Styled.ReservationButtonWrapper>
</Styled.ShowPreviewContainer>
</Styled.ShowPreviewPage>
</>
</NavermapsProvider>
);
};

Expand Down
Loading
Loading