482 lines
13 KiB
Svelte
482 lines
13 KiB
Svelte
<script lang="ts">
|
|
import { applicationStore, type Application } from '$lib/ApplicationsStore.svelte';
|
|
|
|
import { put, preventDefault, post, get } from '$lib/utils';
|
|
import { onMount } from 'svelte';
|
|
import ExtractTextDialog from './ExtractTextDialog.svelte';
|
|
import Flair from '../flair/Flair.svelte';
|
|
import NewUrlDialog from './NewUrlDialog.svelte';
|
|
import { userStore } from '$lib/UserStore.svelte';
|
|
import LinkApplication from './LinkApplication.svelte';
|
|
import SearchApplication from './SearchApplication.svelte';
|
|
import NewApplication from './NewApplication.svelte';
|
|
import Timeline from './Timeline.svelte';
|
|
import CompanyField from './CompanyField.svelte';
|
|
import AutoDropZone from './AutoDropZone.svelte';
|
|
import { statusStore } from '$lib/Types.svelte';
|
|
|
|
// Not this represents the index in the store array
|
|
let activeItem: number | undefined = $state();
|
|
|
|
let derivedItem: Application | undefined = $derived(applicationStore.all[activeItem ?? -1]);
|
|
|
|
let extractTokens: HTMLDialogElement;
|
|
let changeUrl: HTMLDialogElement;
|
|
let linkApplication: HTMLDialogElement;
|
|
|
|
let lastExtData: any = $state(undefined);
|
|
let autoExtData = false;
|
|
|
|
let showExtraData = $state(false);
|
|
|
|
let drag = $state(true);
|
|
|
|
async function activate(item?: Application, ow = true) {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (ow && item.id !== derivedItem?.id) {
|
|
openWindow(item.url);
|
|
}
|
|
openCV(item.id);
|
|
|
|
try {
|
|
const toLoadItem: Application = await get(`application/${item.id}`);
|
|
if (!toLoadItem?.simple_url) {
|
|
window.requestAnimationFrame(() => {
|
|
changeUrl.showModal();
|
|
});
|
|
}
|
|
activeItem = applicationStore.getIndexById(toLoadItem.id);
|
|
applicationStore.all[activeItem ?? -1] = toLoadItem;
|
|
applicationStore.reset();
|
|
} catch (e) {
|
|
// Show User
|
|
console.log('info data', e);
|
|
}
|
|
|
|
autoExtData = false;
|
|
lastExtData = undefined;
|
|
}
|
|
|
|
function openWindow(url: string) {
|
|
if (!url.startsWith('https://')) {
|
|
url = `https://${url}`;
|
|
}
|
|
|
|
window.open(
|
|
url,
|
|
'new window',
|
|
`location,height=${window.screen.height},width=${window.screen.width},scrollbars,status,toolbar,menubar,popup`
|
|
);
|
|
}
|
|
|
|
function openCV(id?: string) {
|
|
window.open(
|
|
`/cv?id=${id ?? derivedItem!.id}`,
|
|
'cv viwer',
|
|
`location,height=${window.screen.height},width=${window.screen.width},scrollbars,status,toolbar,menubar,popup`
|
|
);
|
|
}
|
|
|
|
//
|
|
// Make the CV open on a new page
|
|
//
|
|
function docKey(e: KeyboardEvent) {
|
|
if (derivedItem && e.ctrlKey && e.code === 'KeyO') {
|
|
openCV(derivedItem.id);
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
$effect(() => {
|
|
document.addEventListener('keydown', docKey, false);
|
|
return () => {
|
|
document.removeEventListener('keydown', docKey);
|
|
};
|
|
});
|
|
//
|
|
//
|
|
//
|
|
|
|
// Load Ext
|
|
$effect(() => {
|
|
function onMessage(e: MessageEvent) {
|
|
if (e.data.type === 'GOT_INFO_R') {
|
|
lastExtData = e.data;
|
|
if (autoExtData) {
|
|
window.requestAnimationFrame(() => {
|
|
setExtData();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('message', onMessage);
|
|
window.postMessage({ type: 'REGISTER_INTEREST' });
|
|
return () => {
|
|
window.removeEventListener('message', onMessage);
|
|
};
|
|
});
|
|
|
|
function setExtData() {
|
|
if (!lastExtData || activeItem === undefined || !derivedItem) return;
|
|
applicationStore.all[activeItem].title = lastExtData.jobTitle.replace(/\&/, '&');
|
|
applicationStore.all[activeItem].company = lastExtData.company.replace(/\&/, '&');
|
|
|
|
if (
|
|
!(
|
|
applicationStore.all[activeItem].payrange.match('Glassdoor est.') &&
|
|
lastExtData.money.match('Glassdoor est.')
|
|
) &&
|
|
!(applicationStore.all[activeItem].payrange !== '' && lastExtData.money === '')
|
|
) {
|
|
applicationStore.all[activeItem].payrange = lastExtData.money;
|
|
}
|
|
|
|
const title: string = lastExtData.jobTitle;
|
|
if (title.match(/intern|apprenticeship/i)) {
|
|
applicationStore.all[activeItem].job_level = 'intern';
|
|
} else if (title.match(/graduate/i)) {
|
|
applicationStore.all[activeItem].job_level = 'entry';
|
|
} else if (title.match(/junior|associate/i)) {
|
|
applicationStore.all[activeItem].job_level = 'junior';
|
|
} else if (title.match(/mid/i)) {
|
|
applicationStore.all[activeItem].job_level = 'mid';
|
|
} else if (title.match(/senior|III/i)) {
|
|
applicationStore.all[activeItem].job_level = 'senior';
|
|
} else if (title.match(/staff/i)) {
|
|
applicationStore.all[activeItem].job_level = 'staff';
|
|
} else if (title.match(/lead/i)) {
|
|
applicationStore.all[activeItem].job_level = 'lead';
|
|
}
|
|
|
|
window.requestAnimationFrame(() => {
|
|
save().then(async () => {
|
|
if (activeItem === undefined) return;
|
|
if (!lastExtData.description) {
|
|
lastExtData = undefined;
|
|
return;
|
|
}
|
|
await post('application/text/flair', {
|
|
id: derivedItem.id ?? '',
|
|
text: lastExtData.description
|
|
});
|
|
activate(derivedItem, false);
|
|
lastExtData = undefined;
|
|
});
|
|
});
|
|
}
|
|
|
|
onMount(() => {
|
|
userStore.checkLogin();
|
|
statusStore.load();
|
|
});
|
|
|
|
$effect(() => {
|
|
if (!applicationStore.loadItem) {
|
|
return;
|
|
}
|
|
(async () => {
|
|
await activate(applicationStore.loadItem, applicationStore.loadItemOpen);
|
|
applicationStore.loadItem = undefined;
|
|
})();
|
|
});
|
|
|
|
$effect(() => {
|
|
applicationStore.loadAll();
|
|
});
|
|
|
|
async function save() {
|
|
try {
|
|
await put('application/update', applicationStore.all[activeItem ?? -1]);
|
|
} catch (e) {
|
|
// Show User
|
|
console.log('info data', e);
|
|
}
|
|
}
|
|
|
|
async function resetUrl() {
|
|
try {
|
|
activeItem = await post(`application/reset/url/${derivedItem?.id}`, {});
|
|
} catch (e) {
|
|
// Show User
|
|
console.log('info data', e);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="flex flex-col w-full gap-2 min-w-0 relative" role="none">
|
|
{#if activeItem !== undefined && (!applicationStore.dragging || applicationStore.dragging?.id === derivedItem.id)}
|
|
<div
|
|
draggable={drag}
|
|
ondrag={() => {
|
|
applicationStore.dragStart(derivedItem);
|
|
}}
|
|
ondragend={() => {
|
|
window.requestAnimationFrame(() => {
|
|
applicationStore.dragEnd();
|
|
});
|
|
}}
|
|
role="none"
|
|
class="flex flex-col p-2 h-full gap-2 card min-w-0 flex-grow min-h-[50vh]"
|
|
>
|
|
<div class="w-full">
|
|
<!-- TODO add item -->
|
|
{#if derivedItem.status_id !== null}
|
|
<div class="bg-danger text-white p-2 rounded-lg text-lg font-bold">
|
|
{statusStore.nodesR[derivedItem.status_id].name}
|
|
</div>
|
|
{/if}
|
|
{#if showExtraData}
|
|
<fieldset class="max-w-full min-w-0 overflow-hidden">
|
|
<div class="flabel">Id</div>
|
|
<div class="finput bg-white w-full break-keep">
|
|
{derivedItem.id}
|
|
</div>
|
|
</fieldset>
|
|
<fieldset class="max-w-full min-w-0 overflow-hidden">
|
|
<div class="flabel">Create Time</div>
|
|
<div class="finput bg-white w-full break-keep">
|
|
{derivedItem.create_time}
|
|
</div>
|
|
</fieldset>
|
|
{/if}
|
|
<div class="flex gap-2">
|
|
<CompanyField index={activeItem} {save} />
|
|
<fieldset class="grow">
|
|
<label class="flabel" for="title">Recruiter</label>
|
|
<input
|
|
class="finput"
|
|
id="title"
|
|
bind:value={applicationStore.all[activeItem].recruiter}
|
|
onchange={save}
|
|
/>
|
|
</fieldset>
|
|
<fieldset>
|
|
<label class="flabel" for="title">Agency</label>
|
|
<input
|
|
class="finput"
|
|
id="title"
|
|
type="checkbox"
|
|
bind:checked={applicationStore.all[activeItem].agency}
|
|
onchange={save}
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
<fieldset>
|
|
<label class="flabel" for="title">Title</label>
|
|
<input
|
|
class="finput"
|
|
id="title"
|
|
bind:value={applicationStore.all[activeItem].title}
|
|
onchange={save}
|
|
/>
|
|
</fieldset>
|
|
<fieldset>
|
|
<label class="flabel" for="payrange">Pay Range</label>
|
|
<input
|
|
class="finput"
|
|
id="payrange"
|
|
bind:value={applicationStore.all[activeItem].payrange}
|
|
onchange={save}
|
|
/>
|
|
</fieldset>
|
|
{#if !derivedItem.simple_url || showExtraData}
|
|
<fieldset class="max-w-full min-w-0 overflow-hidden">
|
|
<div class="flabel">Url</div>
|
|
<div class="finput bg-white w-full break-keep">
|
|
{derivedItem.url}
|
|
</div>
|
|
</fieldset>
|
|
{/if}
|
|
{#if derivedItem.simple_url}
|
|
<fieldset class="max-w-full min-w-0 overflow-hidden">
|
|
<div class="flabel">Simple Url</div>
|
|
<div class="finput bg-white w-full break-keep">
|
|
{derivedItem.simple_url}
|
|
</div>
|
|
</fieldset>
|
|
{/if}
|
|
{#if showExtraData}
|
|
<div>
|
|
<div class="flabel">Simple Url</div>
|
|
<div class="flex flex-col gap-2">
|
|
{#each derivedItem.urls as url}
|
|
<div>
|
|
<button
|
|
class="text-violet-300 text-nowrap whitespace-nowrap overflow-x-hidden"
|
|
onclick={() => {
|
|
openWindow(url);
|
|
}}>{url}</button
|
|
>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
<fieldset>
|
|
<label class="flabel" for="title">Job Level</label>
|
|
<select
|
|
class="finput"
|
|
id="job_level"
|
|
bind:value={applicationStore.all[activeItem].job_level}
|
|
onchange={save}
|
|
>
|
|
<option value="intern"> Intern </option>
|
|
<option value="entry"> Entry </option>
|
|
<option value="junior"> Junior </option>
|
|
<option value="mid"> Mid </option>
|
|
<option value="senior"> Senior </option>
|
|
<option value="staff"> Staff </option>
|
|
<option value="lead"> Lead </option>
|
|
</select>
|
|
</fieldset>
|
|
<div>
|
|
<div class="flabel">Tags</div>
|
|
<div class="flex gap-2 flex-wrap">
|
|
{#each derivedItem.flairs as item}
|
|
<Flair
|
|
{item}
|
|
allowDelete
|
|
application={derivedItem}
|
|
updateApplication={(item) =>
|
|
(applicationStore.all[activeItem ?? -1] = item)}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
<fieldset>
|
|
<label class="flabel" for="extra">Extra Info</label>
|
|
<textarea
|
|
class="finput"
|
|
id="extra"
|
|
bind:value={applicationStore.all[activeItem].extra_data}
|
|
onchange={save}
|
|
></textarea>
|
|
</fieldset>
|
|
<fieldset>
|
|
<label class="flabel" for="extra">Message</label>
|
|
<textarea
|
|
class="finput"
|
|
id="extra"
|
|
bind:value={applicationStore.all[activeItem].message}
|
|
onchange={save}
|
|
></textarea>
|
|
</fieldset>
|
|
</div>
|
|
<div class="flex btns">
|
|
<button
|
|
class="btn-confirm"
|
|
onclick={() => {
|
|
openWindow(derivedItem!.url);
|
|
}}
|
|
>
|
|
Open
|
|
</button>
|
|
<button class="btn-primary" onclick={() => openCV()}> Open CV </button>
|
|
<div class="px-10"></div>
|
|
<button class="btn-primary" onclick={() => extractTokens.showModal()}>
|
|
Extract Flair
|
|
</button>
|
|
{#if lastExtData !== undefined}
|
|
<button class="btn-primary" onclick={() => setExtData()}> Ext Data </button>
|
|
{:else}
|
|
<button
|
|
class="btn-primary"
|
|
onclick={() => {
|
|
autoExtData = true;
|
|
window.postMessage({ type: 'R_GET_DATA_FROM_PAGE' });
|
|
}}
|
|
>
|
|
Get Ext Data
|
|
</button>
|
|
{/if}
|
|
<button
|
|
class={derivedItem.simple_url ? 'btn-danger' : 'btn-primary'}
|
|
onclick={() => changeUrl.showModal()}
|
|
>
|
|
Update Url
|
|
</button>
|
|
<div class="px-10"></div>
|
|
<button class="btn-primary" onclick={() => linkApplication.showModal()}>
|
|
Link Application
|
|
</button>
|
|
<button
|
|
class:btn-primary={drag}
|
|
class:btn-danger={!drag}
|
|
onclick={() => (drag = !drag)}
|
|
>
|
|
👋
|
|
</button>
|
|
<button
|
|
class:btn-primary={!showExtraData}
|
|
class:btn-confirm={showExtraData}
|
|
onclick={() => (showExtraData = !showExtraData)}
|
|
>
|
|
🔬
|
|
</button>
|
|
</div>
|
|
<Timeline application={derivedItem} showAll={showExtraData} />
|
|
</div>
|
|
<AutoDropZone bind:index={activeItem} />
|
|
{:else}
|
|
<div
|
|
class="p-2 h-full w-full gap-2 flex-grow card grid place-items-center min-h-[50vh]"
|
|
ondragover={(e) => {
|
|
e.preventDefault();
|
|
}}
|
|
ondragenter={preventDefault(() => {})}
|
|
ondrop={() => {
|
|
activate(applicationStore.dragging);
|
|
}}
|
|
role="none"
|
|
>
|
|
<div class="text-center relative">
|
|
<span
|
|
class="bi bi-layers-fill text-7xl text-white absolute"
|
|
class:animate-bounce={applicationStore.dragging}
|
|
></span>
|
|
<span class="bi bi-layers-fill text-7xl text-white opacity-0"></span>
|
|
<div class="text-xl">Drop Application To Manage</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<ExtractTextDialog
|
|
id={derivedItem?.id ?? ''}
|
|
bind:dialog={extractTokens}
|
|
onreload={() => activate(derivedItem, false)}
|
|
/>
|
|
|
|
{#if derivedItem}
|
|
<NewUrlDialog
|
|
id={derivedItem?.id ?? ''}
|
|
bind:dialog={changeUrl}
|
|
index={activeItem}
|
|
onreload={async (item) => {
|
|
activate(item, false);
|
|
}}
|
|
/>
|
|
{/if}
|
|
|
|
{#if derivedItem}
|
|
<LinkApplication
|
|
application={derivedItem}
|
|
bind:dialog={linkApplication}
|
|
onreload={(item) => activate(item, false)}
|
|
/>
|
|
{/if}
|
|
|
|
<SearchApplication
|
|
application={derivedItem}
|
|
onreload={async (item) => {
|
|
activate(item);
|
|
}}
|
|
/>
|
|
|
|
<NewApplication onreload={activate} />
|