Fix admin dashboard build: remove date-fns, replace Card with native divs, fix Button props
Made-with: Cursor
This commit is contained in:
@@ -3,8 +3,24 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useParams, useRouter } from 'next/navigation'
|
import { useParams, useRouter } from 'next/navigation'
|
||||||
import { getAdminOrg, grantPlan, type AdminOrgDetail } from '@/lib/api/admin'
|
import { getAdminOrg, grantPlan, type AdminOrgDetail } from '@/lib/api/admin'
|
||||||
import { Card, CardHeader, CardTitle, CardContent, Button, LoadingOverlay, Select, toast } from '@ciphera-net/ui'
|
import { Button, LoadingOverlay, Select, toast } from '@ciphera-net/ui'
|
||||||
import { format, addMonths, addYears } from 'date-fns'
|
|
||||||
|
function formatDate(d: Date) {
|
||||||
|
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
||||||
|
}
|
||||||
|
function formatDateTime(d: Date) {
|
||||||
|
return d.toLocaleDateString('en-US', { dateStyle: 'long' }) + ' ' + d.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' })
|
||||||
|
}
|
||||||
|
function addMonths(d: Date, months: number) {
|
||||||
|
const out = new Date(d)
|
||||||
|
out.setMonth(out.getMonth() + months)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
function addYears(d: Date, years: number) {
|
||||||
|
const out = new Date(d)
|
||||||
|
out.setFullYear(out.getFullYear() + years)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
const PLAN_OPTIONS = [
|
const PLAN_OPTIONS = [
|
||||||
{ value: 'free', label: 'Free' },
|
{ value: 'free', label: 'Free' },
|
||||||
@@ -105,12 +121,9 @@ export default function AdminOrgDetailPage() {
|
|||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<div className="grid gap-6 md:grid-cols-2">
|
||||||
{/* Current Status */}
|
{/* Current Status */}
|
||||||
<Card>
|
<div className="rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm">
|
||||||
<CardHeader>
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white mb-4">Current Status</h3>
|
||||||
<CardTitle>Current Status</CardTitle>
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
||||||
<span className="text-neutral-500">Plan:</span>
|
<span className="text-neutral-500">Plan:</span>
|
||||||
<span className="font-medium">{org.plan_id}</span>
|
<span className="font-medium">{org.plan_id}</span>
|
||||||
|
|
||||||
@@ -125,7 +138,7 @@ export default function AdminOrgDetailPage() {
|
|||||||
|
|
||||||
<span className="text-neutral-500">Period End:</span>
|
<span className="text-neutral-500">Period End:</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{org.current_period_end ? format(new Date(org.current_period_end), 'PPP p') : '-'}
|
{org.current_period_end ? formatDateTime(new Date(org.current_period_end)) : '-'}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-neutral-500">Stripe Cust:</span>
|
<span className="text-neutral-500">Stripe Cust:</span>
|
||||||
@@ -133,35 +146,27 @@ export default function AdminOrgDetailPage() {
|
|||||||
|
|
||||||
<span className="text-neutral-500">Stripe Sub:</span>
|
<span className="text-neutral-500">Stripe Sub:</span>
|
||||||
<span className="font-mono text-xs">{org.stripe_subscription_id || '-'}</span>
|
<span className="font-mono text-xs">{org.stripe_subscription_id || '-'}</span>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Sites */}
|
{/* Sites */}
|
||||||
<Card>
|
<div className="rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm">
|
||||||
<CardHeader>
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white mb-4">Sites ({org.sites.length})</h3>
|
||||||
<CardTitle>Sites ({org.sites.length})</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<ul className="space-y-2 max-h-60 overflow-y-auto">
|
<ul className="space-y-2 max-h-60 overflow-y-auto">
|
||||||
{org.sites.map((site) => (
|
{org.sites.map((site) => (
|
||||||
<li key={site.id} className="flex justify-between items-center text-sm p-2 bg-neutral-50 dark:bg-neutral-900 rounded">
|
<li key={site.id} className="flex justify-between items-center text-sm p-2 bg-neutral-50 dark:bg-neutral-900 rounded">
|
||||||
<span className="font-medium">{site.domain}</span>
|
<span className="font-medium">{site.domain}</span>
|
||||||
<span className="text-neutral-500 text-xs">{format(new Date(site.created_at), 'MMM d, yyyy')}</span>
|
<span className="text-neutral-500 text-xs">{formatDate(new Date(site.created_at))}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{org.sites.length === 0 && <li className="text-neutral-500 text-sm">No sites found</li>}
|
{org.sites.length === 0 && <li className="text-neutral-500 text-sm">No sites found</li>}
|
||||||
</ul>
|
</ul>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Grant Plan Form */}
|
{/* Grant Plan Form */}
|
||||||
<Card>
|
<div className="rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm">
|
||||||
<CardHeader>
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white mb-4">Grant Plan (Manual Override)</h3>
|
||||||
<CardTitle>Grant Plan (Manual Override)</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form onSubmit={handleGrantPlan} className="space-y-4">
|
<form onSubmit={handleGrantPlan} className="space-y-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -232,8 +237,7 @@ export default function AdminOrgDetailPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { listAdminOrgs, type AdminOrgSummary } from '@/lib/api/admin'
|
import { listAdminOrgs, type AdminOrgSummary } from '@/lib/api/admin'
|
||||||
import { Card, CardHeader, CardTitle, CardContent, Button, LoadingOverlay } from '@ciphera-net/ui'
|
import { Button, LoadingOverlay } from '@ciphera-net/ui'
|
||||||
import { format } from 'date-fns'
|
|
||||||
|
function formatDate(d: Date) {
|
||||||
|
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
||||||
|
}
|
||||||
|
|
||||||
export default function AdminOrgsPage() {
|
export default function AdminOrgsPage() {
|
||||||
const [orgs, setOrgs] = useState<AdminOrgSummary[]>([])
|
const [orgs, setOrgs] = useState<AdminOrgSummary[]>([])
|
||||||
@@ -26,12 +29,9 @@ export default function AdminOrgsPage() {
|
|||||||
<h2 className="text-xl font-semibold text-neutral-900 dark:text-white">Organizations</h2>
|
<h2 className="text-xl font-semibold text-neutral-900 dark:text-white">Organizations</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<div className="rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm">
|
||||||
<CardHeader>
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white mb-4">All Organizations</h3>
|
||||||
<CardTitle>All Organizations</CardTitle>
|
<div className="overflow-x-auto">
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="w-full text-left text-sm">
|
<table className="w-full text-left text-sm">
|
||||||
<thead className="border-b border-neutral-200 dark:border-neutral-800">
|
<thead className="border-b border-neutral-200 dark:border-neutral-800">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -70,20 +70,19 @@ export default function AdminOrgsPage() {
|
|||||||
{new Intl.NumberFormat().format(org.pageview_limit)}
|
{new Intl.NumberFormat().format(org.pageview_limit)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-neutral-500 text-xs">
|
<td className="px-4 py-3 text-neutral-500 text-xs">
|
||||||
{format(new Date(org.updated_at), 'MMM d, yyyy')}
|
{formatDate(new Date(org.updated_at))}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<Link href={`/admin/orgs/${org.organization_id}`}>
|
<Link href={`/admin/orgs/${org.organization_id}`}>
|
||||||
<Button variant="ghost" size="sm">Manage</Button>
|
<Button variant="ghost">Manage</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,19 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@ciphera-net/ui'
|
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard() {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
<Link href="/admin/orgs" className="block transition-transform hover:scale-[1.02]">
|
<Link
|
||||||
<Card>
|
href="/admin/orgs"
|
||||||
<CardHeader>
|
className="block transition-transform hover:scale-[1.02] rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm"
|
||||||
<CardTitle>Organizations</CardTitle>
|
>
|
||||||
<CardDescription>Manage organization plans and limits</CardDescription>
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white">Organizations</h3>
|
||||||
</CardHeader>
|
<p className="text-sm text-neutral-500 dark:text-neutral-400 mt-1">Manage organization plans and limits</p>
|
||||||
<CardContent>
|
<p className="text-sm text-neutral-500 dark:text-neutral-400 mt-4">
|
||||||
<p className="text-sm text-neutral-500 dark:text-neutral-400">
|
View all organizations, check billing status, and manually grant plans.
|
||||||
View all organizations, check billing status, and manually grant plans.
|
</p>
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user