mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-03 01:50:52 +00:00
Merge pull request #134 from EETagent/admin_page_improvements
(frontend) admin page improvements
This commit is contained in:
commit
ac4aa9643f
7 changed files with 90 additions and 46 deletions
|
|
@ -8,6 +8,22 @@ import type {
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { API_URL, errorHandler, type Fetch } from '.';
|
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
|
// Login as admin /admin/login
|
||||||
export const apiLogin = async (data: AdminLogin): Promise<number> => {
|
export const apiLogin = async (data: AdminLogin): Promise<number> => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
@apply absolute;
|
@apply fixed;
|
||||||
@apply p-4;
|
@apply p-4;
|
||||||
@apply rounded-xl;
|
@apply rounded-xl;
|
||||||
@apply transform:
|
@apply transform:
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each candidates as candidate}
|
{#each candidates as candidate}
|
||||||
<tr class="border-b bg-white hover:cursor-pointer">
|
<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
|
><a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import Document from '../icons/Document.svelte';
|
import Document from '../icons/Document.svelte';
|
||||||
import Download from '../icons/Download.svelte';
|
import Download from '../icons/Download.svelte';
|
||||||
import { sticky } from 'tippy.js';
|
import { sticky } from 'tippy.js';
|
||||||
|
import Logout from '../icons/Logout.svelte';
|
||||||
|
|
||||||
export let showDetails: boolean;
|
export let showDetails: boolean;
|
||||||
|
|
||||||
|
|
@ -50,19 +51,7 @@
|
||||||
delay: 0
|
delay: 0
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<Logout />
|
||||||
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
|
|
||||||
>
|
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span
|
<span
|
||||||
|
|
|
||||||
13
frontend/src/lib/components/icons/Logout.svelte
Normal file
13
frontend/src/lib/components/icons/Logout.svelte
Normal 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 |
|
|
@ -1,10 +1,19 @@
|
||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
|
|
||||||
import { redirect } from '@sveltejs/kit';
|
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');
|
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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,44 +9,52 @@
|
||||||
import Table from '$lib/components/admin/table/Table.svelte';
|
import Table from '$lib/components/admin/table/Table.svelte';
|
||||||
|
|
||||||
import bacgkround from '$lib/assets/background.jpg';
|
import bacgkround from '$lib/assets/background.jpg';
|
||||||
|
import Logout from '$lib/components/icons/Logout.svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
export let data: PageServerData;
|
export let data: PageServerData;
|
||||||
|
|
||||||
let candidates: Array<CandidatePreview> = data.preview;
|
let candidates: Array<CandidatePreview> = data.preview;
|
||||||
|
|
||||||
const getCandidates = async (field?: string) => {
|
const getCandidates = async () => {
|
||||||
try {
|
try {
|
||||||
candidates = await apiListCandidates(
|
candidates = await apiListCandidates(undefined, activeFilter.filter);
|
||||||
undefined,
|
|
||||||
field ?? activeFilter !== 'Vše' ? activeFilter : ''
|
|
||||||
);
|
|
||||||
} catch {
|
} catch {
|
||||||
console.log('error');
|
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];
|
let activeFilter: Filter = filters[0];
|
||||||
|
|
||||||
const changeFilter = (filter: Filter) => {
|
const changeFilter = async (filter: Filter) => {
|
||||||
activeFilter = filter;
|
activeFilter = filter;
|
||||||
switch (activeFilter) {
|
await getCandidates();
|
||||||
case 'Vše':
|
|
||||||
getCandidates();
|
|
||||||
break;
|
|
||||||
case 'KBB':
|
|
||||||
getCandidates('KB');
|
|
||||||
break;
|
|
||||||
case 'IT':
|
|
||||||
getCandidates('IT');
|
|
||||||
break;
|
|
||||||
case 'GYM':
|
|
||||||
getCandidates('G');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let scrollTop = 0;
|
let scrollTop = 0;
|
||||||
|
|
@ -88,6 +96,10 @@
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logout = async () => {
|
||||||
|
goto('/admin/auth/logout');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if createCandidateModal}
|
{#if createCandidateModal}
|
||||||
|
|
@ -98,7 +110,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div>
|
<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" />
|
<img class="h-12 w-full object-cover blur-sm filter" src={bacgkround} alt="Background" />
|
||||||
</header>
|
</header>
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
|
|
@ -106,12 +118,17 @@
|
||||||
{#each filters as filter}
|
{#each filters as filter}
|
||||||
<div class:selected={filter === activeFilter}>
|
<div class:selected={filter === activeFilter}>
|
||||||
<Home />
|
<Home />
|
||||||
<button on:click={() => changeFilter(filter)}>{filter}</button>
|
<button on:click={() => changeFilter(filter)}>{filter.class}</button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="body relative overflow-scroll">
|
<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">
|
<div class="controls my-8">
|
||||||
<TextField on:keyup={search} bind:value={searchValue} placeholder="Hledat" />
|
<TextField on:keyup={search} bind:value={searchValue} placeholder="Hledat" />
|
||||||
<button
|
<button
|
||||||
|
|
@ -157,14 +174,14 @@
|
||||||
@apply flex items-center;
|
@apply flex items-center;
|
||||||
@apply rounded-xl;
|
@apply rounded-xl;
|
||||||
|
|
||||||
@apply transition-all duration-300;
|
@apply transition-all duration-200;
|
||||||
|
|
||||||
@apply hover:bg-sspsBlue focus:bg-sspsBlue;
|
@apply hover:bg-sspsBlue focus:bg-sspsBlue;
|
||||||
@apply hover:text-white focus:text-white;
|
@apply hover:text-white focus:text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list div :global(path) {
|
.list div :global(path) {
|
||||||
@apply transition-all duration-300;
|
@apply transition-all duration-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list div:hover :global(path) {
|
.list div:hover :global(path) {
|
||||||
|
|
@ -194,7 +211,7 @@
|
||||||
.body {
|
.body {
|
||||||
@apply h-full w-full;
|
@apply h-full w-full;
|
||||||
@apply float-left overflow-hidden;
|
@apply float-left overflow-hidden;
|
||||||
@apply my-6 mx-12 ml-[27rem];
|
@apply my-6 mx-12 mt-16 ml-[27rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
.body .controls {
|
.body .controls {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue