This commit is contained in:
louiscklaw
2025-02-01 01:58:47 +08:00
parent b3da7aaef5
commit 04dbefcbaf
1259 changed files with 280657 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
img {
width: 300px;
height: auto;
}
.word {
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
font-size: 50px;
margin: 30px;
}

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/404.css" />
<title>404 Not Found</title>
</head>
<body>
<div class="word">404 Not Found</div>
<img src="/asset/PngItem_6655441.png" />
</body>
</html>

View File

@@ -0,0 +1,59 @@
/* login nav css */
.header-nav {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 92px;
background-color: #efefd0;
}
.navbar-brand {
display: flex;
justify-content: flex-start;
align-items: center;
}
.navbar-brand > img {
border-radius: 10px;
}
.navbar > .navbar-brand {
font-size: 30px;
font-family: 'Pacifico', cursive;
margin: 5px;
}
.navbar > .btn {
font-size: 20px;
color: #293241;
}
.login-part {
height: 50%;
}
.landing-Page-login-btn {
font-size: 20px;
color: #495871;
font-family: 'Lato', sans-serif;
}
.landing-Page-login-btn > .bi {
margin: 0px 10px 0px 2px;
}
.login-part .dropdown-menu {
color: #495871;
font-family: 'Lato', sans-serif;
}
.logout {
cursor: pointer;
}
@media only screen and (max-width: 450px) {
.navbar-logo-text {
display: none;
}
}

Binary file not shown.

Binary file not shown.

BIN
_tecky/party-planner/backend/public/asset/party_icon.jpg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,41 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="161"
height="72"
fill="none"
viewBox="0 0 161 72"
>
<g filter="url(#a)" opacity=".482">
<rect
width="56.047"
height="7.606"
x="52.31"
y="47.698"
fill="#F29559"
rx="2"
/>
</g>
<rect width="160.667" height="51.718" y=".543" fill="#F29559" rx="6" />
<path
fill="#fff"
d="M33.651 32.18c-1.32 0-2.454-.264-3.402-.792a5.468 5.468 0 0 1-2.16-2.268c-.504-.984-.756-2.142-.756-3.474 0-1.332.252-2.484.756-3.456a5.354 5.354 0 0 1 2.16-2.25c.948-.54 2.082-.81 3.402-.81.852 0 1.656.132 2.412.396.768.264 1.398.642 1.89 1.134l-.756 1.836c-.54-.456-1.092-.786-1.656-.99a5.04 5.04 0 0 0-1.818-.324c-1.284 0-2.262.39-2.934 1.17-.672.768-1.008 1.866-1.008 3.294s.336 2.532 1.008 3.312c.672.78 1.65 1.17 2.934 1.17.648 0 1.254-.102 1.818-.306.564-.216 1.116-.552 1.656-1.008l.756 1.836c-.492.48-1.122.858-1.89 1.134a7.264 7.264 0 0 1-2.412.396Zm6.097-.18v-8.802h2.196v1.548c.432-1.044 1.356-1.62 2.772-1.728l.684-.054.144 1.908-1.296.126c-1.476.144-2.214.9-2.214 2.268V32h-2.286Zm11.246.18c-.996 0-1.854-.186-2.574-.558a4.024 4.024 0 0 1-1.674-1.584c-.384-.684-.576-1.494-.576-2.43 0-.912.186-1.71.558-2.394a4.186 4.186 0 0 1 1.566-1.602c.672-.396 1.434-.594 2.286-.594 1.248 0 2.232.396 2.952 1.188.732.792 1.098 1.872 1.098 3.24v.666h-6.3c.168 1.572 1.068 2.358 2.7 2.358.492 0 .984-.072 1.476-.216a4.19 4.19 0 0 0 1.35-.72l.63 1.512c-.42.348-.948.624-1.584.828a6.216 6.216 0 0 1-1.908.306Zm-.324-7.632c-.66 0-1.194.204-1.602.612-.408.408-.654.96-.738 1.656h4.428c-.048-.732-.252-1.29-.612-1.674-.348-.396-.84-.594-1.476-.594Zm8.64 7.632c-.636 0-1.206-.12-1.71-.36a3.1 3.1 0 0 1-1.17-1.008 2.532 2.532 0 0 1-.414-1.422c0-.648.168-1.158.504-1.53.336-.384.882-.66 1.638-.828.756-.168 1.77-.252 3.042-.252h.63v-.378c0-.6-.132-1.032-.396-1.296s-.708-.396-1.332-.396a5.21 5.21 0 0 0-1.512.234 6.547 6.547 0 0 0-1.566.684l-.648-1.53a4.948 4.948 0 0 1 1.098-.558 7.248 7.248 0 0 1 1.35-.378c.48-.096.93-.144 1.35-.144 1.284 0 2.238.3 2.862.9.624.588.936 1.506.936 2.754V32h-2.106v-1.404a2.508 2.508 0 0 1-.972 1.17c-.444.276-.972.414-1.584.414Zm.468-1.548c.588 0 1.074-.204 1.458-.612.396-.408.594-.924.594-1.548v-.396h-.612c-1.128 0-1.914.09-2.358.27-.432.168-.648.48-.648.936 0 .396.138.72.414.972.276.252.66.378 1.152.378Zm10.51 1.548c-2.328 0-3.492-1.152-3.492-3.456V24.89h-1.692v-1.692h1.692V20.57h2.25v2.628h2.664v1.692h-2.664v3.708c0 .576.126 1.008.378 1.296.252.288.66.432 1.224.432.168 0 .342-.018.522-.054.18-.048.366-.096.558-.144l.342 1.656c-.216.12-.492.216-.828.288a4.39 4.39 0 0 1-.954.108Zm7.126 0c-.996 0-1.854-.186-2.574-.558a4.024 4.024 0 0 1-1.674-1.584c-.384-.684-.576-1.494-.576-2.43 0-.912.186-1.71.558-2.394a4.186 4.186 0 0 1 1.566-1.602c.672-.396 1.434-.594 2.286-.594 1.248 0 2.232.396 2.952 1.188.732.792 1.098 1.872 1.098 3.24v.666h-6.3c.168 1.572 1.068 2.358 2.7 2.358.492 0 .984-.072 1.476-.216a4.19 4.19 0 0 0 1.35-.72l.63 1.512c-.42.348-.948.624-1.584.828a6.216 6.216 0 0 1-1.908.306Zm-.324-7.632c-.66 0-1.194.204-1.602.612-.408.408-.654.96-.738 1.656h4.428c-.048-.732-.252-1.29-.612-1.674-.348-.396-.84-.594-1.476-.594Zm14.932 7.632c-.996 0-1.854-.186-2.574-.558a4.024 4.024 0 0 1-1.674-1.584c-.384-.684-.576-1.494-.576-2.43 0-.912.186-1.71.558-2.394a4.186 4.186 0 0 1 1.566-1.602c.672-.396 1.434-.594 2.286-.594 1.248 0 2.232.396 2.952 1.188.732.792 1.098 1.872 1.098 3.24v.666h-6.3c.168 1.572 1.068 2.358 2.7 2.358.492 0 .984-.072 1.476-.216a4.19 4.19 0 0 0 1.35-.72l.63 1.512c-.42.348-.948.624-1.584.828a6.216 6.216 0 0 1-1.908.306Zm-.324-7.632c-.66 0-1.194.204-1.602.612-.408.408-.654.96-.738 1.656h4.428c-.048-.732-.252-1.29-.612-1.674-.348-.396-.84-.594-1.476-.594ZM99.89 32l-3.816-8.802h2.394l2.448 6.156 2.538-6.156h2.25L101.835 32h-1.944Zm11.062.18c-.996 0-1.854-.186-2.574-.558a4.024 4.024 0 0 1-1.674-1.584c-.384-.684-.576-1.494-.576-2.43 0-.912.186-1.71.558-2.394a4.186 4.186 0 0 1 1.566-1.602c.672-.396 1.434-.594 2.286-.594 1.248 0 2.232.396 2.952 1.188.732.792 1.098 1.872 1.098 3.24v.666h-6.3c.168 1.572 1.068 2.358 2.7 2.358.492 0 .984-.072 1.476-.216a4.19 4.19 0 0 0 1.35-.72l.63 1.512c-.42.348-.948.624-1.584.828a6.216 6.216 0 0 1-1.908.306Zm-.324-7.632c-.66 0-1.194.204-1.602.612-.408.408-.654.96-.738 1.656h4.428c-.048-.732-.252-1.29-.612-1.674-.348-.396-.84-.594-1.476-.594ZM116.371 32v-8.802h2.196v1.368c.3-.504.702-.888 1.206-1.152a3.738 3.738 0 0 1 1.728-.396c2.076 0 3.114 1.206 3.114 3.618V32h-2.25v-5.256c0-.684-.132-1.182-.396-1.494-.252-.312-.648-.468-1.188-.468-.66 0-1.188.21-1.584.63-.384.408-.576.954-.576 1.638V32h-2.25Zm14.561.18c-2.328 0-3.492-1.152-3.492-3.456V24.89h-1.692v-1.692h1.692V20.57h2.25v2.628h2.664v1.692h-2.664v3.708c0 .576.126 1.008.378 1.296.252.288.66.432 1.224.432.168 0 .342-.018.522-.054.18-.048.366-.096.558-.144l.342 1.656c-.216.12-.492.216-.828.288a4.39 4.39 0 0 1-.954.108Z"
/>
<defs>
<filter
id="a"
width="88.666"
height="40.225"
x="36.001"
y="31.388"
color-interpolation-filters="sRGB"
filterUnits="userSpaceOnUse"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur
result="effect1_foregroundBlur_38_26948"
stdDeviation="8.155"
/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,28 @@
export function addLoginNavbar() {
document.querySelector('head').innerHTML += `
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Pacifico&display=swap"
rel="stylesheet" />
<link rel="stylesheet" href="/addNavbar.css" />
`;
document.querySelector('.navbar-container').innerHTML = `
<div class="row">
<div class="col-12 header-nav">
<nav class="navbar">
<a class="navbar-brand" href="/index.html">
<img src="/asset/party_icon.jpg" width="54" height="54" class="d-inline-block align-top">
Party Planner
</a>
</nav>
<div class="login-part">
<div class="user-login">
<button type="button" class="btn landing-Page-login-btn" data-bs-toggle="modal" data-bs-target="#login-modal">
<i class="bi bi-person-circle"></i>
Login
</button>
</div>
</div>
</div>
</div>
`;
}

View File

@@ -0,0 +1,56 @@
export function addNavbar() {
// innerHTML only works for link and img and text, not Script tag
document.querySelector('head').innerHTML += `
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Pacifico&display=swap"
rel="stylesheet" />
<link rel="stylesheet" href="/addNavbar.css" />
`;
document.querySelector('.navbar-container').innerHTML = `
<div class="row">
<div class="col-12 header-nav">
<nav class="navbar">
<a class="navbar-brand" href="/index.html">
<img src="/asset/party_icon.jpg" width="54" height="54" class="d-inline-block align-top">
<div class="navbar-logo-text">
Party Planner
</div>
</a>
</nav>
<div class="login-part">
<div class="user-login dropdown">
<button type="button" class="btn landing-Page-login-btn dropdown-toggle"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/personalPage/personalPage.html">Edit Profile</a></li>
<li><a class="dropdown-item" href="/comment/comment.html">Comments</a></li>
<li><a class="dropdown-item logout">Log out</a></li>
</ul>
</div>
</div>
</div>
</div>
`;
document.querySelector('.logout').addEventListener('click', async () => {
const res = await fetch('/login/logout', {
method: 'POST'
});
if (res.status !== 200) {
const data = await res.json();
alert(data.msg);
return;
}
const result = await res.json();
if (result.status) {
window.location.href = '/';
} else {
alert('Unable to log out!');
}
});
}

