feat: complete day 16
This commit is contained in:
+15
-15
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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') %>
|
||||
@@ -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') %>
|
||||
@@ -0,0 +1 @@
|
||||
<div style="height: 40px"></div>
|
||||
@@ -0,0 +1,11 @@
|
||||
<div
|
||||
style="
|
||||
background: #063970;
|
||||
color: white;
|
||||
padding: 24px 24px 12px 24px;
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
"
|
||||
>
|
||||
Calendar
|
||||
</div>
|
||||
@@ -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') %>
|
||||
@@ -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') %>
|
||||
Reference in New Issue
Block a user