(feat): car and checkout static texts
This commit is contained in:
parent
bfd3cb9f0a
commit
25da28f001
@ -35,8 +35,7 @@ const FeatherIcon : FC<IFeatherIcon> = (props : IFeatherIcon) => {
|
|||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
className={`feather feather-${icon} ${className}`}
|
className={`feather feather-${icon} ${className}`}
|
||||||
{...otherProps}
|
{...otherProps}>
|
||||||
>
|
|
||||||
<IconInner icon={icon} />
|
<IconInner icon={icon} />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -29,20 +29,18 @@ const NavMenu : FC = () => {
|
|||||||
{content?.topMenu ? content.topMenu.map((item: MenuItemModel, index: number) => {
|
{content?.topMenu ? content.topMenu.map((item: MenuItemModel, index: number) => {
|
||||||
return <NavItem key={index}>
|
return <NavItem key={index}>
|
||||||
<NavLink tag={Link} className="text-dark" to={item.target}>
|
<NavLink tag={Link} className="text-dark" to={item.target}>
|
||||||
{item.icon ? <FeatherIcon icon={item.icon}/> : ''}
|
{item.icon ? <><FeatherIcon icon={item.icon}/> </> : ''}{item.title}
|
||||||
{item.title}
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</NavItem>
|
</NavItem>
|
||||||
}) : ''}
|
}) : ''}
|
||||||
</ul>
|
</ul>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<form className="d-flex">
|
{/* <form className="d-flex">
|
||||||
<button className="btn btn-outline-dark" type="submit">
|
<button className="btn btn-outline-dark" type="submit">
|
||||||
<FeatherIcon icon='shopping-cart' className="me-1"/>Cart
|
<FeatherIcon icon='shopping-cart' className="me-1"/>Cart <span className="badge bg-dark text-white ms-1 rounded-pill">0</span>
|
||||||
<span className="badge bg-dark text-white ms-1 rounded-pill">0</span>
|
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form> */}
|
||||||
</Navbar>
|
</Navbar>
|
||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AuthorModel, ImageModel } from "./"
|
import { AuthorModel, FormItemModel, ImageModel } from "./"
|
||||||
|
|
||||||
|
|
||||||
export interface RequestModel {
|
export interface RequestModel {
|
||||||
@ -17,6 +17,18 @@ export interface PageSectionModel {
|
|||||||
text?: string
|
text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddressPageSectionModel extends PageSectionModel {
|
||||||
|
firstName: FormItemModel,
|
||||||
|
lastName: FormItemModel,
|
||||||
|
address: FormItemModel,
|
||||||
|
address2: FormItemModel,
|
||||||
|
country: FormItemModel,
|
||||||
|
state: FormItemModel,
|
||||||
|
city: FormItemModel,
|
||||||
|
zip: FormItemModel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface PersonModel {
|
export interface PersonModel {
|
||||||
id: string,
|
id: string,
|
||||||
image?: ImageModel
|
image?: ImageModel
|
||||||
|
|||||||
@ -28,7 +28,8 @@ export interface FeatureModel {
|
|||||||
|
|
||||||
export interface FormItemModel {
|
export interface FormItemModel {
|
||||||
title?: string,
|
title?: string,
|
||||||
placeHolder?: string
|
optional?: string,
|
||||||
|
placeHolder?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImageModel {
|
export interface ImageModel {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialModel } from "./"
|
import { FeatureModel, FormItemModel, ImageModel, LinkModel, MenuItemModel, TestimonialModel } from "./"
|
||||||
import { PageSectionModel } from "./abstractions"
|
import { AddressPageSectionModel, PageSectionModel } from "./abstractions"
|
||||||
|
|
||||||
export interface BlogTitleSectionModel extends PageSectionModel {
|
export interface BlogTitleSectionModel extends PageSectionModel {
|
||||||
postedOnBy: string
|
postedOnBy: string
|
||||||
@ -41,7 +41,39 @@ export interface TitleSectionModel extends PageSectionModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface CartProductsSectionModel extends PageSectionModel {
|
||||||
|
product: string,
|
||||||
|
price: string,
|
||||||
|
quantity: string,
|
||||||
|
subtotal: string,
|
||||||
|
continueShopping: LinkModel,
|
||||||
|
submit: FormItemModel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BillingAddressSectionModel extends AddressPageSectionModel { }
|
||||||
|
|
||||||
|
export interface ShippingAddressSectionModel extends AddressPageSectionModel { }
|
||||||
|
|
||||||
|
export interface CheckoutSummarySectionModel extends PageSectionModel {
|
||||||
|
title: string,
|
||||||
|
total: string,
|
||||||
|
|
||||||
|
promoCode: FormItemModel,
|
||||||
|
submit: FormItemModel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckoutSettingsSectionModel extends PageSectionModel {
|
||||||
|
shippingAddressSameAsBillingAddress: string,
|
||||||
|
saveThisInformation: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentSectionModel extends PageSectionModel {
|
||||||
|
title: string,
|
||||||
|
nameOnCard: FormItemModel,
|
||||||
|
cardNumber: FormItemModel,
|
||||||
|
expiration: FormItemModel,
|
||||||
|
cvv: FormItemModel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
import { FormItemModel, LinkModel } from "."
|
import { FormItemModel, LinkModel } from "."
|
||||||
import { PageModel } from "./abstractions"
|
import { PageModel } from "./abstractions"
|
||||||
import { BlogTitleSectionModel, CallToActionSectionModel, CommentsSectionModel, FeaturedBlogSectionModel, FeaturedBlogsSectionModel, FeaturesSectionModel, ProductSectionModel, RelatedProductsSectionModel, TestimonialsSectionModel, TitleSectionModel } from "./pageSections"
|
import * as PageSection from "./pageSections"
|
||||||
|
|
||||||
export interface BlogCatalogPageModel extends PageModel {
|
export interface BlogCatalogPageModel extends PageModel {
|
||||||
titleSection: TitleSectionModel,
|
titleSection: PageSection.TitleSectionModel,
|
||||||
featuredBlogSection: FeaturedBlogSectionModel
|
featuredBlogSection: PageSection.FeaturedBlogSectionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlogItemPageModel extends PageModel {
|
export interface BlogItemPageModel extends PageModel {
|
||||||
titleSection: BlogTitleSectionModel,
|
titleSection: PageSection.BlogTitleSectionModel,
|
||||||
commentsSection: CommentsSectionModel
|
commentsSection: PageSection.CommentsSectionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HomePageModel extends PageModel {
|
export interface HomePageModel extends PageModel {
|
||||||
titleSection: TitleSectionModel,
|
titleSection: PageSection.TitleSectionModel,
|
||||||
featuresSection: FeaturesSectionModel,
|
featuresSection: PageSection.FeaturesSectionModel,
|
||||||
testimonialsSection: TestimonialsSectionModel,
|
testimonialsSection: PageSection.TestimonialsSectionModel,
|
||||||
featuredBlogsSection: FeaturedBlogsSectionModel,
|
featuredBlogsSection: PageSection.FeaturedBlogsSectionModel,
|
||||||
callToActionSection: CallToActionSectionModel
|
callToActionSection: PageSection.CallToActionSectionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShopCatalogPageModel extends PageModel {
|
export interface ShopCatalogPageModel extends PageModel {
|
||||||
titleSection: TitleSectionModel
|
titleSection: PageSection.TitleSectionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SignInPageModel extends PageModel {
|
export interface SignInPageModel extends PageModel {
|
||||||
@ -45,6 +45,21 @@ export interface SignUpPageModel extends PageModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ShopItemPageModel extends PageModel {
|
export interface ShopItemPageModel extends PageModel {
|
||||||
productSection: ProductSectionModel
|
productSection: PageSection.ProductSectionModel
|
||||||
relatedProductsSection: RelatedProductsSectionModel
|
relatedProductsSection: PageSection.RelatedProductsSectionModel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CartPageModel extends PageModel {
|
||||||
|
titleSection: PageSection.TitleSectionModel
|
||||||
|
productsSection: PageSection.CartProductsSectionModel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckoutPageModel extends PageModel {
|
||||||
|
titleSection: PageSection.TitleSectionModel,
|
||||||
|
billingAddressSection: PageSection.BillingAddressSectionModel,
|
||||||
|
shippingAddressSection: PageSection.ShippingAddressSectionModel,
|
||||||
|
settingsSection: PageSection.CheckoutSettingsSectionModel,
|
||||||
|
summarySection: PageSection.CheckoutSummarySectionModel,
|
||||||
|
paymentSection: PageSection.PaymentSectionModel,
|
||||||
|
submit: FormItemModel
|
||||||
}
|
}
|
||||||
@ -39,3 +39,5 @@ export interface GetShopItemRequestModel extends RequestModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GetShopRelatedRequestModel extends RequestModel { }
|
export interface GetShopRelatedRequestModel extends RequestModel { }
|
||||||
|
|
||||||
|
export interface GetShopCartRequestModel extends RequestModel {}
|
||||||
|
|||||||
@ -7,9 +7,14 @@ import {
|
|||||||
ShopCatalogPageModel,
|
ShopCatalogPageModel,
|
||||||
ShopItemPageModel,
|
ShopItemPageModel,
|
||||||
SignInPageModel,
|
SignInPageModel,
|
||||||
SignUpPageModel
|
SignUpPageModel,
|
||||||
|
CartPageModel,
|
||||||
|
CheckoutPageModel
|
||||||
} from "./pages"
|
} from "./pages"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Shop response models
|
// Shop response models
|
||||||
export interface GetShopCatalogResponseModel extends PaginationModel<ShopItemModel>, ResponseModel {}
|
export interface GetShopCatalogResponseModel extends PaginationModel<ShopItemModel>, ResponseModel {}
|
||||||
|
|
||||||
@ -29,6 +34,11 @@ export interface GetShopRelatedResponseModel extends ResponseModel {
|
|||||||
items: ShopItemModel []
|
items: ShopItemModel []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GetShopCartResponseModel extends ResponseModel {
|
||||||
|
quantity: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Static content response model
|
// Static content response model
|
||||||
export interface GetContentResponseModel extends ResponseModel {
|
export interface GetContentResponseModel extends ResponseModel {
|
||||||
siteName: string,
|
siteName: string,
|
||||||
@ -44,6 +54,8 @@ export interface GetContentResponseModel extends ResponseModel {
|
|||||||
|
|
||||||
shopCatalog: ShopCatalogPageModel,
|
shopCatalog: ShopCatalogPageModel,
|
||||||
shopItem: ShopItemPageModel,
|
shopItem: ShopItemPageModel,
|
||||||
|
cart: CartPageModel,
|
||||||
|
checkout: CheckoutPageModel,
|
||||||
|
|
||||||
blogCatalog: BlogCatalogPageModel,
|
blogCatalog: BlogCatalogPageModel,
|
||||||
blogItem: BlogItemPageModel,
|
blogItem: BlogItemPageModel,
|
||||||
|
|||||||
@ -149,19 +149,19 @@ const BlogCatalog = () => {
|
|||||||
const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0]
|
const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0]
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// dispatch(blogCatalogActionCreators.requestBlogCatalog({
|
dispatch(blogCatalogActionCreators.requestBlogCatalog({
|
||||||
// currentPage: params?.page ? params.page : "1"
|
currentPage: params?.page ? params.page : "1"
|
||||||
// }))
|
}))
|
||||||
// dispatch(blogFeaturedActionCreators.requestBlogFeatured())
|
dispatch(blogFeaturedActionCreators.requestBlogFeatured())
|
||||||
dispatch(blogCategoriesActionCreators.requestBlogCategories())
|
dispatch(blogCategoriesActionCreators.requestBlogCategories())
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// blogCatalog?.isLoading
|
blogCatalog?.isLoading
|
||||||
// ? dispatch(loaderActionCreators.show())
|
? dispatch(loaderActionCreators.show())
|
||||||
// : setTimeout(() => {
|
: setTimeout(() => {
|
||||||
// dispatch(loaderActionCreators.hide())
|
dispatch(loaderActionCreators.hide())
|
||||||
// }, 1000)
|
}, 1000)
|
||||||
}, [blogCatalog?.isLoading])
|
}, [blogCatalog?.isLoading])
|
||||||
|
|
||||||
const blogItem = blogFeatured?.items[0]
|
const blogItem = blogFeatured?.items[0]
|
||||||
|
|||||||
@ -57,18 +57,18 @@ const BlogItem = () => {
|
|||||||
const page = content?.blogItem
|
const page = content?.blogItem
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if(params?.slug)
|
if(params?.slug)
|
||||||
// dispatch(blogItemActionCreators.requestBlogItem({
|
dispatch(blogItemActionCreators.requestBlogItem({
|
||||||
// slug: params.slug
|
slug: params.slug
|
||||||
// }))
|
}))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// blogItem?.isLoading
|
blogItem?.isLoading
|
||||||
// ? dispatch(loaderActionCreators.show())
|
? dispatch(loaderActionCreators.show())
|
||||||
// : setTimeout(() => {
|
: setTimeout(() => {
|
||||||
// dispatch(loaderActionCreators.hide())
|
dispatch(loaderActionCreators.hide())
|
||||||
// }, 1000)
|
}, 1000)
|
||||||
}, [blogItem?.isLoading])
|
}, [blogItem?.isLoading])
|
||||||
|
|
||||||
const blogItemTitle: BlogItemTitle = {
|
const blogItemTitle: BlogItemTitle = {
|
||||||
|
|||||||
5
webapi/ClientApp/src/pages/Profile/index.tsx
Normal file
5
webapi/ClientApp/src/pages/Profile/index.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const Profile = () => {}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Profile
|
||||||
|
}
|
||||||
79
webapi/ClientApp/src/pages/Shop/Cart/index.tsx
Normal file
79
webapi/ClientApp/src/pages/Shop/Cart/index.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Container } from 'reactstrap'
|
||||||
|
import { FeatherIcon } from '../../../components/FeatherIcons'
|
||||||
|
|
||||||
|
import style from './scss/style.module.scss'
|
||||||
|
|
||||||
|
const Cart = () => {
|
||||||
|
return <Container fluid>
|
||||||
|
<section className="pt-5 pb-5">
|
||||||
|
<div className="row w-100">
|
||||||
|
<div className="col-lg-12 col-md-12 col-12">
|
||||||
|
<h3 className="display-5 mb-2 text-center">Shopping Cart</h3>
|
||||||
|
<p className="mb-5 text-center">
|
||||||
|
<i className="text-info font-weight-bold">3</i> items in your cart</p>
|
||||||
|
<table id="shoppingCart" className="table table-condensed table-responsive">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ width: "60%" }}>Product</th>
|
||||||
|
<th style={{ width: "12%" }}>Price</th>
|
||||||
|
<th style={{ width: "10%" }}>Quantity</th>
|
||||||
|
<th style={{ width: "16%" }}></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{[{}, {}, {}, {}].map((item, index) => <tr key={index}>
|
||||||
|
<td data-th="Product">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-3 text-left">
|
||||||
|
<img src="https://dummyimage.com/250x250/ced4da/6c757d.jpg" alt="" className="img-fluid d-none d-md-block rounded mb-2 shadow" />
|
||||||
|
</div>
|
||||||
|
<div className="col-md-9 text-left mt-sm-2">
|
||||||
|
<h4>Product Name</h4>
|
||||||
|
<p className="font-weight-light">Brand & Name</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td data-th="Price">$49.00</td>
|
||||||
|
<td data-th="Quantity">
|
||||||
|
<input type="number" className="form-control form-control-lg text-center" value="1" />
|
||||||
|
</td>
|
||||||
|
<td className="actions" data-th="">
|
||||||
|
<div className="text-right">
|
||||||
|
<button className="btn btn-white border-secondary bg-white btn-md mb-2">
|
||||||
|
<FeatherIcon icon="refresh-cw" />
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-white border-secondary bg-white btn-md mb-2">
|
||||||
|
<FeatherIcon icon="trash-2" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>)}
|
||||||
|
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="float-right text-right">
|
||||||
|
<h4>Subtotal:</h4>
|
||||||
|
<h1>$99.00</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row mt-4 d-flex align-items-center">
|
||||||
|
<div className="col-sm-6 order-md-2 text-right">
|
||||||
|
<a href="catalog.html" className="btn btn-primary mb-4 btn-lg pl-5 pr-5">Checkout</a>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-6 mb-3 mb-m-1 order-md-1 text-md-left">
|
||||||
|
<a href="catalog.html">
|
||||||
|
<i className="fas fa-arrow-left mr-2"></i> Continue Shopping</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Cart
|
||||||
|
}
|
||||||
@ -111,17 +111,17 @@ const ShopCatalog = () => {
|
|||||||
const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0]
|
const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0]
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// dispatch(shopCatalogActionCreators.requestShopCatalog({
|
dispatch(shopCatalogActionCreators.requestShopCatalog({
|
||||||
// currentPage: params?.page ? params.page : "1"
|
currentPage: params?.page ? params.page : "1"
|
||||||
// }))
|
}))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// shopCatalog?.isLoading
|
shopCatalog?.isLoading
|
||||||
// ? dispatch(loaderActionCreators.show())
|
? dispatch(loaderActionCreators.show())
|
||||||
// : setTimeout(() => {
|
: setTimeout(() => {
|
||||||
// dispatch(loaderActionCreators.hide())
|
dispatch(loaderActionCreators.hide())
|
||||||
// }, 1000)
|
}, 1000)
|
||||||
}, [shopCatalog?.isLoading])
|
}, [shopCatalog?.isLoading])
|
||||||
|
|
||||||
const shopItems: ShopItems = {
|
const shopItems: ShopItems = {
|
||||||
|
|||||||
221
webapi/ClientApp/src/pages/Shop/Checkout/index.tsx
Normal file
221
webapi/ClientApp/src/pages/Shop/Checkout/index.tsx
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Container } from 'reactstrap'
|
||||||
|
|
||||||
|
// CSS Modules
|
||||||
|
import style from './scss/style.module.scss'
|
||||||
|
|
||||||
|
const Checkout = () => {
|
||||||
|
return <Container fluid className={style.container}>
|
||||||
|
<main>
|
||||||
|
<div className="py-5 text-center">
|
||||||
|
<img className="d-block mx-auto mb-4" src="../assets/brand/bootstrap-logo.svg" alt="" width="72" height="57" />
|
||||||
|
<h2>Checkout form</h2>
|
||||||
|
<p className="lead">Below is an example form built entirely with Bootstrap’s form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row g-5">
|
||||||
|
<div className="col-md-5 col-lg-4 order-md-last">
|
||||||
|
<h4 className="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<span className="text-primary">Your cart</span>
|
||||||
|
<span className="badge bg-primary rounded-pill">3</span>
|
||||||
|
</h4>
|
||||||
|
<ul className="list-group mb-3">
|
||||||
|
<li className="list-group-item d-flex justify-content-between lh-sm">
|
||||||
|
<div>
|
||||||
|
<h6 className="my-0">Product name</h6>
|
||||||
|
<small className="text-muted">Brief description</small>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted">$12</span>
|
||||||
|
</li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between lh-sm">
|
||||||
|
<div>
|
||||||
|
<h6 className="my-0">Second product</h6>
|
||||||
|
<small className="text-muted">Brief description</small>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted">$8</span>
|
||||||
|
</li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between lh-sm">
|
||||||
|
<div>
|
||||||
|
<h6 className="my-0">Third item</h6>
|
||||||
|
<small className="text-muted">Brief description</small>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted">$5</span>
|
||||||
|
</li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between bg-light">
|
||||||
|
<div className="text-success">
|
||||||
|
<h6 className="my-0">Promo code</h6>
|
||||||
|
<small>EXAMPLECODE</small>
|
||||||
|
</div>
|
||||||
|
<span className="text-success">−$5</span>
|
||||||
|
</li>
|
||||||
|
<li className="list-group-item d-flex justify-content-between">
|
||||||
|
<span>Total (USD)</span>
|
||||||
|
<strong>$20</strong>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form className="card p-2">
|
||||||
|
<div className="input-group">
|
||||||
|
<input type="text" className="form-control" placeholder="Promo code" />
|
||||||
|
<button type="submit" className="btn btn-secondary">Redeem</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-7 col-lg-8">
|
||||||
|
<h4 className="mb-3">Billing address</h4>
|
||||||
|
<form className="needs-validation" noValidate>
|
||||||
|
<div className="row g-3">
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<label htmlFor="firstName" className="form-label">First name</label>
|
||||||
|
<input type="text" className="form-control" id="firstName" placeholder="" value="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Valid first name is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<label htmlFor="lastName" className="form-label">Last name</label>
|
||||||
|
<input type="text" className="form-control" id="lastName" placeholder="" value="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Valid last name is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="col-12">
|
||||||
|
<label htmlFor="address" className="form-label">Address</label>
|
||||||
|
<input type="text" className="form-control" id="address" placeholder="1234 Main St" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please enter your shipping address.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12">
|
||||||
|
<label htmlFor="address2" className="form-label">Address 2 <span className="text-muted">(Optional)</span></label>
|
||||||
|
<input type="text" className="form-control" id="address2" placeholder="Apartment or suite" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="country" className="form-label">Country</label>
|
||||||
|
<select className="form-select" id="country" required>
|
||||||
|
<option value="">Choose...</option>
|
||||||
|
<option>United States</option>
|
||||||
|
</select>
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please select a valid country.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="state" className="form-label">State</label>
|
||||||
|
<select className="form-select" id="state" required>
|
||||||
|
<option value="">Choose...</option>
|
||||||
|
<option>California</option>
|
||||||
|
</select>
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please provide a valid state.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="state" className="form-label">City</label>
|
||||||
|
<select className="form-select" id="state" required>
|
||||||
|
<option value="">Choose...</option>
|
||||||
|
<option>California</option>
|
||||||
|
</select>
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please provide a valid city.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="zip" className="form-label">Zip</label>
|
||||||
|
<input type="text" className="form-control" id="zip" placeholder="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Zip code required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="my-4" />
|
||||||
|
|
||||||
|
<div className="form-check">
|
||||||
|
<input type="checkbox" className="form-check-input" id="same-address" />
|
||||||
|
<label className="form-check-label" htmlFor="same-address">Shipping address is the same as my billing address</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-check">
|
||||||
|
<input type="checkbox" className="form-check-input" id="save-info" />
|
||||||
|
<label className="form-check-label" htmlFor="save-info">Save this information for next time</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="my-4" />
|
||||||
|
|
||||||
|
<h4 className="mb-3">Payment</h4>
|
||||||
|
|
||||||
|
<div className="my-3">
|
||||||
|
<div className="form-check">
|
||||||
|
<input id="credit" name="paymentMethod" type="radio" className="form-check-input" checked required />
|
||||||
|
<label className="form-check-label" htmlFor="credit">Credit card</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-check">
|
||||||
|
<input id="debit" name="paymentMethod" type="radio" className="form-check-input" required />
|
||||||
|
<label className="form-check-label" htmlFor="debit">Debit card</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-check">
|
||||||
|
<input id="paypal" name="paymentMethod" type="radio" className="form-check-input" required />
|
||||||
|
<label className="form-check-label" htmlFor="paypal">PayPal</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label htmlFor="cc-name" className="form-label">Name on card</label>
|
||||||
|
<input type="text" className="form-control" id="cc-name" placeholder="" required />
|
||||||
|
<small className="text-muted">Full name as displayed on card</small>
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Name on card is required
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-6">
|
||||||
|
<label htmlFor="cc-number" className="form-label">Credit card number</label>
|
||||||
|
<input type="text" className="form-control" id="cc-number" placeholder="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Credit card number is required
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="cc-expiration" className="form-label">Expiration</label>
|
||||||
|
<input type="text" className="form-control" id="cc-expiration" placeholder="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Expiration date required
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<label htmlFor="cc-cvv" className="form-label">CVV</label>
|
||||||
|
<input type="text" className="form-control" id="cc-cvv" placeholder="" required />
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Security code required
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr className="my-4" />
|
||||||
|
|
||||||
|
<button className="w-100 btn btn-primary btn-lg" type="submit">Continue to checkout</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Checkout
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
.container {
|
||||||
|
max-width: 960px;
|
||||||
|
}
|
||||||
@ -19,18 +19,18 @@ const ShopItem = () => {
|
|||||||
const page = content?.shopItem
|
const page = content?.shopItem
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if(params?.slug)
|
if(params?.slug)
|
||||||
// dispatch(shopItemActionCreators.requestShopItem({
|
dispatch(shopItemActionCreators.requestShopItem({
|
||||||
// slug: params.slug
|
slug: params.slug
|
||||||
// }))
|
}))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// shopItem?.isLoading
|
shopItem?.isLoading
|
||||||
// ? dispatch(loaderActionCreators.show())
|
? dispatch(loaderActionCreators.show())
|
||||||
// : setTimeout(() => {
|
: setTimeout(() => {
|
||||||
// dispatch(loaderActionCreators.hide())
|
dispatch(loaderActionCreators.hide())
|
||||||
// }, 1000)
|
}, 1000)
|
||||||
}, [shopItem?.isLoading])
|
}, [shopItem?.isLoading])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { ShopCatalog } from './Catalog'
|
import { ShopCatalog } from './Catalog'
|
||||||
import { ShopItem } from './Item'
|
import { ShopItem } from './Item'
|
||||||
|
import { Cart } from './Cart'
|
||||||
|
import { Checkout } from './Checkout'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ShopCatalog,
|
ShopCatalog,
|
||||||
ShopItem
|
ShopItem,
|
||||||
|
Cart,
|
||||||
|
Checkout
|
||||||
}
|
}
|
||||||
@ -8,9 +8,11 @@ import { AdminHome } from './AdminHome'
|
|||||||
|
|
||||||
import { Signin } from './Signin'
|
import { Signin } from './Signin'
|
||||||
import { Signup } from './Signup'
|
import { Signup } from './Signup'
|
||||||
import { ShopCatalog, ShopItem } from './Shop'
|
import { ShopCatalog, ShopItem, Cart, Checkout } from './Shop'
|
||||||
import { BlogCatalog, BlogItem } from './Blog'
|
import { BlogCatalog, BlogItem } from './Blog'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface IPages {
|
interface IPages {
|
||||||
[key: string]: FC<any>;
|
[key: string]: FC<any>;
|
||||||
}
|
}
|
||||||
@ -20,6 +22,8 @@ const pages: IPages = {
|
|||||||
|
|
||||||
ShopCatalog,
|
ShopCatalog,
|
||||||
ShopItem,
|
ShopItem,
|
||||||
|
Cart,
|
||||||
|
Checkout,
|
||||||
|
|
||||||
BlogCatalog,
|
BlogCatalog,
|
||||||
BlogItem,
|
BlogItem,
|
||||||
|
|||||||
69
webapi/ClientApp/src/store/reducers/Cart.ts
Normal file
69
webapi/ClientApp/src/store/reducers/Cart.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Action, Reducer } from 'redux'
|
||||||
|
import { AppThunkAction } from '..'
|
||||||
|
import { GetShopCartRequestModel } from '../../models/requests'
|
||||||
|
import { GetShopCartResponseModel } from '../../models/responses'
|
||||||
|
|
||||||
|
export interface CartState extends GetShopCartResponseModel {
|
||||||
|
isLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestAction extends GetShopCartRequestModel {
|
||||||
|
type: 'REQUEST_CART'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReceiveAction extends GetShopCartResponseModel {
|
||||||
|
type: 'RECEIVE_CART'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KnownAction = RequestAction | ReceiveAction
|
||||||
|
|
||||||
|
export const actionCreators = {
|
||||||
|
requestCart: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
addToCart: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||||
|
|
||||||
|
// Get<Promise<GetBlogItemResponseModel>>('https://localhost:7151/api/BlogItem', props)
|
||||||
|
// .then(response => response)
|
||||||
|
// .then(data => {
|
||||||
|
// if(data)
|
||||||
|
// dispatch({ type: 'RECEIVE_BLOG_ITEM', ...data })
|
||||||
|
// })
|
||||||
|
|
||||||
|
// dispatch({ type: 'REQUEST_BLOG_ITEM', slug: props.slug })
|
||||||
|
},
|
||||||
|
|
||||||
|
remFromCart: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unloadedState: CartState = {
|
||||||
|
quantity: 0,
|
||||||
|
|
||||||
|
isLoading: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reducer: Reducer<CartState> = (state: CartState | undefined, incomingAction: Action): CartState => {
|
||||||
|
if (state === undefined) {
|
||||||
|
return unloadedState
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = incomingAction as KnownAction
|
||||||
|
switch (action.type) {
|
||||||
|
case 'REQUEST_CART':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: true
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'RECEIVE_CART':
|
||||||
|
return {
|
||||||
|
...action,
|
||||||
|
isLoading: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
@ -44,6 +44,10 @@ const unloadedState: ContentState = {
|
|||||||
{ target: ":page", component: "ShopCatalog" },
|
{ target: ":page", component: "ShopCatalog" },
|
||||||
{ target: ":page" , childRoutes: [
|
{ target: ":page" , childRoutes: [
|
||||||
{ target: ":slug", component: "ShopItem" }
|
{ target: ":slug", component: "ShopItem" }
|
||||||
|
]},
|
||||||
|
{ target:"cart", childRoutes: [
|
||||||
|
{ target: "", component: "Cart" },
|
||||||
|
{ target: "checkout", component: "Checkout" }
|
||||||
]}
|
]}
|
||||||
]},
|
]},
|
||||||
{ target: "/blog", childRoutes: [
|
{ target: "/blog", childRoutes: [
|
||||||
@ -66,7 +70,9 @@ const unloadedState: ContentState = {
|
|||||||
{ target: "/blog", title: "Blog" },
|
{ target: "/blog", title: "Blog" },
|
||||||
|
|
||||||
{ target: "/signin", title: "Sing in" },
|
{ target: "/signin", title: "Sing in" },
|
||||||
{ target: "/signup", title: "Sign up" }
|
{ target: "/signup", title: "Sign up" },
|
||||||
|
|
||||||
|
{ target: "/shop/cart", icon: "shopping-cart", title: "Cart ({quantity})" }
|
||||||
],
|
],
|
||||||
sideMenu: [],
|
sideMenu: [],
|
||||||
|
|
||||||
@ -144,6 +150,143 @@ const unloadedState: ContentState = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cart: {
|
||||||
|
titleSection: {
|
||||||
|
title: "Shopping Cart"
|
||||||
|
},
|
||||||
|
productsSection: {
|
||||||
|
title: "Shopping Cart",
|
||||||
|
text: "{quantity} items in your cart",
|
||||||
|
product: "Product",
|
||||||
|
price: "Price",
|
||||||
|
quantity: "Quantity",
|
||||||
|
subtotal: "Subtotal",
|
||||||
|
continueShopping: {
|
||||||
|
target: "#",
|
||||||
|
anchorText: "Continue shopping"
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
title: "Checkout"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkout: {
|
||||||
|
titleSection: {
|
||||||
|
title: "Checkout",
|
||||||
|
text: "Below is an example form built entirely with Bootstrap’s form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it."
|
||||||
|
},
|
||||||
|
billingAddressSection: {
|
||||||
|
title: "Billing address",
|
||||||
|
firstName: {
|
||||||
|
title: "First name",
|
||||||
|
placeHolder: "First name..."
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
title: "Last name",
|
||||||
|
placeHolder: "Last name..."
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
title: "Address",
|
||||||
|
placeHolder: "1234 Main Str.."
|
||||||
|
},
|
||||||
|
address2: {
|
||||||
|
title: "Address",
|
||||||
|
optional: "(Optional)",
|
||||||
|
placeHolder: "1234 Main Str.."
|
||||||
|
},
|
||||||
|
country: {
|
||||||
|
title: "Country",
|
||||||
|
placeHolder: "Country..."
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
title: "State",
|
||||||
|
placeHolder: "State..."
|
||||||
|
},
|
||||||
|
city: {
|
||||||
|
title: "City",
|
||||||
|
placeHolder: "City..."
|
||||||
|
},
|
||||||
|
zip: {
|
||||||
|
title: "Zip",
|
||||||
|
placeHolder: "Zip..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shippingAddressSection: {
|
||||||
|
title: "Shipping address",
|
||||||
|
firstName: {
|
||||||
|
title: "First name",
|
||||||
|
placeHolder: "First name..."
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
title: "Last name",
|
||||||
|
placeHolder: "Last name..."
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
title: "Address",
|
||||||
|
placeHolder: "1234 Main Str.."
|
||||||
|
},
|
||||||
|
address2: {
|
||||||
|
title: "Address",
|
||||||
|
optional: "(Optional)",
|
||||||
|
placeHolder: "1234 Main Str.."
|
||||||
|
},
|
||||||
|
country: {
|
||||||
|
title: "Country",
|
||||||
|
placeHolder: "Country..."
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
title: "State",
|
||||||
|
placeHolder: "State..."
|
||||||
|
},
|
||||||
|
city: {
|
||||||
|
title: "City",
|
||||||
|
placeHolder: "City..."
|
||||||
|
},
|
||||||
|
zip: {
|
||||||
|
title: "Zip",
|
||||||
|
placeHolder: "Zip..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
settingsSection: {
|
||||||
|
shippingAddressSameAsBillingAddress: "Shipping address is the same as my billing address",
|
||||||
|
saveThisInformation: "Save this information for next time"
|
||||||
|
},
|
||||||
|
summarySection: {
|
||||||
|
title: "Your cart",
|
||||||
|
total: "Total ({currency})",
|
||||||
|
promoCode: {
|
||||||
|
placeHolder: "Promo code"
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
title: "Redeem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paymentSection: {
|
||||||
|
title: "Payment",
|
||||||
|
nameOnCard: {
|
||||||
|
title: "Name on card",
|
||||||
|
placeHolder: "John Doe"
|
||||||
|
},
|
||||||
|
cardNumber: {
|
||||||
|
title: "Credit card number",
|
||||||
|
placeHolder: ""
|
||||||
|
},
|
||||||
|
expiration: {
|
||||||
|
title: "Expiration",
|
||||||
|
placeHolder: "MM/YY"
|
||||||
|
},
|
||||||
|
cvv: {
|
||||||
|
title: "CVV",
|
||||||
|
placeHolder: "123"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
title: "Continue to checkout"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
blogCatalog: {
|
blogCatalog: {
|
||||||
titleSection: {
|
titleSection: {
|
||||||
title: "Welcome to Blog Home!",
|
title: "Welcome to Blog Home!",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user