feat: complete day 16

This commit is contained in:
Ayobami
2025-07-21 22:23:26 +01:00
parent cbbb0ed4c4
commit dc35cfcb3f
20 changed files with 764 additions and 20 deletions
+15 -15
View File
@@ -1,11 +1,11 @@
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
const db = require("./models");
var cors = require("cors");
@@ -13,17 +13,17 @@ var cors = require("cors");
var app = express();
app.set("db", db);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(cors());
app.use(logger('dev'));
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, "public")));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use("/", indexRouter);
app.use("/users", usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
@@ -34,11 +34,11 @@ app.use(function (req, res, next) {
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
res.render("error");
});
module.exports = app;
+4 -1
View File
@@ -3,15 +3,18 @@
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
"start": "node ./bin/www",
"dev": "node --watch --env-file=.env ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.4",
"cors": "^2.8.5",
"debug": "~2.6.9",
"ejs": "^3.1.10",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"moment-timezone": "^0.6.0",
"morgan": "~1.9.1",
"mysql2": "^2.3.3",
"sequelize": "^6.15.1"
+120 -4
View File
@@ -1,9 +1,125 @@
var express = require('express');
var express = require("express");
var router = express.Router();
const moment = require("moment-timezone");
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
// Helper: Group timezones by region for the timezone selection screen
function getTimezoneGroups() {
const regions = {
"USA/CANADA": [
"America/Los_Angeles",
"America/Denver",
"America/New_York",
"America/Halifax",
],
EUROPE: [
"Europe/Berlin",
"Europe/Helsinki",
"Europe/Dublin",
"Europe/Samara",
],
ASIA: ["Asia/Hong_Kong", "Asia/Jakarta", "Asia/Kabul", "Asia/Kathmandu"],
"SOUTH AMERICA": [
"America/Bogota",
"America/Campo_Grande",
"America/Caracas",
"America/Lima",
],
};
const groups = {};
for (const region in regions) {
groups[region] = regions[region].map((tz) => ({
value: tz,
label: moment().tz(tz).zoneAbbr() + " " + moment().tz(tz).format("h:mma"),
}));
}
return groups;
}
// Helper: All timezones for dropdown
function getAllTimezones() {
return moment.tz.names().map((tz) => ({
value: tz,
label: tz,
}));
}
// Helper: Generate week days and slots
function getWeekDaysAndSlots(selectedTz) {
const weekDays = [];
const today = moment().tz(selectedTz);
for (let i = 0; i < 7; i++) {
const day = today.clone().add(i, "days");
weekDays.push({
label: day.format("dddd"),
date: day.format("MMMM D"),
dateISO: day.format("YYYY-MM-DD"),
slots: [
"9:00am",
"9:15am",
"9:30am",
"9:45am",
"10:00am",
"10:15am",
"10:30am",
"10:45am",
"11:00am",
"11:15am",
"11:30am",
"11:45am",
"12:00pm",
"12:15pm",
"12:30pm",
"12:45pm",
"1:00pm",
"1:15pm",
],
});
}
return { weekDays, maxSlots: 17 };
}
// Timezone selection screen
router.get("/timezone", function (req, res) {
res.render("timezone", {
timezones: getAllTimezones(),
timezoneGroups: getTimezoneGroups(),
});
});
// Calendar slot selection screen
router.get("/calendar", function (req, res) {
const selectedTimezone = req.query.tz || "America/New_York";
const { weekDays, maxSlots } = getWeekDaysAndSlots(selectedTimezone);
res.render("calendar", {
selectedTimezone,
weekDays,
maxSlots,
showPrevWeek: false, // For demo, only next week link
});
});
// Booking form screen
router.get("/book", function (req, res) {
const selectedTimezone = req.query.tz || "America/New_York";
res.render("booking-form", {
selectedTimezone,
});
});
// Booking POST (simulate success)
router.post("/book", function (req, res) {
// Here you would save booking info to DB
res.redirect("/success");
});
// Success screen
router.get("/success", function (req, res) {
res.render("success");
});
// Home page redirect to timezone selection
router.get("/", function (req, res) {
res.redirect("/timezone");
});
module.exports = router;
+56
View File
@@ -0,0 +1,56 @@
<%- 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>
<%- include('partials/footer') %>
+58
View File
@@ -0,0 +1,58 @@
<%- 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>
</div>
<%- include('partials/footer') %>
+1
View File
@@ -0,0 +1 @@
<div style="height: 40px"></div>
+11
View File
@@ -0,0 +1,11 @@
<div
style="
background: #063970;
color: white;
padding: 24px 24px 12px 24px;
font-size: 2rem;
font-weight: bold;
"
>
Calendar
</div>
+7
View File
@@ -0,0 +1,7 @@
<%- include('partials/header') %>
<div style="padding: 32px; max-width: 700px; margin: 0 auto">
<p style="margin-top: 32px; font-size: 1.2em">
Thanks for filling in the form. You will be emailed next steps.
</p>
</div>
<%- include('partials/footer') %>
+48
View File
@@ -0,0 +1,48 @@
<%- 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>
<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>
</div>
</div>
</div>
<%- include('partials/footer') %>