Skip to content

Commit

Permalink
Merge pull request #244 from Nexters/base/mvp-9th
Browse files Browse the repository at this point in the history
[base -> develop] 방문자, 결제 관리
  • Loading branch information
alstn2468 authored Nov 20, 2024
2 parents d863c5c + e5fe367 commit 03c6e4c
Show file tree
Hide file tree
Showing 27 changed files with 1,004 additions and 245 deletions.
43 changes: 27 additions & 16 deletions apps/admin/src/components/EnteranceTable/EnteranceTable.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,23 @@ const HeaderItem = styled.span`
margin-right: 12px;
}
&:nth-of-type(1) {
width: 108px;
width: 120px;
}
&:nth-of-type(2) {
width: 80px;
width: 100px;
}
&:nth-of-type(3) {
width: 180px;
width: 140px;
}
&:nth-of-type(4) {
width: 100px;
width: 80px;
}
&:nth-of-type(5) {
width: 140px;
}
&:nth-of-type(6) {
width: 80px;
}
&:nth-of-type(7) {
width: 148px;
width: 180px;
flex: 1 0 auto;
}
`;

Expand All @@ -66,32 +64,33 @@ const Item = styled.span`
text-align: left;
${({ theme }) => theme.typo.b2};
color: ${({ theme }) => theme.palette.grey.g90};
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
& strong {
background-color: ${({ theme }) => theme.palette.primary.o0};
}
&:not(:last-of-type) {
margin-right: 12px;
}
&:nth-of-type(1) {
width: 108px;
width: 120px;
}
&:nth-of-type(2) {
width: 80px;
width: 100px;
}
&:nth-of-type(3) {
width: 180px;
width: 140px;
}
&:nth-of-type(4) {
width: 100px;
width: 80px;
}
&:nth-of-type(5) {
width: 140px;
}
&:nth-of-type(6) {
width: 80px;
}
&:nth-of-type(7) {
width: 148px;
width: 180px;
flex: 1 0 auto;
}
`;

Expand All @@ -112,6 +111,16 @@ const ResetButton = styled(Button)`
margin-top: 18px;
`;

const DisabledText = styled.span`
${({ theme }) => theme.typo.b2};
color: ${({ theme }) => theme.palette.grey.g30};
`;

const SearchResult = styled.span`
white-space: nowrap;
overflow: hidden;
`;

