Skip to content

Commit a19f916

Browse files
committed
Initial Commit -> Dashboard, NewReservation
implemented api.js support for making API Requests to the backend. added Dashboard, ReservationRow, NewReservation(with validation for user) components. updated Routes.js
0 parents  commit a19f916

40 files changed

+44293
-0
lines changed

.env.sample

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REACT_APP_API_BASE_URL=http://localhost:5000

.eslintcache

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/index.js":"1","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/App.js":"2","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Layout.js":"3","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Routes.js":"4","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Menu.js":"5","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/NotFound.js":"6","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/dashboard/Dashboard.js":"7","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/date-time.js":"8","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/ErrorAlert.js":"9","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/api.js":"10","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/format-reservation-date.js":"11","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/reservations/NewReservation.js":"12","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/useQuery.js":"13","/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/dashboard/ReservationRow.js":"14"},{"size":286,"mtime":1646216366406,"results":"15","hashOfConfig":"16"},{"size":333,"mtime":1646216366405,"results":"17","hashOfConfig":"16"},{"size":550,"mtime":1646216366406,"results":"18","hashOfConfig":"16"},{"size":1740,"mtime":1646257701136,"results":"19","hashOfConfig":"16"},{"size":1817,"mtime":1646216366406,"results":"20","hashOfConfig":"16"},{"size":327,"mtime":1646216366406,"results":"21","hashOfConfig":"16"},{"size":2385,"mtime":1646259100018,"results":"22","hashOfConfig":"16"},{"size":2386,"mtime":1646216366407,"results":"23","hashOfConfig":"16"},{"size":486,"mtime":1646216366406,"results":"24","hashOfConfig":"16"},{"size":2856,"mtime":1646218310730,"results":"25","hashOfConfig":"16"},{"size":628,"mtime":1646216366407,"results":"26","hashOfConfig":"16"},{"size":7681,"mtime":1646259206688,"results":"27","hashOfConfig":"16"},{"size":361,"mtime":1646216366407,"results":"28","hashOfConfig":"16"},{"size":1492,"mtime":1646227385301,"results":"29","hashOfConfig":"16"},{"filePath":"30","messages":"31","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},"fxdet9",{"filePath":"33","messages":"34","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"35","messages":"36","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"37","messages":"38","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"39","messages":"40","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"41","messages":"42","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"43","messages":"44","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"45","messages":"46","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"47","messages":"48","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"49","messages":"50","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"51","messages":"52","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"53","messages":"54","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"55","messages":"56","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},{"filePath":"57","messages":"58","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"32"},"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/index.js",[],["59","60"],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/App.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Layout.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Routes.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/Menu.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/NotFound.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/dashboard/Dashboard.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/date-time.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/layout/ErrorAlert.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/api.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/format-reservation-date.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/reservations/NewReservation.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/utils/useQuery.js",[],"/Users/brandon/Desktop/Web-Dev/Thinkful-Projects/restaurant-reservations-version-02/front-end/src/dashboard/ReservationRow.js",[],{"ruleId":"61","replacedBy":"62"},{"ruleId":"63","replacedBy":"64"},"no-native-reassign",["65"],"no-negated-in-lhs",["66"],"no-global-assign","no-unsafe-negation"]

.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
/test-report
11+
12+
# production
13+
/build
14+
15+
# misc
16+
.DS_Store
17+
.env
18+
.env.local
19+
.env.development.local
20+
.env.test.local
21+
.env.production.local
22+
.vercel
23+
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*

.screenshots/us-01-cancel-after.png

47.2 KB
Loading

.screenshots/us-01-cancel-before.png

42.4 KB
Loading

.screenshots/us-01-submit-before.png

53.1 KB
Loading

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Capstone: Restaurant Reservation System Frontend
2+
3+
This starter code for the backend of the capstone project in the Thinkful curriculum.
4+
5+
See [../README.md](../README.md) for detailed instructions.

