Skip to content
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
38 changes: 38 additions & 0 deletions apps/owner/src/app/(tabs)/order/_components/OrderHistoryCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";

import { Card } from "@compasser/design-system";
import type { ReservationItem } from "../_types/order";
import { getStatusClassName, getStatusLabel } from "../_utils/orderStatus";

interface OrderHistoryCardProps {
order: ReservationItem;
}

export default function OrderHistoryCard({ order }: OrderHistoryCardProps) {
return (
<Card variant="default-black-shadow" className="w-full px-[1.6rem] py-[1.4rem]">
<div className="flex flex-col">
<div className="grid grid-cols-[7.2rem_1fr_auto] gap-y-[0.4rem] items-start">
<span className="body1-m text-gray-700">주문자</span>
<span className="body1-m text-default">{order.customerName}</span>
<span className={getStatusClassName(order.status)}>
{getStatusLabel(order.status)}
</span>

<span className="body1-m text-gray-700">주문내역</span>
<span className="body1-m text-default col-span-2">{order.orderDetail}</span>

<span className="body1-m text-gray-700">가격</span>
<span className="body1-m text-default col-span-2">{order.price}</span>

<span className="body1-m text-gray-700">수량</span>
<span className="body1-m text-default col-span-2">{order.quantity}</span>
</div>

<div className="mt-[1.6rem] flex justify-end">
<span className="body2-m text-gray-700">{order.processedAt}</span>
</div>
</div>
</Card>
);
}
44 changes: 44 additions & 0 deletions apps/owner/src/app/(tabs)/order/_components/OrderList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import ReservationCard from "./ReservationCard";
import OrderHistoryCard from "./OrderHistoryCard";
import type { OrderTabKey, ReservationItem } from "../_types/order";

interface OrderListProps {
activeTab: OrderTabKey;
orders: ReservationItem[];
onAccept: (orderId: number) => void;
onReject: (orderId: number) => void;
}

export default function OrderList({
activeTab,
orders,
onAccept,
onReject,
}: OrderListProps) {
if (orders.length === 0) {
return (
<div className="flex h-full items-center justify-center">
<p className="body1-m text-gray-600">주문 내역이 없어요.</p>
</div>
);
}

return (
<div className="flex flex-col gap-[1.2rem]">
{activeTab === "reservation"
? orders.map((order) => (
<ReservationCard
key={order.id}
order={order}
onAccept={onAccept}
onReject={onReject}
/>
))
: orders.map((order) => (
<OrderHistoryCard key={order.id} order={order} />
))}
</div>
);
}
70 changes: 70 additions & 0 deletions apps/owner/src/app/(tabs)/order/_components/ReservationCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import { Card, Button } from "@compasser/design-system";
import type { ReservationItem } from "../_types/order";
import { getStatusClassName, getStatusLabel } from "../_utils/orderStatus";

interface ReservationCardProps {
order: ReservationItem;
onAccept: (orderId: number) => void;
onReject: (orderId: number) => void;
}

