Initial React project
This commit is contained in:
135
src/mvvm/viewmodels/SimulatorViewModel.ts
Normal file
135
src/mvvm/viewmodels/SimulatorViewModel.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user