feat: complete day 17

This commit is contained in:
Ayobami
2025-07-22 19:21:32 +01:00
parent 29e6eb82c7
commit 743187b216
19 changed files with 721 additions and 226 deletions
+94 -53
View File
@@ -1,56 +1,97 @@
<%- include('partials/header') %>
<div style="padding: 32px; max-width: 700px; margin: 0 auto">
<h3>Pick a date and time</h3>
<p><b>Duration:</b> 1 hour</p>
<p>Your timezone: <%= selectedTimezone %></p>
<form action="/book" method="POST" style="margin-top: 32px">
<h4>Additional Information</h4>
<label>*Full Name</label>
<input
type="text"
name="fullName"
required
style="width: 100%; margin-bottom: 12px"
/>
<label>*Email</label>
<input
type="email"
name="email"
required
style="width: 100%; margin-bottom: 12px"
/>
<label>*Company</label>
<input
type="text"
name="company"
required
style="width: 100%; margin-bottom: 12px"
/>
<label>*Phone</label>
<input
type="tel"
name="phone"
required
style="width: 100%; margin-bottom: 12px"
/>
<label>*Your Notes</label>
<textarea
name="notes"
required
style="width: 100%; margin-bottom: 16px"
></textarea>
<button
type="submit"
style="
background: #063970;
color: #fff;
padding: 8px 24px;
border: none;
border-radius: 4px;
"
>
Done
</button>
</form>
<div class="calendar-container">
<div class="calendar-header">Calendar</div>
<div class="calendar-content">
<div class="calendar-labels">
<div class="calendar-label-main">Pick a date and time</div>
<div class="calendar-label-duration">Duration: <span>1 hour</span></div>
<div class="calendar-label-timezone">
Your timezone: <%= selectedTimezone %>
</div>
<% if (selectedDate && selectedTime) { %>
<div class="calendar-label-selected">
<strong>Selected:</strong> <%= selectedDate %> at <%= selectedTime %>
</div>
<% } %>
</div>
<form class="booking-form" id="bookingForm">
<input type="hidden" name="date" value="<%= selectedDate %>" />
<input type="hidden" name="time" value="<%= selectedTime %>" />
<input type="hidden" name="tz" value="<%= selectedTimezone %>" />
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text" id="fullName" name="name" required />
<div class="form-error" id="error-name"></div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<div class="form-error" id="error-email"></div>
</div>
<div class="form-group">
<label for="company">Company</label>
<input type="text" id="company" name="company" required />
<div class="form-error" id="error-company"></div>
</div>
<div class="form-group">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" required />
<div class="form-error" id="error-phone"></div>
</div>
<div class="form-group">
<label for="notes">Your Notes</label>
<textarea id="notes" name="notes" required></textarea>
<div class="form-error" id="error-notes"></div>
</div>
<button type="submit" class="form-submit-btn">Done</button>
<div class="form-error" id="error-date"></div>
<div class="form-error" id="error-time"></div>
<div class="form-error" id="error-timezone"></div>
<div class="form-error" id="error-general"></div>
</form>
</div>
</div>
<%- include('partials/footer') %>
<script>
document.getElementById("bookingForm").onsubmit = async function (e) {
e.preventDefault();
// Clear previous errors
document
.querySelectorAll(".form-error")
.forEach((el) => (el.textContent = ""));
const form = e.target;
const data = {
name: form.fullName.value,
email: form.email.value,
company: form.company.value,
phone: form.phone.value,
notes: form.notes.value,
date: form.date.value,
time: form.time.value,
timezone: form.tz.value,
};
try {
const res = await fetch("/api/bookings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
const result = await res.json();
if (result.success) {
window.location.href = "/success";
} else if (result.error) {
if (typeof result.error === "object") {
for (const key in result.error) {
const el = document.getElementById("error-" + key);
if (el) el.textContent = result.error[key];
}
} else {
document.getElementById("error-general").textContent = result.error;
}
}
} catch (err) {
document.getElementById("error-general").textContent =
"An error occurred. Please try again.";
}
};
</script>
+56 -54
View File
@@ -1,58 +1,60 @@
<%- include('partials/header') %>
<div style="padding: 32px; max-width: 1100px; margin: 0 auto">
<h3>Pick a date and time</h3>
<p><b>Duration:</b> 1 hour</p>
<p>Your timezone: <%= selectedTimezone %> <a href="/timezone">(Change)</a></p>
<div style="overflow-x: auto; margin-top: 32px">
<table style="width: 100%; border-collapse: collapse">
<thead>
<tr>
<% weekDays.forEach(function(day) { %>
<th style="padding: 8px 12px; border-bottom: 2px solid #eee">
<div style="font-weight: bold; font-size: 1.1em">
<%= day.label %>
</div>
<div style="font-size: 0.95em; color: #888"><%= day.date %></div>
</th>
<% }) %>
</tr>
</thead>
<tbody>
<% for (let i = 0; i < maxSlots; i++) { %>
<tr>
<% weekDays.forEach(function(day) { %>
<td style="padding: 8px 12px; text-align: center">
<% if (day.slots[i]) { %>
<form action="/book" method="GET" style="display: inline">
<input type="hidden" name="date" value="<%= day.dateISO %>" />
<input type="hidden" name="time" value="<%= day.slots[i] %>" />
<button
type="submit"
style="
background: #fff;
border: 1px solid #063970;
color: #063970;
border-radius: 4px;
padding: 4px 12px;
cursor: pointer;
"
>
<%= day.slots[i] %>
</button>
</form>
<% } %>
</td>
<% }) %>
</tr>
<% } %>
</tbody>
</table>
</div>
<div style="margin-top: 16px">
<% if (showPrevWeek) { %>
<a href="/calendar?week=prev">Previous Week</a>
<% } %>
<a href="/calendar?week=next" style="margin-left: 24px">Next Week</a>
<div class="calendar-container">
<div class="calendar-header">Calendar</div>
<div class="calendar-content">
<div class="calendar-labels">
<div class="calendar-label-main">Pick a date and time</div>
<div class="calendar-label-duration">Duration: <span>1 hour</span></div>
<div class="calendar-label-timezone">
Your timezone: <%= selectedTimezone %> <a href="/timezone">(Change)</a>
</div>
</div>
<div class="calendar-table-wrapper">
<table class="calendar-table">
<thead>
<tr>
<% weekDays.forEach(function(day) { %>
<th>
<div class="calendar-day-label"><%= day.label %></div>
<div class="calendar-date-label"><%= day.date %></div>
</th>
<% }) %>
</tr>
</thead>
<tbody>
<% for (let i = 0; i < maxSlots; i++) { %>
<tr>
<% weekDays.forEach(function(day) { %>
<td>
<% if (day.slots[i]) { %>
<form action="/book" method="get">
<input type="hidden" name="date" value="<%= day.dateISO %>" />
<input type="hidden" name="time" value="<%= day.slots[i] %>" />
<input
type="hidden"
name="tz"
value="<%= selectedTimezone %>"
/>
<button class="calendar-slot-btn"><%= day.slots[i] %></button>
</form>
<% } %>
</td>
<% }) %>
</tr>
<% } %>
</tbody>
</table>
</div>
<div class="calendar-week-nav">
<% if (showPrevWeek) { %>
<a href="/calendar?tz=<%= selectedTimezone %>&week=<%= prevWeek %>"
>Previous Week</a
>
<% } %>
<a href="/calendar?tz=<%= selectedTimezone %>&week=<%= nextWeek %>"
>Next Week</a
>
</div>
</div>
</div>
<%- include('partials/footer') %>
+10
View File
@@ -0,0 +1,10 @@
<%- include('partials/header') %>
<div class="calendar-container">
<div class="calendar-header">Error</div>
<div class="calendar-content">
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>
</div>
</div>
<%- include('partials/footer') %>
-6
View File
@@ -1,6 +0,0 @@
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}
+8
View File
@@ -0,0 +1,8 @@
<%- include('partials/header') %>
<div class="calendar-container">
<div class="calendar-header"><%= title %></div>
<div class="calendar-content">
<p>Welcome to <%= title %></p>
</div>
</div>
<%- include('partials/footer') %>
-5
View File
@@ -1,5 +0,0 @@
extends layout
block content
h1= title
p Welcome to #{title}
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body>
<%- body %>
</body>
</html>
-7
View File
@@ -1,7 +0,0 @@
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
+2 -1
View File
@@ -1 +1,2 @@
<div style="height: 40px"></div>
</body>
</html>
+9 -11
View File
@@ -1,11 +1,9 @@
<div
style="
background: #063970;
color: white;
padding: 24px 24px 12px 24px;
font-size: 2rem;
font-weight: bold;
"
>
Calendar
</div>
<!DOCTYPE html>
<html>
<head>
<title>Calendar</title>
<link rel="stylesheet" href="/stylesheets/style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body></body>
</html>
+4 -3
View File
@@ -1,7 +1,8 @@
<%- include('partials/header') %>
<div style="padding: 32px; max-width: 700px; margin: 0 auto">
<p style="margin-top: 32px; font-size: 1.2em">
<div class="calendar-container">
<div class="calendar-header">Calendar</div>
<div class="calendar-content success-message">
Thanks for filling in the form. You will be emailed next steps.
</p>
</div>
</div>
<%- include('partials/footer') %>
+71 -41
View File
@@ -1,48 +1,78 @@
<%- include('partials/header') %>
<div style="padding: 32px; max-width: 900px; margin: 0 auto">
<h3>Pick a date and time</h3>
<p><b>Duration:</b> 1 hour</p>
<label>Your timezone:</label>
<select name="timezone" id="timezone-select">
<% timezones.forEach(function(tz) { %>
<option value="<%= tz.value %>"><%= tz.label %></option>
<% }) %>
</select>
<hr />
<div style="display: flex; justify-content: center; margin-top: 40px">
<div
style="
background: #fff;
padding: 32px;
border-radius: 8px;
box-shadow: 0 2px 8px #0001;
min-width: 400px;
"
>
<h4>TIME ZONE</h4>
<div style="display: flex; gap: 32px">
<% Object.keys(timezoneGroups).forEach(function(region) { %>
<div>
<b><%= region %></b>
<% timezoneGroups[region].forEach(function(tz) { %>
<div>
<input type="radio" name="timezoneRadio" value="<%= tz.value %>" />
<%= tz.label %>
</div>
<% }) %>
</div>
<% }) %>
<div class="calendar-container">
<div class="calendar-header">Calendar</div>
<div class="calendar-content">
<div class="calendar-labels">
<div class="calendar-label-main">Pick a date and time</div>
<div class="calendar-label-duration">Duration: <span>1 hour</span></div>
<div class="calendar-label-timezone">
Your timezone:
<button id="select-timezone-btn" class="timezone-btn">
Please Select
</button>
</div>
<div style="margin-top: 16px">
<label
><input type="radio" name="format" value="ampm" checked />
am/pm</label
>
<label style="margin-left: 16px"
><input type="radio" name="format" value="24hr" /> 24hr</label
>
</div>
<!-- Modal Overlay -->
<div id="timezone-modal" class="modal-overlay" style="display: none">
<div class="modal-content">
<div class="modal-title">TIME ZONE</div>
<div class="modal-format-switch">
<label
><input type="radio" name="format" value="ampm" checked />
am/pm</label
>
<label><input type="radio" name="format" value="24hr" /> 24hr</label>
</div>
<div class="timezone-groups">
<% for (const group in timezoneGroups) { %>
<div class="timezone-group">
<div class="timezone-group-title"><%= group %></div>
<% timezoneGroups[group].forEach(function(tz) { %>
<label class="timezone-option">
<input type="radio" name="timezone" value="<%= tz.name %>" />
<span
class="tz-time"
data-am="<%= tz.time_am %>"
data-24="<%= tz.time_24 %>"
>
<%= tz.label %>
<span class="tz-time-value"><%= tz.time_am %></span>
</span>
</label>
<% }) %>
</div>
<% } %>
</div>
</div>
</div>
</div>
</div>
<%- include('partials/footer') %>
<script>
// Modal logic
const btn = document.getElementById("select-timezone-btn");
const modal = document.getElementById("timezone-modal");
btn.onclick = () => {
modal.style.display = "flex";
};
modal.onclick = (e) => {
if (e.target === modal) modal.style.display = "none";
};
document.querySelectorAll('input[name="timezone"]').forEach((el) => {
el.onclick = () => {
window.location.href = "/calendar?tz=" + encodeURIComponent(el.value);
};
});
// Time format toggle logic
document.querySelectorAll('input[name="format"]').forEach((el) => {
el.onchange = function () {
const is24 = this.value === "24hr";
document.querySelectorAll(".tz-time").forEach((span) => {
const am = span.getAttribute("data-am");
const t24 = span.getAttribute("data-24");
span.querySelector(".tz-time-value").textContent = is24 ? t24 : am;
});
};
});
</script>