[style] negodata/front: 상품·카드 목록 포털형 카드(툴바+테이블 한 카드로 결합)

This commit is contained in:
Mina Choi 2026-07-22 17:23:48 +09:00
parent bcb87de35e
commit f8f8f70f54
4 changed files with 26 additions and 13 deletions

View File

@ -7,11 +7,14 @@ type CardTableProps = {
data: NegotiationCard[]; data: NegotiationCard[];
onEdit: (card: NegotiationCard) => void; onEdit: (card: NegotiationCard) => void;
footer?: ReactNode; footer?: ReactNode;
/** 툴바와 한 카드로 붙일 때 테이블 자체 테두리/라운드를 죽이는 용도 */
className?: string;
}; };
export function CardTable({ data, onEdit, footer }: CardTableProps) { export function CardTable({ data, onEdit, footer, className }: CardTableProps) {
return ( return (
<DataTable <DataTable
className={className}
data={data} data={data}
rowKey={(card) => card.id} rowKey={(card) => card.id}
onRowClick={onEdit} onRowClick={onEdit}

View File

@ -15,6 +15,8 @@ type ProductTableProps = {
totalCount: number; totalCount: number;
pageSize: number; pageSize: number;
onPageChange: (page: number) => void; onPageChange: (page: number) => void;
/** 툴바와 한 카드로 붙일 때 테이블 자체 테두리/라운드를 죽이는 용도 */
className?: string;
}; };
export function ProductTable({ export function ProductTable({
@ -27,10 +29,12 @@ export function ProductTable({
totalCount, totalCount,
pageSize, pageSize,
onPageChange, onPageChange,
className,
}: ProductTableProps) { }: ProductTableProps) {
const label = useLabels(); // 회사 설정 용어(카테고리/상품 단가 등) const label = useLabels(); // 회사 설정 용어(카테고리/상품 단가 등)
return ( return (
<DataTable <DataTable
className={className}
data={data} data={data}
rowKey={(prod) => prod.item_id} rowKey={(prod) => prod.item_id}
onRowClick={onRowClick} onRowClick={onRowClick}
@ -69,7 +73,7 @@ export function ProductTable({
), ),
}, },
{ {
header: '상품코드', header: label('item.code'),
align: 'left', align: 'left',
cellClassName: 'font-mono font-medium text-muted-foreground', cellClassName: 'font-mono font-medium text-muted-foreground',
cell: (prod) => prod.code, cell: (prod) => prod.code,

View File

@ -7,7 +7,7 @@ import { PageToolbar, SearchInput } from '@/components/layout/PageToolbar';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu';
import { TablePagination } from '@/components/ui/table-pagination'; import { TablePagination } from '@/components/ui/table-pagination';
import { Typography } from '@/components/ui/typography'; import { cn } from '@/lib/utils';
import { useServerList } from '@/lib/useServerList'; import { useServerList } from '@/lib/useServerList';
import { useCards } from '@/features/cards/hooks/useCards'; import { useCards } from '@/features/cards/hooks/useCards';
import { useGetCard } from '@/api/generated/card/card'; import { useGetCard } from '@/api/generated/card/card';
@ -62,8 +62,10 @@ export default function CardsPage() {
return ( return (
<PageContainer> <PageContainer>
{/* 플랫 툴바 — 다른 목록 페이지와 동일 문법(검색 좌측, 주버튼+⋯ 우측) */} {/* 검색/액션 + 탭 + 테이블을 한 카드로 붙인다(포털형, 상품관리와 동일 문법). */}
<div className="overflow-hidden rounded-lg border border-border bg-card">
<PageToolbar <PageToolbar
className="rounded-none border-0 border-b border-border"
actions={ actions={
<> <>
<DropdownMenu> <DropdownMenu>
@ -99,32 +101,30 @@ export default function CardsPage() {
onClear={list.clearSearch} onClear={list.clearSearch}
placeholder="전체 카드이름, 카드번호, 코드 검색..." placeholder="전체 카드이름, 카드번호, 코드 검색..."
/> />
</PageToolbar> </PageToolbar>
{/* Primary Navigation Tab */} {/* 구분 탭 — 검색/액션 바 아래, 테이블 바로 위 */}
<div className="flex border-b border-border"> <div className="flex border-b border-border px-3">
{tabs.map((tab) => ( {tabs.map((tab) => (
<button <button
key={tab.id} key={tab.id}
id={`card-tab-${tab.id.toLowerCase()}`} id={`card-tab-${tab.id.toLowerCase()}`}
onClick={() => list.setFilter('tab', tab.id)} onClick={() => list.setFilter('tab', tab.id)}
className={`py-2 px-6 font-bold text-xs tracking-tight border-b-2 transition-all cursor-pointer ${ className={cn(
'cursor-pointer border-b-2 px-6 py-2 text-xs font-bold tracking-tight transition-all',
activeTab === tab.id activeTab === tab.id
? 'border-primary text-primary' ? 'border-primary text-primary'
: 'border-transparent text-muted-foreground hover:text-foreground' : 'border-transparent text-muted-foreground hover:text-foreground'
}`} )}
> >
{tab.label} ({tab.count}) {tab.label} ({tab.count})
</button> </button>
))} ))}
</div> </div>
{/* 배너에 있던 공용 카드 안내는 캡션 한 줄로 유지 */}
<Typography variant="caption" className="-mt-3 block">
() . · .
</Typography>
<CardTable <CardTable
className="rounded-none border-0"
data={cards} data={cards}
onEdit={openEdit} onEdit={openEdit}
footer={ footer={
@ -139,6 +139,7 @@ export default function CardsPage() {
/> />
} }
/> />
</div>
{isFormOpen && ( {isFormOpen && (
<CardFormSheet <CardFormSheet

View File

@ -91,7 +91,10 @@ export default function ProductsPage() {
return ( return (
<PageContainer> <PageContainer>
{/* 검색/액션 바 + 테이블을 한 카드로 붙인다(포털형). 툴바는 카드 상단 헤더, 테이블은 본문. */}
<div className="overflow-hidden rounded-lg border border-border bg-card">
<PageToolbar <PageToolbar
className="rounded-none border-0 border-b border-border"
actions={ actions={
<> <>
<Button <Button
@ -166,6 +169,7 @@ export default function ProductsPage() {
</PageToolbar> </PageToolbar>
<ProductTable <ProductTable
className="rounded-none border-0"
data={products} data={products}
selectedIds={selectedIds} selectedIds={selectedIds}
onSelectionChange={setSelectedIds} onSelectionChange={setSelectedIds}
@ -176,6 +180,7 @@ export default function ProductsPage() {
pageSize={list.pageSize} pageSize={list.pageSize}
onPageChange={list.setPage} onPageChange={list.setPage}
/> />
</div>
{isFormOpen && ( {isFormOpen && (
<ProductFormSheet <ProductFormSheet