Skip to content
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

Feature: bottom sheet 데이터 꽂기 #33

Merged
merged 10 commits into from
Aug 13, 2024
21 changes: 21 additions & 0 deletions src/assets/icons/BackwordIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const BackwordIcon = () => {
return (
<svg
width="16"
height="16"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.3334 10.9998H1.66669M1.66669 10.9998L11 20.3332M1.66669 10.9998L11 1.6665"
stroke="#60646C"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

export default BackwordIcon;
21 changes: 21 additions & 0 deletions src/assets/icons/ListIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const ListIcon = () => {
return (
<svg
width="40"
height="40"
viewBox="0 0 40 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13.3333 10H35M13.3333 20H35M13.3333 30H35M5 10H5.01667M5 20H5.01667M5 30H5.01667"
stroke="white"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

export default ListIcon;
21 changes: 21 additions & 0 deletions src/assets/icons/RemoveListIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const RemoveListIcon = () => {
return (
<svg
width="32"
height="38"
viewBox="0 0 32 38"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19.3334 2.33301H6.00008C5.11603 2.33301 4.26818 2.6842 3.64306 3.30932C3.01794 3.93444 2.66675 4.78229 2.66675 5.66634V32.333C2.66675 33.2171 3.01794 34.0649 3.64306 34.69C4.26818 35.3152 5.11603 35.6663 6.00008 35.6663H26.0001C26.8841 35.6663 27.732 35.3152 28.3571 34.69C28.9822 34.0649 29.3334 33.2171 29.3334 32.333V12.333M19.3334 2.33301L29.3334 12.333M19.3334 2.33301V12.333H29.3334M11.0001 23.9997H21.0001"
stroke="#FC6B02"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

export default RemoveListIcon;
18 changes: 18 additions & 0 deletions src/assets/icons/WriteIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const WriteIcon = () => {
return (
<svg
width="26"
height="26"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.41667 20.5824H6.96042L17.55 9.99277L16.0063 8.44902L5.41667 19.0386V20.5824ZM3.25 22.749L3.25 18.1449L17.55 3.87194C17.7667 3.67333 18.0059 3.51986 18.2677 3.41152C18.5295 3.30319 18.8049 3.24902 19.0938 3.24902C19.3826 3.24902 19.6625 3.30319 19.9333 3.41152C20.2042 3.51986 20.4389 3.68236 20.6375 3.89902L22.1271 5.41569C22.3438 5.6143 22.5017 5.84902 22.601 6.11986C22.7003 6.39069 22.75 6.66152 22.75 6.93236C22.75 7.22125 22.7003 7.49659 22.601 7.7584C22.5017 8.0202 22.3438 8.25944 22.1271 8.47611L7.85417 22.749H3.25ZM16.7646 9.23444L16.0063 8.44902L17.55 9.99277L16.7646 9.23444Z"
fill="#FC6B02"
/>
</svg>
);
};

export default WriteIcon;
38 changes: 31 additions & 7 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useAtom } from 'jotai';
import styled from 'styled-components';
import RestDetailView from '~/components/bottomSheet/reaturantDetail/RestDetailView';
import RestaurantSummary from '~/components/bottomSheet/restaurantSummary/RestaurantSummary';
import useGetDetailRestaurants from '~/hooks/api/restaurants/useGetDetailRestaurants';
import useDraggable from '~/hooks/useDraggable';
import { restaurantAtom } from '~/store/restaurants';
import { restaurantAtom, selectedRestaurantIdAtom } from '~/store/restaurants';

type BottomSheetProps = {
onClose: () => void;
Expand Down Expand Up @@ -43,16 +45,38 @@ const BottomSheetContent = styled.div`
const BottomSheet: React.FC<BottomSheetProps> = ({ onClose }) => {
const { translateY, handleMouseDown } = useDraggable(onClose);
const [restaurants] = useAtom(restaurantAtom);

const [selectedId, setSelectedId] = useAtom(selectedRestaurantIdAtom);
const {
data: restaurantDetail,
isLoading,
isError,
} = useGetDetailRestaurants(selectedId || 0);
const moreButtonClick = (id: number) => {
setSelectedId(id);
};
return (
<BottomSheetWrapper $translateY={translateY}>
<Handle onMouseDown={handleMouseDown} />
<BottomSheetContent>
<ul>
{restaurants?.map((restaurant) => (
<RestaurantSummary key={restaurant.id} restaurant={restaurant} />
))}
</ul>
{selectedId ? (
<>
{isLoading && <p>Loading...</p>}
{isError && <p>Error fetching data</p>}
{restaurantDetail && (
<RestDetailView restaurantDetail={restaurantDetail} />
)}
</>
) : (
<ul>
{restaurants?.map((restaurant) => (
<RestaurantSummary
key={restaurant.id}
restaurant={restaurant}
moreButtonClick={() => moreButtonClick(restaurant.id)}
/>
))}
</ul>
)}
</BottomSheetContent>
</BottomSheetWrapper>
);
Expand Down
49 changes: 49 additions & 0 deletions src/components/bottomSheet/reaturantDetail/BackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useSetAtom } from 'jotai';
import styled from 'styled-components';
import BackwordIcon from '~/assets/icons/BackwordIcon';
import { selectedRestaurantIdAtom } from '~/store/restaurants';

const BackButton = () => {
const setSelectedId = useSetAtom(selectedRestaurantIdAtom);
const clickHandler = () => {
setSelectedId(null);
};

return (
<Button onClick={clickHandler}>
<BackwordIcon />
</Button>
);
};

export default BackButton;

const Button = styled.button`
position: absolute;
top: -30px;
width: 24px;
height: 24px;
margin-bottom: 8px;
background-color: ${({ theme }) => theme.colors.white};
border: none;
font-size: 10px;
font-weight: ${({ theme }) => theme.fontWeights.Bold};
text-align: center;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
cursor: pointer;
& svg > path {
stroke: ${({ theme }) => theme.colors.black};
}

&:hover {
background-color: ${({ theme }) => theme.colors.whitegray};
& svg > path {
stroke: ${({ theme }) => theme.colors.gray};
}
}
`;
116 changes: 116 additions & 0 deletions src/components/bottomSheet/reaturantDetail/ListButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { useSetAtom } from 'jotai';
import { useState } from 'react';
import { styled } from 'styled-components';
import ListIcon from '~/assets/icons/ListIcon';
import RemoveListIcon from '~/assets/icons/RemoveListIcon';
import XIcon from '~/assets/icons/XIcon';
import useDelRestaurant from '~/hooks/api/restaurants/useDelRestaurant';
import usePostRestaurant from '~/hooks/api/restaurants/usePostRestarurant';
import { selectedRestaurantIdAtom } from '~/store/restaurants';
interface ListButtonProps {
restaurantId: number;
isAdd: boolean;
}

const ListButton: React.FC<ListButtonProps> = ({ restaurantId, isAdd }) => {
const [isVisible, setIsVisible] = useState<boolean>(true);
const setSelectedId = useSetAtom(selectedRestaurantIdAtom);
const { refetch: postRest } = usePostRestaurant(restaurantId);
const { refetch: delRest } = useDelRestaurant(restaurantId);

const addList = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.preventDefault();
postRest();
setSelectedId(null);
};

const delList = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.preventDefault();
delRest();
setSelectedId(null);
};

return (
<ButtonWrapper $isVisible={isVisible} $isAdd={isAdd}>
{isAdd ? (
<>
<AddButton onClick={addList}>
<ListIcon />
맛집 리스트에 추가!
</AddButton>
</>
) : (
<>
<RemoveButton onClick={delList}>
<RemoveListIcon />
맛집 리스트에서 제거
</RemoveButton>
</>
)}
<CloseButton onClick={() => setIsVisible(false)} $isAdd={isAdd}>
<XIcon />
</CloseButton>
</ButtonWrapper>
);
};

export default ListButton;

const ButtonWrapper = styled.div<{ $isVisible: boolean; $isAdd: boolean }>`
display: ${({ $isVisible }) => ($isVisible ? 'flex' : 'none')};
justify-content: space-between;
align-items: center;
margin-top: 16px;
background-color: ${({ $isAdd, theme }) =>
$isAdd ? theme.colors.orange : theme.colors.whitegray};
border-radius: 8px;
`;

const AddButton = styled.button`
color: ${({ theme }) => theme.colors.white};
flex: 1;
height: 60px;
padding: 8px 16px;
border: none;
background: none;
cursor: pointer;

display: flex;
justify-content: center;
align-items: center;
gap: 20px;

font-size: 16px;
font-weight: ${({ theme }) => theme.fontWeights.Bold};
`;

const RemoveButton = styled.button`
color: ${({ theme }) => theme.colors.orange};
flex: 1;
height: 60px;
padding: 8px 16px;
font-size: 16px;
border: none;
background: none;
cursor: pointer;

display: flex;
justify-content: center;
align-items: center;
gap: 20px;

font-size: 16px;
font-weight: ${({ theme }) => theme.fontWeights.Bold};
`;

const CloseButton = styled.button<{ $isAdd: boolean }>`
border: none;
background: none;
cursor: pointer;
padding: 20px;

& svg > path {
stroke: ${({ $isAdd, theme }) =>
$isAdd ? theme.colors.white : theme.colors.orange};
}
`;
67 changes: 67 additions & 0 deletions src/components/bottomSheet/reaturantDetail/PlatformRate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { styled } from 'styled-components';

interface PlatformRateProps {
platform: string;
rating: number | string | null;
}

export const PlatformRate: React.FC<PlatformRateProps> = ({
platform,
rating,
}) => {
if (typeof rating === 'string') {
rating = parseFloat(rating);
}
const score = rating ? rating : 0;
return (
<Wrapper>
<PlatformName>{platform}</PlatformName>
<ColDevider />
<ScoreText>{score.toFixed(1)}</ScoreText>
<BackgroundBar>
<FilledBar $filledPercentage={(score / 5) * 100} />
</BackgroundBar>
</Wrapper>
);
};

const Wrapper = styled.div`
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 8px;
`;

const PlatformName = styled.div`
font-size: 14px;
font-weight: ${({ theme }) => theme.fontWeights.Regular};
width: 40px;
text-align: center;
flex-shrink: 0;
`;

const ColDevider = styled.div`
width: 1px;
height: 12px;
background-color: ${({ theme }) => theme.colors.whitegray};
`;

const ScoreText = styled.div`
font-size: 14px;
font-weight: ${({ theme }) => theme.fontWeights.Regular};
flex-shrink: 0;
`;

const BackgroundBar = styled.div`
display: flex;
width: 100%;
height: 3px;
background-color: ${({ theme }) => theme.colors.whitegray};
border-radius: 3px;
`;

const FilledBar = styled.div<{ $filledPercentage: number }>`
width: ${({ $filledPercentage }) => $filledPercentage}%;
background-color: ${({ theme }) => theme.colors.orange};
border-radius: 3px;
`;
Loading
Loading