136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { JobsPageViewModel, type JobsListItem } from './JobsPageViewModel';
|
|
import { SimulationService } from '../services/simulation.service';
|
|
import type { SimulationPersonalityInterface } from '../models/simulation-personality.interface';
|
|
|
|
export interface SimulatorInterviewItem {
|
|
id: string;
|
|
title: string;
|
|
companyName: string;
|
|
dateLabel: string;
|
|
completed: boolean;
|
|
durationMinutes: number | null;
|
|
personality: string;
|
|
}
|
|
|
|
interface RecordLike {
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
function asRecord(value: unknown): RecordLike | null {
|
|
return typeof value === 'object' && value !== null ? (value as RecordLike) : null;
|
|
}
|
|
|
|
function asString(value: unknown): string {
|
|
return typeof value === 'string' ? value : '';
|
|
}
|
|
|
|
function asNumber(value: unknown): number | null {
|
|
return typeof value === 'number' ? value : null;
|
|
}
|
|
|
|
function parseBoolean(value: unknown): boolean | null {
|
|
if (typeof value === 'boolean') {
|
|
return value;
|
|
}
|
|
if (typeof value === 'string') {
|
|
const normalized = value.toLowerCase();
|
|
if (normalized === 'completed' || normalized === 'done' || normalized === 'true') {
|
|
return true;
|
|
}
|
|
if (normalized === 'incomplete' || normalized === 'pending' || normalized === 'false') {
|
|
return false;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function formatDate(value: string): string {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
const parsed = new Date(value);
|
|
if (Number.isNaN(parsed.getTime())) {
|
|
return '';
|
|
}
|
|
return new Intl.DateTimeFormat('da-DK', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
}).format(parsed);
|
|
}
|
|
|
|
function mapInterview(item: unknown, index: number): SimulatorInterviewItem | null {
|
|
const source = asRecord(item);
|
|
if (!source) {
|
|
return null;
|
|
}
|
|
|
|
const id = asString(source.id) || asString(source.interview_id) || `interview-${index}`;
|
|
const title = asString(source.job_name) || asString(source.job_title) || asString(source.title) || 'Interview';
|
|
const companyName = asString(source.company_name) || asString(source.companyName) || 'Ukendt virksomhed';
|
|
|
|
const dateRaw = asString(source.interview_date)
|
|
|| asString(source.created_at)
|
|
|| asString(source.updated_at)
|
|
|| asString(source.date);
|
|
|
|
const completed = parseBoolean(source.is_completed) ?? parseBoolean(source.completed) ?? parseBoolean(source.status) ?? true;
|
|
const durationMinutes = asNumber(source.duration_minutes) ?? asNumber(source.duration) ?? asNumber(source.length_minutes);
|
|
const personality = asString(source.personality_name)
|
|
|| asString(source.simulation_personality_name)
|
|
|| asString(source.personality)
|
|
|| 'Professionel';
|
|
|
|
return {
|
|
id,
|
|
title,
|
|
companyName,
|
|
dateLabel: formatDate(dateRaw),
|
|
completed,
|
|
durationMinutes,
|
|
personality,
|
|
};
|
|
}
|
|
|
|
export class SimulatorViewModel {
|
|
constructor(
|
|
private readonly jobsViewModel: JobsPageViewModel = new JobsPageViewModel(),
|
|
private readonly simulationService: SimulationService = new SimulationService(),
|
|
) {}
|
|
|
|
async getCandidateProfile(): Promise<{ imageUrl?: string; name: string }> {
|
|
return this.jobsViewModel.getCandidateProfile();
|
|
}
|
|
|
|
async getJobs(): Promise<JobsListItem[]> {
|
|
try {
|
|
return await this.jobsViewModel.getTabItems('jobs');
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async getPersonalities(): Promise<SimulationPersonalityInterface[]> {
|
|
try {
|
|
const list = await this.simulationService.listSimulationPersonalities();
|
|
return Array.isArray(list) ? list : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async getInterviews(limit: number = 12): Promise<SimulatorInterviewItem[]> {
|
|
try {
|
|
const payload = await this.simulationService.listInterviews(limit, 0);
|
|
const root = asRecord(payload);
|
|
const list = Array.isArray(root?.interviews) ? root.interviews : (Array.isArray(payload) ? payload : []);
|
|
|
|
return list
|
|
.map((item, index) => mapInterview(item, index))
|
|
.filter((item): item is SimulatorInterviewItem => Boolean(item));
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
}
|