'use client';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useState, useMemo } from 'react';
import { useRouter } from 'next/navigation';
import { Search, SlidersHorizontal, Archive, Inbox, X, ArrowDownAZ, ArrowUpAZ, FolderPlus, FolderOpen, Sparkles, LayoutGrid, GitBranch, Github, } from 'lucide-react';
import { toast } from 'sonner';
import { Trans, useTranslation } from 'react-i18next';
import { FeatureTreeTable } from '../../components/features/feature-tree-table/index.js';
import { FeatureRowActionsManager } from '../../components/features/feature-tree-table/feature-row-actions-manager.js';
import { RepositoryGroupActionsManager } from '../../components/features/feature-tree-table/repository-group-actions.js';
import { ApplicationRowActionsManager } from '../../components/features/feature-tree-table/application-row-actions-manager.js';
import { DeleteFeatureDialog } from '../../components/common/delete-feature-dialog/delete-feature-dialog.js';
import { PageHeader } from '../../components/common/page-header/index.js';
import { EmptyState } from '../../components/common/empty-state/index.js';
import { FloatingActionButton, } from '../../components/common/floating-action-button/index.js';
import { FeatureCreateDrawer } from '../../components/common/feature-create-drawer/index.js';
import { NewProjectDialog } from '../../components/features/control-center/new-project-dialog.js';
import { ControlCenterEmptyState } from '../../components/features/control-center/control-center-empty-state.js';
import { Input } from '../../components/ui/input.js';
import { Button } from '../../components/ui/button.js';
import { Badge } from '../../components/ui/badge.js';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../../components/ui/alert-dialog.js';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select.js';
import { DeploymentStatusProvider } from '../../hooks/deployment-status-provider.js';
import { archiveFeature } from '../actions/archive-feature.js';
import { unarchiveFeature } from '../actions/unarchive-feature.js';
import { deleteFeature } from '../actions/delete-feature.js';
import { startFeature } from '../actions/start-feature.js';
import { stopFeature } from '../actions/stop-feature.js';
import { resumeFeature } from '../actions/resume-feature.js';
import { createFeature } from '../actions/create-feature.js';
import { addRepository } from '../actions/add-repository.js';
import { openIde } from '../actions/open-ide.js';
import { openShell } from '../actions/open-shell.js';
import { openFolder } from '../actions/open-folder.js';
import { deployRepository } from '../actions/deploy-repository.js';
import { stopDeployment } from '../actions/stop-deployment.js';
import { useSidebar } from '../../components/ui/sidebar.js';
import { useFabLayout } from '../../hooks/fab-layout-context.js';
const STATUS_LABELS = {
    'action-needed': 'Action Needed',
    'in-progress': 'In Progress',
    pending: 'Pending',
    blocked: 'Blocked',
    error: 'Error',
    done: 'Done',
};
const STATUS_COLORS = {
    'action-needed': 'bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/20',
    'in-progress': 'bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/20',
    pending: 'bg-slate-500/15 text-slate-700 dark:text-slate-400 border-slate-500/20',
    blocked: 'bg-gray-500/15 text-gray-700 dark:text-gray-400 border-gray-500/20',
    error: 'bg-red-500/15 text-red-700 dark:text-red-400 border-red-500/20',
    done: 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/20',
};
const GROUP_BY_LABELS = {
    repositoryName: 'Repository',
    status: 'Status',
    lifecycle: 'Lifecycle',
};
const GROUP_BY_OPTIONS = [
    { value: '__none__', label: 'No grouping' },
    { value: 'repositoryName', label: 'Repository' },
    { value: 'status', label: 'Status' },
    { value: 'lifecycle', label: 'Lifecycle' },
];
/** Item sort fields change based on groupBy — exclude the grouped field. */
export function getItemSortOptions(groupBy) {
    const all = [
        { value: 'name', label: 'Name' },
        { value: 'repositoryName', label: 'Repository' },
        { value: 'status', label: 'Status' },
        { value: 'lifecycle', label: 'Lifecycle' },
        { value: 'branch', label: 'Branch' },
    ];
    if (!groupBy)
        return all;
    return all.filter((o) => o.value !== groupBy);
}
export function isArchived(feature) {
    return feature.lifecycle === 'Archived';
}
export function FeatureTreePageClient({ rows, repos, createData, initialDeployments = [], }) {
    // Features-only view (excludes application rows) — used by status pill counts,
    // repo filter, and the row-actions portal manager which only knows about features.
    const features = useMemo(() => rows.filter((r) => !r._isApplication), [rows]);
    const router = useRouter();
    const { t } = useTranslation('web');
    // Filter state
    const [searchQuery, setSearchQuery] = useState('');
    const [statusFilter, setStatusFilter] = useState(null);
    const [archiveFilter, setArchiveFilter] = useState('active');
    const [repoFilter, setRepoFilter] = useState(null);
    const [showAdvanced, setShowAdvanced] = useState(false);
    // Group + sort state
    const [groupBy, setGroupBy] = useState('repositoryName');
    const [groupSortDir, setGroupSortDir] = useState('asc');
    const [itemSortField, setItemSortField] = useState('name');
    const [itemSortDir, setItemSortDir] = useState('asc');
    // Action wiring state
    const [tableContainer, setTableContainer] = useState(null);
    const [renderTick, setRenderTick] = useState(0);
    const [inFlightIds, setInFlightIds] = useState(new Set());
    const [deleteTarget, setDeleteTarget] = useState(null);
    const [archiveTarget, setArchiveTarget] = useState(null);
    // Create actions state
    const [createDrawerOpen, setCreateDrawerOpen] = useState(false);
    const [isCreatingFeature, setIsCreatingFeature] = useState(false);
    const [newProjectOpen, setNewProjectOpen] = useState(false);
    const [showCreatePrompt, setShowCreatePrompt] = useState(false);
    const addInFlight = useCallback((id) => {
        setInFlightIds((prev) => new Set(prev).add(id));
    }, []);
    const removeInFlight = useCallback((id) => {
        setInFlightIds((prev) => {
            const next = new Set(prev);
            next.delete(id);
            return next;
        });
    }, []);
    const handleRowClick = useCallback((row) => {
        if (row._isApplication && row._applicationId) {
            router.push(`/application/${row._applicationId}`);
            return;
        }
        router.push(`/feature/${row.id}/overview`);
    }, [router]);
    const handleTableRender = useCallback((container) => {
        setTableContainer(container);
        setRenderTick((t) => t + 1);
    }, []);
    // ── Create actions ────────────────────────────────────────────
    // Repo path to pre-fill when creating a feature from a repo group header
    const [createForRepoPath, setCreateForRepoPath] = useState('');
    const handleCreateFeatureForRepo = useCallback((repositoryPath) => {
        setCreateForRepoPath(repositoryPath);
        setCreateDrawerOpen(true);
    }, []);
    const handleCreateFeatureSubmit = useCallback((data) => {
        setIsCreatingFeature(true);
        setCreateDrawerOpen(false);
        createFeature(data)
            .then((result) => {
            if (result.error) {
                toast.error(result.error);
                return;
            }
            toast.success('Feature created');
            router.refresh();
        })
            .catch(() => {
            toast.error('Failed to create feature');
        })
            .finally(() => {
            setIsCreatingFeature(false);
        });
    }, [router]);
    const handleNewProjectCreated = useCallback((path) => {
        addRepository({ path })
            .then((result) => {
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Project created');
                router.refresh();
            }
        })
            .catch(() => {
            toast.error('Failed to add project');
        });
    }, [router]);
    const handlePickFolder = useCallback(() => {
        window.dispatchEvent(new CustomEvent('shep:pick-folder'));
    }, []);
    const fabActions = useMemo(() => {
        const actions = [
            {
                id: 'new-project',
                label: 'New project',
                icon: _jsx(FolderPlus, { className: "h-4 w-4" }),
                onClick: () => setNewProjectOpen(true),
            },
            {
                id: 'new-feature',
                label: t('fab.newFeature'),
                icon: _jsx(Sparkles, { className: "h-4 w-4" }),
                onClick: () => {
                    setCreateForRepoPath('');
                    setCreateDrawerOpen(true);
                },
            },
            {
                id: 'add-local-repo',
                label: t('fab.localFolder'),
                icon: _jsx(FolderOpen, { className: "h-4 w-4" }),
                onClick: handlePickFolder,
            },
            {
                id: 'new-application',
                label: t('fab.newApplication'),
                icon: _jsx(LayoutGrid, { className: "h-4 w-4" }),
                onClick: () => setShowCreatePrompt(true),
            },
        ];
        actions.push({
            id: 'adopt-branch',
            label: t('fab.adoptBranch'),
            icon: _jsx(GitBranch, { className: "h-4 w-4" }),
            onClick: () => router.push('/adopt'),
        });
        actions.push({
            id: 'add-github-repo',
            label: t('fab.fromGithub'),
            icon: _jsx(Github, { className: "h-4 w-4" }),
            onClick: () => {
                window.dispatchEvent(new CustomEvent('shep:open-github-import'));
            },
        });
        return actions;
    }, [t, handlePickFolder, router]);
    // ── Action handlers ──────────────────────────────────────────
    const handleStart = useCallback(async (featureId) => {
        addInFlight(featureId);
        try {
            const result = await startFeature(featureId);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature started');
            }
        }
        catch {
            toast.error('Failed to start feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [addInFlight, removeInFlight, router]);
    const handleStop = useCallback(async (featureId) => {
        addInFlight(featureId);
        try {
            const result = await stopFeature(featureId);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature stopped');
            }
        }
        catch {
            toast.error('Failed to stop feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [addInFlight, removeInFlight, router]);
    const handleRetry = useCallback(async (featureId) => {
        addInFlight(featureId);
        try {
            const result = await resumeFeature(featureId);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature resumed');
            }
        }
        catch {
            toast.error('Failed to resume feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [addInFlight, removeInFlight, router]);
    const handleReview = useCallback((featureId) => {
        router.push(`/feature/${featureId}/overview`);
    }, [router]);
    const handleUnarchive = useCallback(async (featureId) => {
        addInFlight(featureId);
        try {
            const result = await unarchiveFeature(featureId);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature unarchived');
            }
        }
        catch {
            toast.error('Failed to unarchive feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [addInFlight, removeInFlight, router]);
    // Archive opens confirmation dialog
    const handleArchiveRequest = useCallback((featureId) => {
        const feature = features.find((f) => f.id === featureId);
        if (!feature)
            return;
        setArchiveTarget({ featureId, featureName: feature.name });
    }, [features]);
    const handleArchiveConfirm = useCallback(async () => {
        if (!archiveTarget)
            return;
        const { featureId } = archiveTarget;
        setArchiveTarget(null);
        addInFlight(featureId);
        try {
            const result = await archiveFeature(featureId);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature archived');
            }
        }
        catch {
            toast.error('Failed to archive feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [archiveTarget, addInFlight, removeInFlight, router]);
    // Delete opens DeleteFeatureDialog
    const handleDeleteRequest = useCallback((featureId) => {
        const feature = features.find((f) => f.id === featureId);
        if (!feature)
            return;
        setDeleteTarget({
            featureId,
            featureName: feature.name,
            hasChildren: feature.hasChildren ?? false,
            hasOpenPr: feature.hasOpenPr ?? false,
        });
    }, [features]);
    const handleDeleteConfirm = useCallback(async (cleanup, cascadeDelete, closePr) => {
        if (!deleteTarget)
            return;
        const { featureId } = deleteTarget;
        setDeleteTarget(null);
        addInFlight(featureId);
        try {
            const result = await deleteFeature(featureId, cleanup, cascadeDelete, closePr);
            if (result.error) {
                toast.error(result.error);
            }
            else {
                toast.success('Feature deleted');
            }
        }
        catch {
            toast.error('Failed to delete feature');
        }
        finally {
            removeInFlight(featureId);
            router.refresh();
        }
    }, [deleteTarget, addInFlight, removeInFlight, router]);
    // ── Repository action handlers ──────────────────────────────
    const handleRepoOpenIde = useCallback(async (repositoryPath) => {
        const result = await openIde({ repositoryPath });
        if (!result.success) {
            toast.error(result.error ?? 'Failed to open IDE');
        }
    }, []);
    const handleRepoOpenShell = useCallback(async (repositoryPath) => {
        const result = await openShell({ repositoryPath });
        if (!result.success) {
            toast.error(result.error ?? 'Failed to open terminal');
        }
    }, []);
    const handleRepoOpenFolder = useCallback(async (repositoryPath) => {
        const result = await openFolder(repositoryPath);
        if (!result.success) {
            toast.error(result.error ?? 'Failed to open folder');
        }
    }, []);
    const handleRepoStartServer = useCallback(async (repositoryPath) => {
        const result = await deployRepository(repositoryPath);
        if (!result.success) {
            toast.error(result.error ?? 'Failed to start server');
        }
        else {
            toast.success('Server starting...');
        }
    }, []);
    const handleRepoStopServer = useCallback(async (repositoryPath) => {
        const result = await stopDeployment(repositoryPath);
        if (!result.success) {
            toast.error(result.error ?? 'Failed to stop server');
        }
        else {
            toast.success('Server stopped');
        }
    }, []);
    const repoActionCallbacks = useMemo(() => ({
        onOpenIde: handleRepoOpenIde,
        onOpenShell: handleRepoOpenShell,
        onOpenFolder: handleRepoOpenFolder,
        onStartServer: handleRepoStartServer,
        onStopServer: handleRepoStopServer,
        isServerRunning: () => false,
    }), [
        handleRepoOpenIde,
        handleRepoOpenShell,
        handleRepoOpenFolder,
        handleRepoStartServer,
        handleRepoStopServer,
    ]);
    const handleGroupByChange = (value) => {
        const next = value === '__none__' ? null : value;
        setGroupBy(next);
        // Reset item sort if current field matches the new groupBy
        if (next && itemSortField === next) {
            setItemSortField('name');
        }
    };
    // Compute status counts for pills
    const statusCounts = useMemo(() => {
        const counts = {
            'action-needed': 0,
            'in-progress': 0,
            pending: 0,
            blocked: 0,
            error: 0,
            done: 0,
        };
        for (const f of features) {
            counts[f.status]++;
        }
        return counts;
    }, [features]);
    // Unique repo names for advanced filter
    const repoNames = useMemo(() => {
        const names = new Set();
        for (const f of features) {
            names.add(f.repositoryName);
        }
        return Array.from(names).sort();
    }, [features]);
    // Filtered features (sorting handled by table)
    const filteredFeatures = useMemo(() => {
        const query = searchQuery.toLowerCase();
        return features.filter((feature) => {
            if (archiveFilter === 'active' && isArchived(feature))
                return false;
            if (archiveFilter === 'archived' && !isArchived(feature))
                return false;
            if (statusFilter && feature.status !== statusFilter)
                return false;
            if (repoFilter && feature.repositoryName !== repoFilter)
                return false;
            if (query) {
                const matchesName = feature.name.toLowerCase().includes(query);
                const matchesBranch = feature.branch.toLowerCase().includes(query);
                const matchesRepo = feature.repositoryName.toLowerCase().includes(query);
                if (!matchesName && !matchesBranch && !matchesRepo)
                    return false;
            }
            return true;
        });
    }, [features, searchQuery, statusFilter, archiveFilter, repoFilter]);
    const hasActiveFilters = searchQuery !== '' ||
        statusFilter !== null ||
        archiveFilter !== 'active' ||
        repoFilter !== null;
    const clearFilters = () => {
        setSearchQuery('');
        setStatusFilter(null);
        setArchiveFilter('active');
        setRepoFilter(null);
    };
    const archivedCount = useMemo(() => features.filter(isArchived).length, [features]);
    const activeCount = features.length - archivedCount;
    const itemSortOptions = useMemo(() => getItemSortOptions(groupBy), [groupBy]);
    // Combined inventory rows (features + applications) passed through the same
    // filters as features. Apps don't have an SDLC lifecycle, so the "active"
    // archive filter keeps them visible (no archive concept yet for apps).
    const filteredRows = useMemo(() => {
        const featureIdsAfterFilter = new Set(filteredFeatures.map((f) => f.id));
        const query = searchQuery.toLowerCase();
        const filteredApps = rows.filter((row) => {
            if (!row._isApplication)
                return false;
            if (archiveFilter === 'archived')
                return false;
            if (statusFilter && row.status !== statusFilter)
                return false;
            if (repoFilter && row.repositoryName !== repoFilter)
                return false;
            if (query) {
                const matchesName = row.name.toLowerCase().includes(query);
                const matchesRepo = row.repositoryName.toLowerCase().includes(query);
                if (!matchesName && !matchesRepo)
                    return false;
            }
            return true;
        });
        const filteredFeatureRows = rows.filter((r) => !r._isApplication && featureIdsAfterFilter.has(r.id));
        return [...filteredApps, ...filteredFeatureRows];
    }, [rows, filteredFeatures, archiveFilter, statusFilter, repoFilter, searchQuery]);
    const applicationCount = useMemo(() => filteredRows.filter((r) => r._isApplication).length, [filteredRows]);
    return (_jsx(DeploymentStatusProvider, { initialDeployments: initialDeployments, children: _jsxs("div", { "data-testid": "feature-tree-page", className: "flex h-full flex-col gap-4", children: [_jsx(PageHeader, { title: "Inventory", description: "All applications, repositories and features" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-3 size-4 -translate-y-1/2" }), _jsx(Input, { placeholder: "Search by name, branch, or repository...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "ps-9" }), searchQuery ? (_jsx("button", { onClick: () => setSearchQuery(''), className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-3 -translate-y-1/2", children: _jsx(X, { className: "size-4" }) })) : null] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium whitespace-nowrap", children: "Group by:" }), _jsxs(Select, { value: groupBy ?? '__none__', onValueChange: handleGroupByChange, children: [_jsx(SelectTrigger, { className: "w-[150px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: GROUP_BY_OPTIONS.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] })] }), _jsxs(Button, { variant: showAdvanced ? 'secondary' : 'outline', size: "sm", onClick: () => setShowAdvanced((v) => !v), className: "shrink-0", children: [_jsx(SlidersHorizontal, { className: "mr-1.5 size-3.5" }), "Filters"] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "border-input flex items-center rounded-md border", children: [_jsxs("button", { onClick: () => setArchiveFilter('active'), className: `flex items-center gap-1.5 rounded-l-md px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'active'
                                                ? 'bg-primary text-primary-foreground'
                                                : 'hover:bg-muted'}`, children: [_jsx(Inbox, { className: "size-3.5" }), "Active", _jsxs("span", { className: "opacity-70", children: ["(", activeCount, ")"] })] }), _jsxs("button", { onClick: () => setArchiveFilter('archived'), className: `flex items-center gap-1.5 border-x px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'archived'
                                                ? 'bg-primary text-primary-foreground'
                                                : 'hover:bg-muted'}`, children: [_jsx(Archive, { className: "size-3.5" }), "Archived", _jsxs("span", { className: "opacity-70", children: ["(", archivedCount, ")"] })] }), _jsx("button", { onClick: () => setArchiveFilter('all'), className: `rounded-r-md px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'all' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'}`, children: "All" })] }), _jsx("div", { className: "bg-border h-6 w-px" }), _jsx("button", { onClick: () => setStatusFilter(null), className: `rounded-full px-2.5 py-1 text-xs font-medium transition-colors ${statusFilter === null
                                        ? 'bg-foreground text-background'
                                        : 'bg-muted text-muted-foreground hover:bg-muted/80'}`, children: "All Status" }), Object.entries(STATUS_LABELS).map(([status, label]) => {
                                    const count = statusCounts[status];
                                    if (count === 0)
                                        return null;
                                    return (_jsxs("button", { onClick: () => setStatusFilter(statusFilter === status ? null : status), className: `rounded-full px-2.5 py-1 text-xs font-medium transition-colors ${statusFilter === status
                                            ? STATUS_COLORS[status]
                                            : 'bg-muted text-muted-foreground hover:bg-muted/80'}`, children: [label, _jsx("span", { className: "ml-1 opacity-70", children: count })] }, status));
                                }), hasActiveFilters ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-border h-6 w-px" }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: clearFilters, className: "h-7 text-xs", children: [_jsx(X, { className: "mr-1 size-3" }), "Clear"] })] })) : null] }), groupBy ? (_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: GROUP_BY_LABELS[groupBy] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1 text-xs", onClick: () => setGroupSortDir((d) => (d === 'asc' ? 'desc' : 'asc')), children: [groupSortDir === 'asc' ? (_jsx(ArrowDownAZ, { className: "size-3.5" })) : (_jsx(ArrowUpAZ, { className: "size-3.5" })), groupSortDir === 'asc' ? 'A-Z' : 'Z-A'] })] }), _jsx("div", { className: "bg-border h-5 w-px" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Sort by" }), _jsxs(Select, { value: itemSortField, onValueChange: setItemSortField, children: [_jsx(SelectTrigger, { className: "h-7 w-[120px] text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemSortOptions.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1 text-xs", onClick: () => setItemSortDir((d) => (d === 'asc' ? 'desc' : 'asc')), children: [itemSortDir === 'asc' ? (_jsx(ArrowDownAZ, { className: "size-3.5" })) : (_jsx(ArrowUpAZ, { className: "size-3.5" })), itemSortDir === 'asc' ? 'A-Z' : 'Z-A'] })] })] })) : null, showAdvanced ? (_jsx("div", { className: "bg-muted/50 flex flex-wrap items-center gap-3 rounded-lg border p-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Repository" }), _jsxs(Select, { value: repoFilter ?? '__all__', onValueChange: (v) => setRepoFilter(v === '__all__' ? null : v), children: [_jsx(SelectTrigger, { className: "h-8 w-[200px] text-xs", children: _jsx(SelectValue, { placeholder: "All repositories" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__all__", children: "All repositories" }), repoNames.map((name) => (_jsx(SelectItem, { value: name, children: name }, name)))] })] })] }) })) : null] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsxs("span", { children: [applicationCount, " app", applicationCount !== 1 ? 's' : '', " \u00B7 ", filteredFeatures.length, ' ', "feature", filteredFeatures.length !== 1 ? 's' : ''] }), hasActiveFilters ? (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "filtered" })) : null] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 text-xs", onClick: () => setNewProjectOpen(true), children: [_jsx(FolderPlus, { className: "size-3.5" }), "New project"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 text-xs", onClick: () => {
                                        setCreateForRepoPath('');
                                        setCreateDrawerOpen(true);
                                    }, children: [_jsx(Sparkles, { className: "size-3.5" }), "New feature"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 text-xs", onClick: handlePickFolder, children: [_jsx(FolderOpen, { className: "size-3.5" }), "Add local repo"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 text-xs", onClick: () => setShowCreatePrompt(true), children: [_jsx(LayoutGrid, { className: "size-3.5" }), "New application"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 text-xs", onClick: () => {
                                        window.dispatchEvent(new CustomEvent('shep:open-github-import'));
                                    }, children: [_jsx(Github, { className: "size-3.5" }), t('fab.fromGithub')] })] })] }), _jsx("div", { className: "min-h-0 flex-1", children: filteredRows.length > 0 ? (_jsx(FeatureTreeTable, { data: filteredRows, repos: repos, onRowClick: handleRowClick, groupBy: groupBy, groupSortDir: groupSortDir, itemSortField: itemSortField, itemSortDir: itemSortDir, onTableRender: handleTableRender, onCreateFeatureForRepo: handleCreateFeatureForRepo })) : (_jsx(EmptyState, { icon: _jsx(Search, { className: "size-10" }), title: "Nothing to show", description: hasActiveFilters
                            ? 'No applications or features match your current filters. Try adjusting your search or filters.'
                            : 'No applications or features found in any repository.', action: hasActiveFilters ? (_jsx(Button, { variant: "outline", onClick: clearFilters, children: "Clear all filters" })) : undefined })) }), _jsx(FeatureRowActionsManager, { tableContainer: tableContainer, renderTick: renderTick, features: filteredFeatures, inFlightIds: inFlightIds, onStart: handleStart, onStop: handleStop, onRetry: handleRetry, onReview: handleReview, onArchive: handleArchiveRequest, onUnarchive: handleUnarchive, onDelete: handleDeleteRequest }), _jsx(ApplicationRowActionsManager, { tableContainer: tableContainer, renderTick: renderTick, rows: filteredRows }), _jsx(RepositoryGroupActionsManager, { tableContainer: tableContainer, renderTick: renderTick, callbacks: repoActionCallbacks }), _jsx(AlertDialog, { open: archiveTarget !== null, onOpenChange: (open) => {
                        if (!open)
                            setArchiveTarget(null);
                    }, children: _jsxs(AlertDialogContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t('featureNode.archiveConfirmTitle') }), _jsx(AlertDialogDescription, { children: _jsx(Trans, { t: t, i18nKey: "featureNode.archiveConfirmDescription", values: { name: archiveTarget?.featureName }, components: { strong: _jsx("strong", {}) } }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { onClick: () => setArchiveTarget(null), children: t('featureNode.cancel') }), _jsx(AlertDialogAction, { onClick: handleArchiveConfirm, children: t('featureNode.archive') })] })] }) }), _jsx(DeleteFeatureDialog, { open: deleteTarget !== null, onOpenChange: (open) => {
                        if (!open)
                            setDeleteTarget(null);
                    }, onConfirm: handleDeleteConfirm, isDeleting: deleteTarget !== null && inFlightIds.has(deleteTarget.featureId), featureName: deleteTarget?.featureName ?? '', featureId: deleteTarget?.featureId ?? '', hasChildren: deleteTarget?.hasChildren, hasOpenPr: deleteTarget?.hasOpenPr }), _jsx(InventoryFab, { actions: fabActions }), _jsx(FeatureCreateDrawer, { open: createDrawerOpen, onClose: () => {
                        setCreateDrawerOpen(false);
                        setCreateForRepoPath('');
                    }, onSubmit: handleCreateFeatureSubmit, repositoryPath: createForRepoPath, features: createData.featureOptions, repositories: createData.repositoryOptions, workflowDefaults: createData.workflowDefaults, currentAgentType: createData.currentAgentType, currentModel: createData.currentModel, isSubmitting: isCreatingFeature }), _jsx(NewProjectDialog, { open: newProjectOpen, onOpenChange: setNewProjectOpen, onCreated: handleNewProjectCreated }), showCreatePrompt ? (_jsx("div", { className: "fixed inset-0 z-50", children: _jsx(ControlCenterEmptyState, { onRepositorySelect: (path) => {
                            setShowCreatePrompt(false);
                            addRepository({ path })
                                .then((result) => {
                                if (result.error) {
                                    toast.error(result.error);
                                }
                                else {
                                    router.refresh();
                                }
                            })
                                .catch(() => {
                                toast.error('Failed to add repository');
                            });
                        }, onApplicationCreated: (appId) => {
                            setShowCreatePrompt(false);
                            router.push(`/application/${appId}`);
                        }, onClose: () => setShowCreatePrompt(false), className: "bg-background" }) })) : null] }) }));
}
/** (+) FAB positioned fixed at the bottom-right of the viewport. */
function InventoryFab({ actions }) {
    const { state } = useSidebar();
    const { i18n } = useTranslation('web');
    const { swapPosition } = useFabLayout();
    const isRtl = i18n.dir() === 'rtl';
    if (swapPosition) {
        const positionStyle = isRtl
            ? {
                left: 'calc(var(--sidebar-width-icon) + 32px)',
                transition: 'left 200ms ease-in-out',
            }
            : { right: '32px', transition: 'right 200ms ease-in-out' };
        return (_jsx(FloatingActionButton, { actions: actions, className: "!fixed bottom-6", style: positionStyle }));
    }
    const offset = state === 'expanded'
        ? 'calc(var(--sidebar-width) + 32px)'
        : 'calc(var(--sidebar-width-icon) + 32px)';
    const positionStyle = isRtl
        ? { right: offset, transition: 'right 200ms ease-in-out' }
        : { left: offset, transition: 'left 200ms ease-in-out' };
    return (_jsx(FloatingActionButton, { actions: actions, className: "!fixed bottom-6", style: positionStyle }));
}
