Download frontend/src/components/workspace/MainTableWorkspace.jsx from Seth0330/qualifier: direct link, hf CLI and curl.
- Browser
- Download file 6.37 kB
-
https://huggingface.co/spaces/Seth0330/qualifier/resolve/main/frontend/src/components/workspace/MainTableWorkspace.jsx
- Command line
-
hf download hf://spaces/Seth0330/qualifier/frontend/src/components/workspace/MainTableWorkspace.jsx
-
curl -L -o MainTableWorkspace.jsx https://huggingface.co/spaces/Seth0330/qualifier/resolve/main/frontend/src/components/workspace/MainTableWorkspace.jsx
6.37 kB
| 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> | |
| ); | |
| } | |