Skip to content

Commit

Permalink
[Feature] - 여행기 등록 기능 구현 (#115)
Browse files Browse the repository at this point in the history
* feat: types path alias 추가

* refactor(AccordionRoot): 다른 attributes들도 받을 수 있도록 변경

* fix(preview): 스토리북 열리지 않는 문제 해결

div 태그 추가

* chore: type, queries, hooks에 대한 type alias 추가

* feat: DayContent 컴포넌트 구현

* refactor(GoogleMapView): LoadScript 외부로 분리

1. GoogleMapView에서 LoadScript 제거

2. GoogleMapLoadScript 추가

* feat(GoogleSearchPopup): GoogleSearchPopup 컴포넌트 구현

* refactor(ModalBottomSheet): currentY 값 open시 0이 되도록 변경

* refactor(ModalBottomSheet): stylelint 적용

* refactor(MultiImageUpload): useImageUpload 반환 값을 외부에서 주입 받도록 변경

* feat(PageInfo): PageInfo 컴포넌트 구현

* test(PageInfo): PageInfo 컴포넌트 스토리북 구현

* chore: useDragScroll, useImageUpload 경로 변경

hooks에 위치하도록 변경

* refactor(ThumbnailUpload): useImageUpload 반환 값을 외부에서 주입 받도록 변경

* feat(TravelogueMultiImgaeUpload): TravelogueMultiImgaeUpload 컴포넌트 구현

* feat: register route path 추가

* feat(useTravelDays): useTravelDays hook 구현

* feat: travelogue 공통 타입들 추가

* feat: usePostTrevelogue 구현

* feat: usePostUploadImages 구현

* chore: reexport 추가

* feat: 여행기 등록 기능 구현

1. router에 추가

2. 여행기 등록 페이지 추가

* feat: 여행기 등록 완료 시 해당 상세 페이지로 이동하는 기능 추가
  • Loading branch information
jinyoung234 authored Jul 24, 2024
1 parent 51f1960 commit 75347fd
Show file tree
Hide file tree
Showing 32 changed files with 977 additions and 99 deletions.
3 changes: 3 additions & 0 deletions frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const config: StorybookConfig = {
"@apis": path.resolve(__dirname, "../src/apis"),
"@mocks": path.resolve(__dirname, "../src/mocks"),
"@constants": path.resolve(__dirname, "../src/constants"),
"@type": path.resolve(__dirname, "../src/types"),
"@queries": path.resolve(__dirname, "../src/queries"),
"@hooks": path.resolve(__dirname, "../src/hooks"),
};
}
config.module = config.module || {};
Expand Down
5 changes: 3 additions & 2 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ const preview: Preview = {
<ThemeProvider theme={theme}>
<Global styles={globalStyle} />
<div id="root">
<div style={rootStyle}>
<Story />
<div style={rootStyle}>
<Story />
</div>
</div>
</ThemeProvider>
</QueryClientProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { AccordionContext } from "@components/common/Accordion/AccordionRoot/acc

import * as S from "./AccordionRoot.styled";

const AccordionRoot = ({ children }: React.PropsWithChildren) => {
const AccordionRoot = ({
children,
...props
}: React.PropsWithChildren<React.ComponentPropsWithoutRef<"div">>) => {
const [item, setItem] = useState<Set<string>>(new Set());

const handleToggleAccordion = useCallback(
Expand All @@ -24,7 +27,7 @@ const AccordionRoot = ({ children }: React.PropsWithChildren) => {

return (
<AccordionContext.Provider value={{ value: item, handleToggleAccordion }}>
<S.Layout>{children}</S.Layout>
<S.Layout {...props}>{children}</S.Layout>
</AccordionContext.Provider>
);
};
Expand Down
95 changes: 95 additions & 0 deletions frontend/src/components/common/DayContent/DayContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useState } from "react";

import { TravelRegisterDay, TravelRegisterPlace } from "@type/domain/travelogue";

import {
Accordion,
GoogleMapView,
GoogleSearchPopup,
IconButton,
Textarea,
} from "@components/common";

import * as S from "../../pages/travelogueRegister/TravelogueRegisterPage.styled";

const DayContent = ({
children,
travelDay,
dayIndex,
onDeleteDay,
onDeletePlace,
onChangePlaceDescription,
onAddPlace,
}: {
children: (placeIndex: number) => JSX.Element;
travelDay: TravelRegisterDay;
dayIndex: number;
onDeleteDay: (dayIndex: number) => void;
onDeletePlace: (dayIndex: number, placeIndex: number) => void;
onChangePlaceDescription: (
e: React.ChangeEvent<HTMLTextAreaElement>,
dayIndex: number,
placeIndex: number,
) => void;
onAddPlace: (dayIndex: number, travelParams: TravelRegisterPlace) => void;
}) => {
const [isPopupOpen, setIsPopupOpen] = useState(false);

const onSelectSearchResult = (
placeInfo: Pick<TravelRegisterPlace, "name" | "position">,
dayIndex: number,
) => {
onAddPlace(dayIndex, placeInfo);
setIsPopupOpen(false);
};

const onClickAddPlaceButton = () => {
setIsPopupOpen(true);
};

return (
<Accordion.Item key={`${travelDay}-${dayIndex}}`} value={`day-${dayIndex}`}>
<Accordion.Trigger onDeleteItem={() => onDeleteDay(dayIndex)}>
{`Day ${dayIndex + 1}`}
</Accordion.Trigger>
<Accordion.Content>
<Accordion.Root>
<GoogleMapView places={travelDay.places.map((place) => place.position)} />
{travelDay.places.map((place, placeIndex) => (
<Accordion.Item key={`${place}-${dayIndex}}`} value={`place-${dayIndex}-${placeIndex}`}>
<Accordion.Trigger onDeleteItem={() => onDeletePlace(dayIndex, placeIndex)}>
{place.name || `장소 ${placeIndex + 1}`}
</Accordion.Trigger>
<Accordion.Content>
{children(placeIndex)}
<Textarea
placeholder="장소에 대한 간단한 설명을 남겨주세요"
onChange={(e) => onChangePlaceDescription(e, dayIndex, placeIndex)}
count={travelDay.places[placeIndex].description?.length ?? 0}
maxLength={300}
maxCount={300}
/>
</Accordion.Content>
</Accordion.Item>
))}
</Accordion.Root>
<IconButton
size="16"
iconType="plus"
position="left"
css={[S.addTravelAddButtonStyle, S.addDayButtonStyle]}
onClick={onClickAddPlaceButton}
>
장소 추가하기
</IconButton>
</Accordion.Content>
{isPopupOpen && (
<GoogleSearchPopup
onSearchPlaceInfo={(placeInfo) => onSelectSearchResult(placeInfo, dayIndex)}
/>
)}
</Accordion.Item>
);
};

export default DayContent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Libraries, LoadScript } from "@react-google-maps/api";

const GoogleMapLoadScript = ({
children,
libraries,
}: React.PropsWithChildren<{ libraries: Libraries }>) => {
return (
<LoadScript
googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAP_API_KEY ?? ""}
libraries={libraries}
>
{children}
</LoadScript>
);
};

export default GoogleMapLoadScript;
72 changes: 35 additions & 37 deletions frontend/src/components/common/GoogleMapView/GoogleMapView.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useCallback } from "react";