View File

@@ -0,0 +1,13 @@
export async function loadName() {
const res = await fetch(`/login/name`);
if (res.status !== 200) {
const data = await res.json();
alert(data.msg);
return;
}
const result = await res.json();
if (result.status) {
const nameHTML = document.querySelector('.user-login button');
nameHTML.innerHTML = `<i class="bi bi-person-circle"></i>${result.user}`;
}
}

View File

@@ -0,0 +1,76 @@
.invitation-container {
height: 85vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
.invitation-frame {
width: 80%;
height: 90%;
background-color: #f9f9da;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 20px;
}
.event-name-container,
.datetime-container,
.venue-container,
.join-button-container {
font-size: 20px;
font-family: 'Calibri';
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.event-name-container,
.join-button-container {
margin: 20px 0;
}
.venue-container,
.datetime-container {
margin: 20px 40px;
}
.event-name-container {
font-size: 50px;
font-weight: bold;
}
.title {
font-size: 30px;
font-weight: bold;
}
.subtitle {
font-weight: bold;
}
#join-event-button {
font-size: 20px;
color: #293241;
font-family: 'Lato', sans-serif;
background-color: #f2d492;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
min-width: 110px;
border: none;
box-shadow: 0px 1px 12px #f2d492;
}
.content-container {
height: 50%;
width: 90%;
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="./invitation.css" />
<title>Invitation</title>
</head>
<body style="display: none">
<div class="navbar-container container">
<!-- NAVBAR: to be loaded with js -->
</div>
<div class="invitation-container">
<div class="invitation-frame">
<div class="event-name-container"></div>
<div class="content-container">
<div class="datetime-container"></div>
<div class="venue-container"></div>
</div>
<div class="join-button-container">
<button id="join-event-button" type="submit">Join Event Now</button>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
crossorigin="anonymous"
></script>
<script type="module" src="./invitation.js"></script>
</body>
</html>

View File

@@ -0,0 +1,107 @@
import { addNavbar } from '/functions/addNavbar.js';
import { loadName } from '/functions/loadName.js';
window.addEventListener('load', async () => {
await checkInvitationValidity();
});
async function checkInvitationValidity() {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const token = params.get('token');
const res = await fetch(`/events/invitation/validation/${eventId}/${token}`, {
method: 'POST'
});
const result = await res.json();
if (result.status) {
addNavbar();
await loadName();
// Load invitation page content
document.querySelector('.event-name-container').innerHTML = `
<div>
🎉 ${result.eventDetail.name}
</div>
`;
let dateString = '';
if (!result.eventDetail.start_datetime) {
dateString += 'To Be Confirmed';
} else {
dateString += `
<div class="subtitle">
Start
</div>
<div>
${new Date(result.eventDetail.start_datetime)
.toLocaleString('en-US', { hour12: false })
.replace(', ', ' ')
.slice(0, -3)}
</div>
<div class="subtitle">
End
</div>
<div>
${new Date(result.eventDetail.end_datetime).toLocaleString('en-US', { hour12: false }).replace(', ', ' ').slice(0, -3)}
</div>
`;
}
document.querySelector('.datetime-container').innerHTML = `
<div class="title">
Date & Time
</div>
<div>
${dateString}
</div>
`;
document.querySelector('.venue-container').innerHTML = `
<div class="title">
Venue
</div>
<div>
${result.eventDetail.venue ? result.eventDetail.venue : 'To Be Confirmed'}
</div>
`;
document.body.style.display = 'block';
} else {
if (result.login) {
alert('Invitation link is invalid or expired!');
window.location.href = '/index.html';
} else {
alert('Please log in or register to join event!');
window.location.href = `/?event-id=${eventId}&token=${token}`;
}
}
}
document.querySelector('#join-event-button').addEventListener('click', async () => {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const token = params.get('token');
const res = await fetch(`/events/invitation/participation/${eventId}/${token}`, {
method: 'POST'
});
const result = await res.json();
if (result.status) {
alert('You have successfully joined the event!');
window.location.href = `/eventSummary/event.html?event-id=${eventId}&is-creator=0`;
} else {
if (result.login) {
if (result.isCreator) {
alert('You are already a creator of the event, no need to join again!');
window.location.href = `/eventSummary/event.html?event-id=${eventId}&is-creator=1`;
} else if (result.joined) {
alert('You have already joined the event!');
window.location.href = `/eventSummary/event.html?event-id=${eventId}&is-creator=0`;
} else {
alert('Invitation link is invalid or expired!');
window.location.href = '/index.html';
}
} else {
alert('Please log in or register to join event!');
window.location.href = `/?event-id=${eventId}&token=${token}`;
}
}
});

View File

@@ -0,0 +1,302 @@
/* font family: */
/* font-family: 'Lato', sans-serif;
font-family: 'Pacifico', cursive; */
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #efefd0;
}
.navbar-brand > img {
border-radius: 10px;
}
.navbar > .navbar-brand {
font-size: 30px;
font-family: 'Pacifico', cursive;
margin: 5px;
}
.navbar > .btn {
font-size: 20px;
font-family: 'Lato', sans-serif;
color: #293241;
}
#logo-name {
font-size: 60px;
color: #293241;
font-family: 'Pacifico', cursive;
display: flex;
justify-content: center;
padding: 30px;
}
.login-part {
display: flex;
justify-items: center;
}
.user-login {
display: flex;
align-content: center;
}
.landing-Page-login-btn {
font-size: 20px;
color: #495871;
}
.landing-Page-login-btn > .bi {
margin: 0px 10px 0px 2px;
}
.introduction-box {
background-color: #efefd0;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
height: 500px;
margin-top: 20px;
}
.description-box {
background-color: #a6b7f4;
width: 400px;
border-radius: 20px;
height: 220px;
display: flex;
flex-direction: column;
align-items: center;
}
.description {
font-size: 30px;
font-family: 'Lato', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 30px;
}
.description-details {
font-size: 18px;
font-family: 'Lato', sans-serif;
padding: 10px;
width: 320px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.buttons > button {
font-size: 20px;
color: #293241;
font-family: 'Lato', sans-serif;
background-color: #f2d492;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
width: 200px;
box-sizing: border-box;
border: none;
box-shadow: 0px 1px 14px #f2d492;
touch-action: manipulation;
}
#logo-name {
font-size: 60px;
font-family: 'Pacifico', cursive;
display: flex;
justify-content: center;
padding: 30px;
}
.introduction-box {
background-color: #efefd0;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
height: 500px;
margin-top: 20px;
}
.description-box {
background-color: #a6b7f4;
width: 90%;
border-radius: 20px;
height: 220px;
display: flex;
flex-direction: column;
align-items: center;
}
.description {
font-size: 30px;
font-family: 'Lato', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 30px;
}
.description-details {
font-size: 18px;
font-family: 'Lato', sans-serif;
padding: 10px;
width: 320px;
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 20px;
}
.buttons > button {
font-size: 20px;
color: #293241;
font-family: 'Lato', sans-serif;
background-color: #f2d492;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
width: 200px;
box-sizing: border-box;
border: none;
box-shadow: 0px 1px 14px #f2d492;
touch-action: manipulation;
}
/* Modal */
.modal-login {
font-size: 14px;
color: #293241;
font-family: 'Lato', sans-serif;
background-color: #f2d492;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
width: 110px;
border: none;
box-shadow: 0px 1px 12px #f2d492;
}
.modal-footer {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
}
.modal-login, .modal-login:hover {
font-size: 14px;
color: #293241;
font-family: 'Lato', sans-serif;
background-color: #f2d492;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
width: 110px;
border: none;
box-shadow: 0px 1px 12px #f2d492;
text-decoration: none;
}
.modal-login:hover {
text-decoration: none;
}
.modal-header {
display: flex;
justify-content: space-between;
}
.modal-title {
font-size: 25px;
font-weight: bold;
color: #293241;
font-family: 'Lato', sans-serif;
}
.form-label {
color: #293241;
font-family: 'Lato', sans-serif;
}
.input-group {
display: flex;
flex-direction: column;
justify-content: center;
}
.input-title {
text-align: center;
}
.form-control {
border-width: 2px;
border-radius: 10px;
}
#user-email {
border-color: #495871;
}
#user-password {
border-color: #ee6c4d;
}
#login-form-submit,
#register-submit {
width: 100%;
}
.form-label {
position: absolute;
top: -12px;
left: 20px;
background-color: white;
}
.form-header {
margin: 0 0 5px 0;
}
#login-modal .input-panel {
margin: 20px 20px;
position: relative;
}
#register-modal .input-panel {
margin: 0 20px;
}
.user-email .form-label {
color: #495871;
}
.user-password .form-label {
color: #ee6c4d;
}
#or-separator {
position: absolute;
top: -18px;
background-color: white;
}
#google-separator {
position: relative;
}

