/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { type ColumnDef } from '@tanstack/react-table' import { Eye, Info, Pencil, Settings2, Timer, Trash2 } from 'lucide-react' import { useTranslation } from 'react-i18next' import { formatTimestampToDate } from '@/lib/format' import { Button } from '@/components/ui/button' import { StatusBadge } from '@/components/status-badge' import { TableId } from '@/components/table-id' import { getDeploymentStatusConfig } from '../constants' import { formatRemainingMinutes, normalizeDeploymentStatus, } from '../lib/deployments-utils' import type { Deployment } from '../types' export function useDeploymentsColumns(opts: { onViewLogs: (id: string | number) => void onViewDetails: (id: string | number) => void onUpdateConfig: (id: string | number) => void onExtend: (id: string | number) => void onRename: (id: string | number, currentName: string) => void onDelete: (deployment: Deployment) => void }): ColumnDef[] { const { t } = useTranslation() const STATUS = getDeploymentStatusConfig(t) return [ { accessorKey: 'id', header: t('ID'), meta: { mobileHidden: true }, cell: ({ row }) => { const id = row.original.id return }, size: 120, }, { id: 'name', accessorFn: (row) => row.container_name || row.deployment_name || row.name || '', header: t('Name'), meta: { mobileTitle: true }, cell: ({ getValue }) => { const name = String(getValue() || '-') || '-' return ( ) }, minSize: 220, }, { accessorKey: 'status', header: t('Status'), meta: { mobileBadge: true }, cell: ({ row }) => { const raw = row.original.status const key = normalizeDeploymentStatus(raw) const config = STATUS[key] || { label: typeof raw === 'string' && raw.trim() ? raw.trim() : t('Unknown'), variant: 'neutral' as const, } return ( ) }, filterFn: (row, id, value) => { if ( !Array.isArray(value) || value.length === 0 || value.includes('all') ) { return true } const status = normalizeDeploymentStatus(row.getValue(id)) return value.includes(status) }, size: 160, enableSorting: false, }, { accessorKey: 'provider', header: t('Provider'), cell: ({ row }) => { const provider = row.original.provider if (!provider) return - return ( ) }, size: 140, enableSorting: false, }, { accessorKey: 'time_remaining', header: t('Time remaining'), cell: ({ row }) => { const status = normalizeDeploymentStatus(row.original.status) const remainingText = typeof row.original.time_remaining === 'string' && row.original.time_remaining.trim() ? row.original.time_remaining.trim() : '-' const remainingHuman = formatRemainingMinutes( row.original.compute_minutes_remaining ) const percentUsed = typeof row.original.completed_percent === 'number' && Number.isFinite(row.original.completed_percent) ? Math.max( 0, Math.min(100, Math.round(row.original.completed_percent)) ) : null const percentRemain = percentUsed === null ? null : Math.max(0, Math.min(100, 100 - percentUsed)) return (
{remainingText} {status === 'running' && percentRemain !== null ? ( ) : null}
{remainingHuman ? (
{t('Approx.')} {remainingHuman}
) : null}
) }, minSize: 220, enableSorting: false, }, { id: 'hardware', header: t('Hardware'), meta: { mobileHidden: true }, accessorFn: (row) => row.hardware_info || row.hardware_name || row.brand_name || '', cell: ({ row }) => { const hardware = row.original.hardware_name || (typeof row.original.hardware_info === 'string' ? row.original.hardware_info : '') const qty = typeof row.original.hardware_quantity === 'number' ? row.original.hardware_quantity : null if (!hardware) return - return (
{qty !== null ? ( ×{qty} ) : null}
) }, minSize: 220, enableSorting: false, }, { accessorKey: 'created_at', header: t('Created'), meta: { mobileHidden: true }, cell: ({ row }) => { const ts = typeof row.original.created_at === 'number' ? row.original.created_at : typeof row.original.created_at === 'string' ? Number(row.original.created_at) : undefined return (
{formatTimestampToDate(ts)}
) }, size: 180, }, { id: 'actions', header: () => t('Actions'), enableHiding: false, enableSorting: false, cell: ({ row }) => { const id = row.original.id const currentName = row.original.container_name || row.original.deployment_name || row.original.name || '' return (
) }, size: 180, meta: { pinned: 'right' as const }, }, ] }