Skip to content

Commit

Permalink
Merge pull request #42 from chingu-voyages/form-data-db
Browse files Browse the repository at this point in the history
Save Form Data to Database
  • Loading branch information
tdkent authored Nov 16, 2024
2 parents 1dcc19c + 90bf69c commit 08f4d77
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 73 deletions.
23 changes: 12 additions & 11 deletions client/components/form/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,17 @@ import TimeRangeInput from "./TimeRangeInput";
import { requestAppt } from "@/actions/form";

const schema = Joi.object({
name: Joi.string()
.pattern(new RegExp(/^[A-Za-z]+$/))
.min(2)
.max(30)
.required(),
email: Joi.string().email({
minDomainSegments: 2,
tlds: { allow: false },
}),
name: Joi.string().min(2).max(255).required().trim(),
email: Joi.string()
.email({
minDomainSegments: 2,
tlds: { allow: false },
})
.trim(),
phone: Joi.string()
.pattern(new RegExp(/^[0-9]*$/))
.length(10),
.length(10)
.trim(),
address: Joi.string().required(),
earlyTimeHour: Joi.number().min(9).max(16).required(),
lateTimeHour: Joi.number()
Expand All @@ -50,7 +49,9 @@ export default function Form() {
const [lateTime, setLateTime] = useState(
dayjs().hour(10).minute(0).second(0)
);
const [address, setAddress] = useState([]);
//! Adding default fake address to pass validation only
//! Update this once the address validator is fully functional
const [address, setAddress] = useState("123 Main St, Los Angeles, CA 90012");
// error state
const [errorMsg, setErrorMsg] = useState("");
const [errorPath, setErrorPath] = useState("");
Expand Down
72 changes: 70 additions & 2 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@ Server application built with Node.js
## Major Dependencies

- Express
- Mongoose
- Joi

## Getting Started

#### Get set up with Mongo

- Create user/password in RayVolution cluster
- Whitelist your IP address
- Copy connection string
- Recommended: install MongoDB Compass, connect to cluster using connection string
- Create your test database

#### Add .env file in root folder:

```
PORT = 4000
DATABASE_CONNECTION_STRING = <your-mongodb-connection-string-here>
PORT=4000
DATABASE_CONNECTION_STRING=<your-mongodb-connection-string-here>
MY_TEST_DB=<your-test-db-name>
```

#### Run the development server:
Expand All @@ -36,3 +47,60 @@ Navigate to `http://localhost:4000`. The following should be displayed in your b
```
{"message":"Hello team Radiant Minds"}
```

## Routes

### General

#### Test server

```
GET base_url
```

Response object

```
{ message }
```

#### Test database

```
GET base_url + '/database-health'
```

Response object

```
{ status }
```

### Form

#### Create new appointment request

```
POST base_url + '/form'
Request object:
{ name, email, phone, address, earlyTimeHour, lateTimeHour }
```

Validation

- `name`: Required. String.
- `email`: Required. Must be a valid email.
- `phone`: Required. Must be a 10-character string consisting of valid digits 0-9.
- `address`: Required. Must be a valid Los Angeles address.
- `earlyTimeHour`: Required. Integer. Min: 9, max: 16.
- `lateTimeHour`: Required. Integer: Min: 10, max: 17.

Behavior: A new row is added to the `forms` collection with default "Pending" status.

Response object

```
{ message }
```
19 changes: 8 additions & 11 deletions server/src/app.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import dotenv from "dotenv";
import formRouter from "./routes/form.js";
import mongoose from "mongoose";

// load env vars from .env
dotenv.config();
import formRouter from "./routes/form.route.js";
import connectDb from "./config/db.js";
import { port, dbConnectStr } from "./config/env.js";

const app = express();

connectDb();

// parse incoming request bodies
app.use(bodyParser.json());

Expand All @@ -20,7 +20,7 @@ app.use("/form", formRouter);

app.use("/database-health", async (_, res) => {
try {
await mongoose.connect(process.env.DATABASE_CONNECTION_STRING);
await mongoose.connect(dbConnectStr);
return res.json({ status: "connected" });
} catch (e) {
return res.json({ status: "error", error: e });
Expand All @@ -38,9 +38,6 @@ app.use((error, req, res, next) => {
});

// init server
app.listen(process.env.PORT, () => {
console.log(
"Rayvolution node/express server is listening on port",
process.env.PORT
);
app.listen(port, () => {
console.log("Rayvolution node/express server is listening on port", port);
});
16 changes: 16 additions & 0 deletions server/src/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import mongoose from "mongoose";
import { dbConnectStr, myTestDb } from "./env.js";

export default async function connectDb() {
try {
await mongoose.connect(dbConnectStr, {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: myTestDb,
});
console.log("Successfully connected to MongoDB");
} catch (error) {
console.log("Error with Mongo connection:", error);
process.exit(1);
}
}
7 changes: 7 additions & 0 deletions server/src/config/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import dotenv from "dotenv";
dotenv.config(); // load env vars from .env

// Env variables
export const port = process.env.PORT;
export const dbConnectStr = process.env.DATABASE_CONNECTION_STRING;
export const myTestDb = process.env.MY_TEST_DB;
42 changes: 42 additions & 0 deletions server/src/controllers/form.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import formSchema from "../validators/form.validator.js";
import Form from "../models/form.model.js";

export async function newAppt(req, res, next) {
try {
const { error } = formSchema.validate(req.body);

// handle validation errors
if (error) {
res.status(422);
return next({ message: error.details[0].message });
}

// send value to db
const { earlyTimeHour, lateTimeHour, ...rest } = req.body;
const newForm = new Form({
...rest,
timeRange: {
earlyTimeHour: "hello",
lateTimeHour,
},
});
await newForm.save();

// send success response
res.status(201);
res.json({ message: "ok" });
} catch (error) {
console.log(error);
// Mongo validation error
if (error.name === "ValidationError") {
res.status(400);
return next({
message: "Invalid request",
});
}
res.status(500);
return next({
message: "An internal server error occurred",
});
}
}
48 changes: 0 additions & 48 deletions server/src/controllers/form.js

This file was deleted.

22 changes: 22 additions & 0 deletions server/src/models/form.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import mongoose from "mongoose";

const formSchema = new mongoose.Schema({
name: String,
email: String,
phone: String,
address: String,
timeRange: {
earlyTimeHour: Number,
lateTimeHour: Number,
},
dateCreated: { type: Date, default: Date.now },
status: {
type: String,
enum: ["Pending", "Confirmed", "Cancelled", "Visited"],
default: "Pending",
},
});

const Form = mongoose.model("Form", formSchema, "forms");

export default Form;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from "express";
import { newAppt } from "../controllers/form.js";
import { newAppt } from "../controllers/form.controller.js";

const router = Router();

Expand Down
25 changes: 25 additions & 0 deletions server/src/validators/form.validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Joi from "joi";

const formSchema = Joi.object({
name: Joi.string().min(2).max(255).required().trim(),
email: Joi.string()
.email({
minDomainSegments: 2,
tlds: { allow: false },
})
.trim(),
phone: Joi.string()
.pattern(new RegExp(/^[0-9]*$/))
.length(10)
.trim(),
address: Joi.string().required(),
earlyTimeHour: Joi.number().min(9).max(16).required(),
lateTimeHour: Joi.number()
.min(10)
.max(17)
.required()
// check that lateTime > earlyTime
.greater(Joi.ref("earlyTimeHour")),
});

export default formSchema;

0 comments on commit 08f4d77

Please sign in to comment.