Resume Logic
This commit is contained in:
parent
fc97a4271c
commit
77f3d182d4
13
src/app.css
13
src/app.css
@ -99,6 +99,15 @@ h1 {
|
||||
'opsz' 20
|
||||
}
|
||||
|
||||
.icon-48 {
|
||||
font-size: 48px !important;
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 0,
|
||||
'opsz' 20
|
||||
}
|
||||
|
||||
input[type='search'], input[type='text'], input[type='password'], input[type='email'], input[type='tel'], input[type='number'], textarea, select {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
@ -429,3 +438,7 @@ h2 {
|
||||
/*width: 100%;*/
|
||||
}
|
||||
|
||||
/*.drop_zone {*/
|
||||
/* border: 1px dashed var(--dull-primary-color);*/
|
||||
/*}*/
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ import {
|
||||
type Application
|
||||
} from '$lib/types';
|
||||
import { sendEmployerNotificationEmail } from '$lib/emailer.server';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export async function createUser(user: User): Promise<number> {
|
||||
const password_hash: string = await bcrypt.hash(user.password!, 12);
|
||||
@ -340,6 +342,7 @@ export async function getCompanyFullData(
|
||||
),
|
||||
user_data AS (
|
||||
SELECT
|
||||
id,
|
||||
username,
|
||||
email,
|
||||
phone,
|
||||
@ -491,7 +494,7 @@ export async function getCompanyEmployers(
|
||||
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
|
||||
company_id as "companyId"
|
||||
FROM users
|
||||
WHERE "company_code" = (SELECT company_code FROM companies WHERE id = ${id}))
|
||||
WHERE "company_id" = ${id})
|
||||
SELECT
|
||||
(
|
||||
SELECT row_to_json(company_data)
|
||||
@ -530,6 +533,101 @@ export async function getCompanyEmployers(
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCompanyEmployersAndRequests(
|
||||
id: number
|
||||
): Promise<{ company: Company; employers: User[]; requests: User[] }> {
|
||||
const data = await sql`
|
||||
WITH company_data AS (
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
website,
|
||||
created_at AT TIME ZONE 'UTC' AS "createdAt",
|
||||
company_code AS "companyCode"
|
||||
FROM companies
|
||||
WHERE id = ${id}
|
||||
),
|
||||
employer_data AS (SELECT id,
|
||||
username,
|
||||
email,
|
||||
phone,
|
||||
full_name AS "fullName",
|
||||
created_at AT TIME ZONE 'UTC' AS "createdAt",
|
||||
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
|
||||
company_id as "companyId"
|
||||
FROM users
|
||||
WHERE "company_id" = ${id}),
|
||||
request_data AS (SELECT id,
|
||||
username,
|
||||
email,
|
||||
phone,
|
||||
full_name AS "fullName",
|
||||
created_at AT TIME ZONE 'UTC' AS "createdAt",
|
||||
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
|
||||
company_id as "companyId"
|
||||
FROM users
|
||||
WHERE "company_code" = (SELECT company_code FROM companies WHERE id = ${id}))
|
||||
SELECT
|
||||
(
|
||||
SELECT row_to_json(company_data)
|
||||
FROM company_data
|
||||
) AS company,
|
||||
(
|
||||
SELECT json_agg(row_to_json(employer_data))
|
||||
FROM employer_data
|
||||
) AS employers,
|
||||
(
|
||||
SELECT json_agg(row_to_json(request_data))
|
||||
FROM request_data
|
||||
) AS requests;
|
||||
`;
|
||||
|
||||
if (!data) {
|
||||
error(404, 'Company not found');
|
||||
}
|
||||
if (data[0].employers) {
|
||||
data[0].employers.forEach(
|
||||
(user: {
|
||||
company: { id: any };
|
||||
companyId: any;
|
||||
createdAt: string | number | Date;
|
||||
lastSignIn: string | number | Date;
|
||||
}) => {
|
||||
user.company = {
|
||||
id: user.companyId
|
||||
};
|
||||
user.createdAt = new Date(user.createdAt);
|
||||
user.lastSignIn = new Date(user.lastSignIn);
|
||||
delete user.companyId;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (data[0].requests) {
|
||||
data[0].requests.forEach(
|
||||
(user: {
|
||||
company: { id: any };
|
||||
companyId: any;
|
||||
createdAt: string | number | Date;
|
||||
lastSignIn: string | number | Date;
|
||||
}) => {
|
||||
user.company = {
|
||||
id: user.companyId
|
||||
};
|
||||
user.createdAt = new Date(user.createdAt);
|
||||
user.lastSignIn = new Date(user.lastSignIn);
|
||||
delete user.companyId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
company: <Company>data[0].company,
|
||||
employers: <User[]>data[0].employers,
|
||||
requests: <User[]>data[0].requests
|
||||
};
|
||||
}
|
||||
|
||||
export async function removeEmployerFromCompany(companyId: number, userId: number): Promise<void> {
|
||||
await sql`
|
||||
UPDATE users
|
||||
@ -748,7 +846,10 @@ export async function getApplications(postingId: number): Promise<Application[]>
|
||||
username: application.username,
|
||||
email: application.email,
|
||||
phone: application.phone,
|
||||
fullName: application.fullName
|
||||
fullName: application.fullName,
|
||||
resume: fs.existsSync(
|
||||
path.join(process.cwd(), 'static', 'uploads', 'resumes', `${application.userId}.pdf`)
|
||||
)
|
||||
};
|
||||
delete application.userId;
|
||||
delete application.username;
|
||||
|
||||
@ -12,6 +12,7 @@ export interface User {
|
||||
company: Company | null;
|
||||
companyCode: string | null;
|
||||
companyId: number | null | undefined;
|
||||
resume: boolean | null | undefined;
|
||||
}
|
||||
|
||||
export interface Company {
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..40,400,0,0&display=block&icon_names=account_circle,arrow_drop_down,arrow_drop_up,calendar_today,call,check,close,dark_mode,delete,description,edit,group,info,light_mode,login,mail,open_in_new,person,search,sell,store,upload,visibility,visibility_off,work"
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..40,400,0,0&display=block&icon_names=account_circle,arrow_drop_down,arrow_drop_up,calendar_today,call,check,close,cloud_upload,dark_mode,delete,description,edit,group,info,light_mode,login,mail,open_in_new,person,search,sell,store,upload,visibility,visibility_off,work"
|
||||
/>
|
||||
|
||||
<div class="flex min-h-screen flex-col">
|
||||
|
||||
@ -18,5 +18,16 @@
|
||||
We provide an accessible way for students to find internships and co-op opportunities, and for
|
||||
employers to find students to fill their positions.
|
||||
</h2>
|
||||
<p class="mt-16">
|
||||
Create an account or sign in <a class="hyperlink-color hyperlink-underline" href="/signin"
|
||||
>here</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
Or head over to our postings page <a
|
||||
class="hyperlink-color hyperlink-underline"
|
||||
href="/postings">here</a
|
||||
> to view all job opportunities.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { deleteApplicationWithUser, getUserWithCompanyAndApplications } from '$lib/db/index.server';
|
||||
import { getUserId } from '$lib/index.server';
|
||||
import { type Actions, fail } from '@sveltejs/kit';
|
||||
import { type Actions, fail, json, redirect } from '@sveltejs/kit';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
const id = getUserId(cookies);
|
||||
const userData = await getUserWithCompanyAndApplications(id);
|
||||
const resumeExists = fs.existsSync(
|
||||
path.join(process.cwd(), 'static', 'uploads', 'resume', `${id}.pdf`)
|
||||
path.join(process.cwd(), 'static', 'uploads', 'resumes', `${id}.pdf`)
|
||||
);
|
||||
|
||||
return {
|
||||
@ -19,12 +20,26 @@ export const load: PageServerLoad = async ({ cookies }) => {
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
delete: async ({ url, cookies }) => {
|
||||
deleteApplication: async ({ url, cookies }) => {
|
||||
const id = parseInt(url.searchParams.get('id')!);
|
||||
try {
|
||||
await deleteApplicationWithUser(id, getUserId(cookies));
|
||||
} catch (err) {
|
||||
return fail(500, { errorMessage: `Internal Server Error: ${err}` });
|
||||
}
|
||||
},
|
||||
uploadResume: async ({ request, cookies }) => {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get('resume') as File;
|
||||
|
||||
if (!file) {
|
||||
fail(400, { message: 'invalid' });
|
||||
}
|
||||
writeFileSync(
|
||||
`static/uploads/resumes/${getUserId(cookies)}.pdf`,
|
||||
Buffer.from(await file.arrayBuffer())
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
let applicationToDelete: number = $state(0);
|
||||
let { data, form }: PageProps = $props();
|
||||
let resumeState = $state(data.resumeExists);
|
||||
|
||||
onMount(() => {
|
||||
if (!document.cookie.includes('jwt=')) {
|
||||
@ -42,8 +44,78 @@
|
||||
document.getElementById('deleteConfirmModal')!.style.display = 'none';
|
||||
}
|
||||
|
||||
let resumeUpload;
|
||||
let { data, form }: PageProps = $props();
|
||||
function openUpload() {
|
||||
document.getElementById('uploadModal')!.style.display = 'block';
|
||||
}
|
||||
|
||||
function closeUpload() {
|
||||
document.getElementById('uploadModal')!.style.display = 'none';
|
||||
}
|
||||
|
||||
let files: FileList | undefined | null = $state();
|
||||
let file: File | undefined | null = $state();
|
||||
|
||||
$effect(() => {
|
||||
if (files) {
|
||||
if (files[files.length - 1]?.type == 'application/pdf') {
|
||||
file = files[files.length - 1];
|
||||
console.log(file.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function dropHandler(event: DragEvent) {
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
event.preventDefault();
|
||||
|
||||
if (event.dataTransfer?.items) {
|
||||
const items = event.dataTransfer.items;
|
||||
// Use DataTransferItemList interface to access the file(s)
|
||||
// If dropped items aren't files, reject them
|
||||
if (items[0].kind === 'file') {
|
||||
const upload = items[0].getAsFile();
|
||||
if (upload?.type !== 'application/pdf') {
|
||||
console.error('Invalid file type');
|
||||
return;
|
||||
}
|
||||
file = upload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dragOverHandler(event: DragEvent) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
async function handleSubmit(event: SubmitEvent) {
|
||||
event.preventDefault(); // Prevent default form submission
|
||||
|
||||
if (!file) {
|
||||
alert('Please select a file first');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('resume', file); // Manually append the file
|
||||
|
||||
try {
|
||||
const response = await fetch('?/uploadResume', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
console.log('File uploaded successfully:', result);
|
||||
closeUpload();
|
||||
resumeState = true;
|
||||
} else {
|
||||
console.error('Upload failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error uploading file:', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="base-container">
|
||||
@ -142,19 +214,18 @@
|
||||
{/if}
|
||||
<div>
|
||||
<div class="font-semibold">Résumé:</div>
|
||||
{#if data.resumeExists}
|
||||
{#if resumeState}
|
||||
<a class="pb-2" href="/uploads/resumes/{data.user.id}.pdf">
|
||||
<button class="dull-primary-bg-color rounded-md px-2.5 py-1"
|
||||
><span class="material-symbols-outlined align-middle">open_in_new</span> View résumé</button
|
||||
>
|
||||
</a>
|
||||
<input type="file" accept=".pdf" />
|
||||
<button class="ml-2 rounded-md border px-2.5 py-1"
|
||||
<button class="mb-2 ml-1 rounded-md border px-2.5 py-1" onclick={openUpload}
|
||||
><span class="material-symbols-outlined align-middle">upload</span> Upload new version</button
|
||||
>
|
||||
{:else}
|
||||
<!-- <div class="">No résumé submitted.</div>-->
|
||||
<button class="dull-primary-bg-color mb-2 rounded-md px-2.5 py-1"
|
||||
<div class="">No résumé submitted.</div>
|
||||
<button class="dull-primary-bg-color mb-1 rounded-md px-2.5 py-1" onclick={openUpload}
|
||||
><span class="material-symbols-outlined align-middle">upload</span> Upload</button
|
||||
>
|
||||
{/if}
|
||||
@ -203,7 +274,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="mb-2 inline-flex w-full justify-between">
|
||||
<h2 class="font-semibold">Are you sure?</h2>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm}>close</button>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm} type="button">close</button>
|
||||
</div>
|
||||
<p>This will permanently delete this application. This action cannot be undone.</p>
|
||||
|
||||
@ -211,7 +282,7 @@
|
||||
<button
|
||||
class="danger-bg-color rounded px-2 py-1"
|
||||
type="submit"
|
||||
formaction="?/delete&id={applicationToDelete}">Delete application</button
|
||||
formaction="?/deleteApplication&id={applicationToDelete}">Delete application</button
|
||||
>
|
||||
<button
|
||||
class="separator-borders bg-color rounded px-2 py-1"
|
||||
@ -221,3 +292,42 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form id="uploadModal" method="POST" class="modal" onsubmit={handleSubmit}>
|
||||
<div class="modal-content">
|
||||
<div class="mb-2 inline-flex w-full justify-between">
|
||||
<h2 class="font-semibold">Résumé Upload</h2>
|
||||
<button class="material-symbols-outlined" onclick={closeUpload} type="button">close</button>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
role="region"
|
||||
class="dull-primary-border-color rounded-lg border-2 border-dashed"
|
||||
ondrop={dropHandler}
|
||||
ondragover={dragOverHandler}
|
||||
>
|
||||
<label for="resume" class="cursor-pointer p-4">
|
||||
<div class="text-center">
|
||||
<span class="material-symbols-outlined icon-48">cloud_upload</span>
|
||||
<h3>Drag & drop your résumé here</h3>
|
||||
<p class="">
|
||||
or <span class="hyperlink-color hyperlink-underline">click here to browse.</span>
|
||||
</p>
|
||||
<!-- <label class="dull-primary-bg-color cursor-pointer rounded px-2 py-1" for="resume"-->
|
||||
<!-- >Select a file</label-->
|
||||
<!-- >-->
|
||||
<input bind:files type="file" id="resume" accept=".pdf" class="hidden" />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-2">{file?.name}</div>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-between">
|
||||
<button class="dull-primary-bg-color rounded px-2 py-1" type="submit">Submit</button>
|
||||
<button
|
||||
class="separator-borders bg-color rounded px-2 py-1"
|
||||
type="button"
|
||||
onclick={closeUpload}>Cancel</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -174,7 +174,9 @@
|
||||
<div class="modal-content">
|
||||
<div class="mb-2 inline-flex w-full justify-between">
|
||||
<h2 class="font-semibold">Are you sure?</h2>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm}>close</button>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm} type="button"
|
||||
>close</button
|
||||
>
|
||||
</div>
|
||||
<p>This will permanently delete your account. This action cannot be undone.</p>
|
||||
<p>Please type "I understand" into the box below to confirm</p>
|
||||
|
||||
@ -393,7 +393,9 @@
|
||||
<div class="modal-content">
|
||||
<div class="mb-2 inline-flex w-full justify-between">
|
||||
<h2 class="font-semibold">Are you sure?</h2>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm}>close</button>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm} type="button"
|
||||
>close</button
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
This will permanently delete user <span class="font-semibold">{data.user?.username}.</span
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
<img
|
||||
class="mb-2 inline-block h-32 rounded-lg"
|
||||
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
|
||||
alt="User avatar"
|
||||
alt="Company Logo"
|
||||
onerror={logoFallback}
|
||||
height="128"
|
||||
width="128"
|
||||
@ -77,7 +77,7 @@
|
||||
<div class="flex">
|
||||
<img
|
||||
class="mb-2 inline-block h-min rounded"
|
||||
src="/uploads/avatars/{data.company.id}.svg?timestamp=${Date.now()}"
|
||||
src="/uploads/avatars/{user.id}.svg?timestamp=${Date.now()}"
|
||||
alt="User avatar"
|
||||
onerror={(e) => avatarFallback(e, user)}
|
||||
height="32"
|
||||
@ -85,7 +85,7 @@
|
||||
/>
|
||||
<div class="pl-2">
|
||||
<div class="pb-1 font-semibold">
|
||||
{user.username}{user.fullName ? ` (${user.fullName})` : ''}
|
||||
{user.fullName}{`(${user.username})`}
|
||||
</div>
|
||||
{#if user.email}
|
||||
<div class="pb-1">
|
||||
|
||||
@ -93,7 +93,9 @@
|
||||
<div class="modal-content">
|
||||
<div class="mb-2 inline-flex w-full justify-between">
|
||||
<h2 class="font-semibold">Are you sure?</h2>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm}>close</button>
|
||||
<button class="material-symbols-outlined" onclick={closeConfirm} type="button"
|
||||
>close</button
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
This will permanently delete company <span class="font-semibold"
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
editCompany,
|
||||
getCompany,
|
||||
getCompanyEmployers,
|
||||
getCompanyEmployersAndRequests,
|
||||
removeEmployerFromCompany
|
||||
} from '$lib/db/index.server';
|
||||
import { PERMISSIONS } from '$lib/consts';
|
||||
@ -15,7 +16,7 @@ export const load: PageServerLoad = async ({ cookies, params }) => {
|
||||
const id = parseInt(params.company);
|
||||
const perms = getUserPerms(cookies);
|
||||
if (perms >= 0 && (perms & PERMISSIONS.MANAGE_COMPANIES) > 0) {
|
||||
return await getCompanyEmployers(id);
|
||||
return await getCompanyEmployersAndRequests(id);
|
||||
}
|
||||
error(403, 'Unauthorized');
|
||||
};
|
||||
|
||||
@ -50,8 +50,8 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if data.users !== undefined}
|
||||
{#each data.users as user}
|
||||
{#if data.employers !== undefined}
|
||||
{#each data.employers as user}
|
||||
{#if user.company?.id === data.id}
|
||||
<tr class="h-8">
|
||||
<td class="left">{user.id}</td>
|
||||
@ -112,9 +112,7 @@
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
{#if data.users && data.users.some((user) => {
|
||||
return user.company?.id !== data.id;
|
||||
})}
|
||||
{#if data.requests !== null}
|
||||
<div class="content">
|
||||
<div class="elevated separator-borders m-4 rounded">
|
||||
<div class="bottom-border flex place-content-between">
|
||||
@ -134,38 +132,36 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if data.users !== undefined}
|
||||
{#each data.users as user}
|
||||
{#if user.company?.id !== data.id}
|
||||
<tr class="h-8">
|
||||
<td class="left">{user.id}</td>
|
||||
<td>{user.username}</td>
|
||||
<td>{user.fullName || 'N/A'}</td>
|
||||
<td>{user.email || 'N/A'}</td>
|
||||
<td
|
||||
>{user.createdAt?.toLocaleDateString('en-US', dateFormatOptions) ||
|
||||
'unknown'}</td
|
||||
>
|
||||
<td
|
||||
>{user.lastSignIn?.toLocaleDateString('en-US', dateFormatOptions) ||
|
||||
'unknown'}</td
|
||||
>
|
||||
<td class="material-symbols-outlined"
|
||||
><form method="POST" class="flex">
|
||||
<button
|
||||
class="hover-bg-color m-1 rounded text-green-600"
|
||||
formaction="?/addEmployer&userId={user.id}">check</button
|
||||
>
|
||||
<button
|
||||
class="hover-bg-color danger-color m-1 rounded"
|
||||
formaction="?/removeEmployer&userId={user.id}">close</button
|
||||
>
|
||||
</form></td
|
||||
>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{#each data.requests as user}
|
||||
{#if user.company?.id !== data.id}
|
||||
<tr class="h-8">
|
||||
<td class="left">{user.id}</td>
|
||||
<td>{user.username}</td>
|
||||
<td>{user.fullName || 'N/A'}</td>
|
||||
<td>{user.email || 'N/A'}</td>
|
||||
<td
|
||||
>{user.createdAt?.toLocaleDateString('en-US', dateFormatOptions) ||
|
||||
'unknown'}</td
|
||||
>
|
||||
<td
|
||||
>{user.lastSignIn?.toLocaleDateString('en-US', dateFormatOptions) ||
|
||||
'unknown'}</td
|
||||
>
|
||||
<td class="material-symbols-outlined"
|
||||
><form method="POST" class="flex">
|
||||
<button
|
||||
class="hover-bg-color m-1 rounded text-green-600"
|
||||
formaction="?/addEmployer&userId={user.id}">check</button
|
||||
>
|
||||
<button
|
||||
class="hover-bg-color danger-color m-1 rounded"
|
||||
formaction="?/removeEmployer&userId={user.id}">close</button
|
||||
>
|
||||
</form></td
|
||||
>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -85,6 +85,15 @@
|
||||
Applied: {application.createdAt.toLocaleDateString('en-US', dateFormatOptions)}
|
||||
</p>
|
||||
{/if}
|
||||
{#if application.user?.resume}
|
||||
<a
|
||||
class="hyperlink-underline hyperlink-color"
|
||||
href="/uploads/resumes/{application.user.id}.pdf"
|
||||
target="_blank"
|
||||
>
|
||||
Résumé
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="left-border inline-block pl-4">
|
||||
<h2>Candidate Statement</h2>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</script>
|
||||
|
||||
<div class="signin-container place-items-center pt-8">
|
||||
<div class="elevated separator-borders bg content rounded-md p-8">
|
||||
<div class="elevated separator-borders bg content mb-4 rounded-md p-8">
|
||||
<h1 class="text-weight-semibold mb-4 text-center">Register</h1>
|
||||
<p>Create your account. Its free and only takes a minute!</p>
|
||||
<form method="POST" class="arrange-vertically" use:enhance>
|
||||
|
||||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Loading…
Reference in New Issue
Block a user