Merge pull request #134 from EETagent/admin_page_improvements

(frontend) admin page improvements
This commit is contained in:
Vojtěch Jungmann 2023-01-12 13:49:22 +01:00 committed by GitHub
commit ac4aa9643f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 46 deletions

View file

@ -8,6 +8,22 @@ import type {
import axios from 'axios';
import { API_URL, errorHandler, type Fetch } from '.';
export const apiWhoami = async (fetchSsr?: Fetch): Promise<CreateCandidate> => {
const apiFetch = fetchSsr || fetch;
try {
const res = await apiFetch(API_URL + '/admin/whoami', {
method: 'GET',
credentials: 'include'
});
if (res.status != 200) {
throw Error(await res.text());
}
return await res.json();
} catch (e) {
throw errorHandler(e, 'Failed to fetch whoami');
}
};
// Login as admin /admin/login
export const apiLogin = async (data: AdminLogin): Promise<number> => {
try {

View file

@ -33,7 +33,7 @@
}
.modal {
@apply absolute;
@apply fixed;
@apply p-4;
@apply rounded-xl;
@apply transform:

View file

@ -22,7 +22,7 @@
<tbody>
{#each candidates as candidate}
<tr class="border-b bg-white hover:cursor-pointer">
<td class="text-gray-900"
<td class="text-gray-900 hover:text-sspsBlue hover:font-bold"
><a
target="_blank"
rel="noreferrer"

View file

@ -6,6 +6,7 @@
import Document from '../icons/Document.svelte';
import Download from '../icons/Download.svelte';
import { sticky } from 'tippy.js';
import Logout from '../icons/Logout.svelte';
export let showDetails: boolean;
@ -50,19 +51,7 @@
delay: 0
}}
>
<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"
/></svg
>
<Logout />
</span>
{:else}
<span

View file

@ -0,0 +1,13 @@
<svg
class="h-10 w-10"
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="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/></svg
>

After

Width:  |  Height:  |  Size: 302 B

View file

@ -1,10 +1,19 @@
import type { LayoutServerLoad } from './$types';
import { redirect } from '@sveltejs/kit';
import { apiWhoami } from '$lib/@api/admin';
export const load: LayoutServerLoad = ({ cookies }) => {
export const load: LayoutServerLoad = async ({ cookies, fetch }) => {
const isAuthenticated = cookies.get('id');
if (!isAuthenticated) {
throw redirect(302, '/admin/auth/login');
if (isAuthenticated) {
const whoami = await apiWhoami(fetch).catch(() => {
throw redirect(302, '/admin/auth/logout');
});
return {
whoami: whoami
};
} else {
throw redirect(302, '/admin/auth/logout');
}
};

View file

@ -9,44 +9,52 @@
import Table from '$lib/components/admin/table/Table.svelte';
import bacgkround from '$lib/assets/background.jpg';
import Logout from '$lib/components/icons/Logout.svelte';
import { goto } from '$app/navigation';
export let data: PageServerData;
let candidates: Array<CandidatePreview> = data.preview;
const getCandidates = async (field?: string) => {
const getCandidates = async () => {
try {
candidates = await apiListCandidates(
undefined,
field ?? activeFilter !== 'Vše' ? activeFilter : ''
);
candidates = await apiListCandidates(undefined, activeFilter.filter);
} catch {
console.log('error');
}
};
type Filter = 'Vše' | 'KBB' | 'IT' | 'GYM';
type Class = 'Vše' | 'KBB' | 'IT' | 'GYM';
let filters: Array<Filter> = ['Vše', 'KBB', 'IT', 'GYM'];
type Filter = {
class: Class;
filter: string | undefined;
};
let filters: Array<Filter> = [
{
class: 'Vše',
filter: undefined
},
{
class: 'KBB',
filter: 'KB'
},
{
class: 'IT',
filter: 'IT'
},
{
class: 'GYM',
filter: 'G'
}
];
let activeFilter: Filter = filters[0];
const changeFilter = (filter: Filter) => {
const changeFilter = async (filter: Filter) => {
activeFilter = filter;
switch (activeFilter) {
case 'Vše':
getCandidates();
break;
case 'KBB':
getCandidates('KB');
break;
case 'IT':
getCandidates('IT');
break;
case 'GYM':
getCandidates('G');
break;
}
await getCandidates();
};
let scrollTop = 0;
@ -88,6 +96,10 @@
console.log(e);
}
};
const logout = async () => {
goto('/admin/auth/logout');
};
</script>
{#if createCandidateModal}
@ -98,7 +110,7 @@
{/if}
<div>
<header class="h-14 w-full">
<header class="absolute h-14 w-full">
<img class="h-12 w-full object-cover blur-sm filter" src={bacgkround} alt="Background" />
</header>
<div class="flex flex-row">
@ -106,12 +118,17 @@
{#each filters as filter}
<div class:selected={filter === activeFilter}>
<Home />
<button on:click={() => changeFilter(filter)}>{filter}</button>
<button on:click={() => changeFilter(filter)}>{filter.class}</button>
</div>
{/each}
</div>
<div class="body relative overflow-scroll">
<h1 class="text-3xl font-semibold">Uchazeči</h1>
<div class="flex items-center">
<h1 class="text-3xl font-semibold">Uchazeči</h1>
<button class="ml-2" on:click={logout}>
<Logout />
</button>
</div>
<div class="controls my-8">
<TextField on:keyup={search} bind:value={searchValue} placeholder="Hledat" />
<button
@ -157,14 +174,14 @@
@apply flex items-center;
@apply rounded-xl;
@apply transition-all duration-300;
@apply transition-all duration-200;
@apply hover:bg-sspsBlue focus:bg-sspsBlue;
@apply hover:text-white focus:text-white;
}
.list div :global(path) {
@apply transition-all duration-300;
@apply transition-all duration-100;
}
.list div:hover :global(path) {
@ -194,7 +211,7 @@
.body {
@apply h-full w-full;
@apply float-left overflow-hidden;
@apply my-6 mx-12 ml-[27rem];
@apply my-6 mx-12 mt-16 ml-[27rem];
}
.body .controls {