e2e/api.js

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const fetch = require("cross-fetch");
2+
3+
const API_BASE_URL =
4+
process.env.REACT_APP_API_BASE_URL || "http://localhost:5000";
5+
6+
/**
7+
* Defines the default headers for these functions to work with `json-server`
8+
*/
9+
const headers = { "Content-Type": "application/json" };
10+
11+
/**
12+
* Fetch `json` from the specified URL and handle error status codes and ignore `AbortError`s
13+
*
14+
* This function is NOT exported because it is not needed outside of this file.
15+
*
16+
* @param url
17+
* the url for the requst.
18+
* @param options
19+
* any options for fetch
20+
* @param onCancel
21+
* value to return if fetch call is aborted. Default value is undefined.
22+
* @returns {Promise<Error|any>}
23+
* a promise that resolves to the `json` data or an error.
24+
* If the response is not in the 200 - 399 range the promise is rejected.
25+
*/
26+
async function fetchJson(url, options, onCancel) {
27+
try {
28+
const response = await fetch(url, options);
29+
30+
if (response.status === 204) {
31+
return null;
32+
}
33+
34+
const payload = await response.json();
35+
36+
if (payload.error) {
37+
return Promise.reject({ message: payload.error });
38+
}
39+
return payload.data;
40+
} catch (error) {
41+
if (error.name !== "AbortError") {
42+
console.error(error.stack);
43+
throw error;
44+
}
45+
return Promise.resolve(onCancel);
46+
}
47+
}
48+
49+
/**
50+
* Creates a new reservation
51+
* @returns {Promise<[reservation]>}
52+
* a promise that resolves to the newly created reservation.
53+
*/
54+
async function createReservation(reservation, signal) {
55+
const url = `${API_BASE_URL}/reservations`;
56+
const options = {
57+
method: "POST",
58+
headers,
59+
body: JSON.stringify({ data: reservation }),
60+
signal,
61+
};
62+
return await fetchJson(url, options, reservation);
63+
}
64+
65+
/**
66+
* Creates a new table
67+
* @returns {Promise<[table]>}
68+
* a promise that resolves to the newly created table.
69+
*/
70+
async function createTable(table, signal) {
71+
const url = `${API_BASE_URL}/tables`;
72+
const options = {
73+
method: "POST",
74+
headers,
75+
body: JSON.stringify({ data: table }),
76+
signal,
77+
};
78+
return await fetchJson(url, options, table);
79+
}
80+
81+
async function seatReservation(reservation_id, table_id) {
82+
const url = `${API_BASE_URL}/tables/${table_id}/seat`;
83+
const options = {
84+
method: "PUT",
85+
body: JSON.stringify({ data: { reservation_id } }),
86+
headers,
87+
};
88+
return await fetchJson(url, options, {});
89+
}
90+
91+
module.exports = {
92+
createReservation,
93+
createTable,
94+
seatReservation,
95+
};

