Files
applications-tracker/site/src/routes/work-area/WorkArea.svelte

438 lines
10 KiB
Svelte

<script lang="ts">
import {
ApplicationStatus,
ApplicationStatusMaping,
applicationStore,
type Application
} from '$lib/ApplicationsStore.svelte';
import { put, preventDefault, post, get, deleteR } from '$lib/utils';
import { onMount } from 'svelte';
import ExtractTextDialog from './ExtractTextDialog.svelte';
import Flair from '../flair/Flair.svelte';
import NewUrlDialog from './NewUrlDialog.svelte';
import DropZone from './DropZone.svelte';
import { userStore } from '$lib/UserStore.svelte';
let activeItem: Application | undefined = $state();
let extractTokens: HTMLDialogElement;
let changeUrl: HTMLDialogElement;
let preparingToDrop = $state(false);
let lastExtData: any = $state(undefined);
async function activate(item?: Application) {
if (!item) {
return;
}
if (activeItem && activeItem.id !== item.id && activeItem.status === 1) {
// Deactivate active item
try {
await put('application/status', {
id: activeItem.id,
status: 0
});
} catch (e) {
// Show User
console.log('info data', e);
return;
}
}
if (item.status === 0) {
openWindow(item.url);
openCV(item.id);
}
try {
await put('application/status', {
id: item.id,
status: ApplicationStatus.WorkingOnIt
});
activeItem = await get('application/active');
if (!activeItem?.unique_url) {
window.requestAnimationFrame(() => {
changeUrl.showModal();
});
}
} catch (e) {
// Show User
console.log('info data', e);
}
applicationStore.loadApplications(true);
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 ?? activeItem!.id}`,
'cv viwer',
`location,height=${window.screen.height},width=${window.screen.width},scrollbars,status,toolbar,menubar,popup`
);
}
async function loadActive() {
try {
activeItem = await get('application/active');
if (activeItem?.unique_url === null) {
changeUrl.showModal();
}
} catch (e) {
// Show User
console.log('info data', e);
}
}
// Load Ext
$effect(() => {
function onMessage(e: MessageEvent) {
if (e.data.type === 'GOT_INFO_R') {
lastExtData = e.data;
}
}
console.log('setting up interest');
window.addEventListener('message', onMessage);
window.postMessage({ type: 'REGISTER_INTEREST' });
return () => {
window.removeEventListener('message', onMessage);
};
});
function setExtData() {
if (!lastExtData || !activeItem) return;
activeItem.title = lastExtData.jobTitle;
activeItem.company = lastExtData.company;
activeItem.payrange = lastExtData.money;
window.requestAnimationFrame(() => {
save();
lastExtData = undefined;
});
}
onMount(() => {
userStore.checkLogin();
loadActive();
});
async function moveStatus(status: number) {
if (!activeItem) return;
// Deactivate active item
try {
await put('application/status', {
id: activeItem.id,
status: status
});
} catch (e) {
// Show User
console.log('info data', e);
return;
}
applicationStore.loadApplications(true);
applicationStore.loadAplyed(true);
activeItem = undefined;
//openedWindow?.close();
//openedWindow = undefined;
}
async function remove() {
if (!activeItem) return;
// Deactivate active item
try {
await deleteR(`application/${activeItem.id}`);
} catch (e) {
// Show User
console.log('info data', e);
return;
}
applicationStore.loadApplications(true);
activeItem = undefined;
//openedWindow?.close();
//openedWindow = undefined;
}
async function save() {
try {
await put('application/update', activeItem);
} catch (e) {
// Show User
console.log('info data', e);
}
}
async function resetUrl() {
try {
activeItem = await post(`application/reset/url/${activeItem?.id}`, {});
} catch (e) {
// Show User
console.log('info data', e);
}
}
let drag = $state(true);
const statusMapping: string = $derived(
(ApplicationStatusMaping[
activeItem?.status as (typeof ApplicationStatus)[keyof typeof ApplicationStatus]
] as string) ?? ''
);
</script>
<div class="flex flex-col w-full gap-2 min-w-0" role="none">
{#if activeItem && (!applicationStore.dragging || applicationStore.dragging?.id === activeItem.id)}
<div
draggable={drag}
ondrag={() => {
applicationStore.dragStart(activeItem);
}}
ondragend={() => {
window.requestAnimationFrame(() => {
applicationStore.dragEnd();
});
}}
role="none"
class="flex flex-col p-2 h-full gap-2 card min-w-0 flex-grow"
>
<div class="w-full">
{#if activeItem.status != 1}
<div class="bg-danger text-white p-2 rounded-lg text-lg font-bold">
{statusMapping}
</div>
{/if}
<div class="flex gap-2">
<fieldset class="grow">
<label class="flabel" for="title">Company</label>
<input
class="finput"
id="title"
bind:value={activeItem.company}
onchange={save}
/>
</fieldset>
<fieldset class="grow">
<label class="flabel" for="title">Recruiter</label>
<input
class="finput"
id="title"
bind:value={activeItem.recruiter}
onchange={save}
/>
</fieldset>
</div>
<fieldset>
<label class="flabel" for="title">Title</label>
<input
class="finput"
id="title"
bind:value={activeItem.title}
onchange={save}
/>
</fieldset>
<fieldset>
<label class="flabel" for="payrange">Pay Range</label>
<input
class="finput"
id="payrange"
bind:value={activeItem.payrange}
onchange={save}
/>
</fieldset>
<fieldset
draggable="false"
onmouseenter={() => (drag = false)}
onmouseleave={() => (drag = true)}
class="max-w-full min-w-0 overflow-hidden"
>
<div class="flabel">Url</div>
<div class="finput bg-white w-full break-keep">
{activeItem.url}
</div>
</fieldset>
{#if activeItem.unique_url}
<fieldset
draggable="false"
onmouseenter={() => (drag = false)}
onmouseleave={() => (drag = true)}
>
<div class="flabel">Unique Url</div>
<div class="finput bg-white">
{activeItem.unique_url}
</div>
</fieldset>
{/if}
<div>
<div class="flabel">Tags</div>
<div class="flex gap-2 flex-wrap">
{#each activeItem.flairs as item}
<Flair
{item}
allowDelete
application={activeItem}
updateApplication={(item) => (activeItem = item)}
/>
{/each}
</div>
</div>
<fieldset>
<label class="flabel" for="extra">Extra Info</label>
<textarea
class="finput"
id="extra"
bind:value={activeItem.extra_data}
onchange={save}
></textarea>
</fieldset>
<fieldset>
<label class="flabel" for="extra">Message</label>
<textarea
draggable={false}
class="finput"
id="extra"
bind:value={activeItem.message}
onchange={save}
></textarea>
</fieldset>
{#if activeItem.views.length > 0}
<h1 class="text-sm">Non Loggedin Views Time ({activeItem.views.length})</h1>
{#each activeItem.views as view}
<div>
{view.time}
</div>
{/each}
{/if}
</div>
<div class="flex btns">
<button
class="btn-confirm"
onclick={() => {
openWindow(activeItem!.url);
}}
>
Open
</button>
<button class="btn-primary" onclick={() => openCV()}> Open CV </button>
<button class="btn-primary" onclick={() => extractTokens.showModal()}>
Extract Flair
</button>
{#if lastExtData !== undefined}
<button class="btn-primary" onclick={() => setExtData()}> Ext Data </button>
{/if}
{#if activeItem.original_url == null}
<button class="btn-primary" onclick={() => changeUrl.showModal()}>
Update Url
</button>
{/if}
{#if activeItem.original_url != null}
<button class="btn-danger" onclick={resetUrl}> Reset Url </button>
{/if}
</div>
</div>
{#if applicationStore.dragging}
<div class="flex w-full flex-grow rounded-lg p-1 gap-2">
<!-- Do nothing -->
<DropZone
icon="box-arrow-down"
ondrop={() => {
moveStatus(ApplicationStatus.ToApply);
}}
>
To apply
</DropZone>
<!-- Ignore -->
<DropZone
icon="trash-fill"
ondrop={() => {
moveStatus(ApplicationStatus.Ignore);
}}
>
Ignore it
</DropZone>
<!-- Expired -->
<DropZone
icon="clock-fill text-orange-500"
ondrop={() => {
if (activeItem && activeItem.status === ApplicationStatus.Expired) {
moveStatus(ApplicationStatus.ToApply);
} else {
moveStatus(ApplicationStatus.Expired);
}
}}
>
Mark as expired
</DropZone>
<!-- Repeated -->
<DropZone icon="trash-fill text-danger" ondrop={() => remove()}>Delete it</DropZone>
<!-- Applyed -->
<DropZone
icon="server text-confirm"
ondrop={async () => {
await moveStatus(ApplicationStatus.Applyed);
applicationStore.loadAplyed(true);
}}
>
Apply
</DropZone>
<!-- Rejected -->
<DropZone
icon="fire text-danger"
ondrop={() => moveStatus(ApplicationStatus.ApplyedButSaidNo)}
>
I was rejeted :(
</DropZone>
</div>
{/if}
{:else}
<div
class="p-2 h-full w-full gap-2 flex-grow card grid place-items-center"
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-white text-xl">Drop Application To Apply To.</div>
</div>
</div>
{/if}
</div>
<ExtractTextDialog id={activeItem?.id ?? ''} bind:dialog={extractTokens} onreload={loadActive} />
{#if !activeItem?.original_url}
<NewUrlDialog
id={activeItem?.id ?? ''}
bind:dialog={changeUrl}
onreload={(item) => (activeItem = item)}
/>
{/if}