initial upload

This commit is contained in:
2022-02-01 21:40:17 +01:00
parent e7c0fbc21d
commit 5f0c4f9b74
89 changed files with 24782 additions and 1 deletions

View File

@@ -0,0 +1,67 @@
import * as React from 'react'
import { RouteComponentProps } from "react-router";
import { Product } from './types';
import { Quantity } from './ProductsComponent';
import { Map } from 'immutable'
export type CartComponentState = {
}
// TODO 9: complete the implementation of the type below (1.5 pt)
export type CartComponentProps = {
update_user: (data: string) => void,
user: string,
cart: Map<Product, Quantity>,
update_cart: (cart: Map<Product, Quantity>) => void,
submit: () => void
}
export class CartComponent extends React.Component<CartComponentProps, CartComponentState> {
constructor(props: CartComponentProps) {
super(props)
this.state = {}
}
public render() {
return <form>
<label>User email:</label>
<input type="email" onChange={e => this.props.update_user(e.target.value)} value={this.props.user} required />
<br />
<br />
<div>
{/*
TODO 10: complete the implementation of the type below (1.5 pt)
NOTE: you should be able to remove an item from the cart (or decrease the amount). (hint button)
*/}
{
this.props.cart.count() == 0 ? "Your cart is empty." :
<div>
{
this.props.cart.map((q, p) => {
return <div>
{p.Name} x {q} <button onClick={() => {
let q1 = this.props.cart.get(p)
if (q1 == 1) this.props.update_cart(this.props.cart.remove(p))
else this.props.update_cart(this.props.cart.set(p, q1 - 1))
}}>-</button>
</div>
}).valueSeq()
}
<div>-------------</div>
<p>Total: {this.props.cart.map((q, p) => q * p.Price).reduce((a, b) => a + b, 0)}</p>
</div>
}
</div>
{(() => {
const regex = RegExp('[a-zA-Z]+@[a-zA-Z]+');
let is_email = regex.test(this.props.user)
return <button style={{ cursor: is_email ? "pointer" : "not-allowed" }} disabled={!is_email} onClick={e => {
e.preventDefault()
this.props.submit()
}}>Buy</button>
})()}
</form>
}
}

View File

@@ -0,0 +1,25 @@
import * as React from 'react'
export type ModalComponentProps = {
close: () => void,
text: string
}
export type ModalComponentState = {
}
export class ModalComponent extends React.Component<ModalComponentProps, ModalComponentState> {
constructor(props: ModalComponentProps) {
super(props)
this.state = {}
}
componentWillMount() {
}
public render() {
return <div id="myModal" className="modal row" style={{ display: "block" }}>
<div className="modal-content">
<span className="close" onClick={this.props.close}>&times;</span>
<p>Order status: {this.props.text}</p>
</div>
</div>
}
}

View File

@@ -0,0 +1,52 @@
import * as React from 'react'
import { RouteComponentProps } from "react-router";
import ReactJson from 'react-json-view'
export type OrdersComponentState = {
orders: { kind: "loaded", value: string } | { kind: "loading" } | { kind: "error-or-not-found" } | { kind: "none" }
}
export type OrdersComponentProps = {}
export class OrdersComponent extends React.Component<RouteComponentProps<OrdersComponentProps>, OrdersComponentState> {
constructor(props: RouteComponentProps<OrdersComponentProps>) {
super(props)
this.state = {
orders:{kind:"none"}
}
}
getOrders() {
this.setState(s => ({ ...s, orders: { kind: "loading" } }), () => {
fetch(`/Cart/GetOrders`,
{
method: 'get', credentials: 'include',
headers: { 'content-type': 'application/json' }
}).then(async res => {
try {
if (!res.ok)
this.setState(s => ({ ...s, orders: { kind: "error-or-not-found" } }))
let res1 = await res.json()
this.setState(s => ({ ...s, orders: { kind: "loaded", value: JSON.stringify(res1) } }))
}
catch {
this.setState(s => ({ ...s, orders: { kind: "error-or-not-found" } }))
}
})
})
}
componentWillMount() {
this.getOrders()
}
public render() {
if (this.state.orders.kind != "loaded") {
return <p>{this.state.orders.kind}</p>
}
return <div className="main-order">
<h2>Orders</h2>
{/* Not necessary for the exam */}
<ReactJson src={JSON.parse(this.state.orders.value)} />
</div>
}
}

View File

@@ -0,0 +1,90 @@
import * as React from 'react'
import { RouteComponentProps } from "react-router";
import { Product } from './types';
export type ProductRouteComponentProps = {}
export class ProductRouteComponent extends React.Component<RouteComponentProps<ProductRouteComponentProps>, ProductComponentState> {
render() {
let p_id = this.props.match.params.id
p_id == "" ? undefined : isNaN(p_id) ? undefined : +this.props.product_id
return <ProductComponent product_id={p_id} />
}
}
export type ProductComponentProps = {
product_id?: number
product?: Product
add_product?: () => void
}
export type ProductComponentState = {
product?: { kind: "loaded", product: Product } | { kind: "loading" } | { kind: "error-or-not-found" }
}
export class ProductComponent extends React.Component<ProductComponentProps, ProductComponentState> {
constructor(props: ProductComponentProps) {
super(props)
this.state = {}
}
getProduct() {
{/* TODO 11: complete the implementation of the type below (1 pt) */}
fetch(`/Cart/GetProduct/${this.props.product_id}`, {
method: "GET", credentials: "include"
})
.then(async (response) => {
try {
if (!response.ok) {
this.setState(s => ({ ...s, product: { kind: "error-or-not-found"} }));
}
let responseData = await response.json();
this.setState(s => ({ ...s, product: { kind: "loaded", product: responseData }}));
} catch (error) {
this.setState(s => ({ ...s, product: { kind: "error-or-not-found"} }));
}
})
}
componentWillMount() {
if (this.props.product != undefined) {
this.setState(s => ({ ...s, product: { kind: "loaded", product: this.props.product } }))
return
}
else if (this.props.product_id) {
this.setState(s => ({ ...s, product: { kind: "loading" } }), () => {
this.getProduct()
})
}
else {
this.setState(s => ({ ...s, product: { kind: "error-or-not-found" } }))
}
}
public render() {
if (this.state.product.kind != "loaded") {
return <p>{this.state.product.kind}</p>
}
return <div className='container-fluid row'>
{/*
TODO 12: complete the implementation of the type below (1 pt)
NOTE: when rendering a product you should be able to add it to the cart (hint button)
*/}
<div className='col-sm-6'>
<h3>Product</h3>
<div>Id:<span>{this.state.product.product.Id}</span></div>
<div>Name:<span>{this.state.product.product.Name}</span></div>
<div>Price:<span>{this.state.product.product.Price}</span></div>
</div>
<div className='col-sm-6'>
{this.props.add_product ? <button style={{ height: "70px", marginTop: "50px" }} onClick={this.props.add_product}>+</button> : null}
</div>
</div>
}
}

View File

