438 lines
10 KiB
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}
|