View File

@@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="./landingPage.css" />
<title>Party Planner</title>
</head>
<body style="display: none">
<div class="navbar-container container">
<!-- NAVBAR:to be added by js -->
</div>
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="introduction-box">
<header id="logo-name">Party Planner</header>
<div class="description-box">
<div class="description">Easy to use!</div>
<div class="description-details">
Try using the Party Planner to manage your party agenda with friends!
</div>
</div>
</div>
</div>
<div class="col-md-4 buttons">
<button type="button" class="login-button" data-bs-toggle="modal" data-bs-target="#login-modal">
Log in
</button>
<button type="button" class="signup-button" data-bs-toggle="modal" data-bs-target="#register-modal">
Sign up
</button>
</div>
</div>
</div>
<div class="modal fade" id="login-modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div></div>
<h5 class="modal-title">LOG IN</h5>
<div class="exit-modal">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="input-group">
<div class="input-title">Welcome back! Please enter your details.</div>
<form>
<div class="user-email input-panel mb-3">
<div class="form-label">Email address</div>
<input type="email" class="form-control" id="user-email" aria-describedby="emailHelp"
required />
<div id="emailHelp" class="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div class="user-password input-panel mb-3">
<div class="form-label">Password</div>
<input type="password" class="form-control" id="user-password" required />
</div>
</form>
</div>
<div class="modal-footer">
<button id="login-form-submit" type="button" class="modal-login">Log in</button>
</div>
<div class="modal-footer" id="google-separator">
<span id="or-separator">or</span>
<a href="/connect/google" type="button" class="modal-login">
<i class="fa-brands fa-google"></i>
Google
</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="register-modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div></div>
<h5 class="modal-title">CREATE ACCOUNT</h5>
<div class="exit-modal">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="input-group">
<form class="register-form">
<div class="input-panel mb-3">
<div class="form-header">First Name *</div>
<input type="text" class="form-control" name="first_name" aria-label="first_name"
aria-describedby="basic-addon1" required />
</div>
<div class="input-panel mb-3">
<div class="form-header">Last Name *</div>
<input type="text" class="form-control" name="last_name" aria-label="last_name"
aria-describedby="basic-addon1" required />
</div>
<div class="input-panel mb-3">
<div class="form-header">Email Address *</div>
<input type="email" class="form-control" name="email" aria-label="email"
aria-describedby="basic-addon1" placeholder="name@example.com" required />
</div>
<div class="input-panel mb-3">
<div class="form-header">Phone Number *</div>
<input type="text" class="form-control" name="phone" aria-label="phone"
aria-describedby="basic-addon1" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" required />
<div class="form-text">
Phone number must be in the following format: e.g. 647-111-1111
</div>
</div>
<div class="input-panel mb-3">
<div class="form-header">Create Password *</div>
<input type="password" class="form-control" name="password" aria-label="password"
aria-describedby="basic-addon1" minlength="8" required />
<div class="form-text">Password must be 8 characters long</div>
</div>
<div class="input-panel mb-3">
<div class="form-header">Confirm Password *</div>
<input type="password" class="form-control" name="confirm_password"
aria-label="confirm_password" aria-describedby="basic-addon1" minlength="8"
required />
</div>
<div class="modal-footer">
<button id="register-submit" type="submit" class="btn btn-primary">Register</button>
</div>
</form>
<div class="modal-footer" id="google-separator">
<span id="or-separator">or</span>
<a href="/connect/google" type="button" class="modal-login">
<i class="fa-brands fa-google"></i>
Google
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
crossorigin="anonymous"></script>
<script type="module" src="./landingPage.js"></script>
</body>
</html>

