Merge pull request #105 from EETagent/feature_dashboard_add_endpoints_vojta

Feature dashboard add endpoints fixes
This commit is contained in:
Vojtěch Jungmann 2022-12-23 22:40:46 +01:00 committed by GitHub
commit 4d4bf47b09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 264 additions and 160 deletions

View file

@ -4,7 +4,6 @@
import DashboardUploadCard from './DashboardUploadCard.svelte'; import DashboardUploadCard from './DashboardUploadCard.svelte';
import type { ApiError } from '$lib/@api'; import type { ApiError } from '$lib/@api';
export let compact: boolean = false;
let error: string | null = null; let error: string | null = null;
const onFileDrop = async (detail: any) => { const onFileDrop = async (detail: any) => {
@ -27,7 +26,6 @@
<DashboardUploadCard <DashboardUploadCard
{error} {error}
compact
on:filedrop={(e) => onFileDrop(e.detail)} on:filedrop={(e) => onFileDrop(e.detail)}
on:delete={onDelete} on:delete={onDelete}
title="Motivační dopis" title="Motivační dopis"

View file

@ -12,7 +12,7 @@
export let title: string; export let title: string;
export let status: Status; export let status: Status;
export let showDetails = true; export let showDetails = false;
let loading = false; let loading = false;
const submitPortfolio = async () => { const submitPortfolio = async () => {
@ -43,80 +43,94 @@
const url = window.URL.createObjectURL(new Blob([portfolioBlob])); const url = window.URL.createObjectURL(new Blob([portfolioBlob]));
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.setAttribute('download', 'PORTFOLIO' + '_' + $candidateData.candidate.name + '_' + $candidateData.candidate.surname + '.zip'); link.setAttribute(
'download',
'PORTFOLIO' +
'_' +
$candidateData.candidate.name +
'_' +
$candidateData.candidate.surname +
'.zip'
);
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
} };
</script> </script>
<!-- TODO expand on mouse hover?? -->
<!-- <div class="card flex flex-col" on:mouseenter={(_) => showDetails = true} on:mouseleave={(_) => showDetails = false}> -->
<div class="card flex flex-col"> <div class="card flex flex-col">
<div class="infoBar flex flex-row-reverse"> <div class="infoBar flex flex-row-reverse <2xl:flex-col">
<StatusNotificationBig {loading} {status} on:click={debounce(handleNotificationClick, 150)} /> <StatusNotificationBig {loading} {status} on:click={debounce(handleNotificationClick, 150)} />
<div class="mr-4"> <div class="mr-4">
<div on:click on:keydown class="flex flex-col"> <div on:click on:keydown class="flex flex-col">
<div class="flex flex-col h-20"> <div class="flex flex-col <2xl:ml-auto <2xl:flex-row <2xl:my-2">
<InfoButton bind:showDetails={showDetails} on:download={downloadPortfolio} on:showInfo={(_) => showDetails = !showDetails}></InfoButton> <InfoButton
bind:showDetails
on:download={downloadPortfolio}
on:showInfo={(_) => (showDetails = !showDetails)}
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="relative flex flex-row"> <div class="relative flex flex-col my-2 overflow-hidden">
<div> <div>
<span class="absolute -left-16 -top-36"> <span class="absolute -left-16 -top-36">
<Circles /> <Circles />
</span> </span>
<div class="mt-8 flex flex-col lg:mt-12"> <div class="flex flex-col mt-[5%]">
<h3>{title}</h3> <h3>{title}</h3>
<slot /> <slot />
</div> </div>
</div> </div>
{#if showDetails} {#if showDetails}
<svg class="ml-12 mr-8 h-40 hidden xl:block mt-10" viewBox="0 0 2 80" xmlns="http://www.w3.org/2000/svg"> <div class="overflow-scroll">
<line <div
x1="0" use:tippy={{
y="0" content: '<span>Vámi vyplněné osobní údaje</span>',
x2="0" allowHTML: true,
y2="80" placement: 'top',
stroke="#406280ff" showOnCreate: false,
stroke-width="2" delay: 0
stroke-dasharray="3"/> }}
</svg> class="flex flex-col justify-between leading-10 mt-4"
<div >
use:tippy={{ <span>Adresa: <span class="font-bold">{$candidateData.candidate.address}</span></span>
content: "<span>Vámi vyplněné osobní údaje</span>", <span
allowHTML: true, >Datum narození: <span class="font-bold">{$candidateData.candidate.birthdate}</span></span
placement: 'top', >
showOnCreate: false, <span
delay: 0 >Místo narození: <span class="font-bold">{$candidateData.candidate.birthplace}</span
}} ></span
class="flex flex-col justify-around mt-10"> >
<span>Adresa: <span class="font-bold">{$candidateData.candidate.address}</span></span> <span
<span>Datum narození: <span class="font-bold">{$candidateData.candidate.birthdate}</span></span> >Rodné číslo: <span class="font-bold">{$candidateData.candidate.personalIdNumber}</span
<span>Místo narození: <span class="font-bold">{$candidateData.candidate.birthplace}</span></span> ></span
<span>Rodné číslo: <span class="font-bold">{$candidateData.candidate.personalIdNumber}</span></span> >
<span>Telefon: <span class="font-bold">{$candidateData.candidate.telephone}</span></span> <span>Telefon: <span class="font-bold">{$candidateData.candidate.telephone}</span></span>
</div> </div>
<div <div
use:tippy={{ use:tippy={{
content: "<span>Vámi vyplněné osobní údaje</span>", content: '<span>Vámi vyplněné osobní údaje</span>',
allowHTML: true, allowHTML: true,
placement: 'top', placement: 'top',
showOnCreate: false, showOnCreate: false,
delay: 0 delay: 0
}} }}
class="ml-10 <xl:ml-4 flex flex-col justify-around mt-10"> class="flex flex-col leading-10 mt-4"
{#each $candidateData.parents as parent} >
<div class="flex flex-col"> {#each $candidateData.parents as parent}
<span class="font-bold text-sspsBlue text-xl">{parent.name + " " + parent.surname}</span> <div class="flex flex-col">
<span>Email: <span class="font-bold">{parent.email}</span></span> <span class="text-sspsBlue text-xl font-bold">{parent.name + ' ' + parent.surname}</span
<span>Telefon: <span class="font-bold">{parent.telephone}</span></span> >
<span>Email: <span class="font-bold">{parent.email}</span></span>
<span>Telefon: <span class="font-bold">{parent.telephone}</span></span>
</div>
{/each}
</div>
</div> </div>
{/each}
</div>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -20,7 +20,6 @@
export let filesize: number; export let filesize: number;
export let fileType: number; export let fileType: number;
export let placeholder: string = ''; export let placeholder: string = '';
export let compact: boolean = false;
let fileDropped: boolean = false; let fileDropped: boolean = false;
let progress: number = 1; let progress: number = 1;
@ -102,11 +101,9 @@
<div class="card uploadCard relative"> <div class="card uploadCard relative">
<div class="header"> <div class="header">
<h3 class="mb-4 sm:mb-0">{title}</h3> <h3 class="mb-4 sm:mb-0">{title}</h3>
{#if !compact} <div class="mb-4 mt-1 sm:mb-0 sm:mt-0">
<div class="mb-4 mt-1 sm:mb-0 sm:mt-0"> <FileType {filetype} filesize={filesize + ' MB'} />
<FileType {filetype} filesize={filesize + ' MB'} /> </div>
</div>
{/if}
<div class="absolute right-0 top-4 flex items-center px-7"> <div class="absolute right-0 top-4 flex items-center px-7">
{#if status === 'uploaded'} {#if status === 'uploaded'}
<button <button
@ -117,28 +114,26 @@
<StatusNotificationDot {status} /> <StatusNotificationDot {status} />
</div> </div>
</div> </div>
{#if fileDropped && error === null} {#if fileDropped && error === null}
<div class="body uploaded flex content-around items-center justify-between"> <div class="body uploaded flex content-around items-center justify-between">
{#if !compact} <div class="w-24">
<div class="w-24"> <img
<img class="w-full object-scale-down"
class="w-full object-scale-down" src={filetype == 'PDF' ? documentIcon : archiveIcon}
src={filetype == 'PDF' ? documentIcon : archiveIcon} alt="Icon"
alt="Icon" />
/> </div>
</div> <svg class="h-25 hidden xl:block" viewBox="0 0 2 40" xmlns="http://www.w3.org/2000/svg"
<svg class="h-25 hidden xl:block" viewBox="0 0 2 40" xmlns="http://www.w3.org/2000/svg" ><line
><line x1="0"
x1="0" y="0"
y="0" x2="0"
x2="0" y2="40"
y2="40" stroke="#406280ff"
stroke="#406280ff" stroke-width="2"
stroke-width="2" stroke-dasharray="3"
stroke-dasharray="3" /></svg
/></svg >
>
{/if}
<div class="hidden items-center xl:block"> <div class="hidden items-center xl:block">
{#if bytesTotal === 0 || Math.round(progress * 100) === 100} {#if bytesTotal === 0 || Math.round(progress * 100) === 100}
<h2 class="text-xl font-bold">{status === 'submitted' ? 'Odesláno' : 'Nahráno'}</h2> <h2 class="text-xl font-bold">{status === 'submitted' ? 'Odesláno' : 'Nahráno'}</h2>
@ -160,7 +155,7 @@
> >
<div class="items-center text-center"> <div class="items-center text-center">
<h2 class="text-sspsBlueDark mb-2 text-2xl font-bold">{Math.round(progress * 100)} %</h2> <h2 class="text-sspsBlueDark mb-2 text-2xl font-bold">{Math.round(progress * 100)} %</h2>
<ProgressBar {progress} /> <ProgressBar submitted={status === 'submitted'} {progress} />
</div> </div>
</div> </div>
{:else} {:else}

View file

@ -3,6 +3,8 @@
import 'tippy.js/dist/tippy.css'; import 'tippy.js/dist/tippy.css';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { submissionProgress, UploadStatus } from '$lib/stores/portfolio'; import { submissionProgress, UploadStatus } from '$lib/stores/portfolio';
import Document from '../icons/Document.svelte';
import Download from '../icons/Download.svelte';
export let showDetails: boolean; export let showDetails: boolean;
@ -10,53 +12,76 @@
const showInfo = () => { const showInfo = () => {
dispatch('showInfo'); dispatch('showInfo');
} };
const download = () => { const download = () => {
dispatch('download'); dispatch('download');
} };
</script> </script>
<div class="flex flex-col"> <span
<!-- TODO: better icon for "hide details"? --> on:click={(_) => showInfo()}
<div class="flex flex-col"> on:keydown={(_) => showInfo()}
<span on:click={(_) => showInfo()} on:keydown={(_) => showInfo()} use:tippy={{
use:tippy={{ content: (showDetails ? 'Skrýt' : 'Zobrazit') + ' osobní údaje',
content: (showDetails ? "Skrýt" : "Zobrazit") + " osobní údaje", placement: 'top',
placement: 'top', showOnCreate: false,
showOnCreate: false, delay: 0
delay: 0 }}
}}> class="icon"
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg> class:showDetails
</span> >
<Document />
</span>
{#if $submissionProgress.status === UploadStatus.Submitted} {#if $submissionProgress.status === UploadStatus.Submitted}
<span on:click={(_) => download()} on:keydown={(_) => download()} use:tippy={{ <span
content: "Stáhnout portfolio", on:click={(_) => download()}
placement: 'top', on:keydown={(_) => download()}
showOnCreate: false, use:tippy={{
delay: 0 content: 'Stáhnout portfolio',
}}>
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
</span>
{:else}
<span use:tippy={{
content: "Nelze stáhnout, portfolio nebylo odevzdáno",
placement: 'top', placement: 'top',
showOnCreate: false, showOnCreate: false,
delay: 0 delay: 0
}}> }}
<svg class="disabledIcon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg> >
</span> <svg
{/if} class="icon"
</div> fill="none"
</div> stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/></svg
>
</span>
{:else}
<span
use:tippy={{
content: 'Nelze stáhnout, portfolio nebylo odevzdáno',
placement: 'top',
showOnCreate: false,
delay: 0
}}
class="icon disabledIcon"
>
<Download />
</span>
{/if}
<style> <style>
.icon { .icon {
@apply w-10 h-10 stroke-sspsBlueDark hover:cursor-pointer; @apply text-sspsBlueDark h-10 w-10 transition-colors duration-300 hover:cursor-pointer;
@apply hover:text-sspsBlue;
}
.showDetails {
@apply text-sspsBlue;
} }
.disabledIcon { .disabledIcon {
@apply w-10 h-10 stroke-gray-300 hover:cursor-not-allowed; @apply text-gray-300 hover:cursor-not-allowed hover:text-gray-300;
} }
</style> </style>

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
export let progress: number; export let progress: number;
export let submitted: boolean = false;
</script> </script>
<div class="progress-bar"> <div class="progress-bar">
@ -13,27 +14,30 @@
> >
<line x1="5" y1="3" x2="45" y2="3" stroke="#e6e6e6" stroke-width="6" stroke-linecap="round" /> <line x1="5" y1="3" x2="45" y2="3" stroke="#e6e6e6" stroke-width="6" stroke-linecap="round" />
{#if progress === 1} <line
<line x1="5"
x1="5" y1="3"
y1="3" x2={progress * 45}
x2={progress * 45} y2="3"
y2="3" class:submitted={progress === 1 && submitted}
stroke="#35e000ff" class:not-submitted={progress === 1 && !submitted}
stroke-width="3" class:uploading={progress !== 1}
stroke-linecap="round" stroke-width="3"
/> stroke-linecap="round"
{:else} />
<line
x1="5"
y1="3"
x2={progress * 45}
y2="3"
stroke="#75bff8ff"
stroke-width="3"
stroke-linecap="round"
/>
{/if}
> >
</svg> </svg>
</div> </div>
<style>
.submitted {
@apply stroke-[#35e000ff];
}
.not-submitted {
@apply stroke-[#ef8b46];
}
.uploading {
@apply stroke-[#75bff8ff];
}
</style>

View file

@ -24,7 +24,7 @@
<div on:click on:keydown class="flex flex-col"> <div on:click on:keydown class="flex flex-col">
<div class="info flex flex-col {status}"> <div class="info flex flex-col {status}">
<span class="text-xl font-bold text-white">{title}</span> <span class="2xl:text-xl font-bold text-white">{title}</span>
{#if loading} {#if loading}
<div role="status"> <div role="status">
<svg <svg

View file

@ -0,0 +1,16 @@
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/></svg
>
<style>
svg {
@apply fill-none stroke-current;
}
</style>

After

Width:  |  Height:  |  Size: 337 B

View file

@ -0,0 +1,16 @@
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/></svg
>
<style>
svg {
@apply fill-none stroke-current;
}
</style>

After

Width:  |  Height:  |  Size: 283 B

View file

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { flip } from 'svelte/animate';
import FullLayout from '$lib/components/layout/FullLayout.svelte'; import FullLayout from '$lib/components/layout/FullLayout.svelte';
import { Swiper, SwiperSlide } from 'swiper/svelte'; import { Swiper, SwiperSlide } from 'swiper/svelte';
@ -9,13 +10,18 @@
import PortfolioLetterUploadCard from '$lib/components/dashboard/PortfolioLetterUploadCard.svelte'; import PortfolioLetterUploadCard from '$lib/components/dashboard/PortfolioLetterUploadCard.svelte';
import PortfolioZipUploadCard from '$lib/components/dashboard/PortfolioZipUploadCard.svelte'; import PortfolioZipUploadCard from '$lib/components/dashboard/PortfolioZipUploadCard.svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { fetchSubmProgress, submissionProgress, UploadStatus, type Status } from '$lib/stores/portfolio'; import {
fetchSubmProgress,
submissionProgress,
UploadStatus,
type Status
} from '$lib/stores/portfolio';
import { candidateData } from '$lib/stores/candidate'; import { candidateData } from '$lib/stores/candidate';
export let data: PageData; export let data: PageData;
// TODO: transition // TODO: transition
let showDetails = true; let showDetails = false;
// @ts-ignore // @ts-ignore
$: candidateData.set(data.candidate); $: candidateData.set(data.candidate);
@ -31,30 +37,57 @@
default: default:
return 'missing'; return 'missing';
} }
} };
</script> </script>
<FullLayout> <FullLayout>
<div class="dashboard dashboardDesktop"> <div class="dashboard dashboardDesktop">
<div class="movable name col-span-3" class:showDetailsInfoCard={showDetails}> {#each [0] as animated (animated)}
<DashboardInfoCard bind:showDetails={showDetails} status={getUploadStatus($submissionProgress.status)} title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}> <div
<span class="text-sspsBlue mt-3 truncate">{$candidateData.candidate.email}</span> class="movable name col-span-3 row-span-4"
<span class="text-sspsGray mt-3 text-xs">Uchazeč na SSPŠ</span> animate:flip={{ duration: 400}}
</DashboardInfoCard> class:showDetailsInfoCard={showDetails}
</div> >
<div class="movable coverletter col-span-5" class:showDetailsUploadCard={showDetails}> <DashboardInfoCard
<CoverLetterUploadCard compact={showDetails} /> bind:showDetails
</div> status={getUploadStatus($submissionProgress.status)}
<div class="portfolio col-span-4"> title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}
<PortfolioLetterUploadCard /> >
</div> <span class="text-sspsBlue mt-3 truncate">{$candidateData.candidate.email}</span>
<div class="moreData col-span-4"> <span class="text-sspsGray mt-3 text-xs">Uchazeč na SSPŠ</span>
<PortfolioZipUploadCard /> </DashboardInfoCard>
</div>
{/each}
<div class="movable coverletter col-span-5 row-span-4">
<CoverLetterUploadCard />
</div> </div>
{#each [0] as animated (animated)}
<div
animate:flip={{ duration: 400 }}
class="portfolio col-span-4 row-span-4"
class:showDetailsPortfolio={showDetails}
>
<PortfolioLetterUploadCard />
</div>
{/each}
{#each [0] as animated (animated)}
<div
animate:flip={{ duration: 400 }}
class="moreData col-span-4 row-span-4"
class:showDetailsMoreData={showDetails}
>
<PortfolioZipUploadCard />
</div>
{/each}
</div> </div>
<div class="dashboard dashboardMobile"> <div class="dashboard dashboardMobile">
<div class="name my-10 mx-auto w-[90%]"> <div class="name my-10 mx-auto w-[90%]">
<DashboardInfoCard status={getUploadStatus($submissionProgress.status)} title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}> <DashboardInfoCard
status={getUploadStatus($submissionProgress.status)}
title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}
>
<span class="text-sspsBlue mt-3 truncate">{$candidateData.candidate.email}</span> <span class="text-sspsBlue mt-3 truncate">{$candidateData.candidate.email}</span>
<span class="text-sspsGray mt-3 text-xs">Uchazeč na SSPŠ</span> <span class="text-sspsGray mt-3 text-xs">Uchazeč na SSPŠ</span>
</DashboardInfoCard> </DashboardInfoCard>
@ -81,14 +114,17 @@
<style> <style>
.showDetailsInfoCard { .showDetailsInfoCard {
@apply col-span-5 <2xl: col-span-6; @apply md:row-span-8;
} }
.showDetailsUploadCard { .showDetailsPortfolio {
@apply col-span-3 <2xl: col-span-2; @apply md:hidden;
}
.showDetailsMoreData {
@apply md:col-span-5;
} }
.dashboardDesktop { .dashboardDesktop {
@apply h-[85vh] w-[85vw]; @apply h-[85vh] w-[85vw];
@apply hidden grid-cols-8 grid-rows-2 gap-10 md:grid; @apply grid-rows-8 hidden grid-cols-8 gap-10 md:grid;
} }
.dashboardMobile { .dashboardMobile {