Files

134 lines
8.4 KiB
TypeScript

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, Link, router, usePage } from '@inertiajs/react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/Components/ui/select';
import { Badge } from '@/Components/ui/badge';
import { Button } from '@/Components/ui/button';
import { Tabs, TabsList, TabsTrigger } from '@/Components/ui/tabs';
import { ArrowLeft, Timer, Package, ClipboardList, Play, CheckCircle2, ArrowRight } from 'lucide-react';
export default function ProjectLayout({ project, allowedTransitions, children, currentTab }: { project: any, allowedTransitions?: any[], children: React.ReactNode, currentTab: string }) {
const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
const backTo = params.get('back_to');
const handleBack = (e: React.MouseEvent) => {
if (backTo === 'inventory') {
router.visit(route('inventory.index'));
} else if (typeof window !== 'undefined') {
if (window.history.length > 1) {
window.history.back();
} else {
router.visit(route('projects.index'));
}
}
};
const statusLabel = (s: string) => s?.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()) || '';
const handleTransition = (status: string) => {
router.patch(route('projects.transition', project.ulid), { status });
};
const handleTabChange = (val: string) => {
// Kept for backward compatibility if needed, but tabs are removed
};
const { projects, projectOptions } = usePage<any>().props;
const selectableProjects = projectOptions ?? projects;
return (
<AuthenticatedLayout
header={
project ? (
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-4">
<Button variant="ghost" size="icon-sm" onClick={handleBack}>
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<div className="flex items-baseline gap-2">
<h2 className="text-xl font-semibold leading-tight text-gray-800">
{project.name}
</h2>
<Badge variant="outline" className="capitalize shrink-0">{statusLabel(project.status)}</Badge>
</div>
<p className="text-sm text-gray-500 mt-0.5">{project.code}</p>
</div>
</div>
{allowedTransitions && allowedTransitions.length > 0 && (
<div className="flex items-center gap-2">
{allowedTransitions.map((t: any) => (
<Button key={t.value} variant="outline" size="sm" onClick={() => handleTransition(t.value)}>
{t.value === 'in_progress' && <Play className="mr-1 h-3 w-3" />}
{t.value === 'completed' && <CheckCircle2 className="mr-1 h-3 w-3" />}
{t.label}
</Button>
))}
</div>
)}
</div>
) : undefined
}
>
<Head title={project ? `${project.name} - ${currentTab.charAt(0).toUpperCase() + currentTab.slice(1)}` : 'Select Project'} />
<div className={currentTab === 'tasks' ? "h-[calc(100vh-73px)] flex flex-col pt-2" : "py-6"}>
<div className={currentTab === 'tasks' ? "w-full h-full p-4 flex flex-col" : "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"}>
{!project ? (
<div className="py-10">
<div className="text-center mb-8">
<h3 className="text-2xl font-semibold text-gray-900">Select a Project</h3>
<p className="mt-2 text-sm text-gray-500">Choose a project to view its {currentTab.replace('-', ' ')} data.</p>
</div>
{selectableProjects && selectableProjects.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
{selectableProjects.map((p: any) => (
<div
key={p.ulid}
onClick={() => {
const url = new URL(window.location.href);
url.searchParams.set('project', p.ulid);
window.location.href = url.toString();
}}
className="group relative flex flex-col items-start justify-between rounded-xl border border-gray-200 bg-white p-6 shadow-sm transition-all hover:border-emerald-500 hover:shadow-md cursor-pointer overflow-hidden"
>
<div className="absolute inset-x-0 top-0 h-1 bg-transparent transition-colors group-hover:bg-emerald-500" />
<div className="flex w-full items-start justify-between gap-4 mb-6">
<div className="flex-1">
<h4 className="font-semibold text-lg text-gray-900 group-hover:text-emerald-700 transition-colors line-clamp-2">{p.name}</h4>
<div className="flex flex-wrap items-center gap-2 mt-1">
<span className="text-sm font-mono text-gray-500">{p.code}</span>
{p.project_type === 'extension' && p.parent_project && (
<Badge variant="secondary" className="text-[10px] py-0 px-1.5 font-normal bg-slate-100 text-slate-700 hover:bg-slate-100">
Ext of {p.parent_project.code}
</Badge>
)}
</div>
</div>
{p.status && <Badge variant="outline" className="shrink-0 capitalize">{statusLabel(p.status)}</Badge>}
</div>
<div className="mt-auto flex w-full items-center justify-between">
<span className="text-xs text-gray-400">Click to select</span>
<div className="flex items-center text-sm font-medium text-emerald-600 opacity-0 transition-opacity group-hover:opacity-100">
Open <ArrowRight className="ml-1 h-4 w-4" />
</div>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-20 bg-white shadow-sm sm:rounded-lg border border-dashed border-gray-300 max-w-3xl mx-auto">
<h3 className="text-lg font-medium text-gray-900">No projects found</h3>
<p className="mt-1 text-sm text-gray-500">There are no active projects available for your account.</p>
</div>
)}
</div>
) : (
children
)}
</div>
</div>
</AuthenticatedLayout>
);
}