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
|
'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 {
|
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);
|
background-color: var(--bg-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
@ -429,3 +438,7 @@ h2 {
|
|||||||
/*width: 100%;*/
|
/*width: 100%;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*.drop_zone {*/
|
||||||
|
/* border: 1px dashed var(--dull-primary-color);*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import {
|
|||||||
type Application
|
type Application
|
||||||
} from '$lib/types';
|
} from '$lib/types';
|
||||||
import { sendEmployerNotificationEmail } from '$lib/emailer.server';
|
import { sendEmployerNotificationEmail } from '$lib/emailer.server';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export async function createUser(user: User): Promise<number> {
|
export async function createUser(user: User): Promise<number> {
|
||||||
const password_hash: string = await bcrypt.hash(user.password!, 12);
|
const password_hash: string = await bcrypt.hash(user.password!, 12);
|
||||||
@ -340,6 +342,7 @@ export async function getCompanyFullData(
|
|||||||
),
|
),
|
||||||
user_data AS (
|
user_data AS (
|
||||||
SELECT
|
SELECT
|
||||||
|
id,
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
phone,
|
phone,
|
||||||
@ -491,7 +494,7 @@ export async function getCompanyEmployers(
|
|||||||
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
|
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
|
||||||
company_id as "companyId"
|
company_id as "companyId"
|
||||||
FROM users
|
FROM users
|
||||||
WHERE "company_code" = (SELECT company_code FROM companies WHERE id = ${id}))
|
WHERE "company_id" = ${id})
|
||||||
SELECT
|
SELECT
|
||||||
(
|
(
|
||||||
SELECT row_to_json(company_data)
|
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> {
|
export async function removeEmployerFromCompany(companyId: number, userId: number): Promise<void> {
|
||||||
await sql`
|
await sql`
|
||||||
UPDATE users
|
UPDATE users
|
||||||
@ -748,7 +846,10 @@ export async function getApplications(postingId: number): Promise<Application[]>
|
|||||||
username: application.username,
|
username: application.username,
|
||||||
email: application.email,
|
email: application.email,
|
||||||
phone: application.phone,
|
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.userId;
|
||||||
delete application.username;
|
delete application.username;
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export interface User {
|
|||||||
company: Company | null;
|
company: Company | null;
|
||||||
companyCode: string | null;
|
companyCode: string | null;
|
||||||
companyId: number | null | undefined;
|
companyId: number | null | undefined;
|
||||||
|
resume: boolean | null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Company {
|
export interface Company {
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
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">
|
<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
|
We provide an accessible way for students to find internships and co-op opportunities, and for
|
||||||
employers to find students to fill their positions.
|
employers to find students to fill their positions.
|
||||||
</h2>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { deleteApplicationWithUser, getUserWithCompanyAndApplications } from '$lib/db/index.server';
|
import { deleteApplicationWithUser, getUserWithCompanyAndApplications } from '$lib/db/index.server';
|
||||||
import { getUserId } from '$lib/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 fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ cookies }) => {
|
export const load: PageServerLoad = async ({ cookies }) => {
|
||||||
const id = getUserId(cookies);
|
const id = getUserId(cookies);
|
||||||
const userData = await getUserWithCompanyAndApplications(id);
|
const userData = await getUserWithCompanyAndApplications(id);
|
||||||
const resumeExists = fs.existsSync(
|
const resumeExists = fs.existsSync(
|
||||||
path.join(process.cwd(), 'static', 'uploads', 'resume', `${id}.pdf`)
|
path.join(process.cwd(), 'static', 'uploads', 'resumes', `${id}.pdf`)
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -19,12 +20,26 @@ export const load: PageServerLoad = async ({ cookies }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
delete: async ({ url, cookies }) => {
|
deleteApplication: async ({ url, cookies }) => {
|
||||||
const id = parseInt(url.searchParams.get('id')!);
|
const id = parseInt(url.searchParams.get('id')!);
|
||||||
try {
|
try {
|
||||||
await deleteApplicationWithUser(id, getUserId(cookies));
|
await deleteApplicationWithUser(id, getUserId(cookies));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return fail(500, { errorMessage: `Internal Server Error: ${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';
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
let applicationToDelete: number = $state(0);
|
let applicationToDelete: number = $state(0);
|
||||||
|
let { data, form }: PageProps = $props();
|
||||||
|
let resumeState = $state(data.resumeExists);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!document.cookie.includes('jwt=')) {
|
if (!document.cookie.includes('jwt=')) {
|
||||||
@ -42,8 +44,78 @@
|
|||||||
document.getElementById('deleteConfirmModal')!.style.display = 'none';
|
document.getElementById('deleteConfirmModal')!.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
let resumeUpload;
|
function openUpload() {
|
||||||
let { data, form }: PageProps = $props();
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="base-container">
|
<div class="base-container">
|
||||||
@ -142,19 +214,18 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div>
|
<div>
|
||||||
<div class="font-semibold">Résumé:</div>
|
<div class="font-semibold">Résumé:</div>
|
||||||
{#if data.resumeExists}
|
{#if resumeState}
|
||||||
<a class="pb-2" href="/uploads/resumes/{data.user.id}.pdf">
|
<a class="pb-2" href="/uploads/resumes/{data.user.id}.pdf">
|
||||||
<button class="dull-primary-bg-color rounded-md px-2.5 py-1"
|
<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
|
><span class="material-symbols-outlined align-middle">open_in_new</span> View résumé</button
|
||||||
>
|
>
|
||||||
</a>
|
</a>
|
||||||
<input type="file" accept=".pdf" />
|
<button class="mb-2 ml-1 rounded-md border px-2.5 py-1" onclick={openUpload}
|
||||||
<button class="ml-2 rounded-md border px-2.5 py-1"
|
|
||||||
><span class="material-symbols-outlined align-middle">upload</span> Upload new version</button
|
><span class="material-symbols-outlined align-middle">upload</span> Upload new version</button
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- <div class="">No résumé submitted.</div>-->
|
<div class="">No résumé submitted.</div>
|
||||||
<button class="dull-primary-bg-color mb-2 rounded-md px-2.5 py-1"
|
<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
|
><span class="material-symbols-outlined align-middle">upload</span> Upload</button
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
@ -203,7 +274,7 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="mb-2 inline-flex w-full justify-between">
|
<div class="mb-2 inline-flex w-full justify-between">
|
||||||
<h2 class="font-semibold">Are you sure?</h2>
|
<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>
|
</div>
|
||||||
<p>This will permanently delete this application. This action cannot be undone.</p>
|
<p>This will permanently delete this application. This action cannot be undone.</p>
|
||||||
|
|
||||||
@ -211,7 +282,7 @@
|
|||||||
<button
|
<button
|
||||||
class="danger-bg-color rounded px-2 py-1"
|
class="danger-bg-color rounded px-2 py-1"
|
||||||
type="submit"
|
type="submit"
|
||||||
formaction="?/delete&id={applicationToDelete}">Delete application</button
|
formaction="?/deleteApplication&id={applicationToDelete}">Delete application</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="separator-borders bg-color rounded px-2 py-1"
|
class="separator-borders bg-color rounded px-2 py-1"
|
||||||
@ -221,3 +292,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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="modal-content">
|
||||||
<div class="mb-2 inline-flex w-full justify-between">
|
<div class="mb-2 inline-flex w-full justify-between">
|
||||||
<h2 class="font-semibold">Are you sure?</h2>
|
<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>
|
</div>
|
||||||
<p>This will permanently delete your account. This action cannot be undone.</p>
|
<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>
|
<p>Please type "I understand" into the box below to confirm</p>
|
||||||
|
|||||||
@ -393,7 +393,9 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="mb-2 inline-flex w-full justify-between">
|
<div class="mb-2 inline-flex w-full justify-between">
|
||||||
<h2 class="font-semibold">Are you sure?</h2>
|
<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>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
This will permanently delete user <span class="font-semibold">{data.user?.username}.</span
|
This will permanently delete user <span class="font-semibold">{data.user?.username}.</span
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
<img
|
<img
|
||||||
class="mb-2 inline-block h-32 rounded-lg"
|
class="mb-2 inline-block h-32 rounded-lg"
|
||||||
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
|
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
|
||||||
alt="User avatar"
|
alt="Company Logo"
|
||||||
onerror={logoFallback}
|
onerror={logoFallback}
|
||||||
height="128"
|
height="128"
|
||||||
width="128"
|
width="128"
|
||||||
@ -77,7 +77,7 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<img
|
<img
|
||||||
class="mb-2 inline-block h-min rounded"
|
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"
|
alt="User avatar"
|
||||||
onerror={(e) => avatarFallback(e, user)}
|
onerror={(e) => avatarFallback(e, user)}
|
||||||
height="32"
|
height="32"
|
||||||
@ -85,7 +85,7 @@
|
|||||||
/>
|
/>
|
||||||
<div class="pl-2">
|
<div class="pl-2">
|
||||||
<div class="pb-1 font-semibold">
|
<div class="pb-1 font-semibold">
|
||||||
{user.username}{user.fullName ? ` (${user.fullName})` : ''}
|
{user.fullName}{`(${user.username})`}
|
||||||
</div>
|
</div>
|
||||||
{#if user.email}
|
{#if user.email}
|
||||||
<div class="pb-1">
|
<div class="pb-1">
|
||||||
|
|||||||
@ -93,7 +93,9 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="mb-2 inline-flex w-full justify-between">
|
<div class="mb-2 inline-flex w-full justify-between">
|
||||||
<h2 class="font-semibold">Are you sure?</h2>
|
<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>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
This will permanently delete company <span class="font-semibold"
|
This will permanently delete company <span class="font-semibold"
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
editCompany,
|
editCompany,
|
||||||
getCompany,
|
getCompany,
|
||||||
getCompanyEmployers,
|
getCompanyEmployers,
|
||||||
|
getCompanyEmployersAndRequests,
|
||||||
removeEmployerFromCompany
|
removeEmployerFromCompany
|
||||||
} from '$lib/db/index.server';
|
} from '$lib/db/index.server';
|
||||||
import { PERMISSIONS } from '$lib/consts';
|
import { PERMISSIONS } from '$lib/consts';
|
||||||
@ -15,7 +16,7 @@ export const load: PageServerLoad = async ({ cookies, params }) => {
|
|||||||
const id = parseInt(params.company);
|
const id = parseInt(params.company);
|
||||||
const perms = getUserPerms(cookies);
|
const perms = getUserPerms(cookies);
|
||||||
if (perms >= 0 && (perms & PERMISSIONS.MANAGE_COMPANIES) > 0) {
|
if (perms >= 0 && (perms & PERMISSIONS.MANAGE_COMPANIES) > 0) {
|
||||||
return await getCompanyEmployers(id);
|
return await getCompanyEmployersAndRequests(id);
|
||||||
}
|
}
|
||||||
error(403, 'Unauthorized');
|
error(403, 'Unauthorized');
|
||||||
};
|
};
|
||||||
|
|||||||
@ -50,8 +50,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#if data.users !== undefined}
|
{#if data.employers !== undefined}
|
||||||
{#each data.users as user}
|
{#each data.employers as user}
|
||||||
{#if user.company?.id === data.id}
|
{#if user.company?.id === data.id}
|
||||||
<tr class="h-8">
|
<tr class="h-8">
|
||||||
<td class="left">{user.id}</td>
|
<td class="left">{user.id}</td>
|
||||||
@ -112,9 +112,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
{#if data.users && data.users.some((user) => {
|
{#if data.requests !== null}
|
||||||
return user.company?.id !== data.id;
|
|
||||||
})}
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="elevated separator-borders m-4 rounded">
|
<div class="elevated separator-borders m-4 rounded">
|
||||||
<div class="bottom-border flex place-content-between">
|
<div class="bottom-border flex place-content-between">
|
||||||
@ -134,8 +132,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#if data.users !== undefined}
|
{#each data.requests as user}
|
||||||
{#each data.users as user}
|
|
||||||
{#if user.company?.id !== data.id}
|
{#if user.company?.id !== data.id}
|
||||||
<tr class="h-8">
|
<tr class="h-8">
|
||||||
<td class="left">{user.id}</td>
|
<td class="left">{user.id}</td>
|
||||||
@ -165,7 +162,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -85,6 +85,15 @@
|
|||||||
Applied: {application.createdAt.toLocaleDateString('en-US', dateFormatOptions)}
|
Applied: {application.createdAt.toLocaleDateString('en-US', dateFormatOptions)}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/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>
|
||||||
<div class="left-border inline-block pl-4">
|
<div class="left-border inline-block pl-4">
|
||||||
<h2>Candidate Statement</h2>
|
<h2>Candidate Statement</h2>
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="signin-container place-items-center pt-8">
|
<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>
|
<h1 class="text-weight-semibold mb-4 text-center">Register</h1>
|
||||||
<p>Create your account. Its free and only takes a minute!</p>
|
<p>Create your account. Its free and only takes a minute!</p>
|
||||||
<form method="POST" class="arrange-vertically" use:enhance>
|
<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