Skip to content

Commit

Permalink
add solution to 18, rest client service, and modal
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwgreene committed Oct 2, 2020
1 parent 73a709f commit c877aca
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 85 deletions.
22 changes: 3 additions & 19 deletions demo-app/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,12 @@
"price": 45000
},
{
"id": 2,
"make": "Tesla",
"model": "S",
"year": 2018,
"color": "red",
"price": 100000
},
{
"make": "a",
"model": "a",
"year": 1900,
"color": "a",
"price": 2000,
"id": 3
},
{
"make": "b",
"model": "b",
"year": 1900,
"color": "b",
"price": 0,
"id": 4
"color": "purple",
"price": 100000,
"id": 2
}
]
}
2 changes: 1 addition & 1 deletion demo-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"scripts": {
"start": "run-p web rest",
"web": "react-scripts start",
"rest": "json-server --port 3060 ./db.json",
"rest": "json-server --port 3060 --delay 5000 ./db.json",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
Expand Down
1 change: 1 addition & 0 deletions demo-app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<div id="modal"></div>
</body>
</html>
85 changes: 55 additions & 30 deletions demo-app/src/actions/carToolActions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Action, AnyAction, Dispatch } from 'redux';

import { Car, NewCar, CarKeys } from '../models/car';
import { CarsRestClient } from '../services/CarsRestClient';

const carsRestClient = new CarsRestClient('http://localhost:3060/cars');

export const REFRESH_CARS_REQUEST_ACTION = 'REFRESH_CARS_REQUEST_ACTION';
export const REFRESH_CARS_DONE_ACTION = 'REFRESH_CARS_DONE_ACTION';
export const APPEND_CAR_REQUEST_ACTION = 'APPEND_REQUEST_CAR';
export const REPLACE_CAR_ACTION = 'REPLACE_CAR';
export const REMOVE_CAR_ACTION = 'REMOVE_CAR';
export const REPLACE_CAR_REQUEST_ACTION = 'REPLACE_REQUEST_CAR';
export const REMOVE_CAR_REQUEST_ACTION = 'REMOVE_REQUEST_CAR';
export const EDIT_CAR_ACTION = 'EDIT_CAR';
export const CANCEL_CAR_ACTION = 'CANCEL_CAR';
export const SORT_CARS_ACTION = 'SORT_CARS';
Expand Down Expand Up @@ -61,8 +64,7 @@ export const refreshCars = () => {
// thunk function
return async (dispatch: Dispatch) => {
dispatch(createRefreshCarsRequestCarAction());
const res = await fetch('http://localhost:3060/cars');
const cars = await res.json();
const cars = await carsRestClient.all();
dispatch(createRefreshCarsDoneCarAction(cars));
};
};
Expand Down Expand Up @@ -98,61 +100,84 @@ export const createAppendCarRequestAction: CreateAppendCarRequestAction = (
export const appendCar = (car: NewCar) => {
return async (dispatch: Dispatch<any>) => {
dispatch(createAppendCarRequestAction(car));

await fetch('http://localhost:3060/cars', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(car),
});

await carsRestClient.append(car);
const refreshCarsThunk = refreshCars();
refreshCarsThunk(dispatch);

//dispatch(refreshCars());
};
};

// Existing Car Action
// Replace Car Request Action

