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

feat: 픽업 예약 시간 설정 #61

Merged
merged 2 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions __tests__/utils/date.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import {it, expect} from '@jest/globals';
import date from '@utils/date';
import {format, decimalToTime, zeroPad, isAfter} from '@utils/date';

it('should return format date', () => {
const timestamp = 1618225200000;

expect(date.format(timestamp)).toBe('2021. 04. 12. (월)');
expect(date.format(timestamp, 'YYYY년 M월 D일 (dd)')).toBe(
'2021년 4월 12일 (월)',
);
expect(format(timestamp)).toBe('2021. 04. 12. (월)');
expect(format(timestamp, 'YYYY년 M월 D일 (dd)')).toBe('2021년 4월 12일 (월)');
});

it('should return decimal to time', () => {
expect(date.decimalToTime(1.5)).toEqual([1, 30]);
expect(date.decimalToTime(1.75)).toEqual([1, 45]);
expect(decimalToTime(1.5)).toEqual([1, 30]);
expect(decimalToTime(1.75)).toEqual([1, 45]);
});

it('should return zero pad', () => {
expect(date.zeroPad(1)).toBe('01');
expect(date.zeroPad(10)).toBe('10');
expect(zeroPad(1)).toBe('01');
expect(zeroPad(10)).toBe('10');
});

