applications-tracker/site/src/routes/work-area/WorkArea.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(/\&amp;/, '&');
applicationStore.all[activeItem].company = lastExtData.company.replace(/\&amp;/, '&');
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} />