'use client'; import { useQuery } from '@tanstack/react-query'; import { Link } from '@/i18n/Link'; import { useT } from '@/i18n/I18nProvider'; import type { Dictionary } from '@/i18n/dictionaries/fa'; import api from '@/lib/api'; import type { Application } from '@/types'; import { Rocket, Package, Hexagon, Database, Box, AlertTriangle, Clock } from 'lucide-react'; import { useConfirm } from '@/components/confirm-modal'; import { DeleteButtonLabel } from '@/components/delete-button-label'; import { DeletingTableRowOverlay, deletingRowContentClass, } from '@/components/deleting-overlay'; import { filterApplications } from '@/lib/product-type'; import { useApplicationDelete } from '@/lib/use-application-delete'; import { deploymentStatusBadgeClass, deploymentStatusLabel, lifecycleStatusClass, } from '@/lib/app-list-utils'; const statusColors = deploymentStatusBadgeClass; const lifecycleColors = lifecycleStatusClass; type AppsDict = Dictionary['dashboard']['apps']; function statusLabel(status: string, t: Dictionary): string { return deploymentStatusLabel(status, t); } function lifecycleLabel(lifecycle: string, a: AppsDict): string { return (a.lifecycle as Record)[lifecycle] ?? lifecycle; } function formatExpiry(expiresAt: string | undefined, a: AppsDict): { text: string; urgent: boolean } { if (!expiresAt) return { text: '—', urgent: false }; const now = new Date(); const exp = new Date(expiresAt); const diff = exp.getTime() - now.getTime(); if (diff <= 0) return { text: a.expired, urgent: true }; const hours = Math.floor(diff / 3600000); const days = Math.floor(hours / 24); if (days > 0) return { text: a.daysHours.replace('{d}', String(days)).replace('{h}', String(hours % 24)), urgent: days < 3, }; if (hours > 0) return { text: a.hours.replace('{h}', String(hours)), urgent: hours < 6 }; const mins = Math.floor(diff / 60000); return { text: a.mins.replace('{m}', String(mins)), urgent: true }; } export default function AppsPage() { const t = useT(); const a = t.dashboard.apps; const confirm = useConfirm(); const { data: appsRaw = [], isLoading } = useQuery({ queryKey: ['applications', 'application'], queryFn: () => api.get('/applications', { params: { productType: 'application' } }).then((r) => r.data), }); const apps = filterApplications(appsRaw); const { deleteApplication, isDeleting, isAnyDeleting } = useApplicationDelete({ invalidateKeys: [['applications', 'application'], ['applications']], successMessage: a.deleted, successWithCreditMessage: a.deletedWithCredit, }); if (isLoading) { return (
{[1, 2, 3, 4].map((i) => (
))}
); } return (

{a.title}

{a.count.replace('{n}', String(apps.length))}

{a.newApplication}
{apps.length === 0 ? (

{a.noApplications}

{a.emptyDesc}

{a.deployFirst}
) : ( <>
{apps.map((app) => { const latestStatus = app.deployments?.[0]?.status || 'pending'; const lifecycle = app.lifecycleStatus || 'active'; const expiry = formatExpiry(app.planExpiresAt, a); const rowDeleting = isDeleting(app.id); return ( {rowDeleting && ( )} ); })}
{a.colApplication} {a.colRuntime} {a.colStatus} {a.colServiceStatus} {a.colExpiry} {a.colActions}
{app.name}
{app.runtime} {statusLabel(latestStatus, t)} {lifecycle === 'suspended' && } {lifecycleLabel(lifecycle, a)} {app.planExpiresAt ? ( {expiry.text} ) : ( {a.noPlan} )}
{a.view}
{apps.map((app) => { const latestStatus = app.deployments?.[0]?.status || 'pending'; const lifecycle = app.lifecycleStatus || 'active'; const expiry = formatExpiry(app.planExpiresAt, a); return (

{app.name}

{app.runtime}

{statusLabel(latestStatus, t)}
{lifecycle === 'suspended' && } {lifecycleLabel(lifecycle, a)} {app.planExpiresAt && ( {expiry.text} )}
{app.databaseType} {app.replicas} {app.replicas > 1 ? t.dashboard.home.replicaPlural : t.dashboard.home.replica}
); })}
)}
); }