Skip to content

Commit

Permalink
Merge pull request #124 from k-roffle/#114/product-confirm-page
Browse files Browse the repository at this point in the history
Feat: 상품 썸네일 및 판매시작 api 연동
  • Loading branch information
jiyaaany authored Dec 5, 2021
2 parents 6d396b3 + eb76c79 commit 5364bbb
Show file tree
Hide file tree
Showing 21 changed files with 289 additions and 134 deletions.
6 changes: 4 additions & 2 deletions src/hooks/usePost.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { GENERAL_ERROR, NETWORK_ERROR } from 'constants/errors';

import { useCommonSnackbar } from 'components/CommonSnackbar/useCommonSnackbar';
import { ProductId } from 'pages/CreateProduct/types';
import { useState } from 'react';
import { useMutation, UseMutationResult } from 'react-query';
import { ObjectResponse } from 'utils/requestType';
import { postRequest } from 'utils/requests';

interface Post {
Expand All @@ -13,12 +15,12 @@ interface Post {
onError?: () => void;
}

export const usePost = ({
export const usePost = <T extends ProductId>({
pathname,
errorMessage = GENERAL_ERROR,
onSuccess,
onError,
}: Post): UseMutationResult<void, unknown> => {
}: Post): UseMutationResult<ObjectResponse<T> | void, unknown> => {
const [postErrorMessage, setPostErrorMessage] = useState<string>();

useCommonSnackbar({
Expand Down
5 changes: 2 additions & 3 deletions src/pages/CreateDesign/Detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,8 @@ const Detail = (): React.ReactElement => {
bottomWidth,
armholeDepth,
} = size;
const [localCoverImage, setLocalCoverImage] = useRecoilState(
localCoverImageAtom,
);
const [localCoverImage, setLocalCoverImage] =
useRecoilState(localCoverImageAtom);

const { SWEATER } = DESIGN;
const { TEXT, IMAGE, VIDEO } = PATTERN;
Expand Down
5 changes: 3 additions & 2 deletions src/pages/CreateDesign/Pattern/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,9 @@ const Pattern = (): React.ReactElement => {
toolbarPlugin,
];

const currentPatternLength = editorState.getCurrentContent().getPlainText('')
.length;
const currentPatternLength = editorState
.getCurrentContent()
.getPlainText('').length;

const handleBeforeInput = (
_chars: string,
Expand Down
14 changes: 1 addition & 13 deletions src/pages/CreateDesign/components/FontSize/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,5 @@ export type FontSizeProps = {
};

export const fontSizeOptions = [
8,
10,
12,
14,
16,
18,
24,
30,
36,
48,
60,
72,
96,
8, 10, 12, 14, 16, 18, 24, 30, 36, 48, 60, 72, 96,
];
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,8 @@ type StepController = {

export const useStepController = (): StepController => {
const [currentStep, setCurrentStep] = useRecoilState(currentStepAtom);
const {
name,
stitches,
rows,
size,
needle,
yarn,
description,
techniques,
} = useRecoilValue(currentDesignInputAtom);
const { name, stitches, rows, size, needle, yarn, description, techniques } =
useRecoilValue(currentDesignInputAtom);
const {
totalLength,
sleeveLength,
Expand Down
8 changes: 2 additions & 6 deletions src/pages/CreateDesign/components/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import { useStepController } from './hooks/useStepController';
const Footer = (): React.ReactElement => {
const currentStep = useRecoilValue(currentStepAtom);

const {
onPreviousClick,
onNextClick,
renderNextLabel,
isNextDisabled,
} = useStepController();
const { onPreviousClick, onNextClick, renderNextLabel, isNextDisabled } =
useStepController();

return (
<FooterContainer>
Expand Down
35 changes: 35 additions & 0 deletions src/pages/CreateProduct/Confirm/Confirm.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Typography } from '@material-ui/core';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { theme } from 'themes';

export const ProductCard = styled(Link)`
color: ${theme.palette.text.primary};
text-decoration: none;
width: ${theme.spacing(55)};
border: 5px solid ${theme.palette.grey[200]};
border-radius: ${theme.spacing(6)};
`;

export const RepresentativeImage = styled.img`
width: ${theme.spacing(55)};
height: ${theme.spacing(46)};
border-radius: ${theme.spacing(6)};
`;

export const Tag = styled.div`
display: inline-block;
background: ${theme.palette.grey[300]};
padding: ${theme.spacing(1)};
margin: ${theme.spacing(0.4)};
border-radius: ${theme.spacing(2)};
font-size: ${theme.spacing(1.5)};
`;

export const Price = styled(Typography)`
margin-left: ${theme.spacing(1)};
`;

export const InfoMessage = styled.li`
margin-top: ${theme.spacing(1)};
`;
78 changes: 78 additions & 0 deletions src/pages/CreateProduct/Confirm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Box, Grid, Typography } from '@material-ui/core';
import React from 'react';
import { useRecoilValue } from 'recoil';
import { splitText } from 'utils/splitText';

import { currentProductIdAtom, currentProductInputAtom } from '../recoils';

import {
ProductCard,
RepresentativeImage,
InfoMessage,
Price,
Tag,
} from './Confirm.css';

const Confirm = (): React.ReactElement => {
const { name, fullPrice, discountPrice, representativeImageUrl, tags } =
useRecoilValue(currentProductInputAtom);
const currentProductId = useRecoilValue(currentProductIdAtom);

const getRate = (): string => {
const rate = Math.round((discountPrice / fullPrice) * 100);

return isNaN(rate) ? '' : rate.toString();
};

const getPrice = (): string => {
const price = fullPrice - discountPrice;

return isNaN(price) && Math.sign(price) ? '' : price.toLocaleString();
};

return (
<>
<Grid container>
<ProductCard to={`/product/${currentProductId}`}>
{representativeImageUrl && (
<RepresentativeImage
src="//via.placeholder.com/500x300"
alt="상품 대표 이미지"
/>
)}
<Box px={4} py={2}>
<Typography variant="h4">{name}</Typography>
<Box display="flex" justifyContent="space-between" mt={4}>
<div>
{splitText(tags, '#').map((tag: string) => (
<Tag>#{tag}</Tag>
))}
</div>
<Box display="flex">
<Typography color="primary">
<b>{getRate()}%</b>
</Typography>
<Price variant="h4">{getPrice()}</Price>
</Box>
</Box>
</Box>
</ProductCard>
</Grid>
<ul>
<InfoMessage>
상품은 위와 같이 다른 니터들에게 노출될 예정이에요!
</InfoMessage>
<InfoMessage>클릭하면 상세도 확인해볼 수 있어요!</InfoMessage>
<InfoMessage>
노출되는 기간은 "
<Typography display="inline" color="primary">
상품이 등록된 이후부터 계속해서
</Typography>
" 노출됩니다.
</InfoMessage>
</ul>
</>
);
};

export default Confirm;
62 changes: 62 additions & 0 deletions src/pages/CreateProduct/components/Footer/hooks/useSaveProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FAILED_TO_SAVE_PRODUCT } from 'constants/errors';

import { usePost } from 'hooks/usePost';
import {
currentProductIdAtom,
currentProductInputAtom,
currentStepAtom,
} from 'pages/CreateProduct/recoils';
import { PAGE } from 'pages/CreateProduct/types';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { splitText } from 'utils/splitText';

type SaveProduct = {
saveProduct: () => void;
};

export const useSaveProduct = (): SaveProduct => {
const setCurrentStep = useSetRecoilState(currentStepAtom);
const setCurrentProductId = useSetRecoilState(currentProductIdAtom);

const {
name,
fullPrice,
discountPrice,
representativeImageUrl,
specifiedSalesStartDate,
specifiedSalesEndDate,
tags,
designIds,
} = useRecoilValue(currentProductInputAtom);

const onSuccess = () => {
if (data) {
setCurrentProductId(data.payload.id);
}
setCurrentStep(PAGE.INTRODUCTION);
};

const { data, mutate } = usePost({
pathname: '/product/package',
errorMessage: FAILED_TO_SAVE_PRODUCT,
onSuccess,
});

const saveProduct = (): void => {
const postProductData = {
id: null,
name,
full_price: fullPrice,
discount_price: discountPrice,
representative_image_url: representativeImageUrl,
specified_sales_start_date: specifiedSalesStartDate,
specified_sales_end_date: specifiedSalesEndDate,
tags: splitText(tags, '#'),
design_ids: designIds,
};

mutate(postProductData);
};

return { saveProduct };
};
26 changes: 26 additions & 0 deletions src/pages/CreateProduct/components/Footer/hooks/useStartSale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MY_INFORMATION_ROUTER_ROOT } from 'constants/path';

import { usePost } from 'hooks/usePost';
import { currentProductIdAtom } from 'pages/CreateProduct/recoils';
import { useHistory } from 'react-router-dom';
import { useRecoilValue } from 'recoil';

type StartSale = {
startSale: () => void;
};

export const useStartSale = (): StartSale => {
const currentProductId = useRecoilValue(currentProductIdAtom);
const history = useHistory();

const { mutate } = usePost({
pathname: '/product',
});

const startSale = () => {
mutate({ id: currentProductId });
history.push(MY_INFORMATION_ROUTER_ROOT);
};

return { startSale };
};
65 changes: 10 additions & 55 deletions src/pages/CreateProduct/components/Footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,21 @@
import { FAILED_TO_SAVE_PRODUCT } from 'constants/errors';

import { Button as MaterialButton } from '@material-ui/core';
import { useCommonSnackbar } from 'components/CommonSnackbar/useCommonSnackbar';
import { Button } from 'dumbs';
import { usePost } from 'hooks/usePost';
import {
currentProductInputAtom,
currentStepAtom,
} from 'pages/CreateProduct/recoils';
import { currentStepAtom } from 'pages/CreateProduct/recoils';
import { PAGE } from 'pages/CreateProduct/types';
import React, { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import React from 'react';
import { useRecoilState } from 'recoil';
import { FooterContainer } from 'styles/constants';

import { useSaveProduct } from './hooks/useSaveProduct';
import { useStartSale } from './hooks/useStartSale';

const Footer = (): React.ReactElement => {
const { DESIGN, PACKAGE, INTRODUCTION, CONFIRM } = PAGE;
const [currentStep, setCurrentStep] = useRecoilState(currentStepAtom);
const {
name,
fullPrice,
discountPrice,
representativeImageUrl,
specifiedSalesStartDate,
specifiedSalesEndDate,
tags,
designIds,
} = useRecoilValue(currentProductInputAtom);

const [showError, setShowError] = useState(false);

const { mutate } = usePost({
pathname: '/product/package',
errorMessage: FAILED_TO_SAVE_PRODUCT,
onSuccess: () => setCurrentStep(INTRODUCTION),
onError: () => setShowError(true),
});

useCommonSnackbar({
message: FAILED_TO_SAVE_PRODUCT,
severity: 'error',
dependencies: [showError],
});

const saveProduct = (): void => {
const postProductData = {
id: null,
name,
full_price: fullPrice,
discount_price: discountPrice,
representative_image_url: representativeImageUrl,
specified_sales_start_date: specifiedSalesStartDate,
specified_sales_end_date: specifiedSalesEndDate,
tags: tags
.split('#')
.map((tag) => tag.trim())
.filter((value) => value),
design_ids: designIds,
};

mutate(postProductData);
};
const { saveProduct } = useSaveProduct();
const { startSale } = useStartSale();

const handleOnClickPrevious = (): void => {
const handleOnClickPrevious = async (): Promise<void> => {
switch (currentStep) {
case PACKAGE:
setCurrentStep(DESIGN);
Expand Down Expand Up @@ -104,6 +58,7 @@ const Footer = (): React.ReactElement => {
setCurrentStep(CONFIRM);
break;
case CONFIRM:
startSale();
break;
default:
break;
Expand Down
Loading

0 comments on commit 5364bbb

Please sign in to comment.