Skip to content

Commit

Permalink
Merge pull request #144 from The-Fellowship-of-the-matzip/develop
Browse files Browse the repository at this point in the history
1.2.0 버전 배포 (랜덤 룰렛, UI 수정)
  • Loading branch information
uk960214 authored Oct 26, 2022
2 parents 36cdee4 + b28b9a7 commit 50cbd83
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 5 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mat.zip",
"version": "1.1.3",
"version": "1.2.0",
"private": true,
"homepage": "https://matzip.today",
"dependencies": {
Expand Down
8 changes: 8 additions & 0 deletions src/components/common/StoreListItem/StoreListItem.style.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,13 @@ export const ListItemDistance = styled.p`
`;

export const ListItemStars = styled.div`
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.5rem;
`;

export const RatingText = styled.span`
font-size: 1.2rem;
font-weight: 500;
`;
3 changes: 1 addition & 2 deletions src/components/common/StoreListItem/StoreListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useContext } from "react";
import { useNavigate } from "react-router-dom";
import repeatComponent from "util/repeatComponent";

import { PATHNAME } from "constants/routes";

Expand Down Expand Up @@ -40,7 +39,7 @@ function StoreListItem({
{campusName} 캠퍼스 기준 도보 {distance}
</S.ListItemDistance>
<S.ListItemStars>
{repeatComponent(<Star isFilled />, rating)}
<Star isFilled /> <S.RatingText>{rating.toFixed(2)}</S.RatingText>
</S.ListItemStars>
</S.ListItemTextContainer>
</S.ListItemContainer>
Expand Down
6 changes: 6 additions & 0 deletions src/components/pages/CategoryPage/CategoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import StoreList from "components/common/StoreList/StoreList";

import Category from "components/pages/CategoryPage/Category/Category";
import * as S from "components/pages/CategoryPage/CategoryPage.style";
import RandomRoulette from "components/pages/CategoryPage/RandomRoulette/RandomRoulette";

function CategoryPage() {
const campusName = useContext(campusContext);
Expand All @@ -27,6 +28,7 @@ function CategoryPage() {
() => fetchRandomStoreList(campusId, SIZE.RANDOM_ITEM),
{
retry: NETWORK.RETRY_COUNT,
refetchOnWindowFocus: false,
}
);

Expand All @@ -40,6 +42,10 @@ function CategoryPage() {
<SectionHeader>카테고리</SectionHeader>
<Category />
</section>
<section>
<SectionHeader>추천 메뉴</SectionHeader>
{isLoading ? <Spinner /> : <RandomRoulette campusId={campusId} />}
</section>
<section>
<SectionHeader>이런 메뉴는 어떤가요?</SectionHeader>
{isLoading && <Spinner />}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import styled, { css } from "styled-components";

export const Container = styled.section`
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
`;

export const RecommendBlock = styled.div`
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
`;

export const Label = styled.p`
text-align: center;
`;

export const Outer = styled.div`
height: 4rem;
width: 14rem;
overflow: hidden;
padding: 1rem 0.5rem;
`;

export const Inner = styled.div<{ runAnimation: boolean }>`
font-size: 1.2rem;
display: flex;
flex-direction: column;
gap: 1rem;
${({ runAnimation }) =>
runAnimation &&
css`
animation: slider ease-in-out 5s;
transform: translateY(calc(-100% + 2rem));
`}
@keyframes slider {
0% {
transform: translateY(0);
}
10% {
transform: translateY(1.5rem);
}
85% {
transform: translateY(calc(-100% + 0.5rem));
}
100% {
transform: translateY(calc(-100% + 2rem));
}
}
`;

export const RouletteSlot = styled.div`
text-align: center;
height: 2rem;
line-height: 2rem;
width: 13rem;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;

export const ResultWrapper = styled.div`
width: 100%;
border-bottom: 0.065rem solid ${({ theme }) => theme.secondary};
transform-origin: top;
animation: slide-from-top 0.2s ease;
@keyframes slide-from-top {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
`;

export const ButtonContainer = styled.div`
display: flex;
justify-content: center;
gap: 0.5rem;
`;

export const CustomButton = styled.button`
background-color: ${({ theme }) => theme.primary};
color: ${({ theme }) => theme.white};
border: none;
border-radius: 0.25rem;
padding: 0.5rem 0.8rem;
box-shadow: 1px 1px 1px #8d8d8d;
font-weight: 600;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useEffect } from "react";
import { useQuery } from "react-query";

import { NETWORK, SIZE } from "constants/api";

import useRandomPick from "hooks/useRandomPick";

import fetchRandomStoreList from "api/fetchRandomStoreList";

import ErrorImage from "components/common/ErrorImage/ErrorImage";
import Spinner from "components/common/Spinner/Spinner";
import StoreListItem from "components/common/StoreListItem/StoreListItem";

import * as S from "components/pages/CategoryPage/RandomRoulette/RandomRoulette.style";

type Props = {
campusId: 1 | 2;
};

function RandomRoulette({ campusId }: Props) {
const {
data: stores,
isLoading,
isError,
error,
refetch,
} = useQuery(
"randomStoreRoulette",
() => fetchRandomStoreList(campusId, SIZE.RANDOM_ITEM),
{
retry: NETWORK.RETRY_COUNT,
refetchOnWindowFocus: false,
}
);

const {
state: { rouletteBoard, isResultOpen, result, triggerAnimation },
handleRunClick,
openResult,
reset,
} = useRandomPick(stores || []);

const resetHard = () => {
refetch();
reset();
};

useEffect(() => {
resetHard();
}, [campusId]);

return (
<S.Container>
{isLoading && <Spinner />}
{isError && error instanceof Error && (
<ErrorImage errorMessage={error.message} />
)}
<S.RecommendBlock>
<S.Label>오늘은 </S.Label>
<S.Outer>
<S.Inner runAnimation={triggerAnimation} onAnimationEnd={openResult}>
{rouletteBoard.map((store, index) => (
<S.RouletteSlot key={store + index}>{store}</S.RouletteSlot>
))}
</S.Inner>
</S.Outer>
<S.Label>어때요?</S.Label>
</S.RecommendBlock>
{!isResultOpen ? (
<S.CustomButton
onClick={handleRunClick}
disabled={result !== undefined}
>
룰렛 Go!
</S.CustomButton>
) : (
<S.ButtonContainer>
<S.CustomButton onClick={reset}>다시 돌리기</S.CustomButton>
<S.CustomButton onClick={resetHard}>식당 리셋하기</S.CustomButton>
</S.ButtonContainer>
)}
{isResultOpen && result !== undefined && (
<S.ResultWrapper>
<StoreListItem
id={result.id}
thumbnailUrl={result.imageUrl}
name={result.name}
distance={result.distance}
rating={result.rating}
/>
</S.ResultWrapper>
)}
</S.Container>
);
}

export default RandomRoulette;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Store } from "mock/data";

export const ACTION_TYPES = {
SET_BOARD: "SET_BOARD",
SPIN: "SPIN",
SHOW_RESULT: "SHOW_RESULT",
RESET: "RESET",
} as const;

type State = {
triggerAnimation: boolean;
rouletteBoard: string[];
pickedIndex: number | null;
result: Store | undefined;
isResultOpen: boolean;
};

type Action =
| {
type: typeof ACTION_TYPES.SET_BOARD;
payload: Pick<State, "rouletteBoard">;
}
| {
type: typeof ACTION_TYPES.SPIN;
payload: Pick<State, "pickedIndex" | "result" | "rouletteBoard">;
}
| {
type: typeof ACTION_TYPES.SHOW_RESULT;
}
| {
type: typeof ACTION_TYPES.RESET;
payload: Pick<State, "rouletteBoard">;
};

export const initialState: State = {
triggerAnimation: false,
rouletteBoard: [],
pickedIndex: null,
result: undefined,
isResultOpen: false,
};

export const randomRouletteStateReducer = (state: State, action: Action) => {
switch (action.type) {
case ACTION_TYPES.SET_BOARD: {
return { ...state, rouletteBoard: action.payload.rouletteBoard };
}
case ACTION_TYPES.SPIN: {
const { pickedIndex, result, rouletteBoard } = action.payload;

return {
...state,
pickedIndex,
result,
rouletteBoard,
triggerAnimation: true,
};
}
case ACTION_TYPES.SHOW_RESULT: {
return { ...state, isResultOpen: true };
}
case ACTION_TYPES.RESET: {
return { ...initialState, rouletteBoard: action.payload.rouletteBoard };
}

default:
return initialState;
}
};
Loading

0 comments on commit 50cbd83

Please sign in to comment.