@@ -0,0 +1,123 @@
import * as React from 'react'
import { RouteComponentProps } from "react-router";
import { CartComponent } from './CartComponent';
import { ProductComponent } from './ProductComponent';
import { Product, User } from './types';
import { Map } from 'immutable'
import { ModalComponent } from './Modal';
export type Quantity = number
export type ProductsComponentState = {
products: { kind: "loaded", products: Product[] } | { kind: "loading" } | { kind: "error-or-not-found" } | { kind: "none" }
user: string,
cart: Map<Product, Quantity>
submit_status: { kind: "processing" } | { kind: "order_completed" } | { kind: "error" } | { kind: "none" }
}
export type ProductsComponentProps = {}
export class ProductsComponent extends React.Component<RouteComponentProps<ProductsComponentProps>, ProductsComponentState> {
constructor(props: RouteComponentProps<ProductsComponentProps>) {
super(props)
this.state = {
user: "",
cart: Map(),
submit_status: { kind: "none" },
products: { kind: "none" }
}
}
getProducts() {
this.setState(s => ({ ...s, products: { kind: "loading" } }), () => {
fetch(`/Cart/GetProducts`,
{
method: 'get', credentials: 'include',
headers: { 'content-type': 'application/json' }
}).then(async res => {
try {
if (!res.ok)
this.setState(s => ({ ...s, products: { kind: "error-or-not-found" } }))
let res1 = await res.json()
this.setState(s => ({ ...s, products: { kind: "loaded", products: res1 } }))
}
catch {
this.setState(s => ({ ...s, products: { kind: "error-or-not-found" } }))
}
})
})
}
placeOrder() {
let data_to_sent = {
user: { Name: this.state.user } as User,
products: this.state.cart.map((q, p) => ({ product: p, quantity: q })).valueSeq().toArray()
}
fetch(`/Cart/PlaceOrder`,
{
method: 'put', credentials: 'include',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(data_to_sent)
}).then(async res => {
try {
if (!res.ok)
this.setState(s => ({ ...s, submit_status: { kind: "error" } }))
let res1 = await res.json()
this.setState(s => ({ ...s, submit_status: { kind: "order_completed" } }))
}
catch {
this.setState(s => ({ ...s, submit_status: { kind: "error" } }))
}
})
}
componentWillMount() {
this.getProducts()
}
public render() {
if (this.state.products.kind != "loaded") {
return <p>{this.state.products.kind}</p>
}
return <div className='container-fluid row'>
<div className="row">
<div className='col-sm-6'>
<h2>Products</h2>
{this.state.products.products.map(p => <ProductComponent key={p.Id} product={p} product_id={p.Id} add_product={
() => this.setState(s => {
let s1 = { ...s }
if (!s1.cart.has(p)) {
s1.cart = s1.cart.set(p, 0)
}
s1.cart = s1.cart.set(p, s1.cart.get(p) + 1)
return s1
})
} />)}
</div>
<div className='col-sm-6'>
<CartComponent user={this.state.user}
cart={this.state.cart}
submit={() => {
this.setState(s => ({ ...s, submit_status: { kind: "processing" } }), () => {
this.placeOrder()
})
}}
update_cart={c => this.setState(s1 => ({ ...s1, cart: c }))}
update_user={s => this.setState(s1 => ({ ...s1, user: s }))} />
</div>
</div>
{this.state.submit_status.kind == "none" ? null : <ModalComponent
text={this.state.submit_status.kind}
close={() => this.setState(s => ({ ...s, submit_status: { kind: "none" } }))} />
}
</div>
}
}

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
import { Link } from 'react-router-dom';
export interface LayoutProps {
children?: React.ReactNode;
}
export class Layout extends React.Component<LayoutProps, {}> {
public render() {
return <div>
<div>
<div className="sidenav">
<Link to="/">Home</Link>
<Link to="/orders">Orders</Link>
</div>
<div>
{ this.props.children }
</div>
</div>
</div>;
}
}

View File

@@ -0,0 +1,12 @@
import * as React from 'react';
import { Route } from 'react-router-dom';
import { Layout } from './Layout';
import { ProductRouteComponent } from './ProductComponent';
import { ProductsComponent } from './ProductsComponent';
import { OrdersComponent } from './OrdersComponent';
export const routes = <Layout>
<Route exact path='/'component={ ProductsComponent } />
<Route exact path='/products/:id' component={ ProductRouteComponent } />
<Route exact path='/orders' component={ OrdersComponent } />
</Layout>;

View File

@@ -0,0 +1,10 @@
export type User = {
Id: number,
Name: string,
}
export type Product = {
Id: number,
Name: string,
Price: number
}