import cleanup
All checks were successful
ci / docker_image (push) Successful in 1m37s
ci / deploy (push) Successful in 16s

This commit is contained in:
Drake Marino 2025-03-29 18:13:02 -05:00
parent 1270fb3ae9
commit 77655c779d
45 changed files with 848 additions and 877 deletions

View File

@ -83,8 +83,7 @@ h1 {
.icon-16 {
font-size: 16px !important;
font-variation-settings:
'FILL' 0,
font-variation-settings: 'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 20
@ -92,8 +91,7 @@ h1 {
.icon-20 {
font-size: 20px !important;
font-variation-settings:
'FILL' 0,
font-variation-settings: 'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 20
@ -101,8 +99,7 @@ h1 {
.icon-48 {
font-size: 48px !important;
font-variation-settings:
'FILL' 0,
font-variation-settings: 'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 20
@ -300,8 +297,8 @@ input[type='checkbox']:focus {
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.6); /* Black w/ opacity */
background-color: rgb(0, 0, 0); /* Fallback color */
background-color: rgba(0, 0, 0, 0.6); /* Black w/ opacity */
}
.modal-always-display {
@ -312,8 +309,8 @@ input[type='checkbox']:focus {
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.6); /* Black w/ opacity */
background-color: rgb(0, 0, 0); /* Fallback color */
background-color: rgba(0, 0, 0, 0.6); /* Black w/ opacity */
}
/* Modal Content/Box */

View File

@ -1,11 +1,11 @@
<!doctype html>
<!--suppress HtmlUnknownTarget -->
<html lang="en" data-theme="">
<html data-theme="" lang="en">
<head>
<title>FBLA 25</title>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="%sveltekit.assets%/favicon.png" rel="icon" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View File

@ -18,9 +18,10 @@ export async function createUser(user: User): Promise<number> {
const password_hash: string = await bcrypt.hash(user.password!, 12);
const response = await sql`
INSERT INTO users (username, password_hash, perms, created_at, last_signin, active, email, phone, full_name, company_code)
VALUES (${user.username}, ${password_hash}, ${user.perms}, NOW(), NOW(), ${user.active}, ${user.email}, ${user.phone}, ${user.fullName}, ${user.companyCode})
RETURNING id;
INSERT INTO users (username, password_hash, perms, created_at, last_signin, active, email, phone, full_name,
company_code)
VALUES (${user.username}, ${password_hash}, ${user.perms}, NOW(), NOW(), ${user.active}, ${user.email},
${user.phone}, ${user.fullName}, ${user.companyCode}) RETURNING id;
`;
// TODO: handle custom image uploads
@ -40,15 +41,28 @@ export async function updateUser(user: User): Promise<number> {
// Construct the SQL query
const response = await sql`UPDATE users
SET
username = ${user.username},
${user.perms !== undefined ? sql`perms = ${user.perms},` : sql``}
${user.active !== undefined ? sql`active = ${user.active},` : sql``}
${password_hash !== null ? sql`password_hash = ${password_hash},` : sql``}
email = ${user.email},
phone = ${user.phone},
full_name = ${user.fullName},
company_code = ${user.companyCode}
SET username = ${user.username},
${
user.perms !== undefined
? sql`perms
=
${user.perms},`
: sql``
}
${
user.active !== undefined
? sql`active
=
${user.active},`
: sql``
} ${
password_hash !== null
? sql`password_hash
=
${password_hash},`
: sql``
}
email = ${user.email}, phone = ${user.phone}, full_name = ${user.fullName}, company_code = ${user.companyCode}
WHERE id = ${user.id}
RETURNING id;`;
@ -115,10 +129,17 @@ export async function getCompanies(searchQuery: string | null = null): Promise<C
// should require MANAGE_USERS permission
export async function getUser(id: number): Promise<User> {
const [user] = await sql`
SELECT id, username, perms,
SELECT id,
username,
perms,
created_at AT TIME ZONE 'UTC' AS "createdAt",
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
active, email, phone, full_name AS "fullName", company_id, company_code
active,
email,
phone,
full_name AS "fullName",
company_id,
company_code
FROM users
WHERE id = ${id};
`;
@ -134,8 +155,7 @@ export async function getUser(id: number): Promise<User> {
export async function getUserWithCompany(id: number): Promise<User> {
const [user] = await sql`
SELECT
u.id,
SELECT u.id,
u.username,
u.perms,
u.email,
@ -149,12 +169,10 @@ export async function getUserWithCompany(id: number): Promise<User> {
c.description AS company_description,
c.website AS company_website,
c.created_at AS company_created_at
FROM
users u
FROM users u
LEFT JOIN
companies c ON u.company_id = c.id
WHERE
u.id = ${id};
WHERE u.id = ${id};
`;
if (!user) {
error(404, 'User not found');
@ -181,19 +199,14 @@ export async function getUserWithCompanyAndApplications(
id: number
): Promise<{ user: User; applications: Application[] }> {
const data = await sql`
WITH company_data AS (
SELECT
id,
WITH company_data AS (SELECT id,
name,
description,
website,
created_at AT TIME ZONE 'UTC' AS "createdAt"
FROM companies
WHERE id = (SELECT company_id FROM users WHERE id = ${id})
),
user_data AS (
SELECT
id,
WHERE id = (SELECT company_id FROM users WHERE id = ${id})),
user_data AS (SELECT id,
username,
perms,
email,
@ -203,26 +216,17 @@ export async function getUserWithCompanyAndApplications(
last_signin AT TIME ZONE 'UTC' AS "lastSignIn",
active
FROM users
WHERE "id" = ${id}
),
application_data AS (
SELECT
id,
WHERE "id" = ${id}),
application_data AS (SELECT id,
posting_id AS "postingId",
(SELECT title FROM postings WHERE id = posting_id) AS "postingTitle",
created_at AT TIME ZONE 'UTC' AS "createdAt"
FROM applications
WHERE "user_id" = ${id}
)
SELECT
(
SELECT row_to_json(company_data)
FROM company_data
) AS company,
(
SELECT row_to_json(user_data)
FROM user_data
) AS user,
WHERE "user_id" = ${id})
SELECT (SELECT row_to_json(company_data)
FROM company_data) AS company,
(SELECT row_to_json(user_data)
FROM user_data) AS user,
(
SELECT json_agg(row_to_json(application_data))
FROM application_data
@ -258,7 +262,8 @@ export async function getUserWithCompanyAndApplications(
// should require MANAGE_USERS permission
export async function deleteUser(id: number): Promise<void> {
await sql`
DELETE FROM users
DELETE
FROM users
WHERE id = ${id};
`;
}
@ -283,8 +288,8 @@ export async function updateLastSignin(username: string): Promise<void> {
export async function createCompany(company: Company): Promise<number> {
const response = await sql`
INSERT INTO companies (name, description, website, created_at, company_code)
VALUES (${company.name}, ${company.description}, ${company.website}, NOW(), generate_company_code(CAST(CURRVAL('companies_id_seq') AS INT)))
RETURNING id;
VALUES (${company.name}, ${company.description}, ${company.website}, NOW(),
generate_company_code(CAST(CURRVAL('companies_id_seq') AS INT))) RETURNING id;
`;
await saveLogo(company);
@ -295,9 +300,10 @@ export async function createCompany(company: Company): Promise<number> {
export async function editCompany(company: Company): Promise<number> {
const response = await sql`
UPDATE companies
SET name = ${company.name}, description = ${company.description}, website = ${company.website}
WHERE id = ${company.id}
RETURNING id;
SET name = ${company.name},
description = ${company.description},
website = ${company.website}
WHERE id = ${company.id} RETURNING id;
`;
await saveLogo(company);
@ -307,7 +313,8 @@ export async function editCompany(company: Company): Promise<number> {
export async function deleteCompany(id: number): Promise<void> {
await sql`
DELETE FROM companies
DELETE
FROM companies
WHERE id = ${id};
`;
@ -332,29 +339,21 @@ export async function getCompanyFullData(
id: number
): Promise<{ company: Company; users: User[]; postings: Posting[] }> {
const data = await sql`
WITH company_data AS (
SELECT
id,
WITH company_data AS (SELECT id,
name,
description,
website,
created_at AT TIME ZONE 'UTC' AS "createdAt"
FROM companies
WHERE id = ${id}
),
user_data AS (
SELECT
id,
WHERE id = ${id}),
user_data AS (SELECT id,
username,
email,
phone,
full_name AS "fullName"
FROM users
WHERE "company_id" = ${id}
),
posting_data AS (
SELECT
id,
WHERE "company_id" = ${id}),
posting_data AS (SELECT id,
title,
description,
employer_id AS "employerId",
@ -367,21 +366,13 @@ export async function getCompanyFullData(
updated_at AT TIME ZONE 'UTC' AS "updatedAt",
flyer_link AS "flyerLink"
FROM postings
WHERE "company_id" = ${id}
)
SELECT
(
SELECT row_to_json(company_data)
FROM company_data
) AS company,
(
SELECT json_agg(row_to_json(user_data))
FROM user_data
) AS users,
(
SELECT json_agg(row_to_json(posting_data))
FROM posting_data
) AS postings;
WHERE "company_id" = ${id})
SELECT (SELECT row_to_json(company_data)
FROM company_data) AS company,
(SELECT json_agg(row_to_json(user_data))
FROM user_data) AS users,
(SELECT json_agg(row_to_json(posting_data))
FROM posting_data) AS postings;
`;
if (!data) {
@ -432,9 +423,11 @@ export async function createPosting(posting: Posting): Promise<number> {
}
}
const response = await sql`
INSERT INTO postings (title, description, employer_id, address, employment_type, wage, link, tag_ids, created_at, updated_at, flyer_link, company_id)
VALUES (${posting.title}, ${posting.description}, ${posting.employerId}, ${posting.address}, ${posting.employmentType}, ${posting.wage}, ${posting.link}, ${posting.tagIds}, NOW(), NOW(), ${posting.flyerLink}, ${posting.companyId})
RETURNING id;
INSERT INTO postings (title, description, employer_id, address, employment_type, wage, link, tag_ids, created_at,
updated_at, flyer_link, company_id)
VALUES (${posting.title}, ${posting.description}, ${posting.employerId}, ${posting.address},
${posting.employmentType}, ${posting.wage}, ${posting.link}, ${posting.tagIds}, NOW(), NOW(),
${posting.flyerLink}, ${posting.companyId}) RETURNING id;
`;
return response[0].id;
@ -457,9 +450,18 @@ export async function editPosting(posting: Posting): Promise<number> {
const response = await sql`
UPDATE postings
SET title = ${posting.title}, description = ${posting.description}, employer_id = ${posting.employerId}, address = ${posting.address}, employment_type = ${posting.employmentType}, wage = ${posting.wage}, link = ${posting.link}, tag_ids = ${posting.tagIds}, updated_at = NOW(), flyer_link = ${posting.flyerLink}, company_id = ${posting.companyId}
WHERE id = ${posting.id}
RETURNING id;
SET title = ${posting.title},
description = ${posting.description},
employer_id = ${posting.employerId},
address = ${posting.address},
employment_type = ${posting.employmentType},
wage = ${posting.wage},
link = ${posting.link},
tag_ids = ${posting.tagIds},
updated_at = NOW(),
flyer_link = ${posting.flyerLink},
company_id = ${posting.companyId}
WHERE id = ${posting.id} RETURNING id;
`;
return response[0].id;
@ -467,7 +469,8 @@ export async function editPosting(posting: Posting): Promise<number> {
export async function deletePosting(id: number): Promise<void> {
await sql`
DELETE FROM postings
DELETE
FROM postings
WHERE id = ${id};
`;
}
@ -476,17 +479,14 @@ export async function getCompanyEmployers(
id: number
): Promise<{ company: Company; users: User[] }> {
const data = await sql`
WITH company_data AS (
SELECT
id,
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}
),
WHERE id = ${id}),
user_data AS (SELECT id,
username,
email,
@ -497,15 +497,10 @@ export async function getCompanyEmployers(
company_id as "companyId"
FROM users
WHERE "company_id" = ${id})
SELECT
(
SELECT row_to_json(company_data)
FROM company_data
) AS company,
(
SELECT json_agg(row_to_json(user_data))
FROM user_data
) AS users;
SELECT (SELECT row_to_json(company_data)
FROM company_data) AS company,
(SELECT json_agg(row_to_json(user_data))
FROM user_data) AS users;
`;
if (!data) {
@ -539,17 +534,14 @@ export async function getCompanyEmployersAndRequests(
id: number
): Promise<{ company: Company; employers: User[]; requests: User[] }> {
const data = await sql`
WITH company_data AS (
SELECT
id,
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}
),
WHERE id = ${id}),
employer_data AS (SELECT id,
username,
email,
@ -570,19 +562,12 @@ export async function getCompanyEmployersAndRequests(
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;
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) {
@ -726,28 +711,20 @@ export async function getPosting(id: number): Promise<Posting> {
export async function getPostingWithCompanyUser(id: number): Promise<Posting> {
const data = await sql`
WITH company_data AS (
SELECT
id,
WITH company_data AS (SELECT id,
name,
description,
website,
created_at AS "createdAt"
FROM companies
WHERE id = (SELECT company_id FROM postings WHERE id = ${id})
),
user_data AS (
SELECT
username,
WHERE id = (SELECT company_id FROM postings WHERE id = ${id})),
user_data AS (SELECT username,
email,
phone,
full_name AS "fullName"
FROM users
WHERE "company_id" = (SELECT company_id FROM postings WHERE id = ${id})
),
posting_data AS (
SELECT
id,
WHERE "company_id" = (SELECT company_id FROM postings WHERE id = ${id})),
posting_data AS (SELECT id,
title,
description,
employer_id AS "employerId",
@ -760,17 +737,11 @@ export async function getPostingWithCompanyUser(id: number): Promise<Posting> {
updated_at AT TIME ZONE 'UTC' AS "updatedAt",
flyer_link AS "flyerLink"
FROM postings
WHERE id = ${id}
)
SELECT
(
SELECT row_to_json(company_data)
FROM company_data
) AS company,
(
SELECT row_to_json(user_data)
FROM user_data
) AS user,
WHERE id = ${id})
SELECT (SELECT row_to_json(company_data)
FROM company_data) AS company,
(SELECT row_to_json(user_data)
FROM user_data) AS user,
(
SELECT row_to_json(posting_data)
FROM posting_data
@ -797,8 +768,7 @@ export async function getPostingWithCompanyUser(id: number): Promise<Posting> {
export async function createApplication(application: Application): Promise<number> {
const response = await sql`
INSERT INTO applications (posting_id, user_id, candidate_statement, created_at)
VALUES (${application.postingId}, ${application.userId}, ${application.candidateStatement}, NOW())
RETURNING id;
VALUES (${application.postingId}, ${application.userId}, ${application.candidateStatement}, NOW()) RETURNING id;
`;
sendEmployerNotificationEmail(application.postingId).catch((err) => {
@ -808,8 +778,9 @@ export async function createApplication(application: Application): Promise<numbe
}
export async function deleteApplication(id: number): Promise<void> {
const response = await sql`
DELETE FROM applications
await sql`
DELETE
FROM applications
WHERE id = ${id};
`;
}
@ -819,16 +790,17 @@ export async function deleteApplicationWithUser(
userId: number
): Promise<void> {
console.log(applicationId, userId);
const response = await sql`
DELETE FROM applications
WHERE id = ${applicationId} AND user_id = ${userId};
await sql`
DELETE
FROM applications
WHERE id = ${applicationId}
AND user_id = ${userId};
`;
}
export async function getApplications(postingId: number): Promise<Application[]> {
const data = await sql`
SELECT
a.id,
SELECT a.id,
a.candidate_statement AS "candidateStatement",
a.created_at AS "createdAt",
u.id AS "userId",
@ -876,18 +848,13 @@ export async function getNotificationInfo(
postingId: number
): Promise<{ title: string; emails: string[] }> {
const data = await sql`
WITH posting_data AS (
SELECT title, company_id
WITH posting_data AS (SELECT title, company_id
FROM postings
WHERE id = ${postingId}
),
user_emails AS (
SELECT email
WHERE id = ${postingId}),
user_emails AS (SELECT email
FROM users
WHERE company_id = (SELECT company_id FROM posting_data)
)
SELECT
(SELECT title FROM posting_data) AS title,
WHERE company_id = (SELECT company_id FROM posting_data))
SELECT (SELECT title FROM posting_data) AS title,
(SELECT json_agg(email) FROM user_emails) AS emails;
`;

View File

@ -2,11 +2,11 @@
import { page } from '$app/stores';
</script>
<div style="padding-top: 32px" class="text-center">
<div class="text-center" style="padding-top: 32px">
<h1 class="text-9xl font-bold">
{$page.status}
</h1>
<h1>Thats an error</h1>
<h1>That's an error</h1>
{#if $page.status === 404}
<p>We cant seem to find the page you are looking for.</p>
<p>The address may be mistyped, or the page may have moved or been deleted.</p>

View File

@ -35,19 +35,19 @@
</script>
<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,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"
rel="stylesheet"
/>
<div class="flex min-h-screen flex-col">
<div class="bottom-border bg-color sticky top-0 z-50 flex h-14 justify-between p-3 align-middle">
<nav class="pt-1">
<a href="/" class="hover-bg-color mr-1 rounded-md px-2 pb-2 pt-1.5">
<a class="hover-bg-color mr-1 rounded-md px-2 pb-2 pt-1.5" href="/">
<img
class="inline-block"
src="/mdevtriangle.svg"
alt="MarinoDev Logo"
class="inline-block"
height="24"
src="/mdevtriangle.svg"
width="24"
/>
<div class="inline-block text-sm">Home</div>
@ -76,7 +76,7 @@
{/if}
</nav>
<div>
<button onclick={toggleTheme} class="">
<button class="" onclick={toggleTheme}>
<span class="material-symbols-outlined hover-bg-color rounded-full p-1">
{currentTheme === 'light' ? 'light_mode' : 'dark_mode'}
</span>
@ -103,13 +103,13 @@
<div class="inline-block text-left align-top">
<div class="inline-block pr-3">
<p class="font-semibold">Drake Marino:</p>
<a href="mailto:drake@marinodev.com" class="hyperlink-color hyperlink-underline"
<a class="hyperlink-color hyperlink-underline" href="mailto:drake@marinodev.com"
>drake@marinodev.com</a
>
</div>
<div class="inline-block">
<p class="font-semibold">Chetan Malkan:</p>
<a href="mailto:chetan@marinodev.com" class="hyperlink-color hyperlink-underline"
<a class="hyperlink-color hyperlink-underline" href="mailto:chetan@marinodev.com"
>chetan@marinodev.com</a
>
</div>
@ -121,8 +121,9 @@
<div class="inline-block text-right align-top">
<div class="font-semibold">Source Code:</div>
<a
class="hyperlink-color hyperlink-underline"
href="https://git.marinodev.com/MarinoDev/FBLA25"
class="hyperlink-color hyperlink-underline">https://git.marinodev.com/MarinoDev/FBLA25</a
>https://git.marinodev.com/MarinoDev/FBLA25</a
>
</div>
</footer>

View File

@ -10,7 +10,7 @@
<div class="base-container">
<div class="content pt-16">
<img class="mx-auto" src="/mdevtriangle.svg" alt="MarinoDev Logo" height="256" width="256" />
<img alt="MarinoDev Logo" class="mx-auto" height="256" src="/mdevtriangle.svg" width="256" />
<h1 class="text-center text-8xl font-semibold">CareerConnect</h1>
<h2 class="text-center text-3xl italic">Connecting Students with Opportunities</h2>
<h2 class="pt-8 text-center">We are a platform that connects students with employers.</h2>

View File

@ -1,5 +1,5 @@
<h1>About</h1>
<p>
This is my submission for the 2025 FBLA Website Coding & Development event. It was built using
<a href="https://svelte.dev/docs/kit/introduction" class="text-blue-600">SvelteKit</a>.
<a class="text-blue-600" href="https://svelte.dev/docs/kit/introduction">SvelteKit</a>.
</p>

View File

@ -1,7 +1,7 @@
import type { PageServerLoad } from './$types';
import { deleteApplicationWithUser, getUserWithCompanyAndApplications } from '$lib/db/index.server';
import { getUserId } from '$lib/index.server';
import { type Actions, fail, json, redirect } from '@sveltejs/kit';
import { type Actions, fail } from '@sveltejs/kit';
import fs from 'fs';
import path from 'path';
import { writeFileSync } from 'fs';

View File

@ -3,7 +3,7 @@
import type { PageProps } from './$types';
let applicationToDelete: number = $state(0);
let { data, form }: PageProps = $props();
let { data }: PageProps = $props();
let resumeState = $state(data.resumeExists);
onMount(() => {
@ -65,13 +65,10 @@
});
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') {
@ -88,7 +85,7 @@
}
async function handleSubmit(event: SubmitEvent) {
event.preventDefault(); // Prevent default form submission
event.preventDefault();
if (!file) {
alert('Please select a file first');
@ -96,7 +93,7 @@
}
const formData = new FormData();
formData.append('resume', file); // Manually append the file
formData.append('resume', file);
try {
const response = await fetch('?/uploadResume', {
@ -123,14 +120,14 @@
<div class="elevated separator-borders m-2 inline-block h-min min-w-max rounded align-top">
<div class="inline-block p-4">
<img
id="avatar"
alt="User avatar"
class="mb-2 inline-block rounded-lg"
height="240"
id="avatar"
onerror={avatarFallback}
src="/uploads/avatars/{data.user.id
? data.user.id
: 'default'}.svg?timestamp=${Date.now()}"
onerror={avatarFallback}
alt="User avatar"
height="240"
width="240"
/>
{#if data.user.fullName}
@ -169,8 +166,8 @@
>Edit account</a
>
<button class="danger-border-color m-2 rounded-md border px-2.5 py-1" onclick={signOut}
>Sign out</button
>
>Sign out
</button>
</div>
</div>
<div class="p-3">
@ -217,17 +214,17 @@
{#if resumeState}
<a class="pb-2" href="/uploads/resumes/{data.user.id}.pdf" target="_blank">
<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>
<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
>
><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-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}
</div>
<div class="top-border pt-2 font-semibold">
@ -258,8 +255,9 @@
onclick={() => {
applicationToDelete = application.id;
openConfirm();
}}>delete</button
>
}}
>delete
</button>
</div>
{/each}
</div>
@ -267,7 +265,7 @@
</div>
</div>
</div>
<form id="deleteConfirmModal" method="POST" class="modal">
<form class="modal" id="deleteConfirmModal" method="POST">
<div class="modal-content">
<div class="mb-2 inline-flex w-full justify-between">
<h2 class="font-semibold">Are you sure?</h2>
@ -278,18 +276,20 @@
<div class="mt-4 flex justify-between">
<button
class="danger-bg-color rounded px-2 py-1"
formaction="?/deleteApplication&id={applicationToDelete}"
type="submit"
formaction="?/deleteApplication&id={applicationToDelete}">Delete application</button
>
>Delete application
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
onclick={closeConfirm}
type="button"
onclick={closeConfirm}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</form>
<form id="uploadModal" method="POST" class="modal" onsubmit={handleSubmit}>
<form class="modal" id="uploadModal" method="POST" onsubmit={handleSubmit}>
<div class="modal-content">
<div class="mb-2 inline-flex w-full justify-between">
<h2 class="font-semibold">Résumé Upload</h2>
@ -297,22 +297,19 @@
</div>
<div>
<div
role="region"
class="dull-primary-border-color rounded-lg border-2 border-dashed"
ondrop={dropHandler}
ondragover={dragOverHandler}
ondrop={dropHandler}
role="region"
>
<label for="resume" class="cursor-pointer p-4">
<label class="cursor-pointer p-4" for="resume">
<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" />
<input accept=".pdf" bind:files class="hidden" id="resume" type="file" />
</div>
</label>
</div>
@ -322,9 +319,10 @@
<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"
onclick={closeUpload}
type="button"
onclick={closeUpload}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</form>

View File

@ -1,10 +1,8 @@
import type { PageServerLoad } from './$types';
import { deleteUser, getUser, getUserWithCompany, updateUser } from '$lib/db/index.server';
import { deleteUser, getUserWithCompany, updateUser } from '$lib/db/index.server';
import { type Actions, fail, redirect } from '@sveltejs/kit';
import { PERMISSIONS } from '$lib/consts';
import { getUserId } from '$lib/index.server';
import type { User } from '$lib/types';
import path from 'path';
export const load: PageServerLoad = async ({ cookies }) => {
const id = getUserId(cookies);

View File

@ -3,6 +3,7 @@
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
import { telFormatter } from '$lib/shared.svelte';
let permsAccordions: boolean[] = [false, false, false];
onMount(() => {
@ -91,63 +92,63 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Edit Account</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Username <span class="danger-color">*</span>
<input
type="text"
name="username"
id="username"
value={data.user?.username}
placeholder="Username"
class="w-full rounded font-normal"
id="username"
name="username"
placeholder="Username"
type="text"
value={data.user?.username}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Full name <span class="danger-color">*</span>
<input
type="text"
name="fullName"
id="fullName"
value={data.user?.fullName}
placeholder="Full name"
class="w-full rounded font-normal"
id="fullName"
name="fullName"
placeholder="Full name"
required
type="text"
value={data.user?.fullName}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Email <span class="danger-color">*</span>
<input
type="email"
name="email"
id="email"
value={data.user?.email}
placeholder="Email"
class="w-full rounded font-normal"
id="email"
name="email"
placeholder="Email"
required
type="email"
value={data.user?.email}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Phone (optional)
<input
type="tel"
name="phone"
id="phone"
value={data.user?.phone}
placeholder="Phone"
class="w-full rounded font-normal"
id="phone"
name="phone"
pattern="([0-9]\{3}) [0-9]\{3}-[0-9]\{3}"
placeholder="Phone"
type="tel"
value={data.user?.phone}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Company code (optional)
<input
type="text"
name="companyCode"
id="companyCode"
placeholder="Company code"
value={data.user?.companyCode}
class="w-full rounded font-normal"
id="companyCode"
name="companyCode"
placeholder="Company code"
type="text"
value={data.user?.companyCode}
/>
</div>
<p class="low-emphasis-text pb-4">
@ -161,43 +162,46 @@
<div class="flex justify-between">
<button
class="dull-primary-bg-color mb-4 mt-2 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Update account</button
>
>Update account
</button>
<button
class="danger-bg-color mb-4 mt-2 rounded px-2 py-1"
onclick={openConfirm}
type="button"
onclick={openConfirm}>Delete account</button
>
>Delete account
</button>
</div>
<div id="deleteConfirmModal" class="modal">
<div class="modal" id="deleteConfirmModal">
<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} type="button"
>close</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>
<input
type="text"
name="confirm"
id="confirm"
placeholder="I understand"
class="w-full rounded font-normal"
id="confirm"
name="confirm"
pattern="I understand"
placeholder="I understand"
required
type="text"
/>
<div class="mt-4 flex justify-between">
<button class="danger-bg-color rounded px-2 py-1" type="submit" formaction="?/delete"
>Delete account</button
>
<button class="danger-bg-color rounded px-2 py-1" formaction="?/delete" type="submit"
>Delete account
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
onclick={closeConfirm}
type="button"
onclick={closeConfirm}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
import type { PageServerLoad } from './$types';
import { getCompanies, getUsers } from '$lib/db/index.server';
import { getCompanies } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { error } from '@sveltejs/kit';
import { getUserPerms } from '$lib/index.server';

View File

@ -21,11 +21,11 @@
<form action="" class="flex p-4">
<div class="search-bar">
<input
type="search"
name="searchCompanies"
id="searchCompanies"
placeholder="Search Companies"
class="search-cancel"
id="searchCompanies"
name="searchCompanies"
placeholder="Search Companies"
type="search"
/>
<button><span class="material-symbols-outlined">search</span></button>
</div>

View File

@ -1,5 +1,5 @@
import type { PageServerLoad } from './$types';
import { getPostings, getUsers } from '$lib/db/index.server';
import { getPostings } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { error } from '@sveltejs/kit';
import { getUserPerms } from '$lib/index.server';

View File

@ -23,11 +23,11 @@
<form action="" class="flex p-4">
<div class="search-bar">
<input
type="search"
name="searchPostings"
id="searchPostings"
placeholder="Search Postings"
class="search-cancel"
id="searchPostings"
name="searchPostings"
placeholder="Search Postings"
type="search"
/>
<button><span class="material-symbols-outlined">search</span></button>
</div>

View File

@ -21,11 +21,11 @@
<form action="" class="flex p-4">
<div class="search-bar">
<input
type="search"
name="searchTags"
id="searchTags"
placeholder="Search Tags"
class="search-cancel"
id="searchTags"
name="searchTags"
placeholder="Search Tags"
type="search"
/>
<button><span class="material-symbols-outlined">search</span></button>
</div>

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { userPerms, employerPerms, adminPerms } from '$lib/shared.svelte';
let { data } = $props();
const dateFormatOptions: Intl.DateTimeFormatOptions = {
@ -29,11 +30,11 @@
<form action="" class="flex p-4">
<div class="search-bar">
<input
type="search"
name="searchUsers"
id="searchUsers"
placeholder="Search Users"
class="search-cancel"
id="searchUsers"
name="searchUsers"
placeholder="Search Users"
type="search"
/>
<button><span class="material-symbols-outlined">search</span></button>
</div>

View File

@ -27,11 +27,11 @@
<div class="elevated separator-borders m-2 inline-block h-min min-w-max rounded align-top">
<div class="inline-block p-4">
<img
class="mb-2 inline-block rounded-lg"
src="/uploads/avatars/{data.user.id}.svg?timestamp=${Date.now()}"
alt="User avatar"
onerror={avatarFallback}
class="mb-2 inline-block rounded-lg"
height="240"
onerror={avatarFallback}
src="/uploads/avatars/{data.user.id}.svg?timestamp=${Date.now()}"
width="240"
/>
{#if data.user.fullName}

View File

@ -4,6 +4,7 @@
import type { PageProps } from './$types';
import { PERMISSIONS } from '$lib/consts';
import { userPerms, employerPerms, adminPerms, telFormatter } from '$lib/shared.svelte';
let permsAccordions: boolean[] = [false, false, false];
let passwordVisible = $state(false);
@ -104,32 +105,32 @@
Edit User {data.user.username}{data.user.fullName ? ` (${data.user.fullName})` : ''}
</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Username <span class="text-red-500">*</span>
<input
type="text"
name="username"
id="username"
value={data.user?.username}
placeholder="Username"
class="w-full rounded font-normal"
id="username"
name="username"
placeholder="Username"
required
type="text"
value={data.user?.username}
/>
</div>
<div class="relative pt-4 text-sm font-semibold">
New password (optional)
<input
type="password"
name="password"
id="password"
placeholder="New password"
class="w-full rounded font-normal"
id="password"
name="password"
placeholder="New password"
type="password"
/>
<button
type="button"
onclick={showPassword}
class="absolute right-2.5 -translate-y-1/2 transform pt-12"
onclick={showPassword}
type="button"
>
<span class="material-symbols-outlined"
>{passwordVisible ? 'visibility' : 'visibility_off'}</span
@ -139,48 +140,48 @@
<div class="mt-4 text-sm font-semibold">
Full name <span class="danger-color">*</span>
<input
type="text"
name="fullName"
id="fullName"
value={data.user?.fullName}
placeholder="Full name"
class="w-full rounded font-normal"
id="fullName"
name="fullName"
placeholder="Full name"
required
type="text"
value={data.user?.fullName}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Email <span class="danger-color">*</span>
<input
type="email"
name="email"
id="email"
value={data.user?.email}
placeholder="Email"
class="w-full rounded font-normal"
id="email"
name="email"
placeholder="Email"
required
type="email"
value={data.user?.email}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Phone (optional)
<input
type="tel"
name="phone"
id="phone"
value={data.user?.phone}
placeholder="Phone"
class="w-full rounded font-normal"
id="phone"
name="phone"
pattern="([0-9]\{3}) [0-9]\{3}-[0-9]\{3}"
placeholder="Phone"
type="tel"
value={data.user?.phone}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Company code (optional)
<input
type="text"
name="companyCode"
id="companyCode"
placeholder="Company code"
value={data.user?.companyCode}
class="w-full rounded font-normal"
id="companyCode"
name="companyCode"
placeholder="Company code"
type="text"
value={data.user?.companyCode}
/>
</div>
<p class="low-emphasis-text">
@ -195,12 +196,12 @@
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input
type="checkbox"
name="userPerms"
id="userPerms"
class="select-all"
checked={(perms & userPerms) === userPerms}
class="select-all"
id="userPerms"
indeterminate={(perms & userPerms) !== userPerms && (perms & userPerms) > 0}
name="userPerms"
type="checkbox"
/></span
>User Permissions
</span>
@ -211,25 +212,25 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="view" class="flex place-items-center">
<label class="flex place-items-center" for="view">
<input
type="checkbox"
name="view"
id="view"
class="permCheckbox mx-1"
checked={(perms & PERMISSIONS.VIEW) > 0}
class="permCheckbox mx-1"
id="view"
name="view"
type="checkbox"
/>
<span class="ml-2">View access</span></label
>
</div>
<div class="mb-1">
<label for="apply" class="flex place-items-center">
<label class="flex place-items-center" for="apply">
<input
type="checkbox"
name="apply"
id="apply"
class="permCheckbox mx-1"
checked={(perms & PERMISSIONS.APPLY_FOR_JOBS) > 0}
class="permCheckbox mx-1"
id="apply"
name="apply"
type="checkbox"
/>
<span class="ml-2">Apply for jobs</span></label
>
@ -245,13 +246,13 @@
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input
type="checkbox"
name="companyPerms"
id="companyPerms"
class="select-all"
checked={(perms & employerPerms) === employerPerms}
class="select-all"
id="companyPerms"
indeterminate={(perms & employerPerms) !== employerPerms &&
(perms & employerPerms) > 0}
name="companyPerms"
type="checkbox"
/></span
>Company Permissions
</span>
@ -262,25 +263,25 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="submitPostings" class="flex place-items-center">
<label class="flex place-items-center" for="submitPostings">
<input
type="checkbox"
name="submitPostings"
id="submitPostings"
checked={(perms & PERMISSIONS.SUBMIT_POSTINGS) >= 0}
class="permCheckbox mx-1"
id="submitPostings"
name="submitPostings"
type="checkbox"
/>
<span class="ml-2">Submit postings</span></label
>
</div>
<div class="mb-1">
<label for="manageEmployers" class="flex place-items-center">
<label class="flex place-items-center" for="manageEmployers">
<input
type="checkbox"
name="manageEmployers"
id="manageEmployers"
checked={(perms & PERMISSIONS.MANAGE_EMPLOYERS) > 0}
class="permCheckbox mx-1"
id="manageEmployers"
name="manageEmployers"
type="checkbox"
/>
<span class="ml-2">Manage employers (within their company)</span></label
>
@ -296,12 +297,12 @@
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input
type="checkbox"
name="adminPerms"
id="adminPerms"
class="select-all"
checked={(perms & adminPerms) === adminPerms}
class="select-all"
id="adminPerms"
indeterminate={(perms & adminPerms) !== adminPerms && (perms & adminPerms) > 0}
name="adminPerms"
type="checkbox"
/></span
>Admin Permissions
</span>
@ -312,49 +313,49 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="manageTags" class="flex place-items-center">
<label class="flex place-items-center" for="manageTags">
<input
type="checkbox"
name="manageTags"
id="manageTags"
checked={(perms & PERMISSIONS.MANAGE_TAGS) > 0}
class="permCheckbox mx-1"
id="manageTags"
name="manageTags"
type="checkbox"
/>
<span class="ml-2">Manage tags</span></label
>
</div>
<div class="mb-1">
<label for="managePostings" class="flex place-items-center">
<label class="flex place-items-center" for="managePostings">
<input
type="checkbox"
name="managePostings"
id="managePostings"
checked={(perms & PERMISSIONS.MANAGE_POSTINGS) > 0}
class="permCheckbox mx-1"
id="managePostings"
name="managePostings"
type="checkbox"
/>
<span class="ml-2">Manage postings</span></label
>
</div>
<div class="mb-1">
<label for="manageUsers" class="flex place-items-center">
<label class="flex place-items-center" for="manageUsers">
<input
type="checkbox"
name="manageUsers"
id="manageUsers"
checked={(perms & PERMISSIONS.MANAGE_USERS) > 0}
class="permCheckbox mx-1"
id="manageUsers"
name="manageUsers"
type="checkbox"
/>
<span class="ml-2">Manage users</span></label
>
</div>
<div class="mb-1">
<label for="manageCompanies" class="flex place-items-center">
<label class="flex place-items-center" for="manageCompanies">
<input
type="checkbox"
name="manageCompanies"
id="manageCompanies"
checked={(perms & PERMISSIONS.MANAGE_COMPANIES) > 0}
class="permCheckbox mx-1"
id="manageCompanies"
name="manageCompanies"
type="checkbox"
/>
<span class="ml-2">Manage companies</span></label
>
@ -362,13 +363,13 @@
</div>
</div>
</div>
<label for="accountActive" class="flex place-items-center p-2">
<label class="flex place-items-center p-2" for="accountActive">
<input
type="checkbox"
name="accountActive"
id="accountActive"
checked={data.user?.active}
class="permCheckbox mx-1"
id="accountActive"
name="accountActive"
type="checkbox"
/>
<span class="ml-2">Account active</span></label
>
@ -379,23 +380,25 @@
<div class="flex justify-between">
<button
class="dull-primary-bg-color mb-4 mt-2 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Save user</button
>
>Save user
</button>
<button
class="danger-bg-color mb-4 mt-2 rounded px-2 py-1"
onclick={openConfirm}
type="button"
onclick={openConfirm}>Delete user</button
>
>Delete user
</button>
</div>
</form>
<form id="deleteConfirmModal" class="modal" method="POST" use:enhance>
<form class="modal" id="deleteConfirmModal" method="POST" use:enhance>
<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} type="button"
>close</button
>
>close
</button>
</div>
<p>
This will permanently delete user <span class="font-semibold">{data.user?.username}.</span
@ -403,23 +406,24 @@
</p>
<p>Please type "I understand" into the box below to confirm</p>
<input
type="text"
name="confirm"
id="confirm"
placeholder="I understand"
class="w-full rounded font-normal"
id="confirm"
name="confirm"
pattern="I understand"
placeholder="I understand"
required
type="text"
/>
<div class="mt-4 flex justify-between">
<button class="danger-bg-color rounded px-2 py-1" type="submit" formaction="?/delete"
>Delete user</button
>
<button class="danger-bg-color rounded px-2 py-1" formaction="?/delete" type="submit"
>Delete user
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
onclick={closeConfirm}
type="button"
onclick={closeConfirm}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</form>

View File

@ -1,12 +1,12 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { createUser, getUsers } from '$lib/db/index.server';
import { createUser } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { getUserPerms } from '$lib/index.server';
import type { User } from '$lib/types';
import { employerPerms, userPerms } from '$lib/shared.svelte';
import type { PageServerLoad } from '../../../../../.svelte-kit/types/src/routes/admin/users/$types';
export const load: PageServerLoad = async ({ cookies, url }) => {
export const load: PageServerLoad = async ({ cookies }) => {
const perms = getUserPerms(cookies);
if (!(perms >= 0 && (perms & PERMISSIONS.MANAGE_USERS) > 0)) {
error(403, 'Unauthorized');

View File

@ -3,6 +3,7 @@
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
import { telFormatter } from '$lib/shared.svelte';
let permsAccordions: boolean[] = [false, false, false];
let passwordVisible = $state(false);
@ -85,32 +86,32 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Create new user</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Username <span class="text-red-500">*</span>
<input
type="text"
name="username"
id="username"
placeholder="Username"
class="w-full rounded font-normal"
id="username"
name="username"
placeholder="Username"
required
type="text"
/>
</div>
<div class="relative mt-4 text-sm font-semibold">
Password <span class="text-red-500">*</span>
<input
type="password"
name="password"
id="password"
placeholder="Password"
class="w-full rounded font-normal"
id="password"
name="password"
placeholder="Password"
required
type="password"
/>
<button
type="button"
onclick={showPassword}
class="absolute right-2.5 -translate-y-1/2 transform pt-12"
onclick={showPassword}
type="button"
>
<span class="material-symbols-outlined"
>{passwordVisible ? 'visibility' : 'visibility_off'}</span
@ -120,44 +121,44 @@
<div class="mt-4 text-sm font-semibold">
Full name <span class="danger-color">*</span>
<input
type="text"
name="fullName"
id="fullName"
placeholder="Full Name"
class="w-full rounded font-normal"
id="fullName"
name="fullName"
placeholder="Full Name"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Email <span class="danger-color">*</span>
<input
type="email"
name="email"
id="email"
placeholder="Email"
class="w-full rounded font-normal"
id="email"
name="email"
placeholder="Email"
required
type="email"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Phone (optional)
<input
type="tel"
name="phone"
id="phone"
placeholder="Phone"
class="w-full rounded font-normal"
id="phone"
name="phone"
pattern="([0-9]\{3}) [0-9]\{3}-[0-9]\{3}"
placeholder="Phone"
type="tel"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Company code (optional)
<input
type="text"
name="companyCode"
id="companyCode"
placeholder="Company code"
class="w-full rounded font-normal"
id="companyCode"
name="companyCode"
placeholder="Company code"
type="text"
/>
</div>
<p class="low-emphasis-text">
@ -172,11 +173,11 @@
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input
type="checkbox"
name="userPerms"
id="userPerms"
class="select-all"
id="userPerms"
indeterminate={true}
name="userPerms"
type="checkbox"
/></span
>User Permissions
</span>
@ -187,20 +188,20 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="view" class="flex place-items-center">
<label class="flex place-items-center" for="view">
<input
type="checkbox"
name="view"
id="view"
checked={true}
class="permCheckbox mx-1"
id="view"
name="view"
type="checkbox"
/>
<span class="ml-2">View access</span></label
>
</div>
<div class="mb-1">
<label for="apply" class="flex place-items-center">
<input type="checkbox" name="apply" id="apply" class="permCheckbox mx-1" />
<label class="flex place-items-center" for="apply">
<input class="permCheckbox mx-1" id="apply" name="apply" type="checkbox" />
<span class="ml-2">Apply for jobs</span></label
>
</div>
@ -215,10 +216,10 @@
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input
type="checkbox"
name="companyPerms"
id="companyPerms"
class="select-all"
id="companyPerms"
name="companyPerms"
type="checkbox"
/></span
>Company Permissions
</span>
@ -229,23 +230,23 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="submitPostings" class="flex place-items-center">
<label class="flex place-items-center" for="submitPostings">
<input
type="checkbox"
name="submitPostings"
id="submitPostings"
class="permCheckbox mx-1"
id="submitPostings"
name="submitPostings"
type="checkbox"
/>
<span class="ml-2">Submit postings</span></label
>
</div>
<div class="mb-1">
<label for="manageEmployers" class="flex place-items-center">
<label class="flex place-items-center" for="manageEmployers">
<input
type="checkbox"
name="manageEmployers"
id="manageEmployers"
class="permCheckbox mx-1"
id="manageEmployers"
name="manageEmployers"
type="checkbox"
/>
<span class="ml-2">Manage employers (within their company)</span></label
>
@ -260,7 +261,7 @@
>
<span class="flex place-items-center">
<span class="ml-1 mr-3"
><input type="checkbox" name="adminPerms" id="adminPerms" class="select-all" /></span
><input class="select-all" id="adminPerms" name="adminPerms" type="checkbox" /></span
>Admin Permissions
</span>
<span class="material-symbols-outlined"
@ -270,45 +271,45 @@
<div class="panel hidden p-2">
<div>
<div class="mb-1">
<label for="manageTags" class="flex place-items-center">
<label class="flex place-items-center" for="manageTags">
<input
type="checkbox"
name="manageTags"
id="manageTags"
class="permCheckbox mx-1"
id="manageTags"
name="manageTags"
type="checkbox"
/>
<span class="ml-2">Manage tags</span></label
>
</div>
<div class="mb-1">
<label for="managePostings" class="flex place-items-center">
<label class="flex place-items-center" for="managePostings">
<input
type="checkbox"
name="managePostings"
id="managePostings"
class="permCheckbox mx-1"
id="managePostings"
name="managePostings"
type="checkbox"
/>
<span class="ml-2">Manage postings</span></label
>
</div>
<div class="mb-1">
<label for="manageUsers" class="flex place-items-center">
<label class="flex place-items-center" for="manageUsers">
<input
type="checkbox"
name="manageUsers"
id="manageUsers"
class="permCheckbox mx-1"
id="manageUsers"
name="manageUsers"
type="checkbox"
/>
<span class="ml-2">Manage users</span></label
>
</div>
<div class="mb-1">
<label for="manageCompanies" class="flex place-items-center">
<label class="flex place-items-center" for="manageCompanies">
<input
type="checkbox"
name="manageCompanies"
id="manageCompanies"
class="permCheckbox mx-1"
id="manageCompanies"
name="manageCompanies"
type="checkbox"
/>
<span class="ml-2">Manage companies</span></label
>
@ -316,13 +317,13 @@
</div>
</div>
</div>
<label for="accountActive" class="flex place-items-center p-2">
<label class="flex place-items-center p-2" for="accountActive">
<input
type="checkbox"
name="accountActive"
id="accountActive"
class="permCheckbox mx-1"
checked
class="permCheckbox mx-1"
id="accountActive"
name="accountActive"
type="checkbox"
/>
<span class="ml-2">Account active</span></label
>
@ -332,9 +333,10 @@
{/if}
<button
class="dull-primary-bg-color mb-4 mt-2 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Create user</button
>
>Create user
</button>
</form>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { getPosting, getPostingWithCompanyUser } from '$lib/db/index.server';
import { getPostingWithCompanyUser } from '$lib/db/index.server';
import { error, json } from '@sveltejs/kit';
export async function GET({ url }) {

View File

@ -23,11 +23,11 @@
<div class="bottom-border mb-4 flex justify-between">
<div class="flex">
<img
class="mb-2 inline-block h-32 rounded-lg"
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="Company Logo"
onerror={logoFallback}
class="mb-2 inline-block h-32 rounded-lg"
height="128"
onerror={logoFallback}
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
width="128"
/>
<div class="inline-block h-min pl-4">

View File

@ -7,17 +7,17 @@
<div class="bottom-border h-10 pt-2 text-center">
<a
href={page.url.pathname.endsWith('employers') ? '.' : ''}
class="p-2 {page.url.pathname.endsWith('edit')
? 'primary-underline font-bold'
: 'low-emphasis-text low-emphasis-text-button'}"
href={page.url.pathname.endsWith('employers') ? '.' : ''}
><span class="material-symbols-outlined align-bottom">store</span> Details</a
>
<a
href={page.url.pathname.endsWith('edit') ? 'edit/employers' : ''}
class="p-2 {page.url.pathname.endsWith('employers')
? 'primary-underline font-bold'
: 'low-emphasis-text low-emphasis-text-button'}"
href={page.url.pathname.endsWith('edit') ? 'edit/employers' : ''}
><span class="material-symbols-outlined align-bottom">group</span> Employers</a
>
</div>

View File

@ -1,7 +1,7 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { deleteCompany, editCompany, getCompany } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { getUserCompanyId, getUserPerms } from '$lib/index.server';
import { getUserPerms } from '$lib/index.server';
import type { PageServerLoad } from './$types';
import type { Company } from '$lib/types';

View File

@ -22,11 +22,11 @@
<div class="content">
<div class="m-4">
<img
class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="User avatar"
onerror={logoFallback}
class="mb-2 inline-block rounded-lg"
height="80"
onerror={logoFallback}
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
width="80"
/>
<div class="inline-block pl-4 align-top">
@ -38,38 +38,38 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Edit Company {data.company.name}</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Name <span class="text-red-500">*</span>
<input
type="text"
name="name"
id="name"
placeholder="Name"
value={data.company?.name}
class="w-full rounded font-normal"
id="name"
name="name"
placeholder="Name"
required
type="text"
value={data.company?.name}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Description <span class="text-red-500">*</span>
<textarea
name="description"
class="w-full rounded font-normal"
id="description"
rows="4"
name="description"
placeholder="Description"
class="w-full rounded font-normal">{data.company?.description}</textarea
rows="4">{data.company?.description}</textarea
>
</div>
<div class="mt-4 text-sm font-semibold">
Website <span class="text-red-500">*</span>
<input
type="text"
name="website"
id="website"
placeholder="Website"
value={data.company?.website}
class="w-full rounded font-normal"
id="website"
name="website"
placeholder="Website"
type="text"
value={data.company?.website}
/>
</div>
@ -79,23 +79,25 @@
<div class="mt-4 flex justify-between">
<button
class="dull-primary-bg-color mb-4 mt-2 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Save company</button
>
>Save company
</button>
<button
class="danger-bg-color mb-4 mt-2 rounded px-2 py-1"
onclick={openConfirm}
type="button"
onclick={openConfirm}>Delete company</button
>
>Delete company
</button>
</div>
</form>
<form id="deleteConfirmModal" class="modal" method="POST" use:enhance>
<form class="modal" id="deleteConfirmModal" method="POST" use:enhance>
<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} type="button"
>close</button
>
>close
</button>
</div>
<p>
This will permanently delete company <span class="font-semibold"
@ -104,23 +106,24 @@
</p>
<p>Please type "I understand" into the box below to confirm</p>
<input
type="text"
name="confirm"
id="confirm"
placeholder="I understand"
class="w-full rounded font-normal"
id="confirm"
name="confirm"
pattern="I understand"
placeholder="I understand"
required
type="text"
/>
<div class="mt-4 flex justify-between">
<button class="danger-bg-color rounded px-2 py-1" type="submit" formaction="?/delete"
>Delete company</button
>
<button class="danger-bg-color rounded px-2 py-1" formaction="?/delete" type="submit"
>Delete company
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
onclick={closeConfirm}
type="button"
onclick={closeConfirm}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</form>

View File

@ -1,10 +1,6 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { type Actions, error, fail } from '@sveltejs/kit';
import {
addEmployerToCompany,
deleteCompany,
editCompany,
getCompany,
getCompanyEmployers,
getCompanyEmployersAndRequests,
removeEmployerFromCompany
} from '$lib/db/index.server';

View File

@ -20,11 +20,11 @@
<div class="content">
<div class="m-4">
<img
class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="User avatar"
onerror={logoFallback}
class="mb-2 inline-block rounded-lg"
height="80"
onerror={logoFallback}
src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
width="80"
/>
<div class="inline-block pl-4 align-top">
@ -66,13 +66,14 @@
>{user.lastSignIn?.toLocaleDateString('en-US', dateFormatOptions) ||
'unknown'}</td
>
<td class="material-symbols-outlined hover-bg-color danger-color m-1 rounded"
><button
<td class="material-symbols-outlined hover-bg-color danger-color m-1 rounded">
<button
onclick={() => {
idToRemove = user.id;
}}>close</button
></td
>
}}
>close
</button>
</td>
</tr>
{/if}
{/each}
@ -91,23 +92,26 @@
class="material-symbols-outlined"
onclick={() => {
idToRemove = null;
}}>close</button
>
}}
>close
</button>
</div>
<p>This will remove this employer from the company.</p>
<div class="mt-4 flex justify-between">
<button
class="danger-bg-color rounded px-2 py-1"
type="submit"
formaction="?/removeEmployer&userId={idToRemove}">Remove</button
>
formaction="?/removeEmployer&userId={idToRemove}"
>Remove
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
type="button"
onclick={() => {
idToRemove = null;
}}>Cancel</button
>
}}
>Cancel
</button>
</div>
</div>
</form>
@ -147,18 +151,20 @@
>{user.lastSignIn?.toLocaleDateString('en-US', dateFormatOptions) ||
'unknown'}</td
>
<td class="material-symbols-outlined"
><form method="POST" class="flex">
<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
>
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
>
formaction="?/removeEmployer&userId={user.id}"
>close
</button>
</form>
</td>
</tr>
{/if}
{/each}

View File

@ -1,7 +1,7 @@
import { type Actions, fail, redirect } from '@sveltejs/kit';
import { createCompany } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { getUserCompanyId, getUserPerms } from '$lib/index.server';
import { getUserPerms } from '$lib/index.server';
import type { Company } from '$lib/types';
export const actions: Actions = {

View File

@ -11,38 +11,38 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Create new company</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Name <span class="text-red-500">*</span>
<input
type="text"
name="name"
id="name"
placeholder="Name"
class="w-full rounded font-normal"
id="name"
name="name"
placeholder="Name"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Description <span class="text-red-500">*</span>
<textarea
name="description"
id="description"
rows="4"
placeholder="Description"
class="w-full rounded font-normal"
id="description"
name="description"
placeholder="Description"
required
rows="4"
></textarea>
</div>
<div class="mt-4 text-sm font-semibold">
Website <span class="text-red-500">*</span>
<input
type="text"
name="website"
id="website"
placeholder="Website"
class="w-full rounded font-normal"
id="website"
name="website"
placeholder="Website"
required
type="text"
/>
</div>
@ -51,9 +51,10 @@
{/if}
<button
class="dull-primary-bg-color mb-4 mt-6 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Create company</button
>
>Create company
</button>
</form>
</div>
</div>

View File

@ -15,7 +15,7 @@
</p>
<p>
If you are your company admin, first create your account (without inputting a code). Reach out
to a CareerConnect admin to elevate your account privaleges. Then, go to the company page, and
to a CareerConnect admin to elevate your account privileges. Then, go to the company page, and
use the button in the top right to create a new company. Once created, you will be able to see
the company code, which you can then give to your employees.
</p>

View File

@ -4,19 +4,23 @@
import { onMount } from 'svelte';
import { employmentTypeDisplayName, userState } from '$lib/shared.svelte';
import { PERMISSIONS } from '$lib/consts';
let details: Posting | undefined = $state<Posting>();
// Formating for all dates on the page
const dateFormatOptions: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'short',
day: 'numeric'
};
// If logo isn't found on the server, use a placeholder
function logoFallback(e: Event, posting: Posting | undefined) {
(e.target as HTMLImageElement).src =
`https://ui-avatars.com/api/?background=random&format=svg&name=${encodeURIComponent(posting?.company.name || 'COMPANY')}`;
}
// Fetch the detail pane content of a posting
async function fetchDetails(id: number) {
const response = await fetch(`/api/posting?id=${id}`);
details = await response.json();
@ -30,6 +34,7 @@
let { data }: PageProps = $props();
// Initial fetch of the first posting
onMount(async () => {
await fetchDetails(data.postings[0].id);
});
@ -48,22 +53,14 @@
<form action="" class="flex p-4">
<div class="search-bar">
<input
type="search"
name="searchQuery"
id="searchQuery"
placeholder="Search Postings"
class="search-cancel"
id="searchQuery"
name="searchQuery"
placeholder="Search Postings"
type="search"
/>
<button><span class="material-symbols-outlined">search</span></button>
</div>
<!-- <button class="hover-bg-color mx-2 rounded py-2 pl-3 pr-2 text-sm"-->
<!-- >Filter<span class="material-symbols-outlined icon-20 align-middle">arrow_drop_down</span-->
<!-- ></button-->
<!-- >-->
<!-- <button class="hover-bg-color rounded py-2 pl-3 pr-2 text-sm"-->
<!-- >Sort<span class="material-symbols-outlined icon-20 align-middle">arrow_drop_down</span-->
<!-- ></button-->
<!-- >-->
</form>
<div class="flex">
<div class="right-border inline-block w-1/3">

View File

@ -1,5 +1,5 @@
import type { PageServerLoad } from './$types';
import { getPostingWithCompanyUser, getPostings } from '$lib/db/index.server';
import { getPostingWithCompanyUser } from '$lib/db/index.server';
export const load: PageServerLoad = async ({ params }) => {
return {

View File

@ -24,16 +24,16 @@
<div class="bottom-border elevated-bg flex justify-between pb-2">
<div class="inline-block">
<img
class="inline-block rounded"
src="/uploads/logos/{data.posting.company.id}.jpg"
alt="Company Logo"
class="inline-block rounded"
height="64"
width="64"
onerror={(e) => logoFallback(e, data.posting)}
src="/uploads/logos/{data.posting.company.id}.jpg"
width="64"
/>
<div class="inline-block pl-2 align-top">
<h1>{data.posting.title}</h1>
<a href="/companies/{data.posting.company.id}" class="hover-hyperlink text-xl"
<a class="hover-hyperlink text-xl" href="/companies/{data.posting.company.id}"
>{data.posting.company.name}</a
>
</div>

View File

@ -1,11 +1,7 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import {
createApplication,
getPostingWithCompanyUser,
getUserWithCompany
} from '$lib/db/index.server';
import { getUserCompanyId, getUserId, getUserPerms } from '$lib/index.server';
import { createApplication, getPostingWithCompanyUser } from '$lib/db/index.server';
import { getUserId, getUserPerms } from '$lib/index.server';
import { PERMISSIONS } from '$lib/consts';
import type { Application } from '$lib/types';

View File

@ -24,12 +24,12 @@
<div class="bottom-border elevated-bg flex justify-between pb-2">
<div class="inline-block">
<img
class="inline-block rounded"
src="/uploads/logos/{data.posting?.company.id}.jpg"
alt="Company Logo"
class="inline-block rounded"
height="64"
width="64"
onerror={(e) => logoFallback(e, data.posting)}
src="/uploads/logos/{data.posting?.company.id}.jpg"
width="64"
/>
<div class="inline-block pl-2 align-top">
<h1>{data.posting.title}</h1>
@ -84,16 +84,16 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Apply</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Why do you believe you are the best fit for this role? <span class="text-red-500">*</span>
<textarea
name="candidateStatement"
id="candidateStatement"
rows="4"
placeholder="Answer here"
class="w-full rounded font-normal"
id="candidateStatement"
name="candidateStatement"
placeholder="Answer here"
required
rows="4"
></textarea>
</div>
<p>
@ -107,9 +107,10 @@
{/if}
<button
class="dull-primary-bg-color mb-4 mt-6 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Submit application</button
>
>Submit application
</button>
</form>
</div>
</div>

View File

@ -7,18 +7,17 @@
<div class="bottom-border h-10 pt-2 text-center">
<a
href="applications"
class="p-2 {page.url.pathname.endsWith('applications')
? 'primary-underline font-bold'
: 'low-emphasis-text low-emphasis-text-button'}"
href="applications"
><span class="material-symbols-outlined align-bottom">description</span> Applications</a
>
<a
href="edit"
class="p-2 {page.url.pathname.endsWith('edit')
? 'primary-underline font-bold'
: 'low-emphasis-text low-emphasis-text-button'}"
><span class="material-symbols-outlined align-bottom">work</span> Details</a
href="edit"><span class="material-symbols-outlined align-bottom">work</span> Details</a
>
</div>

View File

@ -1,11 +1,7 @@
import type { PageServerLoad } from './$types';
import { getUserCompanyId, getUserPerms } from '$lib/index.server';
import { PERMISSIONS } from '$lib/consts';
import {
deleteApplication,
getApplications,
getPostingWithCompanyUser
} from '$lib/db/index.server';
import { deleteApplication, getApplications } from '$lib/db/index.server';
import { type Actions, error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, cookies }) => {

View File

@ -1,8 +1,6 @@
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
import { employmentTypeDisplayName } from '$lib/shared.svelte';
import type { Application, Posting } from '$lib/types';
import type { Application } from '$lib/types';
import { onMount } from 'svelte';
const dateFormatOptions: Intl.DateTimeFormatOptions = {
@ -20,7 +18,7 @@
const acc = document.getElementsByClassName('accordion');
for (let i = 0; i < acc.length; i++) {
acc[i].addEventListener('click', function (this: HTMLElement, event: Event) {
acc[i].addEventListener('click', function (this: HTMLElement) {
this.classList.toggle('active');
this.children[1].innerHTML = this.classList.contains('active')
? 'arrow_drop_up'

View File

@ -1,5 +1,5 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { deletePosting, editPosting, getPosting, getPostings, getUser } from '$lib/db/index.server';
import { deletePosting, editPosting, getPosting } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts';
import { getUserCompanyId, getUserId, getUserPerms } from '$lib/index.server';
import type { Posting } from '$lib/types';

View File

@ -20,7 +20,7 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Edit {data.posting.title}</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
{#if !userState.companyId}
<div class="mt-4 text-sm font-semibold">
Company ID <span class="text-red-500">*</span>
@ -38,45 +38,45 @@
<div class="mt-4 text-sm font-semibold">
Title <span class="text-red-500">*</span>
<input
type="text"
name="title"
id="title"
placeholder="Title"
value={data.posting.title}
class="w-full rounded font-normal"
id="title"
name="title"
placeholder="Title"
required
type="text"
value={data.posting.title}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Description <span class="text-red-500">*</span>
<textarea
name="description"
id="description"
rows="4"
placeholder="Description"
class="w-full rounded font-normal"
required>{data.posting.description}</textarea
id="description"
name="description"
placeholder="Description"
required
rows="4">{data.posting.description}</textarea
>
</div>
<div class="mt-4 text-sm font-semibold">
Address <span class="text-red-500">*</span>
<input
type="text"
name="address"
id="address"
placeholder="Address"
value={data.posting.address}
class="w-full rounded font-normal"
id="address"
name="address"
placeholder="Address"
required
type="text"
value={data.posting.address}
/>
</div>
<div class="mt-4 text-sm font-semibold">
<label for="employmentType">Employment type <span class="text-red-500">*</span></label>
<select
name="employmentType"
id="employmentType"
value={data.posting.employmentType}
class="w-full rounded font-normal"
id="employmentType"
name="employmentType"
value={data.posting.employmentType}
>
<option value="full_time">Full time</option>
<option value="part_time">Part time</option>
@ -86,34 +86,34 @@
<div class="mt-4 text-sm font-semibold">
Wage (optional)
<input
type="text"
name="wage"
id="wage"
placeholder="Wage"
value={data.posting.wage}
class="w-full rounded font-normal"
id="wage"
name="wage"
placeholder="Wage"
type="text"
value={data.posting.wage}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Link to external posting information (optional)
<input
type="text"
name="link"
id="link"
placeholder="Link"
value={data.posting.link}
class="w-full rounded font-normal"
id="link"
name="link"
placeholder="Link"
type="text"
value={data.posting.link}
/>
</div>
<div class="mt-4 text-sm font-semibold">
Link to flyer (optional)
<input
type="text"
name="flyerLink"
id="flyerLink"
placeholder="Flyer link"
value={data.posting.flyerLink}
class="w-full rounded font-normal"
id="flyerLink"
name="flyerLink"
placeholder="Flyer link"
type="text"
value={data.posting.flyerLink}
/>
</div>
@ -123,20 +123,22 @@
<div class="mt-4 flex justify-between">
<button
class="dull-primary-bg-color mb-4 mt-2 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Save posting</button
>
>Save posting
</button>
<button
class="danger-bg-color mb-4 mt-2 rounded px-2 py-1"
onclick={openConfirm}
type="button"
onclick={openConfirm}>Delete posting</button
>
>Delete posting
</button>
</div>
</form>
</div>
</div>
</div>
<form id="deleteConfirmModal" method="POST" class="modal">
<form class="modal" id="deleteConfirmModal" method="POST">
<div class="modal-content">
<div class="mb-2 inline-flex w-full justify-between">
<h2 class="font-semibold">Are you sure?</h2>
@ -147,14 +149,16 @@
<div class="mt-4 flex justify-between">
<button
class="danger-bg-color rounded px-2 py-1"
formaction="?/delete&id={data.posting.id}"
type="submit"
formaction="?/delete&id={data.posting.id}">Delete posting</button
>
>Delete posting
</button>
<button
class="separator-borders bg-color rounded px-2 py-1"
onclick={closeConfirm}
type="button"
onclick={closeConfirm}>Cancel</button
>
>Cancel
</button>
</div>
</div>
</form>

View File

@ -12,7 +12,7 @@
<div class="bottom-border flex place-content-between">
<div class="p-3 font-semibold">Create new posting</div>
</div>
<form method="POST" class="px-4" autocomplete="off" use:enhance>
<form autocomplete="off" class="px-4" method="POST" use:enhance>
{#if !userState.companyId}
<div class="mt-4 text-sm font-semibold">
Company ID <span class="text-red-500">*</span>
@ -29,39 +29,39 @@
<div class="mt-4 text-sm font-semibold">
Title <span class="text-red-500">*</span>
<input
type="text"
name="title"
id="title"
placeholder="Title"
class="w-full rounded font-normal"
id="title"
name="title"
placeholder="Title"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Description <span class="text-red-500">*</span>
<textarea
name="description"
id="description"
rows="4"
placeholder="Description"
class="w-full rounded font-normal"
id="description"
name="description"
placeholder="Description"
required
rows="4"
></textarea>
</div>
<div class="mt-4 text-sm font-semibold">
Address <span class="text-red-500">*</span>
<input
type="text"
name="address"
id="address"
placeholder="Address"
class="w-full rounded font-normal"
id="address"
name="address"
placeholder="Address"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
<label for="employmentType">Employment type <span class="text-red-500">*</span></label>
<select name="employmentType" id="employmentType" class="w-full rounded font-normal">
<select class="w-full rounded font-normal" id="employmentType" name="employmentType">
<option value="full_time">Full time</option>
<option value="part_time">Part time</option>
<option value="internship">Internship</option>
@ -70,31 +70,31 @@
<div class="mt-4 text-sm font-semibold">
Wage (optional)
<input
type="text"
name="wage"
id="wage"
placeholder="Wage"
class="w-full rounded font-normal"
id="wage"
name="wage"
placeholder="Wage"
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Link to external posting information (optional)
<input
type="text"
name="link"
id="link"
placeholder="Link"
class="w-full rounded font-normal"
id="link"
name="link"
placeholder="Link"
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Link to flyer (optional)
<input
type="text"
name="flyerLink"
id="flyerLink"
placeholder="Flyer link"
class="w-full rounded font-normal"
id="flyerLink"
name="flyerLink"
placeholder="Flyer link"
type="text"
/>
</div>
@ -103,9 +103,10 @@
{/if}
<button
class="dull-primary-bg-color mb-4 mt-6 rounded px-2 py-1"
formaction="?/submit"
type="submit"
formaction="?/submit">Create posting</button
>
>Create posting
</button>
</form>
</div>
</div>

View File

@ -1,6 +1,5 @@
import * as dotenv from 'dotenv';
import { type Actions, type Cookies, fail, redirect } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';
import { type Actions, fail, redirect } from '@sveltejs/kit';
import { createUser } from '$lib/db/index.server';
import { setJWT } from '$lib/shared.server';

View File

@ -21,85 +21,85 @@
<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>
<form class="arrange-vertically" method="POST" use:enhance>
<div class="mt-4 text-sm font-semibold">
Username <span class="text-red-500">*</span>
<input
type="text"
name="username"
id="username"
placeholder="Username"
class="input-field w-full font-normal"
id="username"
name="username"
placeholder="Username"
required
type="text"
/>
</div>
<div class="relative mt-4 text-sm font-semibold">
Password <span class="text-red-500">*</span>
<input
type="password"
class="input-field w-full font-normal"
placeholder="Password"
name="password"
placeholder="Password"
required
type="password"
/>
</div>
<div class="relative mt-4 text-sm font-semibold">
Confirm password <span class="text-red-500">*</span>
<input
type="password"
class="input-field w-full font-normal"
placeholder="Password"
name="confirmPassword"
placeholder="Password"
required
type="password"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Full name <span class="text-red-500">*</span>
<input
type="text"
name="fullName"
id="fullName"
placeholder="Full name"
class="input-field w-full font-normal"
id="fullName"
name="fullName"
placeholder="Full name"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Email <span class="text-red-500">*</span>
<input
type="text"
name="email"
id="email"
placeholder="Email"
class="input-field w-full font-normal"
id="email"
name="email"
placeholder="Email"
required
type="text"
/>
</div>
<div class="mt-4 text-sm font-semibold">
Phone (optional)
<input
type="tel"
name="phone"
id="phone"
placeholder="Phone"
class="w-full rounded font-normal"
id="phone"
name="phone"
pattern="([0-9]\{3}) [0-9]\{3}-[0-9]\{3}"
placeholder="Phone"
type="tel"
/>
</div>
<div class="relative mt-4 text-sm font-semibold">
<label for="companyCode"> Company code (optional) </label>
<div class="relative">
<input
type="text"
name="companyCode"
id="companyCode"
placeholder="Company code"
class="input-field w-full pr-10 font-normal"
id="companyCode"
name="companyCode"
placeholder="Company code"
type="text"
/>
<a
type="button"
href="/info#company-codes"
class="hyperlink-color tooltip absolute inset-y-0 right-2 flex items-center"
href="/info#company-codes"
type="button"
>
<span class="material-symbols-outlined">info</span><span
class="tooltip-text font-sans text-sm font-normal">About company codes</span
@ -114,10 +114,11 @@
<button
class="primary-bg-color mt-8 w-full rounded px-2 py-2"
formaction="?/register"
type="submit"
formaction="?/register">Create account</button
>
<a href="/signin" class="low-emphasis-text-button mt-2">I already have an account.</a>
>Create account
</button>
<a class="low-emphasis-text-button mt-2" href="/signin">I already have an account.</a>
</form>
</div>
</div>

View File

@ -31,30 +31,30 @@
<div class="signin-container place-items-center pt-8">
<div class="separator-borders elevated content rounded-md p-8">
<h1 class="text-weight-semibold mb-4 text-center">Welcome Back!</h1>
<form method="POST" class="arrange-vertically" use:enhance>
<form class="arrange-vertically" method="POST" use:enhance>
<div class="bg-color my-2 rounded">
<input
class="input-field w-full"
type="text"
placeholder="Username"
name="username"
placeholder="Username"
required
type="text"
/>
</div>
<div class="relative w-full">
<div class="bg-color mt-4 rounded">
<input
type={passwordVisible ? 'text' : 'password'}
class="input-field w-full pr-10"
placeholder="Password"
name="password"
placeholder="Password"
required
type={passwordVisible ? 'text' : 'password'}
/>
</div>
<button
type="button"
onclick={showPassword}
class="absolute right-2.5 top-6 -translate-y-1/2 transform"
onclick={showPassword}
type="button"
>
<span class="material-symbols-outlined"
>{passwordVisible ? 'visibility' : 'visibility_off'}</span
@ -68,10 +68,11 @@
<button
class="primary-bg-color mt-8 w-full rounded px-2 py-2"
formaction="?/signin"
type="submit"
formaction="?/signin">Sign In</button
>
<a href="/register" class="low-emphasis-text-button mt-2"
>Sign In
</button>
<a class="low-emphasis-text-button mt-2" href="/register"
>Don't have an account? Register here.</a
>
</form>