File size: 6,373 Bytes
2d06acb 401b968 2d06acb 97b08c9 2d06acb 97b08c9 2d06acb 4399645 2d06acb 97b08c9 2d06acb 401b968 2d06acb 97b08c9 2d06acb 401b968 2d06acb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import React from 'react';
import { Link } from 'react-router-dom';
import { Search, ChevronDown, ChevronRight, Plus } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
/**
* Shared “Deals-style” workspace: tabs, teal primary action, centered search, right actions,
* optional filters strip, collapsible section header, table area.
*/
export default function MainTableWorkspace({
tabs = [],
primaryAction = null,
search = null,
right = null,
filters = null,
/** 'top' = full-width strip above the table card; 'left' = narrow rail beside the table inside the card */
filtersPlacement = 'top',
sectionIcon: SectionIcon,
sectionTitle,
sectionCount,
sectionOpen,
onSectionToggle,
tableToolbar = null,
children,
}) {
return (
<div className="w-full min-w-0 space-y-4">
{tabs.length > 0 && (
<div className="flex flex-wrap gap-2 border-b border-slate-200 pb-3">
{tabs.map((t) => (
<button
key={t.label}
type="button"
onClick={t.onClick}
disabled={t.disabled}
className={cn(
'rounded-full px-4 py-1.5 text-sm font-medium transition-colors',
t.active
? 'bg-violet-100 text-violet-800'
: 'text-slate-400 hover:text-slate-600 px-3'
)}
>
{t.label}
</button>
))}
</div>
)}
<div className="flex w-full min-w-0 flex-col gap-3 lg:flex-row lg:items-center lg:gap-4">
<div className="shrink-0 order-1">
{primaryAction &&
(primaryAction.to ? (
<Button
asChild
size="sm"
className="bg-teal-600 hover:bg-teal-700 text-white shadow-sm"
>
<Link to={primaryAction.to} className="inline-flex items-center gap-1.5">
<Plus className="h-4 w-4" />
{primaryAction.label}
</Link>
</Button>
) : (
<Button
size="sm"
type="button"
disabled={primaryAction.disabled}
className="bg-teal-600 hover:bg-teal-700 text-white shadow-sm inline-flex items-center gap-1.5"
onClick={primaryAction.onClick}
>
<Plus className="h-4 w-4" />
{primaryAction.label}
</Button>
))}
</div>
{search && (
<div className="order-3 flex min-w-0 flex-1 justify-center lg:order-2">
<div className="relative w-full max-w-md lg:max-w-xl xl:max-w-2xl 2xl:max-w-3xl">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400 pointer-events-none" />
<Input
className="pl-9 border-slate-200 shadow-sm"
placeholder={search.placeholder}
value={search.value}
onChange={search.onChange}
/>
</div>
</div>
)}
<div className="shrink-0 flex flex-wrap gap-2 justify-end order-2 lg:order-3 lg:ml-auto">
{right}
</div>
</div>
{filtersPlacement === 'top' && filters && (
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">{filters}</div>
)}
<div className="w-full min-w-0 overflow-visible rounded-xl border border-slate-200 bg-white shadow-sm">
<button
type="button"
onClick={onSectionToggle}
className="w-full flex items-center gap-2 px-4 py-3 text-left font-semibold text-slate-800 border-b border-slate-100 hover:bg-slate-50"
>
{sectionOpen ? (
<ChevronDown className="h-5 w-5 text-violet-600 shrink-0" />
) : (
<ChevronRight className="h-5 w-5 text-violet-600 shrink-0" />
)}
{SectionIcon ? <SectionIcon className="h-5 w-5 text-slate-500 shrink-0" /> : null}
<span className="truncate">{sectionTitle}</span>
<span className="text-slate-400 font-normal text-sm ml-2 shrink-0">
{sectionCount} total
</span>
</button>
{sectionOpen &&
(filtersPlacement === 'left' && filters ? (
<div className="flex flex-col gap-3 border-t border-slate-100 px-2 pb-3 pt-2 sm:px-4 md:flex-row md:items-start md:gap-3">
<aside className="w-full shrink-0 rounded-lg border border-slate-200 bg-slate-50/80 p-2.5 md:w-52 md:max-w-[13rem] overflow-y-auto max-h-[min(70vh,720px)]">
{filters}
</aside>
<div className="flex min-w-0 flex-1 flex-col gap-2">
{tableToolbar}
{children}
</div>
</div>
) : (
<>
{tableToolbar}
{children}
</>
))}
</div>
</div>
);
}
|