import { GoogleMap, LoadScript, MarkerF, Polyline } from "@react-google-maps/api";
import { GoogleMap, MarkerF, Polyline } from "@react-google-maps/api";

import { markerUrl } from "@assets/svg";

Expand Down Expand Up @@ -49,42 +49,40 @@ const GoogleMapView = ({ places }: GoogleMapViewProps) => {

return (
<div>
<LoadScript googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAP_API_KEY ?? ""}>
<GoogleMap
options={{
disableDefaultUI: true,
styles: [
{
featureType: "poi",
elementType: "labels",
stylers: [{ visibility: "off" }],
},
],
}}
mapContainerStyle={containerStyle}
center={center}
zoom={10}
onLoad={onLoad}
>
{places.map((position, index) => (
<MarkerF
key={`${index}-${position.lat}`}
position={position}
icon={markerUrl}
onClick={() => handleClickMarker(position)}
/>
))}
{places.map(({ lat, lng }, index) => (
<MarkerF
key={`${index}-${lat}`}
position={{ lat, lng }}
icon={markerUrl}
onClick={() => handleClickMarker({ lat, lng })}
/>
))}
<Polyline path={places} options={{ strokeColor: "#72A2FFCC", strokeWeight: 3 }} />
</GoogleMap>
</LoadScript>
<GoogleMap
options={{
disableDefaultUI: true,
styles: [
{
featureType: "poi",
elementType: "labels",
stylers: [{ visibility: "off" }],
},
],
}}
mapContainerStyle={containerStyle}
center={center}
zoom={10}
onLoad={onLoad}
>
{places.map((position, index) => (
<MarkerF
key={`${index}-${position.lat}`}
position={position}
icon={markerUrl}
onClick={() => handleClickMarker(position)}
/>
))}
{places.map(({ lat, lng }, index) => (
<MarkerF
key={`${index}-${lat}`}
position={{ lat, lng }}
icon={markerUrl}
onClick={() => handleClickMarker({ lat, lng })}
/>
))}
<Polyline path={places} options={{ strokeColor: "#72A2FFCC", strokeWeight: 3 }} />
</GoogleMap>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";

import { PRIMITIVE_COLORS } from "@styles/tokens";

export const Layout = styled.div`
display: flex;
position: fixed;
top: 0;
transform: translateX(-3.2rem);
z-index: 1000;
width: 100%;
height: 100vh;
background-color: #fff;
flex-direction: column;
max-width: 48rem;
`;

export const StyledInput = styled.input`
width: 100%;
padding: 1.4rem !important;
padding-right: 4rem !important;
border: none;
font-size: ${(props) => props.theme.typography.mobile.bodyBold};
border-bottom: 2px solid ${(props) => props.theme.colors.border};
&:focus-visible {
outline: none;
border-bottom: 2px solid ${(props) => props.theme.colors.primary};
}
`;

export const autocompleteStyles = css`
.pac-container {
margin-top: 5px;
padding: 5px;
border: none;
background-color: #fff;
box-shadow: none;
}
.pac-item {
border: none;
font-size: 1.4rem;
}
.pac-container .pac-item {
padding: 8px;
cursor: pointer;
}
.pac-container .pac-item:hover {
background-color: ${PRIMITIVE_COLORS.blue[50]};
}
.pac-container .pac-item-query {
color: #333;
font-size: 1.4rem;
}
.pac-container .pac-icon:hover {
background-image: url("https://i.pinimg.com/474x/71/74/46/71744691c23313e65b8bf8309640130d.jpg") !important;
background-repeat: no-repeat !important;
background-position: center !important;
background-size: contain !important;
}
`;

export const TipContainer = styled.div`
display: flex;
width: 100%;
margin-top: 1rem;
padding: ${(props) => props.theme.spacing.m};
gap: 1rem;
p:first-of-type {
${(props) => props.theme.typography.mobile.detailBold}
}
p:nth-of-type(2) {
${(props) => props.theme.typography.mobile.detail}
}
`;

export const InputContainer = styled.div`
position: relative;
width: 100%;
`;

export const InputIcon = styled.svg`
position: absolute;
top: 50%;
right: 1rem;
width: 2rem;
height: 2rem;
transform: translateY(-50%);
pointer-events: none;
`;
Loading

0 comments on commit 75347fd

Please sign in to comment.