145 lines
5.5 KiB
JavaScript
145 lines
5.5 KiB
JavaScript
import React from 'react';
|
|
import { X, MoreVertical } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Card } from '@/components/ui/card';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu';
|
|
|
|
/**
|
|
* ItemDetailPanel Component
|
|
* A modular panel for displaying detailed information about an item, including actions and tabbed content.
|
|
*
|
|
* @param {Object} props
|
|
* @param {string} props.title - Main title of the item (e.g., Client Name).
|
|
* @param {string} props.subtitle - Subtitle or ID.
|
|
* @param {string} props.status - Status text for the badge.
|
|
* @param {string} props.statusColor - specific color class for status (optional).
|
|
* @param {Array<{label: string, icon: React.Node, onClick: Function, variant: string, isDestructive: boolean}>} props.actions - Primary actions displayed as buttons.
|
|
* @param {Array<{label: string, icon: React.Node, onClick: Function}>} props.menuActions - Secondary actions in a dropdown menu.
|
|
* @param {Array<{id: string, label: string, content: React.Node}>} props.tabs - Tabs configuration.
|
|
* @param {Function} props.onClose - Handler for the close button.
|
|
* @param {string} props.className - Additional CSS classes.
|
|
*/
|
|
export const ItemDetailPanel = ({
|
|
title,
|
|
subtitle,
|
|
status,
|
|
statusColor = "bg-primary/10 text-primary",
|
|
actions = [],
|
|
menuActions = [],
|
|
tabs = [],
|
|
onClose,
|
|
className
|
|
}) => {
|
|
return (
|
|
<div className={`flex flex-col h-full bg-white dark:bg-[#1b1b1b] border-l shadow-xl w-full max-w-2xl transition-all duration-300 ${className}`}>
|
|
{/* Header Section */}
|
|
<div className="flex items-start justify-between p-6 pb-2">
|
|
<div className="space-y-1">
|
|
<div className="flex items-center gap-3">
|
|
<h2 className="text-2xl font-bold tracking-tight text-foreground">{title}</h2>
|
|
{status && (
|
|
<Badge variant="secondary" className={`${statusColor} hover:${statusColor}`}>
|
|
{status}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
{subtitle && (
|
|
<p className="text-muted-foreground text-sm">{subtitle}</p>
|
|
)}
|
|
</div>
|
|
<Button variant="ghost" size="icon" onClick={onClose} className="h-8 w-8 rounded-full">
|
|
<X className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Actions Toolbar */}
|
|
<div className="px-6 py-4 flex items-center justify-between gap-2">
|
|
<div className="flex items-center gap-2 overflow-x-auto no-scrollbar">
|
|
{actions.map((action, index) => (
|
|
<Button
|
|
key={index}
|
|
variant={action.variant || "outline"}
|
|
size="sm"
|
|
onClick={action.onClick}
|
|
className={`gap-2 ${action.isDestructive ? 'text-destructive hover:bg-destructive/10 border-destructive/20' : ''}`}
|
|
>
|
|
{action.icon}
|
|
{action.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
|
|
{menuActions.length > 0 && (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="icon">
|
|
<MoreVertical className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
{menuActions.map((action, index) => (
|
|
<DropdownMenuItem
|
|
key={index}
|
|
onClick={action.onClick}
|
|
className={action.isDestructive ? 'text-destructive focus:text-destructive' : ''}
|
|
>
|
|
{action.icon && <span className="mr-2">{action.icon}</span>}
|
|
{action.label}
|
|
</DropdownMenuItem>
|
|
))}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)}
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Tabs and Content */}
|
|
<div className="flex-1 overflow-hidden flex flex-col">
|
|
{tabs.length > 0 ? (
|
|
<Tabs defaultValue={tabs[0].id} className="flex flex-col h-full">
|
|
<div className="px-6 pt-2 border-b bg-muted/30">
|
|
<TabsList className="bg-transparent p-0 w-full justify-start h-auto">
|
|
{tabs.map((tab) => (
|
|
<TabsTrigger
|
|
key={tab.id}
|
|
value={tab.id}
|
|
className="data-[state=active]:bg-background data-[state=active]:shadow-sm rounded-t-lg rounded-b-none border-t border-l border-r border-transparent data-[state=active]:border-border px-4 py-3"
|
|
>
|
|
{tab.label}
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
</div>
|
|
|
|
<ScrollArea className="flex-1 bg-muted/10">
|
|
<div className="p-6">
|
|
{tabs.map((tab) => (
|
|
<TabsContent key={tab.id} value={tab.id} className="m-0 mt-0 space-y-4">
|
|
{tab.content}
|
|
</TabsContent>
|
|
))}
|
|
</div>
|
|
</ScrollArea>
|
|
</Tabs>
|
|
) : (
|
|
<div className="flex-1 flex items-center justify-center text-muted-foreground">
|
|
No details available
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ItemDetailPanel;
|