export interface ReplaceCarAction extends Action<typeof REPLACE_CAR_ACTION> {
export interface ReplaceCarRequestAction
extends Action<typeof REPLACE_CAR_REQUEST_ACTION> {
payload: { car: Car };
}

export type CreateReplaceCarAction = (car: Car) => ReplaceCarAction;
export type CreateReplaceCarRequestAction = (
car: Car,
) => ReplaceCarRequestAction;

export function isReplaceCarAction(
export function isReplaceCarRequestAction(
action: AnyAction,
): action is ReplaceCarAction {
return action?.type === REPLACE_CAR_ACTION;
): action is ReplaceCarRequestAction {
return action?.type === REPLACE_CAR_REQUEST_ACTION;
}

export const createReplaceCarAction: CreateReplaceCarAction = (car) => ({
type: REPLACE_CAR_ACTION,
export const createReplaceCarRequestAction: CreateReplaceCarRequestAction = (
car,
) => ({
type: REPLACE_CAR_REQUEST_ACTION,
payload: { car },
});

// End Existing Car Action
// End Replace Car Request Action

// Remove Car Action
// Replace Car Thunk Function

export interface RemoveCarAction extends Action<typeof REMOVE_CAR_ACTION> {
export const replaceCar = (car: Car) => {
return async (dispatch: Dispatch) => {
dispatch(createReplaceCarRequestAction(car));
await carsRestClient.replace(car);
refreshCars()(dispatch);
};
};

// Remove Car RequestAction

export interface RemoveCarRequestAction
extends Action<typeof REMOVE_CAR_REQUEST_ACTION> {
payload: { carId: number };
}

export type CreateRemoveCarAction = (carId: number) => RemoveCarAction;
export type CreateRemoveCarRequestAction = (
carId: number,
) => RemoveCarRequestAction;

export function isRemoveCarAction(
export function isRemoveCarRequestAction(
action: AnyAction,
): action is RemoveCarAction {
return action.type === REMOVE_CAR_ACTION;
): action is RemoveCarRequestAction {
return action.type === REMOVE_CAR_REQUEST_ACTION;
}

export const createRemoveCarAction: CreateRemoveCarAction = (carId) => ({
type: REMOVE_CAR_ACTION,
export const createRemoveCarRequestAction: CreateRemoveCarRequestAction = (
carId,
) => ({
type: REMOVE_CAR_REQUEST_ACTION,
payload: { carId },
});

// End Remove Action
// End Remove Car Request Action

// Remove Car Thunk Function

export const removeCar = (carId: number) => {
return async (dispatch: Dispatch) => {
dispatch(createRemoveCarRequestAction(carId));
await carsRestClient.remove(carId);
refreshCars()(dispatch);
};
};

// Edit Car Action

Expand Down
3 changes: 2 additions & 1 deletion demo-app/src/components/CarTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { ToolHeader } from './ToolHeader';
import { CarTable } from './CarTable';
import { CarForm } from './CarForm';
import { ModalDialog } from './ModalDialog';
import { CarToolState } from '../models/carToolStore';
import { Car, CarKeys, NewCar } from '../models/car';

Expand Down Expand Up @@ -32,7 +33,6 @@ export function CarTool(props: CarToolProps) {
} = props;
return (
<>
{isLoading && <div>Calling the REST API</div>}
<ToolHeader headerText="Car Tool" />
<button type="button" onClick={refreshCars}>
Refresh Cars
Expand All @@ -48,6 +48,7 @@ export function CarTool(props: CarToolProps) {
onSortCars={sortCars}
/>
<CarForm buttonText="Add Car" onSubmitCar={addCar} />
{isLoading && <ModalDialog>Please wait...</ModalDialog>}
</>
);
}
25 changes: 25 additions & 0 deletions demo-app/src/components/ModalDialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#modal > div {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;

display: flex;
justify-content: center;
align-items: center;

background-color: rgba(150, 150, 150, 0.5);
}

#modal > div > p {
height: 100px;
width: 200px;
text-align: center;
background-color: navy;
color: white;
border: 1px solid white;
display: flex;
justify-content: center;
align-items: center;
}
17 changes: 17 additions & 0 deletions demo-app/src/components/ModalDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { ReactNode } from 'react';
import { createPortal } from 'react-dom';

import './ModalDialog.css';

export type ModalDialogProps = {
children: ReactNode;
};

export function ModalDialog(props: ModalDialogProps) {
return createPortal(
<div>
<p>{props.children}</p>
</div>,
document.querySelector('#modal')!,
);
}
4 changes: 2 additions & 2 deletions demo-app/src/containers/CarToolContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export function CarToolContainer() {
{
onRefreshCars: CarToolActions.refreshCars,
onAddCar: CarToolActions.appendCar,
onSaveCar: CarToolActions.createReplaceCarAction,
onDeleteCar: CarToolActions.createRemoveCarAction,
onSaveCar: CarToolActions.replaceCar,
onDeleteCar: CarToolActions.removeCar,
onEditCar: CarToolActions.createEditCarAction,
onCancelCar: CarToolActions.createCancelCarAction,
onSortCars: CarToolActions.createSortCarsAction,
Expand Down
33 changes: 1 addition & 32 deletions demo-app/src/reducers/carToolReducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { Car, CarsOrder, ORDER_ASC, ORDER_DESC } from '../models/car';
import { CarToolState } from '../models/carToolStore';

import {
isReplaceCarAction,
isRemoveCarAction,
isSortCarsAction,
isEditCarAction,
isCancelCarAction,
Expand Down Expand Up @@ -42,47 +40,18 @@ export const editCarIdReducer: Reducer<number, AnyAction> = (
return action.payload.carId;
}

if (
isRefreshCarsRequestCarAction(action) ||
isReplaceCarAction(action) ||
isRemoveCarAction(action) ||
isCancelCarAction(action)
) {
if (isRefreshCarsRequestCarAction(action) || isCancelCarAction(action)) {
return -1;
}

return editCarId;
};

// const initialCars: Car[] = [
// {
// id: 1,
// make: 'Ford',
// model: 'Fusion Hybrid',
// year: 2020,
// color: 'blue',
// price: 45000,
// },
// { id: 2, make: 'Tesla', model: 'S', year: 2019, color: 'red', price: 120000 },
// ];

export const carsReducer: Reducer<Car[], AnyAction> = (cars = [], action) => {
if (isRefreshCarsDoneCarAction(action)) {
return action.payload.cars;
}

if (isReplaceCarAction(action)) {
const { car } = action.payload;
const carIndex = cars.findIndex((c) => c.id === car.id);
const newCars = [...cars];
newCars[carIndex] = car;
return newCars;
}

if (isRemoveCarAction(action)) {
return cars.filter((c) => c.id !== action.payload.carId);
}

return cars;
};

Expand Down
7 changes: 7 additions & 0 deletions demo-app/src/services/CarsRestClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {RestClient} from "./RestClient";

import { Car } from '../models/car';

export class CarsRestClient extends RestClient<Car> {

}
36 changes: 36 additions & 0 deletions demo-app/src/services/RestClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Item, ItemId } from '../models/item';

export class RestClient<T extends Item> {
constructor(private baseUrl: string) {}

async all() {
const res = await fetch(`${this.baseUrl}`);
const items = await res.json();
return items as T[];
}

async append(newItem: Omit<Item, 'id'>) {
const res = await fetch(`${this.baseUrl}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newItem),
});

const car = await res.json();
return car.id;
}

async replace(item: Item) {
await fetch(`${this.baseUrl}/${encodeURIComponent(item.id)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});
}

async remove(itemId: ItemId) {
await fetch(`${this.baseUrl}/${encodeURIComponent(itemId)}`, {
method: 'DELETE',
});
}
}

0 comments on commit c877aca

Please sign in to comment.