export default function ReservationCard({
order,
onAccept,
onReject,
}: ReservationCardProps) {
const isPending = order.status === "pending";

return (
<Card variant="default-black-shadow" className="w-full px-[1.6rem] py-[1.4rem]">
<div className="flex flex-col">
<div className="grid grid-cols-[7.2rem_1fr_auto] gap-y-[0.4rem] items-start">
<span className="body1-m text-gray-700">주문자</span>
<span className="body1-m text-default">{order.customerName}</span>
<span className={getStatusClassName(order.status)}>
{getStatusLabel(order.status)}
</span>

<span className="body1-m text-gray-700">주문내역</span>
<span className="body1-m text-default col-span-2">{order.orderDetail}</span>

<span className="body1-m text-gray-700">가격</span>
<span className="body1-m text-default col-span-2">{order.price}</span>

<span className="body1-m text-gray-700">수량</span>
<span className="body1-m text-default col-span-2">{order.quantity}</span>
</div>

{isPending ? (
<div className="mt-[1.6rem] flex items-center gap-[1rem]">
<Button
type="button"
kind="simple"
variant="outline-primary"
fullWidth={false}
onClick={() => onAccept(order.id)}
>
수락
</Button>

<Button
type="button"
kind="simple"
variant="outline-gray"
fullWidth={false}
onClick={() => onReject(order.id)}
>
거절
</Button>
</div>
) : (
<div className="mt-[1.6rem] flex justify-end">
<span className="body2-m text-gray-700">{order.processedAt}</span>
</div>
)}
</div>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import { Button, Modal } from "@compasser/design-system";

interface AcceptOrderModalProps {
open: boolean;
onClose: () => void;
onConfirm: () => void;
}

export default function AcceptOrderModal({
open,
onClose,
onConfirm,
}: AcceptOrderModalProps) {
return (
<Modal
open={open}
onClose={onClose}
variant="confirm"
title="주문을 수락하시겠습니까?"
bodyClassName="mt-0"
footerClassName="mt-[2rem]"
footer={
<div className="flex items-center justify-center gap-[2rem]">
<Button
size="sm"
variant="gray"
fullWidth={false}
className="px-[2.2rem]"
onClick={onClose}
>
그만두기
</Button>

<Button
size="sm"
variant="primary"
fullWidth={false}
className="px-[2.2rem]"
onClick={onConfirm}
>
수락하기
</Button>
</div>
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client";

import { useEffect, useState } from "react";
import { Button, Modal } from "@compasser/design-system";

interface RejectOrderModalProps {
open: boolean;
onClose: () => void;
onConfirm: (reason: string) => void;
}

export default function RejectOrderModal({
open,
onClose,
onConfirm,
}: RejectOrderModalProps) {
const [reason, setReason] = useState("");

useEffect(() => {
if (!open) {
setReason("");
}
}, [open]);

return (
<Modal
open={open}
onClose={onClose}
variant="confirm"
title="주문을 거절하시겠습니까?"
bodyClassName="mt-0"
footerClassName="mt-[1.6rem]"
footer={
<div className="flex items-center justify-center gap-[2rem]">
<Button
size="sm"
variant="gray"
fullWidth={false}
className="px-[2.2rem]"
onClick={onClose}
>
그만두기
</Button>

<Button
size="sm"
variant="primary"
fullWidth={false}
className="px-[2.2rem]"
onClick={() => onConfirm(reason)}
>
거절하기
</Button>
</div>
}
>
<div className="my-[1.6rem]">
<input
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="거절 사유를 입력해주세요."
className="
w-full rounded-[8px] border border-primary
px-[1rem] py-[0.6rem]
body2-r text-default
placeholder:body2-r placeholder:text-gray-300
outline-none
"
/>
</div>
</Modal>
);
}
36 changes: 36 additions & 0 deletions apps/owner/src/app/(tabs)/order/_constants/mockOrders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { ReservationItem } from "../_types/order";

export const INITIAL_RESERVATIONS: ReservationItem[] = [
{
id: 1,
customerName: "김00",
orderDetail: "랜덤박스 1레벨",
price: "6,000원",
quantity: "1개",
status: "pending",
},
{
id: 2,
customerName: "고00",
orderDetail: "랜덤박스 1레벨",
price: "6,000원",
quantity: "1개",
status: "pending",
},
{
id: 3,
customerName: "이00",
orderDetail: "랜덤박스 3레벨",
price: "12,000원",
quantity: "2개",
status: "pending",
},
{
id: 4,
customerName: "박00",
orderDetail: "랜덤박스 2레벨",
price: "9,000원",
quantity: "3개",
status: "pending",
}
];
23 changes: 23 additions & 0 deletions apps/owner/src/app/(tabs)/order/_types/order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export type OrderTabKey = "reservation" | "order";

export type ReservationStatus = "pending" | "completed" | "cancelled";

export interface ReservationItem {
id: number;
customerName: string;
orderDetail: string;
price: string;
quantity: string;
status: ReservationStatus;
processedAt?: string;
}

export interface AcceptModalState {
isOpen: boolean;
orderId: number | null;
}

export interface RejectModalState {
isOpen: boolean;
orderId: number | null;
}
7 changes: 7 additions & 0 deletions apps/owner/src/app/(tabs)/order/_utils/formatProcessAt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const formatProcessedAt = (date: Date) => {
const year = String(date.getFullYear()).slice(2);
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");

return `${year}/${month}/${day}`;
};
27 changes: 27 additions & 0 deletions apps/owner/src/app/(tabs)/order/_utils/orderStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ReservationStatus } from "../_types/order";

export const getStatusLabel = (status: ReservationStatus) => {
switch (status) {
case "pending":
return "확인 대기중";
case "completed":
return "거래완료";
case "cancelled":
return "거래취소";
default:
return "";
}
};

export const getStatusClassName = (status: ReservationStatus) => {
switch (status) {
case "pending":
return "body1-m text-secondary";
case "completed":
return "body1-m text-primary";
case "cancelled":
return "body1-m text-gray-500";
default:
return "";
}
};
Loading
Loading