apolinario's picture
Clarification text
e704337
raw
history blame
8.5 kB
'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="mb-6">
<img
src="https://huggingface.co/spaces/multimodalart/ai-toolkit/resolve/main/lora_frenzi.png"
alt="LoRA Frenzi event banner"
className="w-full rounded-lg border border-gray-800"
/>
</div>
<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.'
: 'Authenticate with Hugging Face or add a personal access token to create jobs, upload datasets, and launch training.'}
</p>
</div>
{isAuthenticated ? (
<div className="flex flex-col gap-4 text-sm">
{orgStatus === 'checking' && (
<div className="text-xs text-gray-400">Checking your LoRA Frenzi membership…</div>
)}
{orgStatus === 'missing' && (
<div className="border border-yellow-700 bg-yellow-900/20 text-yellow-100 rounded-lg p-4 space-y-2">
<p className="font-semibold">LoRA Frenzi reminder</p>
<p>
Your didn't authorize the <code className="bg-yellow-800/50 px-1 rounded">lora-training-frenzi</code> organization in your login.
To participate:
</p>
<ol className="list-decimal list-inside space-y-1 text-sm">
<li>
Join the{' '}
<a
href="https://huggingface.co/organizations/lora-training-frenzi/share/kEyyVNQXBPWqmARdwHFVdIiFqqONHZPOtz"
target="_blank"
rel="noopener noreferrer"
className="text-blue-200 underline"
>
LoRA Frenzi organization
</a>
. (if you haven't already)
</li>
<li>
Remove the “Ostris AI Toolkit” app from{' '}
<a
href="https://huggingface.co/settings/connected-applications"
target="_blank"
rel="noopener noreferrer"
className="text-blue-200 underline"
>
Hugging Face &gt; Connected Applications
</a>
.
</li>
<li>Come back here and sign in again authorizing the lora-training-frenzi organization so we can refresh your permissions.</li>
</ol>
</div>
)}
{orgStatus === 'error' && (
<div className="border border-red-700 bg-red-900/20 text-red-100 rounded-lg p-4 text-sm">
We couldn&apos;t verify your organization membership right now. You can retry later or ensure your token is valid.
</div>
)}
<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">
<ol className="list-decimal list-inside space-y-4">
<li>
Join the{' '}
<a
href="https://huggingface.co/organizations/lora-training-frenzi/share/kEyyVNQXBPWqmARdwHFVdIiFqqONHZPOtz"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 underline"
>
LoRA Frenzi organization
</a>{' '}
first, then authorize the <code className="bg-gray-800 px-1 rounded">lorafrenzi</code> org when prompted.
<img
src="https://huggingface.co/spaces/multimodalart/ai-toolkit/resolve/main/add_org_to_oauth.png"
alt="Authorize lorafrenzi organization"
className="mt-3 max-w-sm rounded border border-gray-800"
/>
</li>
<li>
<HFLoginButton size="md" />
</li>
</ol>
<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>
</>
);
}