'use client'; import { useQuery } from '@tanstack/react-query'; import Link from 'next/link'; import api from '@/lib/api'; import type { Application } from '@/types'; import { managedServiceTypeLabel } from '@/lib/optional-service-defaults'; import { Database, Plus, AlertTriangle, Clock } from 'lucide-react'; import { useConfirm } from '@/components/confirm-modal'; import { DeleteButtonLabel } from '@/components/delete-button-label'; import { DeletingCardOverlay, deletingResourceMessage } from '@/components/deleting-overlay'; import { filterManagedServices } from '@/lib/product-type'; import { useApplicationDelete } from '@/lib/use-application-delete'; const lifecycleColors: Record = { active: 'text-green-600 bg-green-50', suspended: 'text-amber-700 bg-amber-50', pending_deletion: 'text-red-700 bg-red-50', deleted: 'text-gray-500 bg-gray-100', }; const lifecycleLabels: Record = { active: 'Active', suspended: 'Suspended — Unpaid', pending_deletion: 'Pending Deletion', deleted: 'Deleted', }; function serviceSubtitle(app: Application): string { if (app.productType === 'managed_database') { return `${app.databaseType}${app.dbVersion ? ` v${app.dbVersion}` : ''}`; } if (app.productType === 'managed_redis') { return `Redis${app.redisVersion ? ` v${app.redisVersion}` : ''}`; } if (app.productType === 'managed_rabbitmq') { return `RabbitMQ${app.rabbitmqVersion ? ` v${app.rabbitmqVersion}` : ''}`; } return app.databaseType !== 'none' ? app.databaseType : '—'; } function formatExpiry(expiresAt?: string): { text: string; urgent: boolean } { if (!expiresAt) return { text: '—', urgent: false }; const diff = new Date(expiresAt).getTime() - Date.now(); if (diff <= 0) return { text: 'Expired', urgent: true }; const hours = Math.floor(diff / 3600000); const days = Math.floor(hours / 24); if (days > 0) return { text: `${days}d ${hours % 24}h remaining`, urgent: days < 3 }; if (hours > 0) return { text: `${hours}h remaining`, urgent: hours < 6 }; const mins = Math.floor(diff / 60000); return { text: `${mins}m remaining`, urgent: true }; } export default function ServicesPage() { const confirm = useConfirm(); const { data: servicesRaw = [], isLoading } = useQuery({ queryKey: ['applications', 'managed'], queryFn: () => api.get('/applications', { params: { productType: 'managed' } }).then((r) => r.data), }); const services = filterManagedServices(servicesRaw); const { deleteApplication, isDeleting, isAnyDeleting } = useApplicationDelete({ invalidateKeys: [['applications', 'managed']], successMessage: 'Service deleted', successWithCreditMessage: 'Service deleted. Prepaid resources are on your dashboard.', }); if (isLoading) { return (
{[1, 2, 3].map((i) => (
))}
); } return (

Databases & Services

Standalone databases, Redis, and RabbitMQ — {services.length} service{services.length !== 1 ? 's' : ''}

New Service
{services.length === 0 ? (

No managed services yet

Provision a database, Redis, or RabbitMQ without deploying a full application.

Create your first service
) : (
{services.map((svc) => { const lifecycle = svc.lifecycleStatus || 'active'; const expiry = formatExpiry(svc.planExpiresAt); const latestStatus = svc.deployments?.[0]?.status || 'pending'; const cardDeleting = isDeleting(svc.id); return (
{cardDeleting && ( )}

{svc.name}

{managedServiceTypeLabel(svc.productType)} · {serviceSubtitle(svc)}

{latestStatus} {lifecycle === 'suspended' && } {lifecycleLabels[lifecycle] || lifecycle} {expiry.text}
); })}
)}
); }