View File

@@ -0,0 +1,116 @@
import { addNavbar } from "/functions/addNavbar.js";
import { loadName } from "/functions/loadName.js";
import { addLoginNavbar } from "/functions/addLoginNavbar.js";
window.addEventListener("load", async () => {
const res = await fetch("/login/name");
if (res.status === 200) {
addNavbar();
loadName();
} else {
addLoginNavbar();
}
document.body.style.display = "block";
});
async function loginFormSubmission() {
const userEmail = document.querySelector("#user-email").value;
const userPassword = document.querySelector("#user-password").value;
if (userEmail !== "" && userPassword !== "") {
const formObj = {
email: userEmail,
password: userPassword,
};
const res = await fetch("/login", {
method: "POST",
headers: {
"content-Type": "application/json",
},
body: JSON.stringify(formObj),
});
const loginResult = await res.json();
if (loginResult.status) {
const params = new URLSearchParams(window.location.search);
if (params.has("event-id") && params.has("token")) {
const eventId = params.get("event-id");
const token = params.get("token");
window.location.href = `/invitationPage/invitation.html?event-id=${eventId}&token=${token}`;
} else {
window.location.href = "/index.html";
}
} else {
alert("Unable to login!");
}
}
}
document.querySelector("#login-form-submit").addEventListener("click", () => {
loginFormSubmission();
});
document.querySelector("#user-password").addEventListener("keypress", (event) => {
if (event.key === "Enter") {
event.preventDefault();
loginFormSubmission();
}
});
document.querySelector(".register-form").addEventListener("submit", async function (e) {
e.preventDefault();
const form = e.target;
const first_name = form.first_name.value;
const last_name = form.last_name.value;
const email = form.email.value;
const phone = form.phone.value;
const password = form.password.value;
const confirm_password = form.confirm_password.value;
let formObj = {
first_name: first_name,
last_name: last_name,
email: email,
phone: phone,
password: password,
};
let dataPass = true;
// Checking data validity
if (!(password === confirm_password)) {
dataPass = false;
alert("Password and confirm password do not match!");
}
if (dataPass) {
const res = await fetch("/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formObj),
});
const registerResult = await res.json();
form.reset();
if (registerResult.duplicate) {
alert("Registered already!");
} else if (registerResult.passwordNotMatch) {
alert("Password not match!");
} else if (!registerResult.status) {
alert("Unable to Register, please try again!");
} else {
const params = new URLSearchParams(window.location.search);
if (params.has("event-id") && params.has("token")) {
alert("Successfully registered! Please login now to join the event.");
const myModal = bootstrap.Modal.getInstance(document.getElementById("register-modal"));
myModal.hide();
} else {
alert("Successfully registered!");
const myModal = bootstrap.Modal.getInstance(document.getElementById("register-modal"));
myModal.hide();
}
}
}
});