it('should return is after', () => {
expect(date.isAfter(new Date(Date.now() - 10000))).toBe(true);
expect(date.isAfter(new Date(Date.now() + 10000))).toBe(false);
expect(isAfter(new Date(Date.now() - 10000))).toBe(true);
expect(isAfter(new Date(Date.now() + 10000))).toBe(false);
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"react": "18.3.1",
"react-native": "0.75.2",
"react-native-config": "^1.5.3",
"react-native-date-picker": "^5.0.7",
"react-native-encrypted-storage": "^4.0.3",
"react-native-gesture-handler": "^2.18.1",
"react-native-paper": "^5.12.5",
Expand Down
21 changes: 0 additions & 21 deletions src/components/common/DatePicker.style.tsx

This file was deleted.

55 changes: 15 additions & 40 deletions src/components/common/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,22 @@
import date from '@/utils/date';
import React, {useState} from 'react';
import S from './DatePicker.style';
import React from 'react';

type Props = {
onChange: (hour: number, minute: number) => void;
hour: number;
minute: number;
step?: number;
range?: [number, number];
};

const DatePicker = ({
onChange,
hour,
minute,
step = 30,
range = [0, 24],
}: Props) => {
const [selectedTime, setSelectedTime] = useState({hour, minute});
import {
DatePickerProps,
default as RNDatePicker,
} from 'react-native-date-picker';

const timeRange = Array.from(
{length: ((range[1] - range[0]) * 60) / step},
(_, i) => range[0] + (i * step) / 60,
);
type Props = {} & DatePickerProps;

const DatePicker = (props: Props) => {
return (
<S.ChipContainer>
{timeRange.map(time => {
const [h, m] = date.decimalToTime(time);
return (
<S.DateChip
key={time}
onPress={() => {
setSelectedTime({hour: h, minute: m});
onChange(h, m);
}}
selected={h === selectedTime.hour && m === selectedTime.minute}
disabled={date.isAfter(
new Date(new Date().setHours(h, m, 0, 0)),
)}>{`${date.zeroPad(h)}:${date.zeroPad(m)}`}</S.DateChip>
);
})}
</S.ChipContainer>
<RNDatePicker
{...props}
modal
mode="time"
date={new Date('2024-01-01T08:00:00')}
confirmText={props.confirmText ?? '확인'}
cancelText={props.cancelText ?? '취소'}
/>
);
};

Expand Down
6 changes: 3 additions & 3 deletions src/components/feedPage/Market.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {MarketType} from '@/types/Market';
import S from './Market.style';
import date from '@utils/date';
import {format} from '@utils/date';
import React from 'react';
type Props = {
market: MarketType;
Expand All @@ -21,10 +21,10 @@ const Market = ({market, onPress}: Props) => {
</S.MarketImageContainer>
<S.MarketTitle>{market.name}</S.MarketTitle>
<S.MarketPickupTime>
{`픽업: ${date.format(
{`픽업: ${format(
market.pickupStartAt,
'HH시 mm분',
)} ~ ${date.format(market.pickupEndAt, 'HH시 mm분')}`}
)} ~ ${format(market.pickupEndAt, 'HH시 mm분')}`}
</S.MarketPickupTime>
</S.MarketWrapper>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/orderHistory/HistoryTimeline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import date from '@utils/date';
import {format} from '@utils/date';
import S from './HistoryTimeline.style';

// TODO: 이미지 경로 수정
Expand All @@ -21,7 +21,7 @@ const HistoryTimeline = ({
<S.TitleLayout>
<S.CheckIcon source={{uri: checkImageSrc}} width={24} height={24} />
<S.Title>{title}</S.Title>
<S.Timestamp>{date.format(timestamp, 'HH시 mm분')}</S.Timestamp>
<S.Timestamp>{format(timestamp, 'HH시 mm분')}</S.Timestamp>
</S.TitleLayout>
<S.DescriptionLayout>
<S.DashedLine dashline={dashline} />
Expand Down
8 changes: 4 additions & 4 deletions src/components/orderHistory/OrderHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import {Alert, Image} from 'react-native';
import {OrderType} from '@/types/OrderType';
import date from '@utils/date';
import HistoryTimeline from './HistoryTimeline';
import S from './OrderHistory.style';
import {format} from '@/utils/date';

const DESCRIPTION_MAX_LENGTH = 30;

Expand Down Expand Up @@ -97,7 +97,7 @@ const OrderHistory = ({historyList, onPressMarket}: Props) => {
</S.OrderDetailButton>
</S.OrderDetailButtonContainer>
</S.InfoHeader>
<S.CreatedAt>{`${date.format(order.createdAt)}${
<S.CreatedAt>{`${format(order.createdAt)}${
status != null ? ` · ${status}` : ''
}`}</S.CreatedAt>
<S.Description numberOfLines={2}>
Expand All @@ -121,7 +121,7 @@ const OrderHistory = ({historyList, onPressMarket}: Props) => {
timestamp={order.pendingAt}
description={
order.doneAt == null
? `${date.format(
? `${format(
order.pickupAt,
'HH시 mm분',
)}까지 가게로 방문해주세요.`
Expand All @@ -133,7 +133,7 @@ const OrderHistory = ({historyList, onPressMarket}: Props) => {
<HistoryTimeline
title="픽업 완료"
timestamp={order.doneAt}
description={`${date.format(
description={`${format(
order.doneAt,
'HH시 mm분',
)}에 픽업이 완료되었습니다.`}
Expand Down
42 changes: 39 additions & 3 deletions src/components/orderPage/DatePickerCard.style.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
import styled from '@emotion/native';
import {Button, Text} from 'react-native-paper';

import C from './Common.style';

const Card = styled(C.CommonCard)``;
const HeaderText = styled(C.HeaderText)``;
const Card = styled(C.CommonCard)`
display: flex;
justify-content: flex-start;
gap: 12px;

padding: 36px 8px;
`;

const PlaneText = styled(Text)`
padding: 0 8px;

color: rgb(0, 0, 0);
font-family: Pretendard;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 30px;
`;

const DatePickerButton = styled(Button)`
display: flex;
flex-direction: row;

gap: 8px;

padding: 8px 0;
`;

const DatePickerText = styled(Text)`
color: rgb(0, 0, 0);
font-family: Pretendard;
font-size: 24px;
font-style: bold;
font-weight: 600;
line-height: 36px;
`;

const S = {Card, HeaderText};
const S = {Card, PlaneText, DatePickerButton, DatePickerText};

export default S;
45 changes: 33 additions & 12 deletions src/components/orderPage/DatePickerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,48 @@
import React, {useState} from 'react';
import {Icon} from 'react-native-paper';

import {DatePicker} from '@components/common';
import {format} from '@/utils/date';

import S from './DatePickerCard.style';

const DatePickerCard = () => {
const [reservedAt, setReservedAt] = useState({hour: 0, minute: 0});
const now = new Date();

// TODO: fetch marketPickupTime from server
const marketPickupAt: [number, number] = [18, 22];
const [isOpen, setIsOpen] = useState(false);
const [reservedAt, setReservedAt] = useState(now);

const onChange = (hour: number, minute: number) => {
setReservedAt({hour, minute});
const onConfirm = (date: Date) => {
setReservedAt(date);
setIsOpen(false);
};

return (
<S.Card>
<S.HeaderText>픽업 시간</S.HeaderText>
<>
<S.Card>
<S.DatePickerButton onPress={() => setIsOpen(true)}>
<S.DatePickerText>
{format(reservedAt.getTime(), 'a HH : mm')}
</S.DatePickerText>
<Icon source={'menu-down'} size={24} />
</S.DatePickerButton>
<S.PlaneText>{'으로 픽업 예약을 확정할게요'}</S.PlaneText>
</S.Card>
<DatePicker
onChange={onChange}
hour={reservedAt.hour}
minute={reservedAt.minute}
range={marketPickupAt}
title={'예약 시간을 선택해주세요'}
open={isOpen}
mode="time"
date={reservedAt}
minimumDate={now}
// TODO: 예약 가능 시간 서버에서 받아와야 함
maximumDate={new Date(now.getTime() + 24 * 60 * 60 * 1000)}
onConfirm={date => {
onConfirm(date);
setIsOpen(false);
}}
onCancel={() => setIsOpen(false)}
/>
</S.Card>
</>
);
};

Expand Down
8 changes: 4 additions & 4 deletions src/screens/MarketDetailScreen/MarketDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
NativeScrollEvent,
LayoutChangeEvent,
} from 'react-native';
import date from '@utils/date';
import {format} from '@utils/date';
import Menu from '@/components/marketDetailPage/Menu';
import S from './MarketDetail.style';
import {MarketType} from '@/types/Market';
Expand Down Expand Up @@ -243,14 +243,14 @@ const MarketDetailPage = ({
내 자식에게 준다는 마음으로 음식을 만들고 있습니다^^
</S.MarketDescription>
<S.MarketTimeDescription>
{`픽업 마감까지 ${date.format(pickupEndAt - Date.now(), 'HH시간 mm분')} 남았습니다!`}
{`픽업 마감까지 ${format(pickupEndAt - Date.now(), 'HH시간 mm분')} 남았습니다!`}
</S.MarketTimeDescription>
</S.MarketMainInfoWrapper>
<S.MarketSideInfoWrapper>
<S.MarketSideInfo>{`픽업: ${date.format(
<S.MarketSideInfo>{`픽업: ${format(
pickupStartAt,
'HH시 mm분',
)} ~ ${date.format(pickupEndAt, 'HH시 mm분')}`}</S.MarketSideInfo>
)} ~ ${format(pickupEndAt, 'HH시 mm분')}`}</S.MarketSideInfo>
<S.MarketSideInfo>{address}</S.MarketSideInfo>
</S.MarketSideInfoWrapper>
<S.SideTagBarScrollView
Expand Down
30 changes: 9 additions & 21 deletions src/screens/PaymentScreen/PaymentPage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
import {CartType} from '@/types/OrderType';
import {RootStackParamList} from '@/types/StackNavigationType';
import {BottomButton} from '@components/common';
import {
DatePickerCard,
PaymentMethod,
PaymentSummary,
} from '@components/orderPage';
import React, {useMemo} from 'react';
import {Text} from 'react-native-paper';

import {useNavigation} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import React, {useMemo, useState} from 'react';
import S from './PaymentPage.style';

const paymentMethodKind = {
toss: '토스',
kakao: '카카오페이',
} as const;
import {CartType} from '@/types/OrderType';
import {RootStackParamList} from '@/types/StackNavigationType';
import {BottomButton} from '@components/common';
import {DatePickerCard, PaymentSummary} from '@components/orderPage';

type PaymentMethodKindKeyType = keyof typeof paymentMethodKind;
import S from './PaymentPage.style';

type Props = {cart: CartType};

const PaymentPage = ({cart}: Props) => {
const [method, setMethod] = useState<PaymentMethodKindKeyType>('toss');
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

const {originalPrice, discountPrice} = useMemo(
Expand All @@ -40,11 +32,7 @@ const PaymentPage = ({cart}: Props) => {
<S.PaymentPage>
<S.ScrollView>
<DatePickerCard />
<PaymentMethod
value={method}
onChange={setMethod}
paymentMethodKind={paymentMethodKind}
/>
<Text>{'// TODO: 결제수단 서버에서 받아와야 함'}</Text>
<PaymentSummary
originalPrice={originalPrice}
discountPrice={discountPrice}
Expand Down
Loading
Loading