export default {
Container,
HeaderItem,
Expand All @@ -120,4 +129,6 @@ export default {
Item,
Empty,
ResetButton,
DisabledText,
SearchResult,
};
55 changes: 30 additions & 25 deletions apps/admin/src/components/EnteranceTable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EntranceResponse } from '@boolti/api';
import {
createColumnHelper,
flexRender,
Expand All @@ -11,55 +10,61 @@ import { boldText } from '~/utils/boldText';
import { formatPhoneNumber } from '~/utils/format';

import Styled from './EnteranceTable.styles';
import { TicketWithReservationResponse } from '@boolti/api/src/types/adminTicket';

const columnHelper = createColumnHelper<EntranceResponse>();
const columnHelper = createColumnHelper<TicketWithReservationResponse>();

const columns = [
columnHelper.accessor('csTicketId', {
header: '티켓 번호',
}),
columnHelper.accessor('ticketType', {
header: '티켓 종류',
cell: (props) => `${props.getValue() === 'INVITE' ? '초청' : '일반'}티켓`,
}),
columnHelper.accessor('ticketName', {
header: '티켓 이름',
}),
columnHelper.accessor('reservationName', {
header: '방문자 이름',
columnHelper.accessor('reservation.reservationHolder.name', {
header: '방문자명',
cell: (props) => {
const { searchText = '' } = (props.table.options.meta ?? {}) as { searchText: string };
return (
<span dangerouslySetInnerHTML={{ __html: boldText(props.getValue(), searchText) }}></span>
<Styled.SearchResult
dangerouslySetInnerHTML={{ __html: boldText(props.getValue(), searchText) }}
/>
);
},
}),
columnHelper.accessor('reservationPhoneNumber', {
columnHelper.accessor('reservation.reservationHolder.phoneNumber', {
header: '연락처',
cell: (props) => {
const { searchText = '' } = (props.table.options.meta ?? {}) as { searchText: string };
return (
<span
<Styled.SearchResult
dangerouslySetInnerHTML={{
__html: boldText(formatPhoneNumber(props.getValue()), searchText),
}}
></span>
/>
);
},
}),
columnHelper.accessor('entered', {
header: '상태',
cell: (props) => (props.getValue() ? '입장 확인' : '미입장'),
columnHelper.accessor('salesTicketType.ticketType', {
header: '티켓 종류',
cell: (props) => `${props.getValue() === 'INVITE' ? '초청' : '일반'}티켓`,
}),
columnHelper.accessor('enteredAt', {
header: '입장 일시',
cell: (props) => (props.getValue() ? format(props.getValue(), 'yyyy/MM/dd HH:mm') : '-'),
columnHelper.accessor('salesTicketType.ticketName', {
header: '티켓명',
}),
columnHelper.accessor('usedAt', {
header: '방문 일시',
cell: (props) => {
const value = props.getValue();
return value ? (
format(value, 'yyyy/MM/dd HH:mm')
) : (
<Styled.DisabledText>아직 방문하지 않았습니다.</Styled.DisabledText>
);
},
}),
];

interface Props {
data: EntranceResponse[];
isEnteredTicket: boolean;
data: TicketWithReservationResponse[];
isEnteredTicket?: boolean;
searchText: string;
onClickReset?: VoidFunction;
}
Expand Down Expand Up @@ -97,9 +102,9 @@ const EnteranceTable = ({ searchText, data, isEnteredTicket, onClickReset }: Pro
</Styled.ResetButton>
</>
) : isEnteredTicket ? (
'입장 관객이 없어요.'
'아직 방문자가 없어요.'
) : (
'미입장 관객이 없어요.'
'미방문자가 없어요.'
)}
</Styled.Empty>
) : (
Expand Down
28 changes: 27 additions & 1 deletion apps/admin/src/components/MobileCardList/MobileCardList.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,44 @@ const DateText = styled.div`
`;

const UserInfoText = styled.div`
width: 100%;
text-align: left;
${({ theme }) => theme.typo.sh1};
color: ${({ theme }) => theme.palette.grey.g90};
`;

const TicketDetailTextWrap = styled.div`
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
`;

const TicketInfoText = styled.div`
${({ theme }) => theme.typo.b1};
color: ${({ theme }) => theme.palette.grey.g70};
`;

const TicketStatusText = styled.div<{ type: 'DISABLED' | 'LINE_THROUGH' | 'NORMAL' }>`
${({ theme }) => theme.typo.b1};
color: ${({ theme, type }) =>
type === 'NORMAL' ? theme.palette.grey.g90 : theme.palette.grey.g30};
text-decoration: ${({ type }) => (type === 'LINE_THROUGH' ? 'line-through' : undefined)};
`;

const ResetButton = styled.button`
font-weight: 600;
text-decoration: underline;
`;

export default { Container, CardItem, Row, DateText, UserInfoText, TicketInfoText, ResetButton };
export default {
Container,
CardItem,
Row,
DateText,
UserInfoText,
TicketInfoText,
ResetButton,
TicketDetailTextWrap,
TicketStatusText,
};
28 changes: 19 additions & 9 deletions apps/admin/src/components/MobileCardList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import Styled from './MobileCardList.style';

type Item = {
id: number;
badgeText: string;
badgeText?: string;
name: string;
phoneNumber: string;
ticketName: string;
type: 'DISABLED' | 'LINE_THROUGH' | 'NORMAL';
status: string;
date?: string;
count: number;
count?: number;
};

interface Props {
Expand All @@ -29,19 +31,27 @@ function MobileCardList({ searchText, items, emptyText, onClickReset }: Props) {
const Elements = items.map((item) => {
return (
<Styled.CardItem key={item.id}>
<Styled.Row>
<Badge colorTheme="grey">{item.badgeText}</Badge>
{item.date && <Styled.DateText>{format(item.date, 'yyyy/MM/dd HH:mm')}</Styled.DateText>}
</Styled.Row>
{item.badgeText && (
<Styled.Row>
<Badge colorTheme="grey">{item.badgeText}</Badge>
{item.date && (
<Styled.DateText>{format(item.date, 'yyyy/MM/dd HH:mm')}</Styled.DateText>
)}
</Styled.Row>
)}
<Styled.Row>
<Styled.UserInfoText
dangerouslySetInnerHTML={{
__html: boldText(`${item.name} (${formatPhoneNumber(item.phoneNumber)})`, searchText),
}}
></Styled.UserInfoText>
<Styled.TicketInfoText>
{item.ticketName} · {item.count}
</Styled.TicketInfoText>
<Styled.TicketDetailTextWrap>
<Styled.TicketInfoText>
{item.ticketName}
{item.count ? ` · ${item.count}매` : ''}
</Styled.TicketInfoText>
<Styled.TicketStatusText type={item.type}>{item.status}</Styled.TicketStatusText>
</Styled.TicketDetailTextWrap>
</Styled.Row>
</Styled.CardItem>
);
Expand Down
16 changes: 10 additions & 6 deletions apps/admin/src/components/Pagination/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { ChevronLeftIcon, ChevronRightIcon } from '@boolti/icon';

import Styled from './Pagination.styles';
import { useDeviceWidth } from '~/hooks/useDeviceWidth';
import { useTheme } from '@emotion/react';

interface Props {
totalPages: number;
currentPage: number;
onClickPage?: (page: number) => void;
}

const SIZE_PER_PAGE = 10;

const Pagination = ({ totalPages, currentPage, onClickPage }: Props) => {
const start = Math.floor(currentPage / SIZE_PER_PAGE) * SIZE_PER_PAGE;
const end = start + SIZE_PER_PAGE;
const deviceWidth = useDeviceWidth();
const theme = useTheme();
const isMobile = deviceWidth < parseInt(theme.breakpoint.mobile, 10);
const sizePerPage = isMobile ? 5 : 10;
const start = Math.floor(currentPage / sizePerPage) * sizePerPage;
const end = start + sizePerPage;
const pages = Array.from({ length: totalPages }, (_, i) => i).slice(start, end);
return (
<Styled.Container>
<Styled.Button
disabled={currentPage === 0}
onClick={() => {
onClickPage?.(Math.max(currentPage - SIZE_PER_PAGE, 0));
onClickPage?.(Math.max(start - sizePerPage, 0));
}}
>
<ChevronLeftIcon />
Expand All @@ -38,7 +42,7 @@ const Pagination = ({ totalPages, currentPage, onClickPage }: Props) => {
<Styled.Button
disabled={currentPage === totalPages - 1}
onClick={() => {
onClickPage?.(Math.min(currentPage + SIZE_PER_PAGE, totalPages - 1));
onClickPage?.(Math.min(end, totalPages - 1));
}}
>
<ChevronRightIcon />
Expand Down
Loading

0 comments on commit 03c6e4c

Please sign in to comment.