apolinario's picture
remove banner
162eb60
'use client';
import { useEffect, useMemo, useState } from 'react';
import JobsTable from '@/components/JobsTable';
import { TopBar, MainContent } from '@/components/layout';
import Link from 'next/link';
import { useAuth } from '@/contexts/AuthContext';
import HFLoginButton from '@/components/HFLoginButton';
import useSettings from '@/hooks/useSettings';
import { apiClient } from '@/utils/api';
export default function Dashboard() {
const { status: authStatus, namespace, token: authToken } = useAuth();
const { settings } = useSettings();
const isAuthenticated = authStatus === 'authenticated';
const effectiveToken = useMemo(() => authToken || settings.HF_TOKEN, [authToken, settings.HF_TOKEN]);
type OrgStatus = 'idle' | 'checking' | 'member' | 'missing' | 'error';
const [orgStatus, setOrgStatus] = useState<OrgStatus>('idle');
useEffect(() => {
if (!isAuthenticated) {
setOrgStatus('idle');
return;
}
if (!effectiveToken) {
setOrgStatus('idle');
return;
}
let cancelled = false;
setOrgStatus('checking');
apiClient
.post('/api/hf-hub', {
action: 'whoami',
token: effectiveToken,
})
.then(response => {
if (cancelled) return;
const orgsRaw = response.data?.user?.orgs ?? response.data?.user?.organizations ?? [];
const REQUIRED_ORG = 'lora-training-frenzi';
const isMember = Array.isArray(orgsRaw)
? orgsRaw.some((org: any) => {
if (!org) return false;
if (typeof org === 'string') {
return org === REQUIRED_ORG;
}
const nameMatch = org?.name || org?.organization || org?.namespace || org?.id;
return nameMatch === REQUIRED_ORG;
})
: false;
setOrgStatus(isMember ? 'member' : 'missing');
})
.catch(() => {
if (!cancelled) {
setOrgStatus('error');
}
});
return () => {
cancelled = true;
};
}, [effectiveToken, isAuthenticated]);
return (
<>
<TopBar>
<div>
<h1 className="text-lg">Dashboard</h1>
</div>
<div className="flex-1" />
</TopBar>
<MainContent>
<div className="border border-gray-800 rounded-xl bg-gray-900 p-6 flex flex-col gap-4">
<div>
<h2 className="text-xl font-semibold text-gray-100">
{isAuthenticated ? `Welcome back, ${namespace || 'creator'}!` : 'Welcome to Ostris AI Toolkit'}
</h2>
<p className="text-sm text-gray-400 mt-2">
{isAuthenticated
? 'You are signed in with Hugging Face and can manage jobs, datasets, and submissions. Train LoRAs at $0.042/minute if you are a PRO user.'
: 'Authenticate with Hugging Face or add a personal access token to create jobs, upload datasets, and launch training. Train LoRAs at $0.042/minute if you are a PRO user.'}
{!isAuthenticated && (
<>
{' '}
<a
href="https://huggingface.co/subscribe/pro"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 underline hover:text-blue-300"
>
Subscribe to PRO
</a>
</>
)}
</p>
</div>
{isAuthenticated ? (
<div className="flex flex-col gap-4 text-sm">
<div className="flex flex-wrap items-center gap-3">
<Link
href="/jobs/new"
className="px-4 py-2 rounded-md bg-blue-600 hover:bg-blue-500 text-white transition-colors"
>
Create a Training Job
</Link>
<Link
href="/datasets"
className="px-4 py-2 rounded-md bg-gray-800 hover:bg-gray-700 text-gray-200 transition-colors"
>
Manage Datasets
</Link>
<Link
href="/settings"
className="px-4 py-2 rounded-md border border-gray-700 text-gray-300 hover:border-gray-600 transition-colors"
>
Settings
</Link>
</div>
</div>
) : (
<div className="flex flex-col gap-4 text-sm text-gray-300">
<div>
<HFLoginButton size="md" />
</div>
<Link
href="/settings"
className="text-xs text-blue-400 hover:text-blue-300"
>
Or manage tokens in Settings
</Link>
</div>
)}
</div>
<div className="w-full mt-6">
<div className="flex justify-between items-center mb-2">
<h1 className="text-md">Active Jobs</h1>
<div className="text-xs text-gray-500">
<Link href="/jobs">View All</Link>
</div>
</div>
{isAuthenticated ? (
<JobsTable onlyActive />
) : (
<div className="border border-gray-800 rounded-lg p-6 bg-gray-900 text-gray-400 text-sm">
Sign in with Hugging Face or add an access token in Settings to view and manage jobs.
</div>
)}
</div>
</MainContent>
</>
);
}