e2e/jest.config.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
preset: "jest-puppeteer",
3+
testTimeout: 8000,
4+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const puppeteer = require("puppeteer");
2+
const { setDefaultOptions } = require('expect-puppeteer');
3+
const fs = require("fs");
4+
const fsPromises = fs.promises;
5+
6+
const baseURL = process.env.BASE_URL || "http://localhost:3000";
7+
8+
const onPageConsole = (msg) =>
9+
Promise.all(msg.args().map((event) => event.jsonValue())).then((eventJson) =>
10+
console.log(`<LOG::page console ${msg.type()}>`, ...eventJson)
11+
);
12+
13+
describe("US-01 - Create and list reservations - E2E", () => {
14+
let page;
15+
let browser;
16+
17+
beforeAll(async () => {
18+
await fsPromises.mkdir("./.screenshots", { recursive: true });
19+
setDefaultOptions({ timeout: 1000 });
20+
browser = await puppeteer.launch();
21+
});
22+
23+
beforeEach(async () => {
24+
page = await browser.newPage();
25+
page.on("console", onPageConsole);
26+
await page.setViewport({ width: 1920, height: 1080 });
27+
await page.goto(`${baseURL}/reservations/new`, { waitUntil: "load" });
28+
});
29+
30+
afterAll(async () => {
31+
await browser.close();
32+
});
33+
34+
describe("/reservations/new page", () => {
35+
test("filling and submitting form creates a new reservation and then displays the dashboard for the reservation date", async () => {
36+
const lastName = Date.now().toString(10);
37+
38+
await page.type("input[name=first_name]", "James");
39+
await page.type("input[name=last_name]", lastName);
40+
await page.type("input[name=mobile_number]", "800-555-1212");
41+
await page.type("input[name=reservation_date]", "01012035");
42+
await page.type("input[name=reservation_time]", "1330");
43+
await page.type("input[name=people]", "2");
44+
45+
await page.screenshot({
46+
path: ".screenshots/us-01-submit-before.png",
47+
fullPage: true,
48+
});
49+
50+
await Promise.all([
51+
page.click("[type=submit]"),
52+
page.waitForNavigation({ waitUntil: "networkidle0" }),
53+
]);
54+
55+
await page.screenshot({
56+
path: ".screenshots/us-01-submit-after.png",
57+
fullPage: true,
58+
});
59+
60+
await expect(page).toMatch(lastName);
61+
});
62+
63+
test("canceling form returns to previous page", async () => {
64+
await page.goto(`${baseURL}/dashboard`, { waitUntil: "networkidle0" });
65+
await page.goto(`${baseURL}/reservations/new`, {
66+
waitUntil: "networkidle0",
67+
});
68+
69+
const [cancelButton] = await page.$x(
70+
"//button[contains(translate(., 'ACDEFGHIJKLMNOPQRSTUVWXYZ', 'acdefghijklmnopqrstuvwxyz'), 'cancel')]"
71+
);
72+
73+
if (!cancelButton) {
74+
throw new Error("button containing cancel not found.");
75+
}
76+
77+
await page.screenshot({
78+
path: ".screenshots/us-01-cancel-before.png",
79+
fullPage: true,
80+
});
81+
82+
await Promise.all([
83+
cancelButton.click(),
84+
page.waitForNavigation({ waitUntil: "networkidle0" }),
85+
]);
86+
87+
await page.screenshot({
88+
path: ".screenshots/us-01-cancel-after.png",
89+
fullPage: true,
90+
});
91+
92+
expect(page.url()).toContain("/dashboard");
93+
});
94+
});
95+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const puppeteer = require("puppeteer");
2+
const { setDefaultOptions } = require('expect-puppeteer');
3+
const fs = require("fs");
4+
const fsPromises = fs.promises;
5+
6+
const baseURL = process.env.BASE_URL || "http://localhost:3000";
7+
8+
const onPageConsole = (msg) =>
9+
Promise.all(msg.args().map((event) => event.jsonValue())).then((eventJson) =>
10+
console.log(`<LOG::page console ${msg.type()}>`, ...eventJson)
11+
);
12+
13+
describe("US-02 - Create reservation on a future, working date - E2E", () => {
14+
let page;
15+
let browser;
16+
17+
beforeAll(async () => {
18+
await fsPromises.mkdir("./.screenshots", { recursive: true });
19+
setDefaultOptions({ timeout: 1000 });
20+
});
21+
22+
beforeEach(async () => {
23+
browser = await puppeteer.launch();
24+
page = await browser.newPage();
25+
page.on("console", onPageConsole);
26+
await page.setViewport({ width: 1920, height: 1080 });
27+
await page.goto(`${baseURL}/reservations/new`, { waitUntil: "load" });
28+
});
29+
30+
afterEach(async () => {
31+
await browser.close();
32+
});
33+
34+
describe("/reservations/new page", () => {
35+
beforeEach(async () => {
36+
await page.type("input[name=first_name]", "John");
37+
await page.type("input[name=last_name]", "Doe");
38+
await page.type("input[name=mobile_number]", "1234567890");
39+
await page.type("input[name=people]", "3");
40+
});
41+
42+
test("displays an error message if the date of the reservation occurs in the past", async () => {
43+
await page.type("input[name=reservation_date]", "12242020");
44+
await page.type("input[name=reservation_time]", "05:30PM");
45+
46+
await page.screenshot({
47+
path: ".screenshots/us-02-reservation-is-future-before.png",
48+
});
49+
50+
await page.click("button[type=submit]");
51+
52+
await page.screenshot({
53+
path: ".screenshots/us-02-reservation-is-future-after.png",
54+
});
55+
56+
const alerts = await page.$$(".alert-danger");
57+
expect(alerts.length).toBeGreaterThan(0);
58+
});
59+
60+
test("displays an error message if reservation date falls on a Tuesday", async () => {
61+
await page.type("input[name=reservation_date]", "02062035");
62+
await page.type("input[name=reservation_time]", "05:30PM");
63+
64+
await page.screenshot({
65+
path: ".screenshots/us-02-reservation-is-working-day-before.png",
66+
});
67+
68+
await page.click("button[type=submit]");
69+
70+
await page.screenshot({
71+
path: ".screenshots/us-02-reservation-is-working-day-after.png",
72+
});
73+
74+
const alerts = await page.$$(".alert-danger");
75+
expect(alerts.length).toBeGreaterThan(0);
76+
});
77+
});
78+
});

0 commit comments

Comments
 (0)