Initial React project
This commit is contained in:
@@ -20,6 +20,11 @@ export interface AiAgentInitialData {
|
||||
escos: EscoInterface[];
|
||||
}
|
||||
|
||||
interface UserProfilePreview {
|
||||
imageUrl?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class AiAgentViewModel {
|
||||
constructor(
|
||||
private candidateService: CandidateService = new CandidateService(),
|
||||
@@ -44,6 +49,17 @@ export class AiAgentViewModel {
|
||||
};
|
||||
}
|
||||
|
||||
async getCandidateProfile(): Promise<UserProfilePreview> {
|
||||
try {
|
||||
const candidate = await this.candidateService.getCandidate();
|
||||
const name = candidate.firstName?.trim() || candidate.name?.trim() || 'Lasse';
|
||||
const imageUrl = candidate.imageUrl || candidate.image || undefined;
|
||||
return { name, imageUrl };
|
||||
} catch {
|
||||
return { name: 'Lasse' };
|
||||
}
|
||||
}
|
||||
|
||||
async addEscoToFilter(escoId: number): Promise<void> {
|
||||
await this.jobAgentService.addEscoToJobAgent(escoId);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CandidateSearchFilterService } from '../services/candidate-search-filter.service';
|
||||
import { CandidateService } from '../services/candidate.service';
|
||||
import { JobService } from '../services/job.service';
|
||||
import { PlacesService } from '../services/places.service';
|
||||
import type { AppliedJobInterface } from '../models/applied-job.interface';
|
||||
@@ -54,6 +55,16 @@ export interface PlaceSelection {
|
||||
longitude: number | null;
|
||||
}
|
||||
|
||||
export interface JobsSearchQuery {
|
||||
desiredTitles?: string[];
|
||||
searchText?: string;
|
||||
}
|
||||
|
||||
interface UserProfilePreview {
|
||||
imageUrl?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const DEFAULT_FILTER: JobsFilterDraft = {
|
||||
escoIds: [],
|
||||
workTypePermanent: false,
|
||||
@@ -118,6 +129,31 @@ function toNumber(source: Record<string, unknown> | null, key: string): number |
|
||||
return typeof value === 'number' ? value : null;
|
||||
}
|
||||
|
||||
function normalizeTerm(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function buildTerms(query?: JobsSearchQuery): string[] {
|
||||
if (!query) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const terms = new Set<string>();
|
||||
const searchText = query.searchText?.trim();
|
||||
if (searchText && searchText.length > 0) {
|
||||
terms.add(searchText);
|
||||
}
|
||||
|
||||
for (const item of query.desiredTitles ?? []) {
|
||||
const normalized = normalizeTerm(item);
|
||||
if (normalized.length > 0) {
|
||||
terms.add(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(terms);
|
||||
}
|
||||
|
||||
function postingToListItem(posting: JobPostingInterface, matchPercent?: number): JobsListItem {
|
||||
return {
|
||||
id: normalizeText(posting.id),
|
||||
@@ -169,8 +205,20 @@ export class JobsPageViewModel {
|
||||
private jobService: JobService = new JobService(),
|
||||
private filterService: CandidateSearchFilterService = new CandidateSearchFilterService(),
|
||||
private placesService: PlacesService = new PlacesService(),
|
||||
private candidateService: CandidateService = new CandidateService(),
|
||||
) {}
|
||||
|
||||
async getCandidateProfile(): Promise<UserProfilePreview> {
|
||||
try {
|
||||
const candidate = await this.candidateService.getCandidate();
|
||||
const name = candidate.firstName?.trim() || candidate.name?.trim() || 'Lasse';
|
||||
const imageUrl = candidate.imageUrl || candidate.image || undefined;
|
||||
return { name, imageUrl };
|
||||
} catch {
|
||||
return { name: 'Lasse' };
|
||||
}
|
||||
}
|
||||
|
||||
async getOccupationOptions(): Promise<OccupationOption[]> {
|
||||
const categorizations: OccupationCategorizationInterface[] = await this.jobService.getOccupationCategorizations();
|
||||
const options: OccupationOption[] = [];
|
||||
@@ -300,7 +348,13 @@ export class JobsPageViewModel {
|
||||
return appliedArray.map((job) => savedOrAppliedToListItem(job as AppliedJobInterface));
|
||||
}
|
||||
|
||||
return this.getJobsFeedItems(searchTerm);
|
||||
return this.getJobsFeedItems(searchTerm ? [searchTerm] : undefined);
|
||||
}
|
||||
|
||||
async applyFiltersAndGetJobs(filter: JobsFilterDraft, query?: JobsSearchQuery): Promise<JobsListItem[]> {
|
||||
await this.saveFilter(filter);
|
||||
const terms = buildTerms(query);
|
||||
return this.getJobsFeedItems(terms);
|
||||
}
|
||||
|
||||
async toggleBookmark(item: Pick<JobsListItem, 'id' | 'fromJobnet'>, save: boolean): Promise<void> {
|
||||
@@ -308,7 +362,7 @@ export class JobsPageViewModel {
|
||||
await this.jobService.bookmarkJobV2(item.id, save, jobType);
|
||||
}
|
||||
|
||||
private async getJobsFeedItems(searchTerm?: string): Promise<JobsListItem[]> {
|
||||
private async getJobsFeedItems(preferredTerms?: string[]): Promise<JobsListItem[]> {
|
||||
const limit = 20;
|
||||
let level = 10;
|
||||
let offset = 0;
|
||||
@@ -317,10 +371,12 @@ export class JobsPageViewModel {
|
||||
const seenIds = new Set<string>();
|
||||
const collected: JobsListItem[] = [];
|
||||
|
||||
const trimmedSearch = searchTerm?.trim() ?? '';
|
||||
let searchWords: string[] = [];
|
||||
if (trimmedSearch.length > 0) {
|
||||
searchWords = [trimmedSearch];
|
||||
const terms = (preferredTerms ?? [])
|
||||
.map((value) => value.trim())
|
||||
.filter((value) => value.length > 0);
|
||||
if (terms.length > 0) {
|
||||
searchWords = terms;
|
||||
} else {
|
||||
try {
|
||||
const words = await this.jobService.getSearchWords();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ChatMessageInterface } from '../models/chat-message.interface';
|
||||
import type { ChatMessageThreadInterface } from '../models/chat-message-thread.interface';
|
||||
import { CandidateService } from '../services/candidate.service';
|
||||
import { ChatMessagesService } from '../services/chat-messages.service';
|
||||
import { MessageService } from '../services/message.service';
|
||||
|
||||
@@ -8,6 +9,11 @@ export interface MessageThreadItem extends ChatMessageThreadInterface {
|
||||
latestMessage: ChatMessageInterface;
|
||||
}
|
||||
|
||||
interface UserProfilePreview {
|
||||
imageUrl?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function toMillis(value?: Date | string): number {
|
||||
if (!value) {
|
||||
return 0;
|
||||
@@ -35,8 +41,20 @@ export class MessagesViewModel {
|
||||
constructor(
|
||||
private readonly chatMessagesService: ChatMessagesService = new ChatMessagesService(),
|
||||
private readonly messageService: MessageService = new MessageService(),
|
||||
private readonly candidateService: CandidateService = new CandidateService(),
|
||||
) {}
|
||||
|
||||
async getCandidateProfile(): Promise<UserProfilePreview> {
|
||||
try {
|
||||
const candidate = await this.candidateService.getCandidate();
|
||||
const name = candidate.firstName?.trim() || candidate.name?.trim() || 'Lasse';
|
||||
const imageUrl = candidate.imageUrl || candidate.image || undefined;
|
||||
return { name, imageUrl };
|
||||
} catch {
|
||||
return { name: 'Lasse' };
|
||||
}
|
||||
}
|
||||
|
||||
async getThreads(): Promise<MessageThreadItem[]> {
|
||||
const threads = await this.chatMessagesService.getChatMessages();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user