import { useEffect, useMemo, useState } from 'react'; import { ArrowRight, Bot, MapPin, Monitor, PenSquare, Save, Sparkles, Target, } from 'lucide-react'; import type { EscoInterface } from '../../../mvvm/models/esco.interface'; import type { JobAgentFilterInterface } from '../../../mvvm/models/job-agent-filter.interface'; import { AiAgentViewModel, type AiAgentInitialData } from '../../../mvvm/viewmodels/AiAgentViewModel'; import { JobsPageViewModel, type JobsListItem } from '../../../mvvm/viewmodels/JobsPageViewModel'; import { DashboardSidebar, type DashboardNavKey } from '../../dashboard/components/DashboardSidebar'; import { DashboardTopbar } from '../../dashboard/components/DashboardTopbar'; import '../../dashboard/pages/dashboard.css'; import './ai-agent.css'; interface AiAgentPageProps { onLogout: () => void; onNavigate: (target: DashboardNavKey) => void; onOpenJobDetail: (jobId: string, fromJobnet: boolean, returnPage?: DashboardNavKey) => void; onToggleTheme: () => void; theme: 'light' | 'dark'; } const EMPTY_DATA: AiAgentInitialData = { paymentOverview: null, jobAgentFilters: [], cvSuggestions: [], escos: [], }; function initials(value: string): string { return value.trim().slice(0, 1).toUpperCase() || 'A'; } function deriveMatch(index: number): number { return Math.max(68, 98 - (index * 4)); } function byLabelEscos(data: EscoInterface[], query: string): EscoInterface[] { const trimmed = query.trim().toLowerCase(); if (!trimmed) { return []; } return data.filter((item) => item.preferedLabelDa.toLowerCase().includes(trimmed)).slice(0, 8); } export function AiAgentPage({ onLogout, onNavigate, onOpenJobDetail, onToggleTheme, theme }: AiAgentPageProps) { const aiViewModel = useMemo(() => new AiAgentViewModel(), []); const jobsViewModel = useMemo(() => new JobsPageViewModel(), []); const [name, setName] = useState('Lasse'); const [imageUrl, setImageUrl] = useState(undefined); const [data, setData] = useState(EMPTY_DATA); const [jobs, setJobs] = useState([]); const [isLoading, setIsLoading] = useState(true); const [agentName, setAgentName] = useState(''); const [keywords, setKeywords] = useState(''); const [workArea, setWorkArea] = useState(''); const [workType, setWorkType] = useState(''); const [workLocation, setWorkLocation] = useState(''); const [distance, setDistance] = useState(25); useEffect(() => { let active = true; async function load() { setIsLoading(true); const [profile, initialData, jobsData] = await Promise.all([ aiViewModel.getCandidateProfile(), aiViewModel.loadInitialData(), jobsViewModel.getTabItems('jobs'), ]); if (!active) { return; } setName(profile.name); setImageUrl(profile.imageUrl); setData(initialData); setJobs(jobsData); setIsLoading(false); } void load(); return () => { active = false; }; }, [aiViewModel, jobsViewModel]); async function refreshAgents() { const next = await aiViewModel.loadInitialData(); setData(next); } async function handleSaveAgent() { const query = keywords.trim() || agentName.trim() || workArea.trim(); const suggestion = aiViewModel.getEscoSuggestions(query, data.escos, data.jobAgentFilters)[0] || byLabelEscos(data.escos, query)[0]; if (!suggestion) { return; } await aiViewModel.addEscoToFilter(suggestion.id); await refreshAgents(); setAgentName(''); setKeywords(''); setWorkArea(''); setWorkType(''); setWorkLocation(''); setDistance(25); } async function handleToggleVisibility(filter: JobAgentFilterInterface) { await aiViewModel.setFilterVisibility(filter, !filter.visible); await refreshAgents(); } const activeFilters = data.jobAgentFilters; const recommendedJobs = (jobs.length > 0 ? jobs : []).slice(0, 6); return (

Jobagenter

Saet din jobsogning pa autopilot. Lad agenter overvage og matche dig med de perfekte jobs.

Opret ny jobagent

setAgentName(event.target.value)} placeholder="F.eks. Frontend Udvikler CPH" />
setKeywords(event.target.value)} placeholder="F.eks. React, TypeScript, Tailwind" />
setWorkLocation(event.target.value)} placeholder="By eller postnummer" />
{distance} km
setDistance(Number(event.target.value))} />

Dine aktive agenter

{activeFilters.length === 0 ?

Ingen aktive agenter endnu.

: null} {activeFilters.map((filter, index) => (
{index % 2 === 0 ? : }

{filter.escoName}

{filter.isCalculated ? 'Aktiv siden i går' : 'Aktiv'}

{filter.escoName} {workLocation || 'København'} {distance} km
))}

Anbefalede jobs til dig

Opdateret for 5 min siden
{isLoading ?

Indlaeser anbefalinger...

: null} {!isLoading && recommendedJobs.length === 0 ?

Ingen jobanbefalinger fundet endnu.

: null} {recommendedJobs.map((job, index) => (
onOpenJobDetail(job.id, job.fromJobnet, 'agents')} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); onOpenJobDetail(job.id, job.fromJobnet, 'agents'); } }} >
{job.companyLogoImage || job.logoUrl ? {job.companyName} :
{initials(job.companyName)}
}
{deriveMatch(index)}% Match
Via: {activeFilters[0]?.escoName || 'Jobagent'}

{job.title}

{job.companyName} • {job.address || 'Lokation'}

{job.occupationName || 'Frontend'} {job.fromJobnet ? 'Jobnet' : 'Arbejd.com'} {job.candidateDistance != null ? `${Math.round(job.candidateDistance)} km` : 'Remote'}
Slået op for nyligt
))}
); }