From 25da28f001cf795e1cc8ae756b6a5a69bce71d82 Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Mon, 27 Jun 2022 10:13:46 +0200 Subject: [PATCH] (feat): car and checkout static texts --- .../components/FeatherIcons/FeatherIcon.tsx | 3 +- .../src/layouts/public/NavMenu/index.tsx | 10 +- webapi/ClientApp/src/models/abstractions.ts | 14 +- webapi/ClientApp/src/models/index.ts | 3 +- webapi/ClientApp/src/models/pageSections.ts | 38 ++- webapi/ClientApp/src/models/pages.ts | 41 ++-- webapi/ClientApp/src/models/requests.ts | 2 + webapi/ClientApp/src/models/responses.ts | 14 +- .../src/pages/Blog/Catalog/index.tsx | 18 +- .../ClientApp/src/pages/Blog/Item/index.tsx | 18 +- webapi/ClientApp/src/pages/Profile/index.tsx | 5 + .../ClientApp/src/pages/Shop/Cart/index.tsx | 79 +++++++ .../pages/Shop/Cart/scss/style.module.scss | 0 .../src/pages/Shop/Catalog/index.tsx | 16 +- .../src/pages/Shop/Checkout/index.tsx | 221 ++++++++++++++++++ .../Shop/Checkout/scss/style.module.scss | 3 + .../ClientApp/src/pages/Shop/Item/index.tsx | 18 +- webapi/ClientApp/src/pages/Shop/index.ts | 6 +- webapi/ClientApp/src/pages/index.tsx | 6 +- webapi/ClientApp/src/store/reducers/Cart.ts | 69 ++++++ .../ClientApp/src/store/reducers/Content.ts | 145 +++++++++++- 21 files changed, 664 insertions(+), 65 deletions(-) create mode 100644 webapi/ClientApp/src/pages/Profile/index.tsx create mode 100644 webapi/ClientApp/src/pages/Shop/Cart/index.tsx create mode 100644 webapi/ClientApp/src/pages/Shop/Cart/scss/style.module.scss create mode 100644 webapi/ClientApp/src/pages/Shop/Checkout/index.tsx create mode 100644 webapi/ClientApp/src/pages/Shop/Checkout/scss/style.module.scss create mode 100644 webapi/ClientApp/src/store/reducers/Cart.ts diff --git a/webapi/ClientApp/src/components/FeatherIcons/FeatherIcon.tsx b/webapi/ClientApp/src/components/FeatherIcons/FeatherIcon.tsx index 26aa5da..30f24bd 100644 --- a/webapi/ClientApp/src/components/FeatherIcons/FeatherIcon.tsx +++ b/webapi/ClientApp/src/components/FeatherIcons/FeatherIcon.tsx @@ -35,8 +35,7 @@ const FeatherIcon : FC = (props : IFeatherIcon) => { strokeLinecap="round" strokeLinejoin="round" className={`feather feather-${icon} ${className}`} - {...otherProps} - > + {...otherProps}> ) diff --git a/webapi/ClientApp/src/layouts/public/NavMenu/index.tsx b/webapi/ClientApp/src/layouts/public/NavMenu/index.tsx index 29f6839..5beabf5 100644 --- a/webapi/ClientApp/src/layouts/public/NavMenu/index.tsx +++ b/webapi/ClientApp/src/layouts/public/NavMenu/index.tsx @@ -29,20 +29,18 @@ const NavMenu : FC = () => { {content?.topMenu ? content.topMenu.map((item: MenuItemModel, index: number) => { return - {item.icon ? : ''} - {item.title} + {item.icon ? <> : ''}{item.title} }) : ''} -
+ {/* -
+ */} } diff --git a/webapi/ClientApp/src/models/abstractions.ts b/webapi/ClientApp/src/models/abstractions.ts index bd5b474..c3dee6c 100644 --- a/webapi/ClientApp/src/models/abstractions.ts +++ b/webapi/ClientApp/src/models/abstractions.ts @@ -1,4 +1,4 @@ -import { AuthorModel, ImageModel } from "./" +import { AuthorModel, FormItemModel, ImageModel } from "./" export interface RequestModel { @@ -17,6 +17,18 @@ export interface PageSectionModel { 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 { id: string, image?: ImageModel diff --git a/webapi/ClientApp/src/models/index.ts b/webapi/ClientApp/src/models/index.ts index 694c117..8d9b016 100644 --- a/webapi/ClientApp/src/models/index.ts +++ b/webapi/ClientApp/src/models/index.ts @@ -28,7 +28,8 @@ export interface FeatureModel { export interface FormItemModel { title?: string, - placeHolder?: string + optional?: string, + placeHolder?: string, } export interface ImageModel { diff --git a/webapi/ClientApp/src/models/pageSections.ts b/webapi/ClientApp/src/models/pageSections.ts index b00dc7a..ab4edd5 100644 --- a/webapi/ClientApp/src/models/pageSections.ts +++ b/webapi/ClientApp/src/models/pageSections.ts @@ -1,5 +1,5 @@ -import { FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialModel } from "./" -import { PageSectionModel } from "./abstractions" +import { FeatureModel, FormItemModel, ImageModel, LinkModel, MenuItemModel, TestimonialModel } from "./" +import { AddressPageSectionModel, PageSectionModel } from "./abstractions" export interface BlogTitleSectionModel extends PageSectionModel { 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 +} diff --git a/webapi/ClientApp/src/models/pages.ts b/webapi/ClientApp/src/models/pages.ts index 080b294..d3ea0fa 100644 --- a/webapi/ClientApp/src/models/pages.ts +++ b/webapi/ClientApp/src/models/pages.ts @@ -1,27 +1,27 @@ import { FormItemModel, LinkModel } from "." 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 { - titleSection: TitleSectionModel, - featuredBlogSection: FeaturedBlogSectionModel + titleSection: PageSection.TitleSectionModel, + featuredBlogSection: PageSection.FeaturedBlogSectionModel } export interface BlogItemPageModel extends PageModel { - titleSection: BlogTitleSectionModel, - commentsSection: CommentsSectionModel + titleSection: PageSection.BlogTitleSectionModel, + commentsSection: PageSection.CommentsSectionModel } export interface HomePageModel extends PageModel { - titleSection: TitleSectionModel, - featuresSection: FeaturesSectionModel, - testimonialsSection: TestimonialsSectionModel, - featuredBlogsSection: FeaturedBlogsSectionModel, - callToActionSection: CallToActionSectionModel + titleSection: PageSection.TitleSectionModel, + featuresSection: PageSection.FeaturesSectionModel, + testimonialsSection: PageSection.TestimonialsSectionModel, + featuredBlogsSection: PageSection.FeaturedBlogsSectionModel, + callToActionSection: PageSection.CallToActionSectionModel } export interface ShopCatalogPageModel extends PageModel { - titleSection: TitleSectionModel + titleSection: PageSection.TitleSectionModel } export interface SignInPageModel extends PageModel { @@ -45,6 +45,21 @@ export interface SignUpPageModel extends PageModel { } export interface ShopItemPageModel extends PageModel { - productSection: ProductSectionModel - relatedProductsSection: RelatedProductsSectionModel + productSection: PageSection.ProductSectionModel + 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 +} \ No newline at end of file diff --git a/webapi/ClientApp/src/models/requests.ts b/webapi/ClientApp/src/models/requests.ts index 1c57fd6..2d66a5f 100644 --- a/webapi/ClientApp/src/models/requests.ts +++ b/webapi/ClientApp/src/models/requests.ts @@ -39,3 +39,5 @@ export interface GetShopItemRequestModel extends RequestModel { } export interface GetShopRelatedRequestModel extends RequestModel { } + +export interface GetShopCartRequestModel extends RequestModel {} diff --git a/webapi/ClientApp/src/models/responses.ts b/webapi/ClientApp/src/models/responses.ts index 44095c2..8f904b5 100644 --- a/webapi/ClientApp/src/models/responses.ts +++ b/webapi/ClientApp/src/models/responses.ts @@ -7,9 +7,14 @@ import { ShopCatalogPageModel, ShopItemPageModel, SignInPageModel, - SignUpPageModel + SignUpPageModel, + CartPageModel, + CheckoutPageModel } from "./pages" + + + // Shop response models export interface GetShopCatalogResponseModel extends PaginationModel, ResponseModel {} @@ -29,6 +34,11 @@ export interface GetShopRelatedResponseModel extends ResponseModel { items: ShopItemModel [] } +export interface GetShopCartResponseModel extends ResponseModel { + quantity: number +} + + // Static content response model export interface GetContentResponseModel extends ResponseModel { siteName: string, @@ -44,6 +54,8 @@ export interface GetContentResponseModel extends ResponseModel { shopCatalog: ShopCatalogPageModel, shopItem: ShopItemPageModel, + cart: CartPageModel, + checkout: CheckoutPageModel, blogCatalog: BlogCatalogPageModel, blogItem: BlogItemPageModel, diff --git a/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx b/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx index a7e2336..f45fceb 100644 --- a/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx +++ b/webapi/ClientApp/src/pages/Blog/Catalog/index.tsx @@ -149,19 +149,19 @@ const BlogCatalog = () => { const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0] useEffect(() => { - // dispatch(blogCatalogActionCreators.requestBlogCatalog({ - // currentPage: params?.page ? params.page : "1" - // })) - // dispatch(blogFeaturedActionCreators.requestBlogFeatured()) + dispatch(blogCatalogActionCreators.requestBlogCatalog({ + currentPage: params?.page ? params.page : "1" + })) + dispatch(blogFeaturedActionCreators.requestBlogFeatured()) dispatch(blogCategoriesActionCreators.requestBlogCategories()) }, []) useEffect(() => { - // blogCatalog?.isLoading - // ? dispatch(loaderActionCreators.show()) - // : setTimeout(() => { - // dispatch(loaderActionCreators.hide()) - // }, 1000) + blogCatalog?.isLoading + ? dispatch(loaderActionCreators.show()) + : setTimeout(() => { + dispatch(loaderActionCreators.hide()) + }, 1000) }, [blogCatalog?.isLoading]) const blogItem = blogFeatured?.items[0] diff --git a/webapi/ClientApp/src/pages/Blog/Item/index.tsx b/webapi/ClientApp/src/pages/Blog/Item/index.tsx index a058831..c5ed5c5 100644 --- a/webapi/ClientApp/src/pages/Blog/Item/index.tsx +++ b/webapi/ClientApp/src/pages/Blog/Item/index.tsx @@ -57,18 +57,18 @@ const BlogItem = () => { const page = content?.blogItem useEffect(() => { - // if(params?.slug) - // dispatch(blogItemActionCreators.requestBlogItem({ - // slug: params.slug - // })) + if(params?.slug) + dispatch(blogItemActionCreators.requestBlogItem({ + slug: params.slug + })) }, []) useEffect(() => { - // blogItem?.isLoading - // ? dispatch(loaderActionCreators.show()) - // : setTimeout(() => { - // dispatch(loaderActionCreators.hide()) - // }, 1000) + blogItem?.isLoading + ? dispatch(loaderActionCreators.show()) + : setTimeout(() => { + dispatch(loaderActionCreators.hide()) + }, 1000) }, [blogItem?.isLoading]) const blogItemTitle: BlogItemTitle = { diff --git a/webapi/ClientApp/src/pages/Profile/index.tsx b/webapi/ClientApp/src/pages/Profile/index.tsx new file mode 100644 index 0000000..4034142 --- /dev/null +++ b/webapi/ClientApp/src/pages/Profile/index.tsx @@ -0,0 +1,5 @@ +const Profile = () => {} + +export { + Profile +} \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/Shop/Cart/index.tsx b/webapi/ClientApp/src/pages/Shop/Cart/index.tsx new file mode 100644 index 0000000..dd659f5 --- /dev/null +++ b/webapi/ClientApp/src/pages/Shop/Cart/index.tsx @@ -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 +
+
+
+

Shopping Cart

+

+ 3 items in your cart

+ + + + + + + + + + + + {[{}, {}, {}, {}].map((item, index) => + + + + + )} + + + +
ProductPriceQuantity
+
+
+ +
+
+

Product Name

+

Brand & Name

+
+
+
$49.00 + + +
+ + +
+
+
+

Subtotal:

+

$99.00

+
+
+
+ +
+
+ +} + +export { + Cart +} \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/Shop/Cart/scss/style.module.scss b/webapi/ClientApp/src/pages/Shop/Cart/scss/style.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/webapi/ClientApp/src/pages/Shop/Catalog/index.tsx b/webapi/ClientApp/src/pages/Shop/Catalog/index.tsx index d651135..d7c15ad 100644 --- a/webapi/ClientApp/src/pages/Shop/Catalog/index.tsx +++ b/webapi/ClientApp/src/pages/Shop/Catalog/index.tsx @@ -111,17 +111,17 @@ const ShopCatalog = () => { const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0] useEffect(() => { - // dispatch(shopCatalogActionCreators.requestShopCatalog({ - // currentPage: params?.page ? params.page : "1" - // })) + dispatch(shopCatalogActionCreators.requestShopCatalog({ + currentPage: params?.page ? params.page : "1" + })) }, []) useEffect(() => { - // shopCatalog?.isLoading - // ? dispatch(loaderActionCreators.show()) - // : setTimeout(() => { - // dispatch(loaderActionCreators.hide()) - // }, 1000) + shopCatalog?.isLoading + ? dispatch(loaderActionCreators.show()) + : setTimeout(() => { + dispatch(loaderActionCreators.hide()) + }, 1000) }, [shopCatalog?.isLoading]) const shopItems: ShopItems = { diff --git a/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx b/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx new file mode 100644 index 0000000..8406a3c --- /dev/null +++ b/webapi/ClientApp/src/pages/Shop/Checkout/index.tsx @@ -0,0 +1,221 @@ +import React from 'react' +import { Container } from 'reactstrap' + +// CSS Modules +import style from './scss/style.module.scss' + +const Checkout = () => { + return +
+
+ +

Checkout form

+

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.

+
+ +
+
+

+ Your cart + 3 +

+
    +
  • +
    +
    Product name
    + Brief description +
    + $12 +
  • +
  • +
    +
    Second product
    + Brief description +
    + $8 +
  • +
  • +
    +
    Third item
    + Brief description +
    + $5 +
  • +
  • +
    +
    Promo code
    + EXAMPLECODE +
    + −$5 +
  • +
  • + Total (USD) + $20 +
  • +
+ +
+
+ + +
+
+
+
+

Billing address

+
+
+
+ + +
+ Valid first name is required. +
+
+ +
+ + +
+ Valid last name is required. +
+
+ + +
+ + +
+ Please enter your shipping address. +
+
+ +
+ + +
+ +
+ + +
+ Please select a valid country. +
+
+ +
+ + +
+ Please provide a valid state. +
+
+ +
+ + +
+ Please provide a valid city. +
+
+ +
+ + +
+ Zip code required. +
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +

Payment

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + Full name as displayed on card +
+ Name on card is required +
+
+ +
+ + +
+ Credit card number is required +
+
+ +
+ + +
+ Expiration date required +
+
+ +
+ + +
+ Security code required +
+
+
+ +
+ + +
+
+
+
+
+ + + +} + +export { + Checkout +} \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/Shop/Checkout/scss/style.module.scss b/webapi/ClientApp/src/pages/Shop/Checkout/scss/style.module.scss new file mode 100644 index 0000000..49e4a9b --- /dev/null +++ b/webapi/ClientApp/src/pages/Shop/Checkout/scss/style.module.scss @@ -0,0 +1,3 @@ +.container { + max-width: 960px; +} \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/Shop/Item/index.tsx b/webapi/ClientApp/src/pages/Shop/Item/index.tsx index b10ed67..b483502 100644 --- a/webapi/ClientApp/src/pages/Shop/Item/index.tsx +++ b/webapi/ClientApp/src/pages/Shop/Item/index.tsx @@ -19,18 +19,18 @@ const ShopItem = () => { const page = content?.shopItem useEffect(() => { - // if(params?.slug) - // dispatch(shopItemActionCreators.requestShopItem({ - // slug: params.slug - // })) + if(params?.slug) + dispatch(shopItemActionCreators.requestShopItem({ + slug: params.slug + })) }, []) useEffect(() => { - // shopItem?.isLoading - // ? dispatch(loaderActionCreators.show()) - // : setTimeout(() => { - // dispatch(loaderActionCreators.hide()) - // }, 1000) + shopItem?.isLoading + ? dispatch(loaderActionCreators.show()) + : setTimeout(() => { + dispatch(loaderActionCreators.hide()) + }, 1000) }, [shopItem?.isLoading]) return <> diff --git a/webapi/ClientApp/src/pages/Shop/index.ts b/webapi/ClientApp/src/pages/Shop/index.ts index 6248aa6..5d8944b 100644 --- a/webapi/ClientApp/src/pages/Shop/index.ts +++ b/webapi/ClientApp/src/pages/Shop/index.ts @@ -1,7 +1,11 @@ import { ShopCatalog } from './Catalog' import { ShopItem } from './Item' +import { Cart } from './Cart' +import { Checkout } from './Checkout' export { ShopCatalog, - ShopItem + ShopItem, + Cart, + Checkout } \ No newline at end of file diff --git a/webapi/ClientApp/src/pages/index.tsx b/webapi/ClientApp/src/pages/index.tsx index 07f3b11..d014326 100644 --- a/webapi/ClientApp/src/pages/index.tsx +++ b/webapi/ClientApp/src/pages/index.tsx @@ -8,9 +8,11 @@ import { AdminHome } from './AdminHome' import { Signin } from './Signin' import { Signup } from './Signup' -import { ShopCatalog, ShopItem } from './Shop' +import { ShopCatalog, ShopItem, Cart, Checkout } from './Shop' import { BlogCatalog, BlogItem } from './Blog' + + interface IPages { [key: string]: FC; } @@ -20,6 +22,8 @@ const pages: IPages = { ShopCatalog, ShopItem, + Cart, + Checkout, BlogCatalog, BlogItem, diff --git a/webapi/ClientApp/src/store/reducers/Cart.ts b/webapi/ClientApp/src/store/reducers/Cart.ts new file mode 100644 index 0000000..17116f2 --- /dev/null +++ b/webapi/ClientApp/src/store/reducers/Cart.ts @@ -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 => (dispatch, getState) => { + + }, + + addToCart: (): AppThunkAction => (dispatch, getState) => { + + // Get>('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 => (dispatch, getState) => { + + } +} + +const unloadedState: CartState = { + quantity: 0, + + isLoading: false +} + +export const reducer: Reducer = (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 +} \ No newline at end of file diff --git a/webapi/ClientApp/src/store/reducers/Content.ts b/webapi/ClientApp/src/store/reducers/Content.ts index f76f6a0..b25f242 100644 --- a/webapi/ClientApp/src/store/reducers/Content.ts +++ b/webapi/ClientApp/src/store/reducers/Content.ts @@ -44,6 +44,10 @@ const unloadedState: ContentState = { { target: ":page", component: "ShopCatalog" }, { target: ":page" , childRoutes: [ { target: ":slug", component: "ShopItem" } + ]}, + { target:"cart", childRoutes: [ + { target: "", component: "Cart" }, + { target: "checkout", component: "Checkout" } ]} ]}, { target: "/blog", childRoutes: [ @@ -66,7 +70,9 @@ const unloadedState: ContentState = { { target: "/blog", title: "Blog" }, { 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: [], @@ -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: { titleSection: { title: "Welcome to Blog Home!",