Commit 7c58b8a1 by Aman Sharma

Merge branch 'master' of 35.155.93.82:amansharma/react-rails-rspec

parents 3f31abfc 32904c98
class Api::V1::EmployeesController < ApplicationController
def index
employee = Employee.all.order(created_at: :desc)
render json: employee
end
def create
employee = Employee.create!(employee_params)
if employee
render json: employee
else
render json: employee.errors
end
end
def show
if employee
render json: employee
else
render json: employee.errors
end
end
def destroy
employee&.destroy
render json: { message: 'Employee deleted!' }
end
private
def employee_params
params.permit(:firstName, :lastName, :email, :phone, :dob, :address, :image)
end
def employee
@employee ||= Employee.find(params[:id])
end
end
import React from "react";
import { Link } from "react-router-dom";
class Employee extends React.Component {
constructor(props) {
super(props);
this.state = { employee: { } };
this.addHtmlEntities = this.addHtmlEntities.bind(this);
this.deleteEmployee = this.deleteEmployee.bind(this);
}
componentDidMount() {
const {
match: {
params: { id }
}
} = this.props;
const url = `/api/v1/show/${id}`;
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.setState({ employee: response }))
.catch(() => this.props.history.push("/employees"));
}
addHtmlEntities(str) {
return String(str)
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">");
}
render() {
const { employee } = this.state;
// let ingredientList = "No ingredients available";
// if (employee.ingredients.length > 0) {
// ingredientList = recipe.ingredients
// .split(",")
// .map((ingredient, index) => (
// <li key={index} className="list-group-item">
// {ingredient}
// </li>
// ));
// }
// const recipeInstruction = this.addHtmlEntities(recipe.instruction);
return (
<div className="">
<div className="hero position-relative d-flex align-items-center justify-content-center">
{/* <img
src={recipe.image}
alt={`${recipe.name} image`}
className="img-fluid position-absolute"
/> */}
<div className="overlay bg-dark position-absolute" />
<h1 className="display-4 position-relative text-white">
{employee.firstName + ' ' + employee.lastName}
</h1>
<h3 className="display-4 position-relative text-white">
{employee.email}
</h3>
<h3 className="display-4 position-relative text-white">
{employee.phone}
</h3>
<h3 className="display-4 position-relative text-white">
{employee.dob}
</h3>
<h3 className="display-4 position-relative text-white">
{employee.address}
</h3>
</div>
<div className="container py-5">
<div className="row">
{/* <div className="col-sm-12 col-lg-3">
<ul className="list-group">
<h5 className="mb-2">Ingredients</h5>
{ingredientList}
</ul>
</div>
<div className="col-sm-12 col-lg-7">
<h5 className="mb-2">Preparation Instructions</h5>
<div
dangerouslySetInnerHTML={{
__html: `${recipeInstruction}`
}}
/>
</div> */}
<div className="col-sm-12 col-lg-2">
<button type="button" className="btn btn-danger" onClick={this.deleteEmployee}>
Delete Employee
</button>
</div>
</div>
<Link to="/employees" className="btn btn-link">
Back to Employees
</Link>
</div>
</div>
);
}
deleteEmployee() {
const {
match: {
params: { id }
}
} = this.props;
const url = `/api/v1/destroy/${id}`;
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "DELETE",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json"
}
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(() => this.props.history.push("/employees"))
.catch(error => console.log(error.message));
}
}
export default Employee;
\ No newline at end of file
import React from "react";
import { Link } from "react-router-dom";
class Employees extends React.Component {
constructor(props) {
super(props);
this.state = {
employees: []
};
}
componentDidMount() {
const url = "/api/v1/employees/index";
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.setState({ employees: response }))
.catch(() => this.props.history.push("/"));
}
render() {
const { employees } = this.state;
const allEmployees = employees.map((employee, index) => (
<div key={index} className="col-md-6 col-lg-4">
<div className="card mb-4">
{/* <img
src={recipe.image}
className="card-img-top"
alt={`${recipe.name} image`}
/> */}
<div className="card-body">
<h5 className="card-title">{employee.firstName + ' ' + employee.lastName}</h5>
<Link to={`/employee/${employee.id}`} className="btn custom-button">
View Employee
</Link>
</div>
</div>
</div>
));
const noEmployee = (
<div className="vw-100 vh-50 d-flex align-items-center justify-content-center">
<h4>
No Employee yet. Why not <Link to="/new_enployee">create one</Link>
</h4>
</div>
);
return (
<>
<section className="jumbotron jumbotron-fluid text-center">
<div className="container py-5">
<h1 className="display-4">Employee</h1>
{/* <p className="lead text-muted">
We’ve pulled together our most popular recipes, our latest
additions, and our editor’s picks, so there’s sure to be something
tempting for you to try.
</p> */}
</div>
</section>
<div className="py-5">
<main className="container">
<div className="text-right mb-3">
<Link to="/employee" className="btn custom-button">
Create New Employee
</Link>
</div>
<div className="row">
{employees.length > 0 ? allEmployees : noEmployee}
</div>
<Link to="/" className="btn btn-link">
Home
</Link>
</main>
</div>
</>
);
}
}
export default Employees;
\ No newline at end of file
...@@ -17,6 +17,14 @@ export default () => ( ...@@ -17,6 +17,14 @@ export default () => (
> >
View Recipes View Recipes
</Link> </Link>
<br></br>
<Link
to="/employees"
className="btn btn-lg custom-button"
role="button"
>
View Employees
</Link>
</div> </div>
</div> </div>
</div> </div>
......
import React from "react";
import { Link } from "react-router-dom";
class NewEmployee extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: "",
email: "",
phone: "",
dob: "",
address: ""
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.stripHtmlEntities = this.stripHtmlEntities.bind(this);
}
stripHtmlEntities(str) {
return String(str)
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
onChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
onSubmit(event) {
event.preventDefault();
const url = "/api/v1/employees/create";
const { firstName, lastName, email, phone, dob, address } = this.state;
if (firstName.length == 0 || lastName.length == 0 || email.length == 0 || phone.length == 0)
return;
const body = {
firstName,
lastName,
phone,
email,
dob,
address
};
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.props.history.push(`/employee/${response.id}`))
.catch(error => console.log(error.message));
}
render() {
return (
<div className="container mt-5">
<div className="row">
<div className="col-sm-12 col-lg-6 offset-lg-3">
<h1 className="font-weight-normal mb-5">
Add New Employee Details
</h1>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label htmlFor="employeeFirstName">First Name</label>
<input
type="text"
name="firstName"
id="employeeFirstName"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="employeeLastName">Last Name</label>
<input
type="text"
name="lastName"
id="employeeLastName"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
id="email"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="phone">Phone Number</label>
<input
type="text"
name="phone"
id="phone"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="dob">D.O.B.</label>
<input
type="date"
name="dob"
id="dob"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input
type="text"
name="address"
id="address"
className="form-control"
required
onChange={this.onChange}
/>
</div>
<button type="submit" className="btn custom-button mt-3">
Create Employee
</button>
<Link to="/employees" className="btn btn-link mt-3">
Back to Employees
</Link>
</form>
</div>
</div>
</div>
);
}
}
export default NewEmployee;
\ No newline at end of file
...@@ -4,6 +4,9 @@ import Home from "../components/Home"; ...@@ -4,6 +4,9 @@ import Home from "../components/Home";
import Recipes from "../components/Recipes"; import Recipes from "../components/Recipes";
import Recipe from "../components/Recipe"; import Recipe from "../components/Recipe";
import NewRecipe from "../components/NewRecipe"; import NewRecipe from "../components/NewRecipe";
import Employees from "../components/Employees";
import Employee from "../components/Employee";
import NewEmployee from "../components/NewEmployee";
export default ( export default (
<Router> <Router>
...@@ -12,6 +15,9 @@ export default ( ...@@ -12,6 +15,9 @@ export default (
<Route path="/recipes" exact component={Recipes} /> <Route path="/recipes" exact component={Recipes} />
<Route path="/recipe/:id" exact component={Recipe} /> <Route path="/recipe/:id" exact component={Recipe} />
<Route path="/recipe" exact component={NewRecipe} /> <Route path="/recipe" exact component={NewRecipe} />
<Route path="/employees" exact component={Employees} />
<Route path="/employee/:id" exact component={Employee} />
<Route path="/employee" exact component={NewEmployee} />
</Switch> </Switch>
</Router> </Router>
); );
\ No newline at end of file
class Employee < ApplicationRecord
validates :firstName, presence: true
validates :lastName, presence: true
validates :email, presence: true
validates :phone, presence: true
end
Rails.application.routes.draw do Rails.application.routes.draw do
namespace :api do namespace :api do
namespace :v1 do namespace :v1 do
get 'employees/index'
post 'employees/create'
get '/show/:id', to: 'employees#show'
delete '/destroy/:id', to: 'employees#destroy'
end
end
namespace :api do
namespace :v1 do
get 'recipes/index' get 'recipes/index'
post 'recipes/create' post 'recipes/create'
get '/show/:id', to: 'recipes#show' get '/show/:id', to: 'recipes#show'
......
class CreateEmployees < ActiveRecord::Migration[5.2]
def change
create_table :employees do |t|
t.string :firstName, null: false
t.string :lastName, null: false
t.string :phone, null: false
t.string :address, null: false
t.string :email, null: false
t.string :dob, null: false
t.string :image
t.timestamps
end
end
end
...@@ -10,11 +10,23 @@ ...@@ -10,11 +10,23 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_09_29_085325) do ActiveRecord::Schema.define(version: 2020_09_30_024252) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
create_table "employees", force: :cascade do |t|
t.string "firstName", null: false
t.string "lastName", null: false
t.string "phone", null: false
t.string "address", null: false
t.string "email", null: false
t.string "dob", null: false
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "recipes", force: :cascade do |t| create_table "recipes", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.text "ingredients", null: false t.text "ingredients", null: false
......
...@@ -12,4 +12,13 @@ ...@@ -12,4 +12,13 @@
ingredients: '227g tub clotted cream, 25g butter, 1 tsp cornflour,100g parmesan, grated nutmeg, 250g fresh fettuccine or tagliatelle, snipped chives or chopped parsley to serve (optional)', ingredients: '227g tub clotted cream, 25g butter, 1 tsp cornflour,100g parmesan, grated nutmeg, 250g fresh fettuccine or tagliatelle, snipped chives or chopped parsley to serve (optional)',
instruction: 'In a medium saucepan, stir the clotted cream, butter, and cornflour over a low-ish heat and bring to a low simmer. Turn off the heat and keep warm.' instruction: 'In a medium saucepan, stir the clotted cream, butter, and cornflour over a low-ish heat and bring to a low simmer. Turn off the heat and keep warm.'
) )
Employee.create(
firstName: "Employee #{i}",
lastName: "Last Name",
email: "email#{i}@agami.com",
phone: "00000000#{i}",
address: "Noida, UP",
dob: "11/11/1111"
)
end end
\ No newline at end of file
require 'rails_helper'
RSpec.describe Employee, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'rails_helper'
RSpec.describe "Api::V1::Employees", type: :request do
describe "GET /index" do
it "returns http success" do
get "/api/v1/employees/index"
expect(response).to have_http_status(:success)
end
end
describe "GET /create" do
it "returns http success" do
get "/api/v1/employees/create"
expect(response).to have_http_status(:success)
end
end
describe "GET /show" do
it "returns http success" do
get "/api/v1/employees/show"
expect(response).to have_http_status(:success)
end
end
describe "GET /destroy" do
it "returns http success" do
get "/api/v1/employees/destroy"
expect(response).to have_http_status(:success)
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment