129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
import { Edit, RotateCcw } from 'lucide-react';
|
|
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { PERMISSIONS } from '@/constants/permissions';
|
|
import { usePermissions } from '@/hooks/usePermissions';
|
|
import type { Plan } from '@/types';
|
|
|
|
function formatDate(value?: string | null) {
|
|
if (!value) return '-';
|
|
return new Intl.DateTimeFormat('en-IN', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
}).format(new Date(value));
|
|
}
|
|
|
|
function formatPrice(value: string, cycle: string) {
|
|
const amount = Number(value);
|
|
const price = Number.isFinite(amount) ? amount.toFixed(2) : value;
|
|
return `${price} / ${cycle}`;
|
|
}
|
|
|
|
interface UsePlanColumnsParams {
|
|
onEdit: (plan: Plan) => void;
|
|
onToggleStatus: (plan: Plan) => void;
|
|
pendingPlanId?: number;
|
|
}
|
|
|
|
export function usePlanColumns({
|
|
onEdit,
|
|
onToggleStatus,
|
|
pendingPlanId,
|
|
}: UsePlanColumnsParams): ColumnDef<Plan>[] {
|
|
const { hasPermission } = usePermissions();
|
|
const canEdit = hasPermission(PERMISSIONS.PLAN.UPDATE);
|
|
const canDelete = hasPermission(PERMISSIONS.PLAN.DELETE);
|
|
|
|
return useMemo(() => {
|
|
const columns: ColumnDef<Plan>[] = [
|
|
{
|
|
accessorKey: 'name',
|
|
header: 'Plan',
|
|
cell: ({ row }) => (
|
|
<div>
|
|
<p>{row.original.name}</p>
|
|
<p className="text-muted-foreground">{row.original.slug}</p>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'price',
|
|
header: 'Price',
|
|
cell: ({ row }) => formatPrice(row.original.price, row.original.billing_cycle),
|
|
},
|
|
{
|
|
accessorKey: 'trial_days',
|
|
header: 'Trial',
|
|
cell: ({ row }) => `${row.original.trial_days} days`,
|
|
},
|
|
{
|
|
id: 'limits',
|
|
header: 'Limits',
|
|
enableSorting: false,
|
|
cell: ({ row }) => (
|
|
<div className="text-muted-foreground">
|
|
<p>{row.original.max_projects} projects</p>
|
|
<p>{row.original.max_users} users</p>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'created_at',
|
|
header: 'Created',
|
|
cell: ({ row }) => formatDate(row.original.created_at),
|
|
},
|
|
{
|
|
accessorKey: 'is_active',
|
|
header: 'Status',
|
|
cell: ({ row }) => (
|
|
<div className="flex flex-wrap gap-1.5">
|
|
<Badge variant={row.original.is_active ? 'default' : 'secondary'}>
|
|
{row.original.is_active ? 'Active' : 'Inactive'}
|
|
</Badge>
|
|
{row.original.is_custom ? <Badge variant="outline">Custom</Badge> : null}
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
if (!canEdit && !canDelete) return columns;
|
|
|
|
columns.push({
|
|
id: 'actions',
|
|
header: () => <div className="text-right">Actions</div>,
|
|
enableSorting: false,
|
|
cell: ({ row }) => {
|
|
const plan = row.original;
|
|
return (
|
|
<div className="flex justify-end gap-2">
|
|
{canEdit ? (
|
|
<Button variant="outline" size="sm" onClick={() => onEdit(plan)}>
|
|
<Edit className="size-4" />
|
|
</Button>
|
|
) : null}
|
|
{canDelete ? (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
disabled={pendingPlanId === plan.id}
|
|
onClick={() => onToggleStatus(plan)}
|
|
>
|
|
<RotateCcw className="mr-2 size-4" />
|
|
{plan.is_active ? 'Deactivate' : 'Activate'}
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
);
|
|
},
|
|
});
|
|
|
|
return columns;
|
|
}, [canDelete, canEdit, onEdit, onToggleStatus, pendingPlanId]);
|
|
}
|