import "./JobsSidebar.js" import "./JobsGrid.js" import "./JobCard.js" import "./JobForm.js" import "../../components/SearchBar.js" import "../../components/AddButton.js" import server from "../../_/code/bridge/server.js" css(` jobs- { font-family: 'Arial'; scrollbar-width: none; -ms-overflow-style: none; } jobs- h1 { font-family: 'Bona'; } jobs- .VStack::-webkit-scrollbar { display: none; width: 0px; height: 0px; } jobs- .VStack::-webkit-scrollbar-thumb { background: transparent; } jobs- .VStack::-webkit-scrollbar-track { background: transparent; } `) class Jobs extends Shadow { static searchableKeys = ['title', 'description', 'location', 'company', 'salary_period', 'salary_number']; constructor() { super() this.jobs = global.currentNetwork.data.jobs; this.searchedJobs = []; this.searchText = ""; // for mainting searchText after re-render } render() { ZStack(() => { JobForm() VStack(() => { HStack(() => { SearchBar(this.searchText, "75vw") AddButton() }) VStack(() => { if (!this.jobs || this.jobs == []) { LoadingCircle() } else if (this.searchText) { if (this.searchedJobs.length > 0) { for (let i = 0; i < this.searchedJobs.length; i++) { JobCard(this.searchedJobs[i]) } } else { h2("Could not find any jobs with your search criteria.") .color("var(--divider)") .fontWeight("bold") .marginTop(7.5, em) .marginBottom(0.5, em) .textAlign("center") } } else if (this.jobs.length > 0) { for (let i = 0; i < this.jobs.length; i++) { JobCard(this.jobs[i]) } } else { h2("No Jobs") .color("var(--divider)") .fontWeight("bold") .marginTop(7.5, em) .marginBottom(0.5, em) .textAlign("center") } }) .overflowY("scroll") .gap(0.75, em) }) .boxSizing("border-box") .height(100, pct) .width(100, pct) .onEvent("jobsearch", this.onJobSearch) .onEvent("new-job", this.onNewJob) }) } onNewJob = (e) => { let newJob = e.detail.job; this.jobs.push(newJob) this.rerender() } onJobSearch = (e) => { let searchText = e.detail.searchText.toLowerCase().trim(); if (!searchText) { this.searchedJobs = []; } else { this.searchedJobs = this.jobs.filter(job => Jobs.searchableKeys.some(key => { if (key === "salary_number" || key === "salary_period") { return String(job["salary_number"]).toLowerCase().includes(searchText.replace(/\/.*$/, '').replace(/[,$]/g, '')) && String(job["salary_period"]).toLowerCase().includes(searchText.replace(/^[^/]*\/?/, '')); } else { return String(job[key]).toLowerCase().includes(searchText); } }) ); } this.searchText = searchText this.rerender() } async getJobs(networkId) { const fetchedJobs = await server.getJobs(networkId) if (this.checkForUpdates(this.jobs, fetchedJobs.data)) { this.jobs = fetchedJobs this.rerender() } } connectedCallback() { this.getJobs(global.currentNetwork.id) } checkForUpdates(currentJobs, fetchedJobs) { if (currentJobs.length !== fetchedJobs.length) return true; const currentMap = new Map(currentJobs.map(job => [job.id, job])); for (const fetchedJob of fetchedJobs) { const currentJob = currentMap.get(fetchedJob.id); // new job added if (!currentJob) return true; // existing job changed if (currentJob.updated_at !== fetchedJob.updated_at) { return true; } } return false; } } register(Jobs)