diff --git a/db/DML/content.json b/db/DML/content.json index 8f5568b..cd4db30 100644 --- a/db/DML/content.json +++ b/db/DML/content.json @@ -1,8 +1,386 @@ { "_id": "b3f39a82-6a1b-46a4-85cc-04c3b4315511", "siteId": "404c8232-9048-4519-bfba-6e78dc7005ca", - "siteName": "MAKS-IT", - "siteUrl": "https://maks-it.com", + + "l10n": [ + { + "locale": 0, + "siteName": "Contoso", + "siteUrl": "https://maks-it.com", + "settings": { + "timeZone": "+1", + "dateFormat": "MMMM YYYY, dddd", + "timeFormat": "HH:mm", + "currency": "EUR", + "currencySymbol": "€" + }, + "homePage": { + "header": { + "title": "Home - {siteName}", + "meta": { + "description": "Single-page application home page" + }, + "link": { + "canonical": "{siteUrl}" + } + }, + "titleSection": { + "title": "Hello, World! by C# and Mongo", + "text": "

Welcome to your new single-page application, built with:

\n ", + "primaryLink": { + "target": "#!", + "anchorText": "Get Started" + }, + "secondaryLink": { + "target": "#!", + "anchorText": "Learn more" + }, + "image": { + "src": "/Image/600x400/343a40/6c757d", + "alt": "..." + } + }, + "featuresSection": { + "title": "To help you get started, we have also set up:", + "items": [ + { + "icon": "navigation", + "title": "Client-side navigation", + "text": "For example, click Counter then Back to return here." + }, + { + "icon": "server", + "title": "Development server integration", + "text": "In development mode, the development server from create-react-app runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file." + }, + { + "icon": "terminal", + "title": "Efficient production builds", + "text": "In production mode, development-time features are disabled, and your dotnet publish configuration produces minified, efficiently bundled JavaScript files." + } + ] + }, + "testimonialsSection": { + "items": [ + { + "text": "The ClientApp subdirectory is a standard React application based on the create-react-app template. If you open a command prompt in that directory, you can run yarn commands such as yarn test or yarn install.", + "reviewer": { + "image": { + "src": "/Image/40x40/ced4da/6c757d", + "alt": "..." + }, + "fullName": "Admin", + "position": "CEO, MAKS-IT" + } + } + ] + }, + "featuredBlogsSection": { + "title": "Featured blogs" + }, + "callToActionSection": { + "title": "New products, delivered to you.", + "text": "Sign up for our newsletter for the latest updates.", + "privacyDisclaimer": "We care about privacy, and will never share your data.", + "email": { + "title": "Sign up", + "placeHolder": "Email address..." + } + } + }, + "shopCatalog": { + "header": { + "title": "Shop catalog - {siteName}", + "meta": { + "description": "Single-page application shop catalog" + }, + "link": { + "canonical": "" + } + }, + "titleSection": { + "title": "Shop in style", + "text": "With this shop hompeage template" + }, + "shopItemsSection": { + "addToCart": "Add to cart" + } + }, + "shopItem": { + "header": { + "title": "{productTitle} - {siteName}", + "meta": { + "description": "Single-page application shop item" + }, + "link": { + "canonical": "" + } + }, + "productSection": { + "availableQuantity": "Available Qty.", + "addToCart": "Add to cart" + }, + "relatedProductsSection": { + "title": "Related products", + "addToCart": "Add to cart" + } + }, + "shopCart": { + "header": { + "title": "Shop cart - {siteName}", + "meta": { + "description": "Single-page application shop cart" + }, + "link": { + "canonical": "" + } + }, + "titleSection": { + "title": "Shopping Cart", + "text": "{quantity} items in your cart" + }, + "productsSection": { + "product": "Product", + "price": "Price", + "quantity": "Quantity", + "subtotal": "Subtotal:", + "continueShopping": { + "target": "/shop", + "anchorText": "Continue shopping" + }, + "checkout": { + "target": "checkout", + "anchorText": "Checkout" + } + } + }, + "shopCheckout": { + "header": { + "title": "Shop - checkout {siteName}", + "meta": { + "description": "Single-page application checkout" + }, + "link": { + "canonical": "" + } + }, + "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": { + "header": { + "title": "Blog catalog - {siteName}", + "meta": { + "description": "Single-page application blog catalog" + }, + "link": { + "canonical": "" + } + }, + "titleSection": { + "title": "Welcome to Blog Home!", + "text": "A Bootstrap 5 starter layout for your next blog homepage" + }, + "featuredBlogSection": { + "readTime": "{date} Time to read: {readTime} min" + } + }, + "blogItem": { + "header": { + "title": "{blogTitle} - {siteName}", + "meta": { + "description": "Single-page application blog item" + }, + "link": { + "canonical": "" + } + }, + "titleSection": { + "postedOnBy": "Posted on {date} by {nickName}" + }, + "commentsSection": { + "leaveComment": "Join the discussion and leave a comment!" + } + }, + "signIn": { + "header": { + "title": "Sign in - {siteName}", + "meta": { + "description": "Single-page application sign in", + "robots": "noindex, nofollow" + }, + "link": { + "canonical": "" + } + }, + "title": "Sign in", + "email": { + "title": "Email address", + "placeHolder": "Email address..." + }, + "password": { + "title": "Password", + "placeHolder": "Password..." + }, + "dontHaveAnAccount": "Don't have an account yet? Please", + "signUpLink": { + "target": "/signup", + "anchorText": "Sign up" + }, + "submit": { + "title": "Sign in" + } + }, + "signUp": { + "header": { + "title": "Sign up - {siteName}", + "meta": { + "description": "Single-page application sign up", + "robots": "noindex, nofollow" + }, + "link": { + "canonical": "" + } + }, + "title": "Sign up", + "username": { + "title": "Username", + "placeHolder": "Username..." + }, + "email": { + "title": "Email address", + "placeHolder": "Email address..." + }, + "reEmail": { + "title": "Repeat email address", + "placeHolder": "Repeat email address..." + }, + "password": { + "title": "Password", + "placeHolder": "Password..." + }, + "rePassword": { + "title": "Repeat password", + "placeHolder": "Repeat password..." + }, + "acceptTermsAndConditions": "Accept terms and conditions", + "submit": { + "title": "Sing up" + } + } + } + ], + "header": { "title": "{siteName}", "meta": { @@ -14,14 +392,7 @@ "canonical": "{siteUrl}" } }, - "localization": { - "timeZone": "+1", - "locale": "en-US", - "dateFormat": "MMMM YYYY, dddd", - "timeFormat": "HH:mm", - "currency": "EUR", - "currencySymbol": "€" - }, + "routes": [ { "target": "/", @@ -103,394 +474,60 @@ "topMenu": [ { "target": "/", - "title": "Home" + "l10n": [ + { + "locale": 0, + "title": "Home" + } + ] }, { "target": "/shop", - "title": "Shop" + "l10n": [ + { + "locale": 0, + "title": "Shop" + } + ] }, { "target": "/blog", - "title": "Blog" + "l10n": [ + { + "locale": 0, + "title": "Blog" + } + ] }, { "target": "/signin", - "title": "Sing in" + "l10n": [ + { + "locale": 0, + "title": "Sign in" + } + ] }, { "target": "/signup", - "title": "Sign up" + "l10n": [ + { + "locale": 0, + "title": "Sign up" + } + ] }, { "target": "/shop/cart", "icon": "shopping-cart", - "title": "Cart ({quantity})" + "l10n": [ + { + "locale": 0, + "title": "Cart ({quantity})" + } + ] } ], - "sideMenu": [], - "homePage": { - "header": { - "title": "Home - {siteName}", - "meta": { - "description": "Single-page application home page" - }, - "link": { - "canonical": "{siteUrl}" - } - }, - "titleSection": { - "title": "Hello, World! by C# and Mongo", - "text": "

Welcome to your new single-page application, built with:

\n ", - "primaryLink": { - "target": "#!", - "anchorText": "Get Started" - }, - "secondaryLink": { - "target": "#!", - "anchorText": "Learn more" - }, - "image": { - "src": "https://dummyimage.com/600x400/343a40/6c757d", - "alt": "..." - } - }, - "featuresSection": { - "title": "To help you get started, we have also set up:", - "items": [ - { - "icon": "navigation", - "title": "Client-side navigation", - "text": "For example, click Counter then Back to return here." - }, - { - "icon": "server", - "title": "Development server integration", - "text": "In development mode, the development server from create-react-app runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file." - }, - { - "icon": "terminal", - "title": "Efficient production builds", - "text": "In production mode, development-time features are disabled, and your dotnet publish configuration produces minified, efficiently bundled JavaScript files." - } - ] - }, - "testimonialsSection": { - "items": [ - { - "text": "The ClientApp subdirectory is a standard React application based on the create-react-app template. If you open a command prompt in that directory, you can run yarn commands such as yarn test or yarn install.", - "reviewer": { - "_id": "c5295208-8950-441f-8217-bd7c4a907a0f", - "image": { - "src": "https://dummyimage.com/40x40/ced4da/6c757d", - "alt": "..." - }, - "fullName": "Admin", - "position": "CEO, MAKS-IT" - } - } - ] - }, - "featuredBlogsSection": { - "title": "Featured blogs" - }, - "callToActionSection": { - "title": "New products, delivered to you.", - "text": "Sign up for our newsletter for the latest updates.", - "privacyDisclaimer": "We care about privacy, and will never share your data.", - "email": { - "title": "Sign up", - "placeHolder": "Email address..." - } - } - }, - "shopCatalog": { - "header": { - "title": "Shop catalog - {siteName}", - "meta": { - "description": "Single-page application shop catalog" - }, - "link": { - "canonical": "" - } - }, - "titleSection": { - "title": "Shop in style", - "text": "With this shop hompeage template" - }, - "shopItemsSection": { - "addToCart": "Add to cart" - } - }, - "shopItem": { - "header": { - "title": "{productTitle} - {siteName}", - "meta": { - "description": "Single-page application shop item" - }, - "link": { - "canonical": "" - } - }, - "productSection": { - "availableQuantity": "Available Qty.", - "addToCart": "Add to cart" - }, - "relatedProductsSection": { - "title": "Related products", - "addToCart": "Add to cart" - } - }, - "shopCart": { - "header": { - "title": "Shop cart - {siteName}", - "meta": { - "description": "Single-page application shop cart" - }, - "link": { - "canonical": "" - } - }, - "titleSection": { - "title": "Shopping Cart", - "text": "{quantity} items in your cart" - }, - "productsSection": { - "product": "Product", - "price": "Price", - "quantity": "Quantity", - "subtotal": "Subtotal:", - "continueShopping": { - "target": "/shop", - "anchorText": "Continue shopping" - }, - "checkout": { - "target": "checkout", - "anchorText": "Checkout" - } - } - }, - "shopCheckout": { - "header": { - "title": "Shop - checkout {siteName}", - "meta": { - "description": "Single-page application checkout" - }, - "link": { - "canonical": "" - } - }, - "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": { - "header": { - "title": "Blog catalog - {siteName}", - "meta": { - "description": "Single-page application blog catalog" - }, - "link": { - "canonical": "" - } - }, - "titleSection": { - "title": "Welcome to Blog Home!", - "text": "A Bootstrap 5 starter layout for your next blog homepage" - }, - "featuredBlogSection": { - "readTime": "{date} Time to read: {readTime} min" - } - }, - "blogItem": { - "header": { - "title": "{blogTitle} - {siteName}", - "meta": { - "description": "Single-page application blog item" - }, - "link": { - "canonical": "" - } - }, - "titleSection": { - "postedOnBy": "Posted on {date} by {nickName}" - }, - "commentsSection": { - "leaveComment": "Join the discussion and leave a comment!" - } - }, - "signIn": { - "header": { - "title": "Sign in - {siteName}", - "meta": { - "description": "Single-page application sign in", - "robots": "noindex, nofollow" - }, - "link": { - "canonical": "" - } - }, - "title": "Sign in", - "email": { - "title": "Email address", - "placeHolder": "Email address..." - }, - "password": { - "title": "Password", - "placeHolder": "Password..." - }, - "dontHaveAnAccount": "Don't have an account yet? Please", - "signUpLink": { - "target": "/signup", - "anchorText": "Sign up" - }, - "submit": { - "title": "Sign in" - } - }, - "signUp": { - "header": { - "title": "Sign up - {siteName}", - "meta": { - "description": "Single-page application sign up", - "robots": "noindex, nofollow" - }, - "link": { - "canonical": "" - } - }, - "title": "Sign up", - "username": { - "title": "Username", - "placeHolder": "Username..." - }, - "email": { - "title": "Email address", - "placeHolder": "Email address..." - }, - "reEmail": { - "title": "Repeat email address", - "placeHolder": "Repeat email address..." - }, - "password": { - "title": "Password", - "placeHolder": "Password..." - }, - "rePassword": { - "title": "Repeat password", - "placeHolder": "Repeat password..." - }, - "acceptTermsAndConditions": "Accept terms and conditions", - "submit": { - "title": "Sing up" - } - } + "sideMenu": [] + } \ No newline at end of file diff --git a/src/ClientApp/.dockerignore b/src/ClientApp/.dockerignore deleted file mode 100644 index 7e297f6..0000000 --- a/src/ClientApp/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!obj\Docker\publish\* -!obj\Docker\empty\ diff --git a/src/ClientApp/.env b/src/ClientApp/.env index 9493bf6..8aae9ac 100644 --- a/src/ClientApp/.env +++ b/src/ClientApp/.env @@ -1,7 +1,12 @@ BROWSER=none -REACT_APP_API=https://localhost:7151/api +REACT_APP_LOCAL_ONLY=Y + +REACT_APP_FRONTEND=https://localhost:7174 +REACT_APP_API=https://localhost:7174/api + REACT_APP_SITEID=404c8232-9048-4519-bfba-6e78dc7005ca +REACT_APP_LOCALE=en-US REACT_APP_ACCOUNT=Account diff --git a/src/ClientApp/ClientApp.njsproj b/src/ClientApp/ClientApp.njsproj index 9bef263..5fc9746 100644 --- a/src/ClientApp/ClientApp.njsproj +++ b/src/ClientApp/ClientApp.njsproj @@ -32,11 +32,6 @@ true - - Content - Dockerfile - - diff --git a/src/ClientApp/Dockerfile b/src/ClientApp/Dockerfile deleted file mode 100644 index b88171d..0000000 --- a/src/ClientApp/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# ==== CONFIGURE ===== -# Use a Node 16 base image -FROM node:16-alpine -# Set the working directory to /app inside the container -WORKDIR /app -# Copy app files -COPY . . -# ==== BUILD ===== -# Install dependencies (npm ci makes sure the exact versions in the lockfile gets installed) -RUN npm ci -# Build the app -RUN npm run build -# ==== RUN ======= -# Set the env to "production" -ENV NODE_ENV production -# Expose the port on which the app will be running (3000 is the default that `serve` uses) -EXPOSE 3000 -# Start the app -CMD [ "npx", "serve", "build" ] \ No newline at end of file diff --git a/src/ClientApp/package-lock.json b/src/ClientApp/package-lock.json index f8c21d6..ceaff3f 100644 --- a/src/ClientApp/package-lock.json +++ b/src/ClientApp/package-lock.json @@ -8,6 +8,7 @@ "name": "react-redux-template", "version": "0.1.0", "dependencies": { + "axios": "^1.3.4", "bootstrap": "5.1.3", "classnames": "^2.3.1", "dayjs": "^1.11.2", @@ -4713,8 +4714,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -4767,6 +4767,29 @@ "node": ">=12" } }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -5607,7 +5630,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -6379,7 +6401,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -7984,7 +8005,6 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true, "funding": [ { "type": "individual", @@ -10723,7 +10743,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -10732,7 +10751,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12841,6 +12859,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -19526,8 +19549,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "at-least-node": { "version": "1.0.0", @@ -19555,6 +19577,28 @@ "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==", "dev": true }, + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -20213,7 +20257,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -20760,8 +20803,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "depd": { "version": "2.0.0", @@ -21994,8 +22036,7 @@ "follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, "fork-ts-checker-webpack-plugin": { "version": "6.5.2", @@ -24041,14 +24082,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -25442,6 +25481,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", diff --git a/src/ClientApp/package.json b/src/ClientApp/package.json index 9bffae3..84986dc 100644 --- a/src/ClientApp/package.json +++ b/src/ClientApp/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "axios": "^1.3.4", "bootstrap": "5.1.3", "classnames": "^2.3.1", "dayjs": "^1.11.2", diff --git a/src/ClientApp/src/App.tsx b/src/ClientApp/src/App.tsx index e49d34c..c027b44 100644 --- a/src/ClientApp/src/App.tsx +++ b/src/ClientApp/src/App.tsx @@ -1,28 +1,37 @@ // React -import React, { FC, useEffect } from 'react' +import React, { FC, useEffect, useState } from 'react' import { Route, Routes, useLocation } from 'react-router' // Redux import { useSelector, useDispatch } from 'react-redux' import { actionCreators as settingsActionCreators } from './store/reducers/Content' + // Components import { DynamicLayout } from './layouts' import { DynamicPage } from './pages' -import { RouteModel } from './models' import { ApplicationState } from './store' import { Loader } from './components/Loader' import { Helmet } from './components/ReactHelmet' +import axios from 'axios' +import ModalComponent from './components/Modal' + +export interface IRoute { + target: string + component?: string + childRoutes?: IRoute [] +} + interface IRouteProp { path: string, element?: JSX.Element } -const NestedRoutes = (routes: RouteModel[], tag: string | undefined = undefined) => { +const NestedRoutes = (routes: IRoute[], tag: string | undefined = undefined) => { if(!Array.isArray(routes)) return - return routes.map((route: RouteModel, index: number) => { + return routes.map((route: IRoute, index: number) => { const routeProps: IRouteProp = { path: route.target } @@ -32,17 +41,48 @@ const NestedRoutes = (routes: RouteModel[], tag: string | undefined = undefined) routeProps.element = tag ? {page} : page } - - return {Array.isArray(route.childRoutes) ? NestedRoutes(route.childRoutes, tag) : ''} }) } const App = () => { + const [loaderCounter, setLoaderCounter] = useState(0) + + // Add a request interceptor + axios.interceptors.request.use((config) => { + setLoaderCounter(loaderCounter + 1) + + // Do something before request is sent + return config + }, err => { + // console.log(err) + setLoaderCounter(0) + + // Do something with request error + return Promise.reject(err) + }) + + // Add a response interceptor + axios.interceptors.response.use(function (response) { + setLoaderCounter(loaderCounter > 0 ? loaderCounter - 1 : 0) + + // Any status code that lie within the range of 2xx cause this function to trigger + // Do something with response data + return response + }, err => { + // console.log(err) + setLoaderCounter(0) + + // Any status codes that falls outside the range of 2xx cause this function to trigger + // Do something with response error + return Promise.reject(err) + }) + + const { pathname } = useLocation() const dispatch = useDispatch() - const { content, header, loader } = useSelector((state: ApplicationState) => state) + const { content, header } = useSelector((state: ApplicationState) => state) useEffect(() => { dispatch(settingsActionCreators.requestContent()) @@ -56,11 +96,7 @@ const App = () => { }, [pathname]) - const { - title = "", - link = {}, - meta = {} - } = header ? header : {} + const { title, link, meta } = header return <> @@ -76,7 +112,13 @@ const App = () => { {content?.serviceRoutes ? NestedRoutes(content.serviceRoutes) : ''} - {loader ? : ''} + {} + + { 0 }} />} } diff --git a/src/ClientApp/src/components/Loader/index.tsx b/src/ClientApp/src/components/Loader/index.tsx index 516dedb..da18603 100644 --- a/src/ClientApp/src/components/Loader/index.tsx +++ b/src/ClientApp/src/components/Loader/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode, useEffect, useState } from 'react' +import React, { FC, ReactNode } from 'react' import './scss/loaders.scss' diff --git a/src/ClientApp/src/components/Modal/index.tsx b/src/ClientApp/src/components/Modal/index.tsx new file mode 100644 index 0000000..e4c92d1 --- /dev/null +++ b/src/ClientApp/src/components/Modal/index.tsx @@ -0,0 +1,40 @@ +import React, { FC, useState } from 'react' +import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap' + + +export interface IModalComponent { + visible: boolean + title: string, + text: string +} + +const ModalComponent : FC = (props) => { + + const { visible, title, text } = props + + const [modal, setModal] = useState(visible) + + const toggle = () => setModal(!modal) + + return ( +
+ {/* */} + + {title} + {text} + {/* + {' '} + + */} + +
+ ) +} + +export default ModalComponent \ No newline at end of file diff --git a/src/ClientApp/src/components/Pagination/index.tsx b/src/ClientApp/src/components/Pagination/index.tsx index 2ad8f2e..865bc7a 100644 --- a/src/ClientApp/src/components/Pagination/index.tsx +++ b/src/ClientApp/src/components/Pagination/index.tsx @@ -4,14 +4,14 @@ import { Pagination as ReactstrapPagination, PaginationItem, PaginationLink } fr import { findChunk, intToArray, splitInChunks } from './utils' -interface PaginationProps { +export interface IPaginationComponent { maxVisiblePages?: number, totalPages: number, currentPage: number, onClick: (page: number) => void } -const Pagination: FC = ({ +const Pagination: FC = ({ maxVisiblePages = 5, totalPages = 1, currentPage = 1, @@ -80,14 +80,14 @@ const Pagination: FC = ({ } -interface SSRPaginationProps { +export interface ISSRPaginationComponent { maxVisiblePages?: number, totalPages: number, currentPage: number, linksPath?: string } -const SSRPagination: FC = ({ +const SSRPagination: FC = ({ maxVisiblePages = 5, totalPages = 1, currentPage = 1, diff --git a/src/ClientApp/src/components/SideWidgets/Categories.tsx b/src/ClientApp/src/components/SideWidgets/Categories.tsx index 0f72def..ad3583b 100644 --- a/src/ClientApp/src/components/SideWidgets/Categories.tsx +++ b/src/ClientApp/src/components/SideWidgets/Categories.tsx @@ -1,9 +1,15 @@ import React, { FC } from 'react' +import { Link } from 'react-router-dom' + import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap' -import { CategoryModel } from '../../models' + +export interface ICategory { + href: string, + anchorText: string +} interface ICategories { - items?: CategoryModel [] + items?: ICategory [] } const Categories: FC = ({ @@ -21,12 +27,12 @@ const Categories: FC = ({
    - {firstHalf.map((item, index) =>
  • {item.text}
  • )} + {firstHalf.map((item, index) =>
  • {item.anchorText}
  • )}
    - {secondHalf.map((item, index) =>
  • {item.text}
  • )} + {secondHalf.map((item, index) =>
  • {item.anchorText}
  • )}
@@ -36,4 +42,4 @@ const Categories: FC = ({ export { Categories -} \ No newline at end of file +} diff --git a/src/ClientApp/src/functions/findRoutes.ts b/src/ClientApp/src/functions/findRoutes.ts index 7ef704d..f99816d 100644 --- a/src/ClientApp/src/functions/findRoutes.ts +++ b/src/ClientApp/src/functions/findRoutes.ts @@ -1,16 +1,24 @@ -import { RouteModel } from "../models" +import { IRoute } from "../App" interface ComponentRoutesModel { targets: string [], component: string } -const findRoutes = (routes: RouteModel[] = [], component: string | undefined, parentTarget: string [] = [], result: ComponentRoutesModel [] = []): ComponentRoutesModel [] => { +/** + * Usege const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0] + * @param routes + * @param component + * @param parentTarget + * @param result + * @returns + */ +const findRoutes = (routes: IRoute[] = [], component: string | undefined, parentTarget: string [] = [], result: ComponentRoutesModel [] = []): ComponentRoutesModel [] => { if(!Array.isArray(routes)) return [] - routes.forEach((route: RouteModel) => { + routes.forEach((route: IRoute) => { const targets: string [] = [] if(parentTarget) { parentTarget.forEach(item => { diff --git a/src/ClientApp/src/interfaces/index.ts b/src/ClientApp/src/interfaces/index.ts new file mode 100644 index 0000000..42eab06 --- /dev/null +++ b/src/ClientApp/src/interfaces/index.ts @@ -0,0 +1,123 @@ + +//new shared interfaces +interface IHeaderLink { + [key: string]: string +} + +interface IMeta { + [key: string]: string +} + +export interface IHeader { + title: string, + link: IHeaderLink, + meta: IMeta +} + + + + +export interface IParams { + [key: string]: string | undefined +} + +export interface IRequest { + pathParams?: T + searchParams?: TT, +} + +export interface IResponse {} + + +export interface IPagination { + totalPages: number, + currentPage: number, + items: T [] +} + + + + + + + + + + + + + + + + + + + + +// export interface PaginationModel { +// totalPages: number, +// currentPage: number, +// items: T [] +// } + + +// export interface AuthorModel extends PersonModel { +// nickName: string +// } + +// export interface BlogItemModel extends PostItemModel { +// readTime?: number, +// likes?: number +// } + + + + + +// export interface FeatureModel { +// icon: string, +// title: string, +// text: string +// } + +// export interface FormItemModel { +// title?: string, +// optional?: string, +// placeHolder?: string, +// } + + + + + + +// export interface LinkModel { +// target: string, +// anchorText: string +// } + + + + + +// export interface ReviewerModel extends PersonModel { +// fullName: string, +// position: string +// } + + + +// export interface ShopItemModel extends PostItemModel { +// images?: ImageModel [], +// sku: string, +// brandName: string, +// rating?: number, +// price: number, +// newPrice?: number, +// quantity?: number +// } + +// export interface TestimonialModel { +// text: string, +// reviewer: ReviewerModel +// } diff --git a/src/ClientApp/src/layouts/admin/SideMenu/index.tsx b/src/ClientApp/src/layouts/admin/SideMenu/index.tsx index 37b3c7e..8ecc1b7 100644 --- a/src/ClientApp/src/layouts/admin/SideMenu/index.tsx +++ b/src/ClientApp/src/layouts/admin/SideMenu/index.tsx @@ -6,6 +6,15 @@ import { Collapse, Nav, NavItem, NavLink } from 'reactstrap' import { FeatherIcon } from '../../../components/FeatherIcons' import style from './scss/style.module.scss' + +export interface ISideMenuItem { + icon?: string, + title?: string, + target?: string + childItems?: ISideMenuItem [] +} + + interface ISubMenu { icon?: string, title: string, diff --git a/src/ClientApp/src/layouts/public/NavMenu/index.tsx b/src/ClientApp/src/layouts/public/NavMenu/index.tsx index 0cc9944..05fc9cf 100644 --- a/src/ClientApp/src/layouts/public/NavMenu/index.tsx +++ b/src/ClientApp/src/layouts/public/NavMenu/index.tsx @@ -4,9 +4,15 @@ import { useSelector } from 'react-redux' import { Collapse, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap' import { FeatherIcon } from '../../../components/FeatherIcons' import { ApplicationState } from '../../../store' -import { MenuItemModel } from '../../../models' +export interface INavMenuItem { + icon?: string, + title?: string, + target?: string + childItems?: INavMenuItem [] +} + const NavMenu : FC = () => { const { content, shopCart } = useSelector((state: ApplicationState) => state) @@ -33,7 +39,7 @@ const NavMenu : FC = () => {
    - {content?.topMenu ? content.topMenu.map((item: MenuItemModel, index: number) => { + {content?.topMenu ? content.topMenu.map((item: INavMenuItem, index: number) => { return <> {item.icon ? <> : ''}{titleFormatter(item.title)} diff --git a/src/ClientApp/src/models/abstractions.ts b/src/ClientApp/src/models/abstractions.ts deleted file mode 100644 index 604c8e1..0000000 --- a/src/ClientApp/src/models/abstractions.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { AuthorModel, FormItemModel, HeaderModel, ImageModel } from "./" -import { TitleSectionModel } from "./pageSections" - -export interface Params { - [key: string]: string | undefined -} - -export interface RequestModel { - pathParams?: T - searchParams?: TT, - -} - -export interface ResponseModel {} - -export interface AddressPageSectionModel extends PageSectionModel { - firstName: FormItemModel, - lastName: FormItemModel, - address: FormItemModel, - address2: FormItemModel, - country: FormItemModel, - state: FormItemModel, - city: FormItemModel, - zip: FormItemModel -} - -export interface PageModel { - header: HeaderModel, - titleSection?: TitleSectionModel -} - -export interface PageSectionModel { - title?: string - text?: string -} - -export interface PersonModel { - id: string, - image?: ImageModel -} - -export interface PostItemModel { - id: string, - slug: string, - image: ImageModel, - badges: string [], - title: string, - shortText?: string, - text?: string, - author: AuthorModel, - created: string, - tags: string [] -} diff --git a/src/ClientApp/src/models/index.ts b/src/ClientApp/src/models/index.ts deleted file mode 100644 index f8475e4..0000000 --- a/src/ClientApp/src/models/index.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { PersonModel, PostItemModel } from "./abstractions" - - -export interface PaginationModel { - totalPages: number, - currentPage: number, - items: T [] -} - - -export interface AuthorModel extends PersonModel { - nickName: string -} - -export interface BlogItemModel extends PostItemModel { - readTime?: number, - likes?: number -} - -export interface CategoryModel { - id: string, - text: string -} - -export interface CommentModel { - author: AuthorModel, - comment: string, - responses?: CommentModel [] -} - -export interface FeatureModel { - icon: string, - title: string, - text: string -} - -export interface FormItemModel { - title?: string, - optional?: string, - placeHolder?: string, -} - -export interface HeaderLink { - [key: string]: string -} - -export interface Meta { - [key: string]: string -} -export interface HeaderModel { - title: string, - link: HeaderLink, - meta: Meta -} - -export interface ImageModel { - src: string, - alt: string -} - -export interface LinkModel { - target: string, - anchorText: string -} - -export interface LocalizationModel { - timeZone: string, - locale: string, - dateFormat: string, - timeFormat: string, - currency: string, - currencySymbol: string -} - -export interface MenuItemModel { - icon?: string, - title?: string, - target?: string - childItems?: MenuItemModel [] -} - -export interface ReviewerModel extends PersonModel { - fullName: string, - position: string -} - -export interface RouteModel { - target: string - component?: string - childRoutes?: RouteModel [] -} - -export interface ShopItemModel extends PostItemModel { - images?: ImageModel [], - sku: string, - brandName: string, - rating?: number, - price: number, - newPrice?: number, - quantity?: number -} - -export interface TestimonialModel { - text: string, - reviewer: ReviewerModel -} diff --git a/src/ClientApp/src/models/pageSections.ts b/src/ClientApp/src/models/pageSections.ts deleted file mode 100644 index 80bf1e1..0000000 --- a/src/ClientApp/src/models/pageSections.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { FeatureModel, FormItemModel, ImageModel, LinkModel, MenuItemModel, TestimonialModel } from "./" -import { AddressPageSectionModel, PageSectionModel } from "./abstractions" - -export interface BillingAddressSectionModel extends AddressPageSectionModel { } - -export interface CallToActionSectionModel extends PageSectionModel { - privacyDisclaimer?: string - email?: FormItemModel -} - -export interface CartProductsSectionModel extends PageSectionModel { - product: string, - price: string, - quantity: string, - subtotal: string, - continueShopping: LinkModel, - checkout: LinkModel -} - -export interface CheckoutSettingsSectionModel extends PageSectionModel { - shippingAddressSameAsBillingAddress: string, - saveThisInformation: string -} - -export interface CheckoutSummarySectionModel extends PageSectionModel { - title: string, - total: string, - - promoCode: FormItemModel, - submit: FormItemModel -} - -export interface CommentsSectionModel extends PageSectionModel { - leaveComment: string -} - -export interface FeaturedBlogSectionModel extends PageSectionModel { - readTime: string -} - -export interface FeaturedBlogsSectionModel extends PageSectionModel {} - -export interface FeaturesSectionModel extends PageSectionModel { - items: FeatureModel [] -} - -export interface PaymentSectionModel extends PageSectionModel { - nameOnCard: FormItemModel, - cardNumber: FormItemModel, - expiration: FormItemModel, - cvv: FormItemModel -} - -export interface ProductSectionModel extends PageSectionModel { - availableQuantity: string, - addToCart: string -} - -export interface RelatedProductsSectionModel extends PageSectionModel { - addToCart: string -} - -export interface ShippingAddressSectionModel extends AddressPageSectionModel { } - -export interface TestimonialsSectionModel extends PageSectionModel { - items: TestimonialModel [] -} - -export interface TitleSectionModel extends PageSectionModel { - image?: ImageModel, - primaryLink?: MenuItemModel, - secondaryLink?: MenuItemModel, - postedOnBy?: string -} - -export interface ShopItemsSectionModel extends PageSectionModel { - addToCart: string -} diff --git a/src/ClientApp/src/models/pages.ts b/src/ClientApp/src/models/pages.ts deleted file mode 100644 index 4389c63..0000000 --- a/src/ClientApp/src/models/pages.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { FormItemModel, LinkModel } from "." -import { PageModel } from "./abstractions" -import * as PageSection from "./pageSections" - -export interface BlogCatalogPageModel extends PageModel { - featuredBlogSection: PageSection.FeaturedBlogSectionModel -} - -export interface BlogItemPageModel extends PageModel { - commentsSection: PageSection.CommentsSectionModel -} - -export interface HomePageModel extends PageModel { - featuresSection: PageSection.FeaturesSectionModel, - testimonialsSection: PageSection.TestimonialsSectionModel, - featuredBlogsSection: PageSection.FeaturedBlogsSectionModel, - callToActionSection: PageSection.CallToActionSectionModel -} - -export interface ShopCartPageModel extends PageModel { - productsSection: PageSection.CartProductsSectionModel -} - -export interface ShopCatalogPageModel extends PageModel { - shopItemsSection: PageSection.ShopItemsSectionModel -} - -export interface ShopCheckoutPageModel extends PageModel { - billingAddressSection: PageSection.BillingAddressSectionModel, - shippingAddressSection: PageSection.ShippingAddressSectionModel, - settingsSection: PageSection.CheckoutSettingsSectionModel, - summarySection: PageSection.CheckoutSummarySectionModel, - paymentSection: PageSection.PaymentSectionModel, - submit: FormItemModel -} - -export interface ShopItemPageModel extends PageModel { - productSection: PageSection.ProductSectionModel - relatedProductsSection: PageSection.RelatedProductsSectionModel -} - -export interface SignInPageModel extends PageModel { - title: string, - email: FormItemModel, - password: FormItemModel, - dontHaveAnAccount: string, - signUpLink: LinkModel, - submit: FormItemModel -} - -export interface SignUpPageModel extends PageModel { - title: string, - username: FormItemModel, - email: FormItemModel, - reEmail: FormItemModel - password: FormItemModel, - rePassword: FormItemModel, - acceptTermsAndConditions: string, - submit: FormItemModel -} \ No newline at end of file diff --git a/src/ClientApp/src/models/requests.ts b/src/ClientApp/src/models/requests.ts deleted file mode 100644 index f236dbd..0000000 --- a/src/ClientApp/src/models/requests.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Params, RequestModel } from "./abstractions" - - -// Blog requests ----------------------------------------------------- -export interface GetBlogcatalogPathParams extends Params {} -export interface GeBlogCatalogSearchParams extends Params {} - - -export interface GetBlogCatalogRequestModel extends RequestModel { - category?: string, - searchText?: string, - currentPage?: string, - itemsPerPage?: string -} - -// -------------------------------------------------------------------- -export interface GetBlogCategoriesPathParams extends Params {} -export interface GetBlogCategoriesSearchParams extends Params {} -export interface GetBlogCategoriesRequestModel extends RequestModel { } - -// -------------------------------------------------------------------- - -export interface GetBlogItemPathParams extends Params {} -export interface GetBlogItemSearchParams extends Params { - slug: string -} -export interface GetBlogItemRequestModel extends RequestModel { - -} - - -// --------------------------------------------------------------------- -export interface GetBlogFeaturedPathParams extends Params {} -export interface GetBlogFeaturedSearchParams extends Params {} -export interface GetBlogFeaturedRequestModel extends RequestModel { } - - - -// Static content ------------------------------------------------- -export interface GetContentPathParams extends Params {} - -export interface GetContentSearchParams extends Params { - locale?: string -} - -export interface GetContentRequestModel extends RequestModel { } - - - - -// Shop requests ------------------------------------------------- -export interface GetShopCatalogPathParams extends Params {} -export interface GetShopCatalogSearchParams extends Params { - category?: string, - searchText?: string, - currentPage?: string, - itemsPerPage?: string -} - -export interface GetShopCatalogRequestModel extends RequestModel { } - -// Shop cart items ------------------------------------------------ -export interface GetShopCartItemsPathParams extends Params { - userId: string -} - -export interface GetShopCartItemsSearchParams extends Params { - userId: string -} - -export interface GetShopCartItemsRequestModel extends RequestModel {} - - - - -// ------------------------------------------------------------------- - -export interface GetShopCategoriesPathParams extends Params {} -export interface GetShopCategoriesSearchParams extends Params {} -export interface GetShopCategoriesRequestModel extends RequestModel { } - - -// ------------------------------------------------------------------ -export interface GetShopFeaturedSearchParams extends Params {} -export interface GetShopFeaturedPathParams extends Params {} -export interface GetShopFeaturedRequestModel extends RequestModel { } - - -// ------------------------------------------------------------------- -export interface GetShopItemPathParams extends Params {} -export interface GetShopItemSearchParams extends Params { - slug: string -} -export interface GetShopItemRequestModel extends RequestModel { - -} - -// ------------------------------------------------------------------- -export interface GetShopRelatedPathParams extends Params {} -export interface GetShopRelatedSearchParams extends Params {} -export interface GetShopRelatedRequestModel extends RequestModel { } - -// ------------------------------------------------------------------- -export interface GetShopCartItemPathParams extends Params {} -export interface GetShopCartItemSearchParams extends Params {} - -export interface GetShopCartItemRequestModel extends RequestModel {} diff --git a/src/ClientApp/src/models/responses.ts b/src/ClientApp/src/models/responses.ts deleted file mode 100644 index 1c98c8c..0000000 --- a/src/ClientApp/src/models/responses.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { BlogItemModel, CategoryModel, CommentModel, HeaderModel, ImageModel, LocalizationModel, MenuItemModel, PaginationModel, RouteModel, ShopItemModel } from "./" -import { ResponseModel } from "./abstractions" -import * as Pages from "./pages" - - - - -// Shop response models -export interface GetShopCatalogResponseModel extends PaginationModel, ResponseModel {} - -export interface GetShopCategoriesResponseModel extends ResponseModel { - items: CategoryModel [] -} - -export interface GetShopFeaturedResponseModel extends ResponseModel { - items: ShopItemModel [] -} - -export interface GetShopItemResponseModel extends ShopItemModel, ResponseModel { - comments: CommentModel [] -} - -export interface GetShopRelatedResponseModel extends ResponseModel { - items: ShopItemModel [] -} - - -export interface GetShopCartItemResponseModel { - slug: string - sku: string, - image: ImageModel, - title: string, - brandName: string, - shortText: string, - created: string, - price: number, - newPrice?: number, - quantity: number -} - - -// Static content response model -export interface GetContentResponseModel extends ResponseModel { - - siteName: string, - siteUrl: string, - - header: HeaderModel, - - localization: LocalizationModel, - - routes: RouteModel [], - adminRoutes: RouteModel [], - serviceRoutes: RouteModel [], - - topMenu: MenuItemModel [], - sideMenu: MenuItemModel [], - - homePage: Pages.HomePageModel, - - shopCatalog: Pages.ShopCatalogPageModel, - shopItem: Pages.ShopItemPageModel, - shopCart: Pages.ShopCartPageModel, - shopCheckout: Pages.ShopCheckoutPageModel, - - blogCatalog: Pages.BlogCatalogPageModel, - blogItem: Pages.BlogItemPageModel, - - signIn: Pages.SignInPageModel, - signUp: Pages.SignUpPageModel -} - -// Blog response models -export interface GetBlogCatalogResponseModel extends PaginationModel, ResponseModel { } - -export interface GetBlogCategoriesResponseModel extends ResponseModel { - items: CategoryModel [] -} -export interface GetBlogFeaturedResponseModel extends ResponseModel { - items: BlogItemModel [] -} - -export interface GetBlogItemResponseModel extends BlogItemModel, ResponseModel { - comments: CommentModel [] -} - -// Weather forecasts -export interface GetWeatherForecastResponseModel extends ResponseModel { - date: string, - temperatireC: number, - temperatureF: number, - summary?: string -} diff --git a/src/ClientApp/src/pages/AdminHome.tsx b/src/ClientApp/src/pages/AdminHome.tsx index 0285754..6e90efa 100644 --- a/src/ClientApp/src/pages/AdminHome.tsx +++ b/src/ClientApp/src/pages/AdminHome.tsx @@ -1,6 +1,10 @@ -import * as React from 'react' +import React, { FC } from 'react' -const AdminHome = () => { +interface IAdminHomeComponent { + +} + +const AdminHome : FC = () => { return
    Admin Home
    } diff --git a/src/ClientApp/src/pages/Blog/Catalog/BlogItemsComponent.tsx b/src/ClientApp/src/pages/Blog/Catalog/BlogItemsComponent.tsx new file mode 100644 index 0000000..fc3a7f6 --- /dev/null +++ b/src/ClientApp/src/pages/Blog/Catalog/BlogItemsComponent.tsx @@ -0,0 +1,101 @@ +// React +import React, { FC } from 'react' +import { Link, useNavigate } from 'react-router-dom' + +// Redux +import { useDispatch } from 'react-redux' + +// Reducers +import { actionCreators as blogCatalogActionCreators } from '../../../store/reducers/BlogCatalog' + +// Reactstrap +import { Card, CardBody, CardImg, Col } from 'reactstrap' + +// Components +import { IPaginationComponent, Pagination } from '../../../components/Pagination' + +// Functions +import { dateFormat } from '../../../functions' + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + +export interface IBlogItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + readTime?: number, + likes?: number +} + +export interface IBlogItems { + path?: string + totalPages?: number, + currentPage?: number, + items?: IBlogItem [] +} + +export interface IBlogItemsSection { + readMore: string +} + +export interface IBlogItemsComponent extends IBlogItemsSection, IBlogItems { } + +const BlogItemsComponent: FC = (props) => { + + const { path, totalPages, currentPage, items = [], readMore } = props + + const dispatch = useDispatch() + const navigate = useNavigate() + + return <> + {items.map((item, index) => + + + + + + {item.badges.map((badge, index) =>
    {badge}
    )} +
    {dateFormat(item.created)}
    +

    {item.title}

    +

    {item.shortText}

    + {readMore} +
    + +
    + )} + + { + dispatch(blogCatalogActionCreators.requestBlogCatalog({ + currentPage: nextPage + "" + })) + + navigate(`${path}/${nextPage}`) + } + } as IPaginationComponent} /> + +} + +export { + BlogItemsComponent +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Blog/Catalog/FeaturedBlogComponent.tsx b/src/ClientApp/src/pages/Blog/Catalog/FeaturedBlogComponent.tsx new file mode 100644 index 0000000..eb8f9d0 --- /dev/null +++ b/src/ClientApp/src/pages/Blog/Catalog/FeaturedBlogComponent.tsx @@ -0,0 +1,82 @@ +import React, { FC } from 'react' + +import { Link } from 'react-router-dom' +import { Card, CardBody, CardFooter, CardImg } from 'reactstrap' +import { dateFormat } from '../../../functions' + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + +export interface IFeaturedBlogItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + readTime?: number, + likes?: number +} + +// static stuff from Content +export interface IFeaturedBlogSection { + readTime?: string +} + +// component props +export interface IFeaturedBlogComponent extends IFeaturedBlogSection { + path: string, + currentPage: number + item: IFeaturedBlogItem, +} + +const FeaturedBlogComponent: FC = (props) => { + + const { currentPage, path, item } = props + let { readTime } = props + + if(readTime && item?.created && item?.readTime) + readTime = readTime?.replace('{date}', dateFormat(item.created)) + .replace('{readTime}', `${item.readTime}`) + + return + + + {item.badges.map((badge, index) =>
    {badge}
    )} + + +
    {item.title}
    + +

    +
    + +
    +
    + +
    +
    {item.author.nickName}
    + {readTime !=null ?
    {readTime}
    : <>} +
    +
    +
    +
    +
    +} + +export { + FeaturedBlogComponent +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Blog/Catalog/TitleComponent.tsx b/src/ClientApp/src/pages/Blog/Catalog/TitleComponent.tsx new file mode 100644 index 0000000..8ba2a83 --- /dev/null +++ b/src/ClientApp/src/pages/Blog/Catalog/TitleComponent.tsx @@ -0,0 +1,30 @@ +// React +import React, { FC } from 'react' + +// Reactstrap +import { Container } from 'reactstrap' + +export interface ITitleSection { + title: string, + text: string +} + +export interface ITitleComponent extends ITitleSection {} + +const TitleSection: FC = (props) => { + + const { title, text } = props + + return
    + +
    +

    {title}

    +

    {text}

    +
    +
    +
    +} + +export { + TitleSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Blog/Catalog/index.tsx b/src/ClientApp/src/pages/Blog/Catalog/index.tsx index d47febd..acb1d93 100644 --- a/src/ClientApp/src/pages/Blog/Catalog/index.tsx +++ b/src/ClientApp/src/pages/Blog/Catalog/index.tsx @@ -1,195 +1,81 @@ +// React import React, { FC, useEffect } from 'react' +import { useLocation, useParams } from 'react-router-dom' +// Redux import { useSelector, useDispatch } from 'react-redux' import { ApplicationState } from '../../../store' -import { actionCreators as contentActionCreators } from '../../../store/reducers/Content' -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' +// Reducers import { actionCreators as blogCatalogActionCreators } from '../../../store/reducers/BlogCatalog' import { actionCreators as blogFeaturedActionCreators } from '../../../store/reducers/BlogFeatured' import { actionCreators as blogCategoriesActionCreators } from '../../../store/reducers/BlogCategories' -import { Link, useNavigate, useParams } from 'react-router-dom' -import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap' - -import { dateFormat, findRoutes } from '../../../functions' +// Reactstrap +import { Col, Container, Row } from 'reactstrap' +// Components +import { TitleSection, ITitleSection } from './TitleComponent' +import { FeaturedBlogComponent, IFeaturedBlogComponent, IFeaturedBlogSection } from './FeaturedBlogComponent' +import { BlogItemsComponent, IBlogItemsSection } from './BlogItemsComponent' import { Categories, Empty, Search } from '../../../components/SideWidgets' -import { Pagination } from '../../../components/Pagination' -import { BlogItemModel } from '../../../models' -import { TitleSectionModel } from '../../../models/pageSections' +// Interfaces +import { IHeader } from '../../../interfaces' -const TitleSection: FC = (props) => { - const { title, text } = props - return
    - -
    -

    {title ? title : ''}

    -

    {text ? text : ''}

    -
    -
    -
    +export interface IBlogCatalogPage { + header: IHeader, + titleSection: ITitleSection, + blogItemsSection: IBlogItemsSection, + featuredBlogSection: IFeaturedBlogSection } -interface FeaturedBlog { - path?: string, - currentPage?: number - item?: BlogItemModel, - readTime?: string -} +export interface IBlogCatalogComponent extends IBlogCatalogPage { } -const FeaturedBlogSection: FC = ({ - currentPage = 1, - path = "", - item = { - id: "", - slug: "demo-post", - - badges: [], - - image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, - alt: "..." - }, - title: "", - shortText: "", - author: { - id: "", - nickName: "", - image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, - alt: "..." - } - }, - created: new Date().toString(), - tags: [], - likes: 0 - }, - readTime = "" -}) => { - - return - - - {item.badges.map((badge, index) =>
    {badge}
    )} - - -
    {item.title}
    - -

    -
    - -
    -
    - -
    -
    {item.author.nickName}
    -
    {readTime}
    -
    -
    -
    -
    -
    -} - -interface BlogItems { - path?: string - totalPages?: number, - currentPage?: number, - items?: BlogItemModel [] -} - -const BlogItemsSection: FC = ({ - path = "", - totalPages = 1, - currentPage = 1, - items = [] -}) => { - - const dispatch = useDispatch() - const navigate = useNavigate() - - return <> - {items.map((item, index) => - - - - - -
    {dateFormat(item.created)}
    -

    {item.title}

    -

    {item.shortText}

    - Read more → -
    - -
    - )} - - { - dispatch(blogCatalogActionCreators.requestBlogCatalog({ - currentPage: nextPage + "" - })) - - navigate(`${path}/${nextPage}`) - } - }} /> - -} - -const BlogCatalog = () => { +const BlogCatalog : FC = () => { + const location = useLocation() const params = useParams() const dispatch = useDispatch() const { content, blogCatalog, blogCategories, blogFeatured } = useSelector((state: ApplicationState) => state) - const page = content?.blogCatalog - const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0] - + const {header, titleSection, featuredBlogSection } = content.blogCatalog + + // update categories slugs + blogCategories.items = blogCategories.items.map(item => { + item.href = `${location.pathname.split('/').slice(0, 2).join('/')}/${item.href}` + return item + }) + useEffect(() => { dispatch(blogCatalogActionCreators.requestBlogCatalog({ - currentPage: params?.page ? params.page : "1" + searchParams: { + 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]) - const blogItem = blogFeatured?.items[0] - const featuredBlog: FeaturedBlog = { - path, - currentPage: blogCatalog?.currentPage, - item: blogItem, - readTime: page?.featuredBlogSection?.readTime - } - - if(featuredBlog.readTime && blogItem?.created && blogItem?.readTime) - featuredBlog.readTime = featuredBlog.readTime?.replace('{date}', dateFormat(blogItem.created)) - .replace('{readTime}', `${blogItem.readTime}`) - return <> - + - + - + - - {/* - */} + + + diff --git a/src/ClientApp/src/components/Comments/index.tsx b/src/ClientApp/src/pages/Blog/Item/CommentsComponent.tsx similarity index 70% rename from src/ClientApp/src/components/Comments/index.tsx rename to src/ClientApp/src/pages/Blog/Item/CommentsComponent.tsx index 4c5707f..8801d3f 100644 --- a/src/ClientApp/src/components/Comments/index.tsx +++ b/src/ClientApp/src/pages/Blog/Item/CommentsComponent.tsx @@ -1,25 +1,43 @@ import React, { FC } from 'react' import { Card, CardBody } from 'reactstrap' -import { CommentModel } from '../../models' -import { CommentsSectionModel } from '../../models/pageSections' - -interface Comments { - staticContent?: CommentsSectionModel, - items?: CommentModel [] +interface IImage { + src: string, + alt: string } -const CommentsSection: FC = ({ - staticContent, - items = [] -}) => { +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + + +export interface IComment { + author: IAuthor, + comment: string, + responses?: IComment [] +} + +export interface ICommentsSection { + leaveComment: string +} + +export interface ICommentsComponent extends ICommentsSection { + items?: IComment [] +} + +const CommentsComponent: FC = (props) => { + + const { leaveComment, items = [] } = props return
    - +
    {items.map((comment, index) =>
    @@ -50,5 +68,5 @@ const CommentsSection: FC = ({ } export { - CommentsSection + CommentsComponent } \ No newline at end of file diff --git a/src/ClientApp/src/pages/Blog/Item/TitleSection.tsx b/src/ClientApp/src/pages/Blog/Item/TitleSection.tsx new file mode 100644 index 0000000..7a2a1c1 --- /dev/null +++ b/src/ClientApp/src/pages/Blog/Item/TitleSection.tsx @@ -0,0 +1,40 @@ + +// React +import React, { FC } from 'react' + +interface IImage { + src: string, + alt: string +} + +export interface ITitleSection { + text :string, +} + +export interface ITitleComponent extends ITitleSection { + title: string, + badges? : string[], + image?: IImage +} + +const TitleSection: FC = (props) => { + + const { title, text, badges = [], image } = props + + return <> +
    +

    {title}

    +
    {text ? text : ''}
    + + {badges ? badges.map((badge, index) => {badge}) : <>} +
    + +
    + {image != null ? : <>} +
    + +} + +export { + TitleSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Blog/Item/index.tsx b/src/ClientApp/src/pages/Blog/Item/index.tsx index 5d16e38..477bcec 100644 --- a/src/ClientApp/src/pages/Blog/Item/index.tsx +++ b/src/ClientApp/src/pages/Blog/Item/index.tsx @@ -4,52 +4,58 @@ import { useParams } from 'react-router-dom' // Redux import { useDispatch, useSelector } from 'react-redux' -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' import { actionCreators as blogItemActionCreators } from '../../../store/reducers/BlogItem' import { Col, Container, Row } from 'reactstrap' -import { CommentsSection } from '../../../components/Comments' + import { Categories, Empty, Search } from '../../../components/SideWidgets' import { ApplicationState } from '../../../store' + +import { TitleSection, ITitleSection, ITitleComponent } from './TitleSection' +import { CommentsComponent, ICommentsSection, ICommentsComponent } from './CommentsComponent' +import { IHeader } from '../../../interfaces' import { dateFormat } from '../../../functions' -import { ImageModel } from '../../../models' -interface BlogItemTitle { - title?: string, - postedOnBy? :string, - badges? : string[], - image?: ImageModel +interface IImage { + src: string, + alt: string } -const BlogTitleSection: FC = ({ - title = "", - postedOnBy = "", - badges = [], - image = { - src: "", - alt: "" - } -}) => { +interface IAuthor { + id: string, + image?: IImage - return <> -
    -

    {title}

    -
    {postedOnBy ? postedOnBy : ''}
    + nickName: string +} +export interface IBlogItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] - {badges ? badges.map((badge, index) => {badge}) : <>} -
    - -
    - -
    - + readTime?: number, + likes?: number } -const BlogItem = () => { +export interface IBlogItemPage { + header: IHeader, + titleSection: ITitleSection, + commentsSection: ICommentsSection +} + +export interface IBlogItemComponent extends IBlogItemPage {} + +const BlogItem : FC = () => { const params = useParams() const dispatch = useDispatch() @@ -65,40 +71,32 @@ const BlogItem = () => { })) }, []) - useEffect(() => { - blogItem?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [blogItem?.isLoading]) - - const blogItemTitle: BlogItemTitle = { + const blogItemTitle: ITitleComponent = { title: blogItem?.title, - postedOnBy: page?.titleSection?.postedOnBy, + text: page?.titleSection?.text, badges: blogItem?.badges, image: blogItem?.image } - if(blogItemTitle.postedOnBy && blogItem?.created) - blogItemTitle.postedOnBy = blogItemTitle.postedOnBy?.replace('{date}', dateFormat(blogItem.created)) + if(blogItemTitle.text && blogItem?.created) + blogItemTitle.text = blogItemTitle.text?.replace('{date}', dateFormat(blogItem.created)) - if(blogItemTitle.postedOnBy && blogItem?.author) - blogItemTitle.postedOnBy = blogItemTitle.postedOnBy?.replace('{nickName}', dateFormat(blogItem.author.nickName)) + if(blogItemTitle.text && blogItem?.author) + blogItemTitle.text = blogItemTitle.text?.replace('{nickName}', dateFormat(blogItem.author.nickName)) return
    - +
    - + diff --git a/src/ClientApp/src/pages/Counter.tsx b/src/ClientApp/src/pages/Counter.tsx index 33b38ae..79672e7 100644 --- a/src/ClientApp/src/pages/Counter.tsx +++ b/src/ClientApp/src/pages/Counter.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { FC } from 'react' // Redux import { useDispatch, useSelector } from 'react-redux' @@ -8,7 +8,9 @@ interface IReduxState { counter: CounterState } -const Counter = () => { +export interface ICounterComponent {} + +const Counter : FC = () => { const dispatch = useDispatch() const counterState = useSelector((state: IReduxState) => state.counter) diff --git a/src/ClientApp/src/pages/FetchData.tsx b/src/ClientApp/src/pages/FetchData.tsx index 34cbfa5..0f62994 100644 --- a/src/ClientApp/src/pages/FetchData.tsx +++ b/src/ClientApp/src/pages/FetchData.tsx @@ -1,5 +1,5 @@ // React -import React, { useEffect } from 'react' +import React, { FC, useEffect } from 'react' import { Link, useLocation, useParams } from 'react-router-dom' // Redux @@ -15,7 +15,9 @@ type IParams = { startDateIndex: string } -const FetchData = () => { +export interface IFetchDataComponent {} + +const FetchData : FC = () => { const location = useLocation() const params = useParams() diff --git a/src/ClientApp/src/pages/Home/CallToActionSection.tsx b/src/ClientApp/src/pages/Home/CallToActionSection.tsx new file mode 100644 index 0000000..4993826 --- /dev/null +++ b/src/ClientApp/src/pages/Home/CallToActionSection.tsx @@ -0,0 +1,43 @@ +import React, { FC } from "react" +import { Col, Container, Row } from "reactstrap" + +interface IEmail { + title: string, + placeHolder: string +} + +export interface ICallToActionSection { + title: string, + text: string, + privacyDisclaimer: string, + email: IEmail +} + +const CallToActionSection: FC = (props) => { + + const { title, text, privacyDisclaimer, email } = props + + return
    + + + +
    +} + +export { + CallToActionSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Home/FeaturedBlogsSection.tsx b/src/ClientApp/src/pages/Home/FeaturedBlogsSection.tsx new file mode 100644 index 0000000..23cfc9e --- /dev/null +++ b/src/ClientApp/src/pages/Home/FeaturedBlogsSection.tsx @@ -0,0 +1,90 @@ +// React +import React, { FC } from 'react' +import { Link } from 'react-router-dom' + +// Reactstrap +import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap' +import { dateFormat } from '../../functions' + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage, + nickName: string +} + +export interface IFeaturedBlogItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + readTime?: number, + likes?: number +} + +export interface IFeaturedBlogsSection { + title: string, + text?: string +} + +interface IFeaturedBlogsFull extends IFeaturedBlogsSection { + items?: IFeaturedBlogItem [] +} + +const FeaturedBlogsSection: FC = (props) => { + + const { title, text = "", items = [] } = props + + return
    + + + +
    +

    {title}

    +

    +
    + +
    + + {items.map((item, index) => + + + +
    {item.badges}
    + +
    {item.title}
    + +

    +
    + +
    +
    + +
    +
    {item.author.nickName}
    +
    {dateFormat(item.created)} · {item.readTime}
    +
    +
    +
    +
    +
    + )} +
    +
    +
    +} + +export { + FeaturedBlogsSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Home/FeaturesSection.tsx b/src/ClientApp/src/pages/Home/FeaturesSection.tsx new file mode 100644 index 0000000..b8d870b --- /dev/null +++ b/src/ClientApp/src/pages/Home/FeaturesSection.tsx @@ -0,0 +1,50 @@ +// React +import React, { FC } from 'react' + +// Reactstrap +import { Col, Container, Row } from 'reactstrap' +import { FeatherIcon } from '../../components/FeatherIcons' + +// +import style from './scss/style.module.scss' + +interface IFeatureItem { + icon: string, + title: string, + text: string +} + +export interface IFeaturesSection { + title: string, + items?: IFeatureItem [] +} + +const FeaturesSection: FC = (props) => { + + const { title, items = [] } = props + + return
    + + + +

    {title}

    + + + + {items ? items.map((item, index) => +
    + +
    +

    {item.title}

    +

    + ) : ''} +
    + +
    +
    +
    +} + +export { + FeaturesSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Home/TestimonialsSection.tsx b/src/ClientApp/src/pages/Home/TestimonialsSection.tsx new file mode 100644 index 0000000..f7cd252 --- /dev/null +++ b/src/ClientApp/src/pages/Home/TestimonialsSection.tsx @@ -0,0 +1,47 @@ +import React, { FC } from "react" +import { Col, Container, Row } from "reactstrap" + +interface IImage { + src: string + alt?: string +} + +interface IReviewer { + image: IImage, + fullName: string, + position: string +} + +interface ITestimonialItem { + text: string, + reviewer: IReviewer +} + +export interface ITestimonialsSection { + items: ITestimonialItem [] +} + +const TestimonialsSection: FC = (props) => { + const item = props.items[0] + + return
    + + + +
    +
    +
    + +
    {item.reviewer.fullName}/{item.reviewer.position} +
    +
    +
    + +
    +
    +
    +} + +export { + TestimonialsSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Home/TitleSection.tsx b/src/ClientApp/src/pages/Home/TitleSection.tsx new file mode 100644 index 0000000..b90c484 --- /dev/null +++ b/src/ClientApp/src/pages/Home/TitleSection.tsx @@ -0,0 +1,36 @@ +import React, { FC } from "react" +import { Col, Container, Row } from "reactstrap" + +export interface ITitleSection { + title: string, + text: string +} + +const TitleSection : FC = (props) => { + + const { title, text } = props + + return
    + + + +
    +

    {title}

    + + + + +
    + +
    ...
    +
    +
    +
    +} + +export { + TitleSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Home/index.tsx b/src/ClientApp/src/pages/Home/index.tsx index 01c153f..ca99d1e 100644 --- a/src/ClientApp/src/pages/Home/index.tsx +++ b/src/ClientApp/src/pages/Home/index.tsx @@ -1,240 +1,59 @@ // React import React, { FC, useEffect } from 'react' -import { Link } from 'react-router-dom' // Redux import { useDispatch, useSelector } from 'react-redux' import { ApplicationState } from '../../store' -import { actionCreators as loaderActionCreators } from '../../store/reducers/Loader' import { actionCreators as blogFeaturedActionCreators } from '../../store/reducers/BlogFeatured' import { actionCreators as headerActionCreators } from '../../store/reducers/Header' -// Reactstrap -import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap' -// Models (interfaces) -import { CallToActionSectionModel, FeaturedBlogsSectionModel, FeaturesSectionModel, TestimonialsSectionModel, TitleSectionModel } from '../../models/pageSections' -import { BlogItemModel, FeatureModel, HeaderModel, TestimonialModel } from '../../models' -// Custom components -import { FeatherIcon } from '../../components/FeatherIcons' +import { IHeader } from '../../interfaces' +import { TitleSection, ITitleSection } from './TitleSection' +import { FeaturesSection, IFeaturesSection } from './FeaturesSection' +import { TestimonialsSection, ITestimonialsSection } from './TestimonialsSection' +import { FeaturedBlogsSection, IFeaturedBlogsSection } from './FeaturedBlogsSection' +import { CallToActionSection, ICallToActionSection } from './CallToActionSection' -// Functions -import { dateFormat } from '../../functions' -// CSS Modules -import style from './scss/style.module.scss' - -const TitleSection : FC = ({ - title = "", - text = "" -}) => { - return
    - - - -
    -

    {title}

    - - - - -
    - -
    ...
    -
    -
    -
    +export interface IHomePage { + header: IHeader, + titleSection: ITitleSection, + featuresSection: IFeaturesSection, + testimonialsSection: ITestimonialsSection, + featuredBlogsSection: IFeaturedBlogsSection, + callToActionSection: ICallToActionSection } -interface Fetures { - title?: string, - items?: FeatureModel [] -} +export interface IHomePageComponent extends IHomePage { } -const FeaturesSection: FC = ({ - title = "", - items = [] -}) => { - return
    - - - -

    {title ? title : ''}

    - - - - {items ? items.map((item, index) => -
    - -
    -

    {item.title}

    -

    - ) : ''} -
    - -
    -
    -
    -} - -interface Testimonials { - items?: TestimonialModel [] -} - -const TestimonialsSection: FC = ({ - items = [] -}) => { - const item = items[0] - - return
    - - - - { item - ?
    -
    -
    - -
    {item.reviewer.fullName}/{item.reviewer.position} -
    -
    -
    - : '' } - -
    -
    -
    - -} - -interface FeaturedBlogs extends FeaturedBlogsSectionModel { - items?: BlogItemModel [] -} -const FeaturedBlogsSection: FC = ({ - title = "", - text = "", - items = [] }) =>
    - - - -
    -

    {title}

    -

    -
    - -
    - - {items.map((item, index) => - - - -
    {item.badges}
    - -
    {item.title}
    - -

    -
    - -
    -
    - -
    -
    {item.author.nickName}
    -
    {dateFormat(item.created)} · {item.readTime}
    -
    -
    -
    -
    -
    - )} -
    -
    -
    - -const CallToActionSection: FC = ({ - title, - text, - privacyDisclaimer, - email = { - placeHolder: "", - title: "" - } -}) => { - - return
    - - - -
    -} - -const Home = () => { +const Home : FC = () => { const dispatch = useDispatch() const { content, blogFeatured } = useSelector((state: ApplicationState) => state) - const page = content?.homePage useEffect(() => { dispatch(blogFeaturedActionCreators.requestBlogFeatured()) }, []) useEffect(() => { - content?.isLoading || blogFeatured?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading, blogFeatured?.isLoading]) + dispatch(headerActionCreators.updateHeader(content?.homePage.header)) + }, [content?.homePage.header]) - const { - header = {}, - titleSection = { - title: "", - text: "" - }, - featuresSection = {}, - testimonialsSection = {}, - featuredBlogsSection = {}, - callToActionSection = { - title: "", - text: "", - privacyDisclaimer: "", - email: { - placeHolder: "", - title: "" - } - } - } = content?.homePage ? content.homePage : {} + if(content?.homePage) { + const { titleSection, featuresSection, testimonialsSection, featuredBlogsSection, callToActionSection } = content.homePage - useEffect(() => { - dispatch(headerActionCreators.updateHeader(header as HeaderModel)) - }, [header]) - - - return <> - - - - - - + return <> + + + + + + + } + else { + return <> + } } export { diff --git a/src/ClientApp/src/pages/Shop/Cart/index.tsx b/src/ClientApp/src/pages/Shop/Cart/index.tsx index 82b517c..7e6f2fc 100644 --- a/src/ClientApp/src/pages/Shop/Cart/index.tsx +++ b/src/ClientApp/src/pages/Shop/Cart/index.tsx @@ -1,9 +1,8 @@ // React -import React, { useEffect, useState } from 'react' +import React, { FC, useEffect, useState } from 'react' // Redux import { useDispatch, useSelector } from 'react-redux' -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' import { actionCreators as shopCartActionCreators } from '../../../store/reducers/ShopCart' import { ApplicationState } from '../../../store' @@ -14,7 +13,32 @@ import { FeatherIcon } from '../../../components/FeatherIcons' import style from './scss/style.module.scss' import { ReservedWords } from '../../../enumerations' -const Cart = () => { +interface IImage { + src: string, + alt: string +} + +export interface IShopCartItem { + slug: string + sku: string, + image: IImage, + title: string, + brandName: string, + shortText: string, + created: string, + price: number, + newPrice?: number, + quantity: number +} + + +export interface IShopCartPage { + +} + +export interface IShopCartComponent {} + +const Cart : FC = () => { const dispatch = useDispatch() const { content, shopCart } = useSelector((state: ApplicationState) => state) @@ -23,40 +47,11 @@ const Cart = () => { dispatch(shopCartActionCreators.requestCart({ pathParams: { userId: "fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60" }})) }, []) - - useEffect(() => { - content?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading]) - const { currencySymbol = "" } = content?.localization ? content.localization : {} - const { - titleSection = { - title: "", - text: "" - }, - productsSection = { - product: "", - price: "", - quantity: "", - subtotal: "", - continueShopping: { - target: "!#", - anchorText: "" - }, - checkout: { - target: "!#", - anchorText: "" - } - } - - } = content?.shopCart ? content.shopCart : {} + //const { titleSection, productsSection } = content.shopCart const [subtotal, setSubtotal] = useState(0) @@ -86,7 +81,7 @@ const Cart = () => { } return -
    + {/*

    {titleSection.title}

    @@ -105,7 +100,7 @@ const Cart = () => {
    - +

    {item.title}

    @@ -147,7 +142,7 @@ const Cart = () => { {productsSection.continueShopping.anchorText} -
    +
    */}
    } diff --git a/src/ClientApp/src/pages/Shop/Catalog/ShopItemsSection.tsx b/src/ClientApp/src/pages/Shop/Catalog/ShopItemsSection.tsx new file mode 100644 index 0000000..052db9b --- /dev/null +++ b/src/ClientApp/src/pages/Shop/Catalog/ShopItemsSection.tsx @@ -0,0 +1,123 @@ +// React +import React, { FC } from 'react' +import { Link, useNavigate } from 'react-router-dom' + +// Redux +import { useDispatch } from 'react-redux' + +// Reducers +import { actionCreators as shopCatalogActionCreators } from '../../../store/reducers/ShopCatalog' + +// Reactstrap +import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap' + +// Components +import { FeatherRating } from '../../../components/FeatherRating' +import { SSRPagination, ISSRPaginationComponent } from '../../../components/Pagination' + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + +export interface IShopItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + images?: IImage [], + sku: string, + brandName: string, + rating?: number, + price: number, + newPrice?: number, + quantity?: number +} + +interface IShopItems { + path: string + totalPages?: number, + currentPage?: number, + items?: IShopItem [] +} + +export interface IShopItemsSection { + addToCart: string +} + +export interface IShopItemComponent extends IShopItemsSection, IShopItems { + currencySymbol: string, +} + +const ShopItemsSection: FC = (props) => { + + const { currencySymbol, addToCart, path = "", totalPages, currentPage, items = [] } = props + + const dispatch = useDispatch() + const navigate = useNavigate() + + return <> + {items.map((item, index) => + +
    + {(item?.badges ? item.badges : []).map((badge, index) =>
    {badge}
    ) } +
    + + + + + + +
    +
    {item.title}
    + + + + {item.newPrice + ? <>{currencySymbol}{item.price.toFixed(2)} {currencySymbol}{item.newPrice.toFixed(2)} + : {currencySymbol}{item.price.toFixed(2)}} +
    +
    + + + +
    + )} + + + { + // dispatch(shopCatalogActionCreators.requestShopCatalog({ + // searchParams: { + // currentPage: nextPage + "" + // } + // })) + + // navigate(`${path}/${nextPage}`) + // } + linksPath: path.split('/').slice(0, 3).join('/') + } as ISSRPaginationComponent} /> + +} + +export { + ShopItemsSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Shop/Catalog/TitleSection.tsx b/src/ClientApp/src/pages/Shop/Catalog/TitleSection.tsx new file mode 100644 index 0000000..10028ee --- /dev/null +++ b/src/ClientApp/src/pages/Shop/Catalog/TitleSection.tsx @@ -0,0 +1,30 @@ +// React +import React, { FC } from 'react' + +// Reactstrap +import { Container } from 'reactstrap' + +export interface ITitleSection { + title: string, + text: string +} + +interface ITitleComponent extends ITitleSection { } + +const TitleSection: FC = (props) => { + + const { title, text } = props + + return
    + +
    +

    {title}

    +

    {text}

    +
    +
    +
    +} + +export { + TitleSection +} \ No newline at end of file diff --git a/src/ClientApp/src/pages/Shop/Catalog/index.tsx b/src/ClientApp/src/pages/Shop/Catalog/index.tsx index 1af39db..8ae72e9 100644 --- a/src/ClientApp/src/pages/Shop/Catalog/index.tsx +++ b/src/ClientApp/src/pages/Shop/Catalog/index.tsx @@ -1,156 +1,87 @@ // React import React, { FC, useEffect } from 'react' -import { Link, useNavigate, useParams } from 'react-router-dom' +import { useLocation, useParams } from 'react-router-dom' // Redux import { useDispatch, useSelector } from 'react-redux' import { ApplicationState } from '../../../store' -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' + +// Reducers import { actionCreators as shopCatalogActionCreators } from '../../../store/reducers/ShopCatalog' +import { actionCreators as shopCategoriesActionCreators } from '../../../store/reducers/ShopCategories' // Reactstrap -import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap' +import { Col, Container, Row } from 'reactstrap' -// Models (interfaces) -import { ShopItemModel } from '../../../models' -import { TitleSectionModel } from '../../../models/pageSections' +// Components +import { TitleSection, ITitleSection } from './TitleSection' +import { ShopItemsSection, IShopItemsSection, IShopItemComponent } from './ShopItemsSection' +import { Categories, Empty, Search } from '../../../components/SideWidgets' -// Custom components -import { FeatherRating } from '../../../components/FeatherRating' -import { Pagination } from '../../../components/Pagination' +// Interfaces +import { IHeader } from '../../../interfaces' -// Functions -import { findRoutes } from '../../../functions' - -const TitleSection: FC = ({ - title = "", - text = "" -}) =>
    - -
    -

    {title}

    -

    {text}

    -
    -
    -
    - - -interface ShopItems { - currencySymbol: string, - addToCart: string, - path: string - totalPages?: number, - currentPage?: number, - items?: ShopItemModel [] +export interface IShopCatalogPage { + header: IHeader, + titleSection: ITitleSection, + shopItemsSection: IShopItemsSection, } -const ShopItemsSection: FC = ({ - currencySymbol = "", - addToCart = "", - path = "", - totalPages = 1, - currentPage = 1, - items = [] -}) => { +export interface IShopCatalogComponent extends IShopCatalogPage { } - const dispatch = useDispatch() - const navigate = useNavigate() - - return
    - - - {items.map((item, index) => - -
    - {(item?.badges ? item.badges : []).map((badge, index) =>
    {badge}
    ) } -
    - - - - - - -
    -
    {item.title}
    - - - - {item.newPrice - ? <>{currencySymbol}{item.price.toFixed(2)} {currencySymbol}{item.newPrice.toFixed(2)} - : {currencySymbol}{item.price.toFixed(2)}} -
    -
    - - - -
    - )} -
    - - { - dispatch(shopCatalogActionCreators.requestShopCatalog({ - searchParams: { - currentPage: nextPage + "" - } - })) - - navigate(`${path}/${nextPage}`) - } - }} /> -
    -
    -} - - -const ShopCatalog = () => { +const ShopCatalog : FC = () => { + const location = useLocation() const params = useParams() const dispatch = useDispatch() - const { content, shopCatalog } = useSelector((state: ApplicationState) => state) - const page = content?.shopCatalog - const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0] + const { content, shopCatalog, shopCategories } = useSelector((state: ApplicationState) => state) - const { - currencySymbol = "" - } = content?.localization ? content.localization : {} + const { currencySymbol } = content.localization + const { header, titleSection, shopItemsSection } = content.shopCatalog + + // update categories slugs + shopCategories.items = shopCategories.items.map(item => { + item.href = `${location.pathname.split('/').slice(0, 2).join('/')}/${item.href}` + return item + }) useEffect(() => { dispatch(shopCatalogActionCreators.requestShopCatalog({ searchParams: { - currentPage: params?.page ? params.page : "1" + category: params?.category, + currentPage: params?.page } })) + dispatch(shopCategoriesActionCreators.requestShopCategories()) }, []) - useEffect(() => { - shopCatalog?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [shopCatalog?.isLoading]) - - const { - shopItemsSection = { - addToCart: "" - } - } = content?.shopCatalog ? content?.shopCatalog : {} - - const shopItems: ShopItems = { - currencySymbol, - path, - ...shopItemsSection, - ...shopCatalog - } - return <> - - + + + + + + + + + + + + + + + + + + + + + } diff --git a/src/ClientApp/src/pages/Shop/Checkout/index.tsx b/src/ClientApp/src/pages/Shop/Checkout/index.tsx index e0131fc..766995d 100644 --- a/src/ClientApp/src/pages/Shop/Checkout/index.tsx +++ b/src/ClientApp/src/pages/Shop/Checkout/index.tsx @@ -1,10 +1,16 @@ -import React from 'react' +import React, { FC } from 'react' import { Container } from 'reactstrap' // CSS Modules import style from './scss/style.module.scss' -const Checkout = () => { +export interface IShopCheckoutPage { + +} + +export interface IShopCheckoutComponent extends IShopCheckoutPage {} + +const Checkout : FC = () => { return
    diff --git a/src/ClientApp/src/pages/Shop/RelatedProducts/index.tsx b/src/ClientApp/src/pages/Shop/Item/RelatedProducts.tsx similarity index 68% rename from src/ClientApp/src/pages/Shop/RelatedProducts/index.tsx rename to src/ClientApp/src/pages/Shop/Item/RelatedProducts.tsx index 80c5c3d..29d15e2 100644 --- a/src/ClientApp/src/pages/Shop/RelatedProducts/index.tsx +++ b/src/ClientApp/src/pages/Shop/Item/RelatedProducts.tsx @@ -4,7 +4,6 @@ import React, { FC, useEffect } from "react" // Reduc import { useDispatch, useSelector } from "react-redux" import { ApplicationState } from "../../../store" -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' // Reactstrap import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from "reactstrap" @@ -12,37 +11,73 @@ import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from "reacts // Components import { FeatherRating } from "../../../components/FeatherRating" -const RelatedProducts: FC = () => { + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + + +interface IRelatedProduct { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + images?: IImage [], + sku: string, + brandName: string, + rating?: number, + price: number, + newPrice?: number, + quantity?: number +} + +export interface IRelatedProducts { + items?: IRelatedProduct [] +} + + +export interface IRelatedProductsSection { + title: string, + addToCart: string +} + + +export interface IRelatedProductsComponent extends IRelatedProductsSection, IRelatedProducts { + +} + + +const RelatedProducts: FC = (props) => { const dispatch = useDispatch() const { content, shopRelated } = useSelector((state: ApplicationState) => state) - const { - currencySymbol = "" - } = content?.localization ? content.localization : {} + const { currencySymbol } = content.localization - const { - relatedProductsSection = { - title: "", - addToCart: "" - } - } = content?.shopItem ? content?.shopItem : {} + const { relatedProductsSection } = content.shopItem const { items = [] } = shopRelated ? shopRelated : {} - useEffect(() => { - content?.isLoading || shopRelated?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading, shopRelated?.isLoading]) - return
    -

    {relatedProductsSection.title}

    + {/*

    {relatedProductsSection.title}

    {items.map((item, index) => @@ -69,7 +104,7 @@ const RelatedProducts: FC = () => { )} - + */}
    } diff --git a/src/ClientApp/src/pages/Shop/Item/index.tsx b/src/ClientApp/src/pages/Shop/Item/index.tsx index 5c46d8d..d751eda 100644 --- a/src/ClientApp/src/pages/Shop/Item/index.tsx +++ b/src/ClientApp/src/pages/Shop/Item/index.tsx @@ -5,7 +5,6 @@ import { useParams } from 'react-router-dom' // Redux import { useDispatch, useSelector } from 'react-redux' import { ApplicationState } from '../../../store' -import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader' import { actionCreators as shopItemActionCreators } from '../../../store/reducers/ShopItem' // Reactstrap @@ -13,9 +12,66 @@ import { Container } from 'reactstrap' // Components import { FeatherIcon } from '../../../components/FeatherIcons' -import { RelatedProducts } from '../RelatedProducts' +import { IRelatedProductsComponent, IRelatedProductsSection, RelatedProducts } from './RelatedProducts' -const ShopItem : FC = () => { + +import { IHeader } from '../../../interfaces' + +interface IImage { + src: string, + alt: string +} + +interface IAuthor { + id: string, + image?: IImage + + nickName: string +} + +export interface IShopItem { + id: string, + slug: string, + image: IImage, + badges: string [], + title: string, + shortText?: string, + text?: string, + author: IAuthor, + created: string, + tags: string [] + + images?: IImage [], + sku: string, + brandName: string, + rating?: number, + price: number, + newPrice?: number, + quantity?: number +} + + +interface IProductSection { + title?: string + text?: string + + availableQuantity: string, + addToCart: string +} + + + + + +export interface IShopItemPage { + header: IHeader, + productSection: IProductSection + relatedProductsSection: IRelatedProductsSection +} + +export interface IShopItemComponent extends IShopItemPage {} + +const ShopItem : FC = () => { const params = useParams() const dispatch = useDispatch() @@ -41,17 +97,6 @@ const ShopItem : FC = () => { })) }, []) - useEffect(() => { - content?.isLoading || shopItem?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading, shopItem?.isLoading]) - - - - return <>
    @@ -81,7 +126,9 @@ const ShopItem : FC = () => {
    - + } diff --git a/src/ClientApp/src/pages/Signin/index.tsx b/src/ClientApp/src/pages/Signin/index.tsx index 8833c6f..4d695d9 100644 --- a/src/ClientApp/src/pages/Signin/index.tsx +++ b/src/ClientApp/src/pages/Signin/index.tsx @@ -1,14 +1,14 @@ -import React, { useEffect, useState } from "react" +import React, { FC, useEffect, useState } from "react" // Redux import { useDispatch, useSelector } from "react-redux" -import { actionCreators as loaderActionCreators } from '../../store/reducers/Loader' import { Link } from "react-router-dom" import { Button, Container, Form, FormGroup, Input, Label } from "reactstrap" import { ApplicationState } from "../../store" import './scss/style.scss' +import { IHeader } from "../../interfaces" interface IStateProp { [key: string]: string; @@ -19,38 +19,42 @@ interface IState extends IStateProp { password: string } -const Signin = () => { +interface IFormControl { + title: string, + placeHolder?: string +} + +interface ILink { + target: string, + anchorText: string +} +export interface ISigninPage { + header: IHeader, + + title: string, + email: IFormControl, + password: IFormControl, + dontHaveAnAccount: string, + signUpLink: ILink, + submit: IFormControl +} + +export interface ISigninComponent extends ISigninPage { + +} + +const Signin : FC = () => { const dispatch = useDispatch() const { content } = useSelector((state: ApplicationState) => state) const { - title = "", - email = { - title: "", - placeHolder: "" - }, - password = { - title: "", - placeHolder: "" - }, - dontHaveAnAccount = "", - signUpLink = { - target: "#", - anchorText: "" - }, - submit = { - title: "" - } - } = content?.signIn ? content.signIn : {} - - - useEffect(() => { - content?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading]) + title, + email, + password, + dontHaveAnAccount, + signUpLink, + submit + } = content.signIn const [state, hookState] = useState({ username: '', @@ -69,6 +73,10 @@ const Signin = () => { setState({ [name]: value }) } + const postSignIn = () => { + + } + return

    {title}

    @@ -95,7 +103,7 @@ const Signin = () => { {dontHaveAnAccount} {signUpLink.anchorText}. - +
    diff --git a/src/ClientApp/src/pages/Signup/index.tsx b/src/ClientApp/src/pages/Signup/index.tsx index 43d49fd..075d7de 100644 --- a/src/ClientApp/src/pages/Signup/index.tsx +++ b/src/ClientApp/src/pages/Signup/index.tsx @@ -1,13 +1,13 @@ -import React, { useEffect, useState } from "react" +import React, { FC, useEffect, useState } from "react" // Redux import { useDispatch, useSelector } from "react-redux" -import { actionCreators as loaderActionCreators } from '../../store/reducers/Loader' import { Button, Container, Form, FormGroup, Input, Label } from "reactstrap" import { ApplicationState } from "../../store" import './scss/style.scss' +import { IHeader } from "../../interfaces" interface IStateProp { [key: string]: string | boolean; @@ -25,46 +25,39 @@ interface IState extends IStateProp { tnc: boolean } -const Signup = () => { +interface IFormControl { + title: string, + placeHolder?: string +} + +export interface ISignupPage { + header: IHeader, + + title: string, + username: IFormControl, + email: IFormControl, + reEmail: IFormControl, + password: IFormControl, + rePassword: IFormControl, + acceptTermsAndConditions: string, + submit: IFormControl +} + +export interface ISignupComponent extends ISignupPage { } + +const Signup : FC = () => { const dispatch = useDispatch() const { content } = useSelector((state: ApplicationState) => state) const { - title = "", - username = { - title: "", - placeHolder: "" - }, - email = { - title: "", - placeHolder: "" - }, - reEmail = { - title: "Repeat email address", - placeHolder: "Repeat email address..." - }, - password = { - title: "", - placeHolder: "" - }, - rePassword = { - title: "Repeat password", - placeHolder: "Repeat password..." - }, + title, username, + email, + reEmail, + password, + rePassword, acceptTermsAndConditions = "", - submit = { - title: "" - } - } = content?.signUp ? content.signUp : {} - - useEffect(() => { - content?.isLoading - ? dispatch(loaderActionCreators.show()) - : setTimeout(() => { - dispatch(loaderActionCreators.hide()) - }, 1000) - }, [content?.isLoading]) - + submit + } = content.signUp const [state, hookState] = useState({ username: '', @@ -96,7 +89,7 @@ const Signup = () => { } return -

    {title}

    + {/*

    {title}

    @@ -153,7 +146,7 @@ const Signup = () => { -
    + */}
    } diff --git a/src/ClientApp/src/restClient.ts b/src/ClientApp/src/restClient.ts index e686dd0..c69262d 100644 --- a/src/ClientApp/src/restClient.ts +++ b/src/ClientApp/src/restClient.ts @@ -1,18 +1,19 @@ -import { Params, RequestModel } from "./models/abstractions" +import axios from "axios" +import { IParams } from "./interfaces" interface FetchData { status: number, - text: string + text: any } const Post = () => { } -const Get = async (apiUrl: string, pathParams?: Params, searchParams?: Params): Promise => { +const Get = async (apiUrl: string, pathParams?: IParams, searchParams?: IParams): Promise => { const url = new URL(apiUrl) if(pathParams) { @@ -36,21 +37,19 @@ const Get = async (apiUrl: string, pathParams?: Params, searchParams?: Params headers: { 'accept': 'application/json', 'content-type': 'application/json' }, } - const fetchData = await fetch(url.toString(), requestParams) + const fetchData = await axios(url.toString(), requestParams) .then(async fetchData => { - return { - status: fetchData.status, - text: await fetchData.text() - } + // console.log(fetchData) + + return fetchData.data as T }) .catch(err => { console.log(err) - }) - if (fetchData?.text) - return JSON.parse((fetchData as FetchData).text) as T - - return null + return null + }) + + return fetchData } const Put = () => { @@ -67,6 +66,3 @@ export { Put, Delete } - - - diff --git a/src/ClientApp/src/store/index.ts b/src/ClientApp/src/store/index.ts index f96374c..9525a30 100644 --- a/src/ClientApp/src/store/index.ts +++ b/src/ClientApp/src/store/index.ts @@ -5,7 +5,6 @@ import * as BlogItem from './reducers/BlogItem' import * as Counter from './reducers/Counter' import * as Header from './reducers/Header' -import * as Loader from './reducers/Loader' import * as Content from './reducers/Content' @@ -20,25 +19,24 @@ import * as WeatherForecasts from './reducers/WeatherForecasts' // The top-level state object export interface ApplicationState { - blogCatalog: BlogCatalog.BlogCatalogState | undefined - blogCategories: BlogCategories.BlogCategoriesState | undefined - blogFeatured: BlogFeatured.BlogFeaturedState | undefined - blogItem: BlogItem.BlogItemState | undefined + blogCatalog: BlogCatalog.BlogCatalogState + blogCategories: BlogCategories.BlogCategoriesState + blogFeatured: BlogFeatured.BlogFeaturedState + blogItem: BlogItem.BlogItemState - content: Content.ContentState | undefined + content: Content.ContentState - counter: Counter.CounterState | undefined - header: Header.HeaderState | undefined - loader: Loader.LoaderState | undefined + counter: Counter.CounterState + header: Header.HeaderState - shopCatalog: ShopCatalog.ShopCatalogState | undefined - shopCategories: ShopCategories.ShopCategoriesState | undefined - shopFeatured: ShopFeatured.ShopFeaturedState | undefined - shopItem: ShopItem.ShopItemState | undefined - shopRelated: ShopRelated.ShopRelatedState | undefined - shopCart: ShopCart.ShopCartState | undefined + shopCatalog: ShopCatalog.ShopCatalogState + shopCategories: ShopCategories.ShopCategoriesState + shopFeatured: ShopFeatured.ShopFeaturedState + shopItem: ShopItem.ShopItemState + shopRelated: ShopRelated.ShopRelatedState + shopCart: ShopCart.ShopCartState - weatherForecasts: WeatherForecasts.WeatherForecastsState | undefined + weatherForecasts: WeatherForecasts.WeatherForecastsState } // Whenever an action is dispatched, Redux will update each top-level application state property using @@ -54,7 +52,6 @@ export const reducers = { counter: Counter.reducer, header: Header.reducer, - loader: Loader.reducer, shopCatalog: ShopCatalog.reducer, shopCategories: ShopCategories.reducer, diff --git a/src/ClientApp/src/store/reducers/BlogCatalog.ts b/src/ClientApp/src/store/reducers/BlogCatalog.ts index bc619ff..c1afe3d 100644 --- a/src/ClientApp/src/store/reducers/BlogCatalog.ts +++ b/src/ClientApp/src/store/reducers/BlogCatalog.ts @@ -1,32 +1,54 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetBlogCatalogRequestModel } from '../../models/requests' -import { GetBlogCatalogResponseModel } from '../../models/responses' +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { IBlogItem } from '../../pages/Blog/Catalog/BlogItemsComponent' + import { Get } from '../../restClient' -export interface BlogCatalogState extends GetBlogCatalogResponseModel { + +// Request +interface IGetBlogcatalogPathParams extends IParams {} + +interface IGeBlogCatalogSearchParams extends IParams {} + +interface IGetBlogCatalogRequestModel extends IRequest { + category?: string, + searchText?: string, + currentPage?: string, + itemsPerPage?: string +} + +// Response +interface IGetBlogCatalogResponseModel extends IPagination, IResponse { } + + +export interface BlogCatalogState extends IGetBlogCatalogResponseModel { isLoading: boolean } -interface RequestAction extends GetBlogCatalogRequestModel { +interface RequestAction extends IGetBlogCatalogRequestModel { type: 'REQUEST_BLOG_CATALOG' } -interface ReceiveAction extends GetBlogCatalogResponseModel { +interface ReceiveAction extends IGetBlogCatalogResponseModel { type: 'RECEIVE_BLOG_CATALOG' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestBlogCatalog: (props?: GetBlogCatalogRequestModel): AppThunkAction => (dispatch, getState) => { + requestBlogCatalog: (props?: IGetBlogCatalogRequestModel): AppThunkAction => (dispatch, getState) => { - const locale = getState().content?.localization.locale + const locale = process.env.REACT_APP_LOCALE const searchParams = {...props?.searchParams, locale} - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_BLOGITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_BLOGITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) .then(response => response) .then(data => { if(data) @@ -38,7 +60,7 @@ export const actionCreators = { } const unloadedState: BlogCatalogState = { - totalPages: 1, + totalPages: 100, currentPage: 1, items: [ { @@ -46,7 +68,7 @@ const unloadedState: BlogCatalogState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -56,7 +78,7 @@ const unloadedState: BlogCatalogState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -70,7 +92,7 @@ const unloadedState: BlogCatalogState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -80,7 +102,7 @@ const unloadedState: BlogCatalogState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -94,7 +116,7 @@ const unloadedState: BlogCatalogState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -104,7 +126,7 @@ const unloadedState: BlogCatalogState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -118,7 +140,7 @@ const unloadedState: BlogCatalogState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -128,7 +150,7 @@ const unloadedState: BlogCatalogState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, diff --git a/src/ClientApp/src/store/reducers/BlogCategories.ts b/src/ClientApp/src/store/reducers/BlogCategories.ts index d911186..35d4b68 100644 --- a/src/ClientApp/src/store/reducers/BlogCategories.ts +++ b/src/ClientApp/src/store/reducers/BlogCategories.ts @@ -1,32 +1,49 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetBlogCategoriesRequestModel } from '../../models/requests' -import { GetBlogCategoriesResponseModel } from '../../models/responses' +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { ICategory } from '../../components/SideWidgets/Categories' + import { Get } from '../../restClient' -export interface BlogCategoriesState extends GetBlogCategoriesResponseModel { + +// Request +interface IGetBlogCategoriesPathParams extends IParams {} + +interface IGetBlogCategoriesSearchParams extends IParams {} + +interface IGetBlogCategoriesRequestModel extends IRequest { } + +// Response +interface IGetBlogCategoriesResponseModel extends IPagination, IResponse { } + + +export interface BlogCategoriesState extends IGetBlogCategoriesResponseModel { isLoading: boolean } -interface RequestAction extends GetBlogCategoriesRequestModel { +interface RequestAction extends IGetBlogCategoriesRequestModel { type: 'REQUEST_BLOG_CATEGORIES' } -interface ReceiveAction extends GetBlogCategoriesResponseModel { +interface ReceiveAction extends IGetBlogCategoriesResponseModel { type: 'RECEIVE_BLOG_CATEGORIES' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestBlogCategories: (props?: GetBlogCategoriesRequestModel): AppThunkAction => (dispatch, getState) => { + requestBlogCategories: (props?: IGetBlogCategoriesRequestModel): AppThunkAction => (dispatch, getState) => { - const locale = getState().content?.localization.locale + const locale = process.env.REACT_APP_LOCALE const searchParams = {...props?.searchParams, locale} - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_CATEGORYITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_CATEGORYITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) .then(response => response) .then(data => { if(data) @@ -38,9 +55,12 @@ export const actionCreators = { } const unloadedState: BlogCategoriesState = { + totalPages: 1, + currentPage: 1, items: [ - { id: "", text: "Software" }, - { id: "", text: "Hardware" } + { href: 'default', anchorText: "Default" }, + { href: 'software', anchorText: "Software" }, + { href: 'hardware', anchorText: "Hardware" } ], isLoading: false } diff --git a/src/ClientApp/src/store/reducers/BlogFeatured.ts b/src/ClientApp/src/store/reducers/BlogFeatured.ts index 12176df..e0b93d2 100644 --- a/src/ClientApp/src/store/reducers/BlogFeatured.ts +++ b/src/ClientApp/src/store/reducers/BlogFeatured.ts @@ -1,32 +1,52 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetBlogFeaturedRequestModel } from '../../models/requests' -import { GetBlogFeaturedResponseModel } from '../../models/responses' +// Interfaces +import { IParams, IRequest, IResponse } from '../../interfaces' +import { IFeaturedBlogItem } from '../../pages/Blog/Catalog/FeaturedBlogComponent' + import { Get } from '../../restClient' -export interface BlogFeaturedState extends GetBlogFeaturedResponseModel { + +// Request +interface IGetBlogFeaturedPathParams extends IParams {} + +interface IGetBlogFeaturedSearchParams extends IParams {} + +interface IGetBlogFeaturedRequestModel extends IRequest { } + + +// Response +interface IGetBlogFeaturedResponseModel extends IResponse { + items: IFeaturedBlogItem [] +} + + +export interface BlogFeaturedState extends IGetBlogFeaturedResponseModel { isLoading: boolean } -interface RequestAction extends GetBlogFeaturedRequestModel { +interface RequestAction extends IGetBlogFeaturedRequestModel { type: 'REQUEST_BLOG_FEATURED' } -interface ReceiveAction extends GetBlogFeaturedResponseModel { +interface ReceiveAction extends IGetBlogFeaturedResponseModel { type: 'RECEIVE_BLOG_FEATURED' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestBlogFeatured: (props?: GetBlogFeaturedRequestModel): AppThunkAction => (dispatch, getState) => { + requestBlogFeatured: (props?: IGetBlogFeaturedRequestModel): AppThunkAction => (dispatch, getState) => { - const locale = getState().content?.localization.locale + const locale = process.env.REACT_APP_LOCALE const searchParams = {...props?.searchParams, locale} - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_BLOGITEMS_FEAUTERED}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_BLOGITEMS_FEAUTERED}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) .then(response => response) .then(data => { if(data) @@ -44,7 +64,7 @@ const unloadedState: BlogFeaturedState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -53,7 +73,7 @@ const unloadedState: BlogFeaturedState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -68,7 +88,7 @@ const unloadedState: BlogFeaturedState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -77,7 +97,7 @@ const unloadedState: BlogFeaturedState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -92,7 +112,7 @@ const unloadedState: BlogFeaturedState = { slug: "demo-post", badges: [ "demo" ], image: { - src: `${process.env.REACT_APP_API}/Image/850x350/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/850x350/dee2e6/6c757d`, alt: "..." }, title: "Lorem ipsum", @@ -101,7 +121,7 @@ const unloadedState: BlogFeaturedState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, diff --git a/src/ClientApp/src/store/reducers/BlogItem.ts b/src/ClientApp/src/store/reducers/BlogItem.ts index 8b1fdf6..7df3025 100644 --- a/src/ClientApp/src/store/reducers/BlogItem.ts +++ b/src/ClientApp/src/store/reducers/BlogItem.ts @@ -1,32 +1,51 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetBlogItemRequestModel } from '../../models/requests' -import { GetBlogItemResponseModel } from '../../models/responses' +// interfaces +import { IParams, IRequest, IResponse } from '../../interfaces' +import { IBlogItem } from '../../pages/Blog/Item' + import { Get } from '../../restClient' -export interface BlogItemState extends GetBlogItemResponseModel { + +// Request +interface IGetBlogItemPathParams extends IParams { } + +interface IGetBlogItemSearchParams extends IParams { + slug: string +} + +interface IGetBlogItemRequestModel extends IRequest { } + +// Response +interface IGetBlogItemResponseModel extends IBlogItem, IResponse { } + + +export interface BlogItemState extends IGetBlogItemResponseModel { isLoading: boolean } -interface RequestAction extends GetBlogItemRequestModel { +interface RequestAction extends IGetBlogItemRequestModel { type: 'REQUEST_BLOG_ITEM' } -interface ReceiveAction extends GetBlogItemResponseModel { +interface ReceiveAction extends IGetBlogItemResponseModel { type: 'RECEIVE_BLOG_ITEM' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestBlogItem: (props?: GetBlogItemRequestModel): AppThunkAction => (dispatch, getState) => { + requestBlogItem: (props?: IGetBlogItemRequestModel): AppThunkAction => (dispatch, getState) => { - const locale = getState().content?.localization.locale + const locale = process.env.REACT_APP_LOCALE const searchParams = {...props?.searchParams, locale} - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_BLOGITEM}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_BLOGITEM}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) .then(response => response) .then(data => { if(data) @@ -41,7 +60,7 @@ const unloadedState: BlogItemState = { id: "", slug: "demo-post", image: { - src: `${process.env.REACT_APP_API}/Image/900x400/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/900x400/ced4da/6c757d`, alt: "..." }, badges: [ @@ -60,63 +79,13 @@ const unloadedState: BlogItemState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, created: new Date().toString(), tags: [ "react", "redux", "webapi" ], - comments: [ - { - author: { - id: "", - nickName: "Commenter Name 1", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.", - responses: [ - { - author: { - id: "", - nickName: "Commenter Name 4", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors." - }, - { - author: { - id: "", - nickName: "Commenter Name 3", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "When you put money directly to a problem, it makes a good headline." - } - ] - }, - { - author: { - id: "", - nickName: "Commenter Name 2", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence." - } - - ], - isLoading: false } diff --git a/src/ClientApp/src/store/reducers/Comments.ts b/src/ClientApp/src/store/reducers/Comments.ts new file mode 100644 index 0000000..526c8ea --- /dev/null +++ b/src/ClientApp/src/store/reducers/Comments.ts @@ -0,0 +1,132 @@ +//Redux +import { Action, Reducer } from 'redux' +import { AppThunkAction } from '../' + +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { IComment } from '../../pages/Blog/Item/CommentsComponent' + +import { Get } from '../../restClient' + +// Request +interface IGetCommentsPathParams extends IParams { } + +interface IGetCommentsSearchParams extends IParams { } + +interface IGetCommentsRequestModel extends IRequest { } + +// Response +interface IGetCommentsResponseModel extends IPagination, IResponse { } + +export interface CommentsState extends IGetCommentsResponseModel { + isLoading: boolean +} + +interface RequestAction extends IGetCommentsRequestModel { + type: 'REQUEST_COMMENTS' +} + +interface ReceiveAction extends IGetCommentsResponseModel { + type: 'RECEIVE_COMMENTS' +} + +type KnownAction = RequestAction | ReceiveAction + +export const actionCreators = { + requestComments: (props?: IGetCommentsRequestModel): AppThunkAction => (dispatch, getState) => { + + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + /* + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_BLOGITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + .then(response => response) + .then(data => { + if(data) + dispatch({ type: 'RECEIVE_BLOG_CATALOG', ...data }) + }) + + dispatch({ type: 'REQUEST_BLOG_CATALOG' }) + */ + } +} + +const unloadedState: CommentsState = { + totalPages: 100, + currentPage: 1, + items: [ + { + author: { + id: "", + nickName: "Commenter Name 1", + image: { + src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + alt: "..." + } + }, + comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.", + + responses: [ + { + author: { + id: "", + nickName: "Commenter Name 4", + image: { + src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + alt: "..." + } + }, + comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors." + }, + { + author: { + id: "", + nickName: "Commenter Name 3", + image: { + src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + alt: "..." + } + }, + comment: "When you put money directly to a problem, it makes a good headline." + } + ] + }, + { + author: { + id: "", + nickName: "Commenter Name 2", + image: { + src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + alt: "..." + } + }, + comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence." + } + + ], + + isLoading: false +} + +export const reducer: Reducer = (state: CommentsState | undefined, incomingAction: Action): CommentsState => { + if (state === undefined) { + return unloadedState + } + + const action = incomingAction as KnownAction + switch (action.type) { + case 'REQUEST_COMMENTS': + return { + ...state, + isLoading: true + } + + case 'RECEIVE_COMMENTS': + return { + ...action, + isLoading: false + } + } + + return state +} diff --git a/src/ClientApp/src/store/reducers/Content.ts b/src/ClientApp/src/store/reducers/Content.ts index 52ee8bd..9d202cd 100644 --- a/src/ClientApp/src/store/reducers/Content.ts +++ b/src/ClientApp/src/store/reducers/Content.ts @@ -2,28 +2,103 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' import { ReservedWords } from '../../enumerations' -import { GetContentRequestModel } from '../../models/requests' -import { GetContentResponseModel } from '../../models/responses' + +// Interfaces +import { IHeader, IParams, IRequest, IResponse } from '../../interfaces' + +import { IRoute } from '../../App' + +import { INavMenuItem } from '../../layouts/public/NavMenu' +import { ISideMenuItem } from '../../layouts/admin/SideMenu' + +import { IHomePage } from '../../pages/Home' + +import { IShopCatalogPage } from '../../pages/Shop/Catalog' +import { IShopItemPage } from '../../pages/Shop/Item' + +import { IBlogCatalogPage } from '../../pages/Blog/Catalog' +import { IBlogItemPage } from '../../pages/Blog/Item' + +import { ISigninPage } from '../../pages/Signin' +import { ISignupPage } from '../../pages/Signup' + +import { IShopCartPage } from '../../pages/Shop/Cart' +import { IShopCheckoutPage } from '../../pages/Shop/Checkout' + + import { Get } from '../../restClient' -export interface ContentState extends GetContentResponseModel { + +// Request +interface IGetContentPathParams extends IParams {} + +interface IGetContentSearchParams extends IParams { + locale?: string +} + +interface IGetContentRequestModel extends IRequest { } + +interface ILocalization { + timeZone: string, + locale: string, + dateFormat: string, + timeFormat: string, + currency: string, + currencySymbol: string +} + +// Response +interface IGetContentResponseModel extends IResponse { + + siteName: string, + siteUrl: string, + + header: IHeader, + + localization: ILocalization, + + routes: IRoute [], + adminRoutes: IRoute [], + serviceRoutes: IRoute [], + + topMenu: INavMenuItem [], + sideMenu: ISideMenuItem [], + + homePage: IHomePage, + + shopCatalog: IShopCatalogPage, + shopItem: IShopItemPage, + shopCart: IShopCartPage, + shopCheckout: IShopCheckoutPage, + + blogCatalog: IBlogCatalogPage, + blogItem: IBlogItemPage, + + signIn: ISigninPage, + signUp: ISignupPage +} + +export interface ContentState extends IGetContentResponseModel { isLoading: boolean } -interface RequestAction extends GetContentRequestModel { +interface RequestAction extends IGetContentRequestModel { type: 'REQUEST_CONTENT' } -interface ReceiveAction extends GetContentResponseModel { +interface ReceiveAction extends IGetContentResponseModel { type: 'RECEIVE_CONTENT' } type KnownAction = RequestAction | ReceiveAction; export const actionCreators = { - requestContent: (props?: GetContentRequestModel): AppThunkAction => (dispatch, getState) => { + requestContent: (props?: IGetContentRequestModel): AppThunkAction => (dispatch, getState) => { - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_CONTENT}/${process.env.REACT_APP_SITEID}`, props?.pathParams, props?.searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_CONTENT}/${process.env.REACT_APP_SITEID}`, props?.pathParams, props?.searchParams) .then(response => response) .then((data) => { if(data) { @@ -36,8 +111,8 @@ export const actionCreators = { } const unloadedState: ContentState = { - siteName: "MAKS-IT", - siteUrl: "https://maks-it.com", + siteName: "Contoso", + siteUrl: "https://contoso.com", header: { title: `${ReservedWords.siteName}`, @@ -57,29 +132,42 @@ const unloadedState: ContentState = { currency: "EUR", currencySymbol: "€" }, + routes: [ { target: "/", component: "Home" }, { target: "/home", component: "Home" }, { target: "/shop", childRoutes: [ - { target: "", component: "ShopCatalog" }, - { target: ":page", component: "ShopCatalog" }, - { target: ":page" , childRoutes: [ - { target: ":slug", component: "ShopItem" } - ]}, + { target: ":category", childRoutes: [ + { target: "", component: "ShopCatalog" }, + { target: ":page", component: "ShopCatalog" } + ] }, + { target: ":category", childRoutes: [ + { target: ":page" , childRoutes: [ + { target: ":slug", component: "ShopItem" } + ]} + ] }, + { target:"cart", childRoutes: [ { target: "", component: "Cart" }, { target: "checkout", component: "Checkout" } ]} ]}, + { target: "/blog", childRoutes: [ - { target: "", component: "BlogCatalog" }, - { target: ":page", component: "BlogCatalog" }, - { target: ":page" , childRoutes: [ - { target: ":slug", component: "BlogItem" } - ]} + { target: ":category", childRoutes: [ + { target: "", component: "BlogCatalog" }, + { target: ":page", component: "BlogCatalog" } + ] }, + { target: ":category", childRoutes: [ + { target: ":page" , childRoutes: [ + { target: ":slug", component: "BlogItem" } + ]} + ] } ]} ], + adminRoutes: [], + serviceRoutes: [ { target: "/signin", component: "Signin" }, { target: "/signup", component: "Signup" } @@ -87,14 +175,15 @@ const unloadedState: ContentState = { topMenu: [ { target: "/", title: "Home" }, - { target: "/shop", title: "Shop" }, - { target: "/blog", title: "Blog" }, + { target: "/shop/default", title: "Shop" }, + { target: "/blog/default", title: "Blog" }, { target: "/signin", title: "Sing in" }, { target: "/signup", title: "Sign up" }, { target: "/shop/cart", icon: "shopping-cart", title: `Cart (${ReservedWords.quantity})` } ], + sideMenu: [], homePage: { @@ -141,8 +230,7 @@ const unloadedState: ContentState = { { text: "The ClientApp subdirectory is a standard React application based on the create-react-app template. If you open a command prompt in that directory, you can run yarn commands such as yarn test or yarn install.", reviewer: { - id: "", - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, fullName: "Admin", position: "CEO, MAKS-IT" } @@ -371,6 +459,9 @@ const unloadedState: ContentState = { title: "Welcome to Blog Home!", text: "A Bootstrap 5 starter layout for your next blog homepage" }, + blogItemsSection: { + readMore: 'Read more →' + }, featuredBlogSection: { readTime: `${ReservedWords.date} Time to read: ${ReservedWords.readTime} min` }, @@ -387,7 +478,7 @@ const unloadedState: ContentState = { } }, titleSection: { - postedOnBy: `Posted on ${ReservedWords.date} by ${ReservedWords.nickName}` + text: `Posted on ${ReservedWords.date} by ${ReservedWords.nickName}` }, commentsSection: { leaveComment: "Join the discussion and leave a comment!" @@ -468,7 +559,6 @@ const unloadedState: ContentState = { export const reducer: Reducer = (state: ContentState | undefined, incomingAction: Action): ContentState => { if (state === undefined) { - console.log(unloadedState) return unloadedState } diff --git a/src/ClientApp/src/store/reducers/Header.ts b/src/ClientApp/src/store/reducers/Header.ts index fdf05e5..a4b4cf6 100644 --- a/src/ClientApp/src/store/reducers/Header.ts +++ b/src/ClientApp/src/store/reducers/Header.ts @@ -2,19 +2,19 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '..' import { ReservedWords } from '../../enumerations' -import { HeaderLink, HeaderModel } from '../../models' +import { IHeader } from '../../interfaces' // ----------------- // STATE - This defines the type of data maintained in the Redux store. -export interface HeaderState extends HeaderModel {} +export interface HeaderState extends IHeader {} // ----------------- // ACTIONS - These are serializable (hence replayable) descriptions of state transitions. // They do not themselves have any side-effects they just describe something that is going to happen. // Use @typeName and isActionType for type detection that works even after serialization/deserialization. -export interface ReceiveAction extends HeaderModel { type: 'RECEIVE_UPDATE_HEADER' } +export interface ReceiveAction extends IHeader { type: 'RECEIVE_UPDATE_HEADER' } // Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the @@ -27,7 +27,7 @@ export type KnownAction = ReceiveAction export const actionCreators = { - updateHeader: (props: HeaderModel): AppThunkAction => (dispatch, getState) => { + updateHeader: (props: IHeader): AppThunkAction => (dispatch, getState) => { const siteName = getState().content?.siteName const baseHeader = getState().content?.header diff --git a/src/ClientApp/src/store/reducers/Loader.ts b/src/ClientApp/src/store/reducers/Loader.ts deleted file mode 100644 index 42cfbe1..0000000 --- a/src/ClientApp/src/store/reducers/Loader.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Action, Reducer } from 'redux' - -// ----------------- -// STATE - This defines the type of data maintained in the Redux store. - -export interface LoaderState { - visible: boolean -} - -interface RequestAction { - type: 'SHOW_LOADER' -} - -interface ReceiveAction { - type: 'HIDE_LOADER' -} - -// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the -// declared type strings (and not any other arbitrary string). -export type KnownAction = RequestAction | ReceiveAction - -// ---------------- -// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition. -// They don't directly mutate state, but they can have external side-effects (such as loading data). - -export const actionCreators = { - show: () => ({ type: 'SHOW_LOADER' } as RequestAction), - hide: () => ({ type: 'HIDE_LOADER' } as ReceiveAction) -} - -// ---------------- -// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state. - -const unloadedState: LoaderState = { - visible: false -} - -export const reducer: Reducer = (state: LoaderState | undefined, incomingAction: Action): LoaderState => { - if (state === undefined) { - return unloadedState - } - - const action = incomingAction as KnownAction - switch (action.type) { - case 'SHOW_LOADER': - return { visible: true } - case 'HIDE_LOADER': - return { visible: false } - } - - return state -} \ No newline at end of file diff --git a/src/ClientApp/src/store/reducers/ShopCart.ts b/src/ClientApp/src/store/reducers/ShopCart.ts index 86e6b2e..dc5a049 100644 --- a/src/ClientApp/src/store/reducers/ShopCart.ts +++ b/src/ClientApp/src/store/reducers/ShopCart.ts @@ -1,22 +1,35 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '..' -import { GetShopCartItemsRequestModel } from '../../models/requests' -import { GetShopCartItemResponseModel } from '../../models/responses' +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { IShopCartItem } from '../../pages/Shop/Cart' + import { Get } from '../../restClient' -export interface ShopCartState { - items: GetShopCartItemResponseModel [], +// Request +interface IGetShopCartItemsPathParams extends IParams { + userId: string +} + +interface IGetShopCartItemsSearchParams extends IParams { } + +interface IGetShopCartItemsRequestModel extends IRequest {} + +// Response +interface IGetShopCartItemsResponseModel extends IPagination, IResponse {} + + +export interface ShopCartState extends IGetShopCartItemsResponseModel { isLoading: boolean } -export interface RequestAction { +export interface RequestAction extends IGetShopCartItemsRequestModel { type: 'REQUEST_CART_ITEMS' } -export interface ReceiveAction { - items: GetShopCartItemResponseModel [], +export interface ReceiveAction extends IGetShopCartItemsResponseModel { type: 'RECEIVE_CART_ITEMS' } @@ -31,14 +44,16 @@ export interface ReceiveAction { export type KnownAction = RequestAction | ReceiveAction //| IncrementItemQuantityAction | DecrementItmeQuantityAction export const actionCreators = { - requestCart: (props?: GetShopCartItemsRequestModel): AppThunkAction => (dispatch, getState) => { + requestCart: (props?: IGetShopCartItemsRequestModel): AppThunkAction => (dispatch, getState) => { + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return - Get>('https://localhost:7151/api/ShopCartItems/404c8232-9048-4519-bfba-6e78dc7005ca', props?.pathParams, props?.searchParams) + Get>('https://localhost:7151/api/ShopCartItems/404c8232-9048-4519-bfba-6e78dc7005ca', props?.pathParams, props?.searchParams) .then(response => response) .then(data => { if(data) - dispatch({ type: 'RECEIVE_CART_ITEMS', items: [...data] }) + dispatch({ type: 'RECEIVE_CART_ITEMS', ...data }) }) dispatch({ type: 'REQUEST_CART_ITEMS' }) @@ -75,11 +90,13 @@ export const actionCreators = { } const unloadedState: ShopCartState = { + totalPages: 1, + currentPage: 1, items: [ { slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, title: "Shop item title", brandName: "Brand & Name", shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...", @@ -91,7 +108,7 @@ const unloadedState: ShopCartState = { { slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, title: "Shop item title", brandName: "Brand & Name", shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...", @@ -103,7 +120,7 @@ const unloadedState: ShopCartState = { { slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, title: "Shop item title", brandName: "Brand & Name", shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...", diff --git a/src/ClientApp/src/store/reducers/ShopCatalog.ts b/src/ClientApp/src/store/reducers/ShopCatalog.ts index c1d87bd..7abbb52 100644 --- a/src/ClientApp/src/store/reducers/ShopCatalog.ts +++ b/src/ClientApp/src/store/reducers/ShopCatalog.ts @@ -1,10 +1,29 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetShopCatalogRequestModel } from '../../models/requests' -import { GetShopCatalogResponseModel } from '../../models/responses' +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { IShopItem } from '../../pages/Shop/Catalog/ShopItemsSection' + import { Get } from '../../restClient' + +// Request +interface IGetShopcatalogPathParams extends IParams {} + +interface IGeShopCatalogSearchParams extends IParams {} + +interface GetShopCatalogRequestModel extends IRequest { + category?: string, + searchText?: string, + currentPage?: string, + itemsPerPage?: string +} + +// Response +interface GetShopCatalogResponseModel extends IPagination, IResponse {} + + export interface ShopCatalogState extends GetShopCatalogResponseModel { isLoading: boolean } @@ -22,11 +41,14 @@ type KnownAction = RequestAction | ReceiveAction export const actionCreators = { requestShopCatalog: (props?: GetShopCatalogRequestModel): AppThunkAction => (dispatch, getState) => { - const locale = getState().content?.localization.locale + const locale = process.env.REACT_APP_LOCALE const searchParams = { ...props?.searchParams, locale } - Get>(`${process.env.REACT_APP_API}/Image/${process.env.REACT_APP_SHOPITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_SITEID}/${process.env.REACT_APP_SHOPITEMS}`, props?.pathParams, searchParams) .then(response => response) .then(data => { if(data) @@ -38,14 +60,14 @@ export const actionCreators = { } const unloadedState: ShopCatalogState = { - totalPages: 1, + totalPages: 100, currentPage: 1, items: [ { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale" ], title: "Shop item title", brandName: "Brand & Name", @@ -54,7 +76,7 @@ const unloadedState: ShopCatalogState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -69,7 +91,7 @@ const unloadedState: ShopCatalogState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale" ], title: "Shop item title", brandName: "Brand & Name", @@ -78,7 +100,7 @@ const unloadedState: ShopCatalogState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -93,7 +115,7 @@ const unloadedState: ShopCatalogState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale", "out of stock" ], title: "Shop item title", brandName: "Brand & Name", @@ -102,7 +124,7 @@ const unloadedState: ShopCatalogState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -117,7 +139,7 @@ const unloadedState: ShopCatalogState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale" ], title: "Shop item title", brandName: "Brand & Name", @@ -126,7 +148,7 @@ const unloadedState: ShopCatalogState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), diff --git a/src/ClientApp/src/store/reducers/ShopCategories.ts b/src/ClientApp/src/store/reducers/ShopCategories.ts index 325aed4..6377fc5 100644 --- a/src/ClientApp/src/store/reducers/ShopCategories.ts +++ b/src/ClientApp/src/store/reducers/ShopCategories.ts @@ -1,10 +1,24 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetShopCategoriesRequestModel } from '../../models/requests' -import { GetShopCategoriesResponseModel } from '../../models/responses' +// Interfaces +import { IPagination, IParams, IRequest, IResponse } from '../../interfaces' +import { ICategory } from '../../components/SideWidgets/Categories' + import { Get } from '../../restClient' +// Request +interface GetShopCategoriesPathParams extends IParams { } + +interface GetShopCategoriesSearchParams extends IParams { } + +interface GetShopCategoriesRequestModel extends IRequest { } + + +// Response +interface GetShopCategoriesResponseModel extends IPagination, IResponse { } + + export interface ShopCategoriesState extends GetShopCategoriesResponseModel { isLoading: boolean } @@ -22,7 +36,10 @@ type KnownAction = RequestAction | ReceiveAction export const actionCreators = { requestShopCategories: (props?: GetShopCategoriesRequestModel): AppThunkAction => (dispatch, getState) => { - Get>('https://localhost:7151/api/ShopCategories', props?.pathParams, props?.searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>(`${process.env.REACT_APP_API}/${process.env.REACT_APP_CATEGORYITEMS}/${process.env.REACT_APP_SITEID}`, props?.pathParams, props?.searchParams) .then(response => response) .then(data => { if(data) @@ -34,9 +51,12 @@ export const actionCreators = { } const unloadedState: ShopCategoriesState = { + totalPages: 1, + currentPage: 1, items: [ - { id: "", text: "Software" }, - { id: "", text: "Hardware" } + { href: 'default', anchorText: "Default" }, + { href: 'software', anchorText: "Software" }, + { href: 'hardware', anchorText: "Hardware" } ], isLoading: false } diff --git a/src/ClientApp/src/store/reducers/ShopFeatured.ts b/src/ClientApp/src/store/reducers/ShopFeatured.ts index 80d2ef5..0d0655f 100644 --- a/src/ClientApp/src/store/reducers/ShopFeatured.ts +++ b/src/ClientApp/src/store/reducers/ShopFeatured.ts @@ -1,28 +1,47 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetShopFeaturedRequestModel, } from '../../models/requests' -import { GetShopFeaturedResponseModel } from '../../models/responses' +// Interfaces +import { IParams, IRequest, IResponse } from '../../interfaces' +import { IRelatedProduct } from '../../pages/Shop/Item/RelatedProducts' + + import { Get } from '../../restClient' -export interface ShopFeaturedState extends GetShopFeaturedResponseModel { +// Request +interface IGetShopFeaturedSearchParams extends IParams { } + +interface IGetShopFeaturedPathParams extends IParams { } + +interface IGetShopFeaturedRequestModel extends IRequest { } + +// Response +interface IGetShopFeaturedResponseModel extends IResponse { + items: IRelatedProduct [] +} + + +export interface ShopFeaturedState extends IGetShopFeaturedResponseModel { isLoading: boolean } -interface RequestAction extends GetShopFeaturedRequestModel { +interface RequestAction extends IGetShopFeaturedRequestModel { type: 'REQUEST_SHOP_FEATURED' } -interface ReceiveAction extends GetShopFeaturedResponseModel { +interface ReceiveAction extends IGetShopFeaturedResponseModel { type: 'RECEIVE_SHOP_FEATURED' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestShopFeatured: (props?: GetShopFeaturedRequestModel): AppThunkAction => (dispatch, getState) => { + requestShopFeatured: (props?: IGetShopFeaturedRequestModel): AppThunkAction => (dispatch, getState) => { - Get>('https://localhost:7151/api/ShopFeatured', props?.pathParams, props?.searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>('https://localhost:7151/api/ShopFeatured', props?.pathParams, props?.searchParams) .then(response => response) .then(data => { if(data) @@ -39,7 +58,7 @@ const unloadedState: ShopFeaturedState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale" ], title: "Shop item title", brandName: "Brand & Name", @@ -48,7 +67,7 @@ const unloadedState: ShopFeaturedState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), diff --git a/src/ClientApp/src/store/reducers/ShopItem.ts b/src/ClientApp/src/store/reducers/ShopItem.ts index 318d657..5878d72 100644 --- a/src/ClientApp/src/store/reducers/ShopItem.ts +++ b/src/ClientApp/src/store/reducers/ShopItem.ts @@ -1,11 +1,27 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetShopItemRequestModel } from '../../models/requests' -import { GetShopItemResponseModel } from '../../models/responses' +// Interfaces +import { IParams, IRequest, IResponse } from '../../interfaces' +import { IShopItem } from '../../pages/Shop/Item' + import { Get } from '../../restClient' +// Reuest +interface IGetShopItemPathParams extends IParams { } + +interface IGetShopItemSearchParams extends IParams { + slug: string +} + +interface GetShopItemRequestModel extends IRequest { } + + +// Response +interface GetShopItemResponseModel extends IShopItem, IResponse { } + + export interface ShopItemState extends GetShopItemResponseModel { isLoading: boolean } @@ -22,7 +38,10 @@ type KnownAction = RequestAction | ReceiveAction export const actionCreators = { requestShopItem: (props?: GetShopItemRequestModel): AppThunkAction => (dispatch, getState) => { - + + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + Get>('https://localhost:7151/api/ShopItem', props?.pathParams, props?.searchParams) .then(response => response) .then(data => { @@ -38,7 +57,7 @@ const unloadedState: ShopItemState = { id: "", slug: "demo-post", image: { - src: `${process.env.REACT_APP_API}/Image/600x700/dee2e6/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/600x700/dee2e6/6c757d`, alt: "..." }, @@ -56,7 +75,7 @@ const unloadedState: ShopItemState = { id: "", nickName: "Admin", image: { - src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, + src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." } }, @@ -68,56 +87,56 @@ const unloadedState: ShopItemState = { quantity: 10, - comments: [ - { - author: { - id: "", - nickName: "Commenter Name 1", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.", + // comments: [ + // { + // author: { + // id: "", + // nickName: "Commenter Name 1", + // image: { + // src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + // alt: "..." + // } + // }, + // comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.", - responses: [ - { - author: { - id: "", - nickName: "Commenter Name 4", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors." - }, - { - author: { - id: "", - nickName: "Commenter Name 3", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "When you put money directly to a problem, it makes a good headline." - } - ] - }, - { - author: { - id: "", - nickName: "Commenter Name 2", - image: { - src: `${process.env.REACT_APP_API}/Image/50x50/ced4da/6c757d`, - alt: "..." - } - }, - comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence." - } + // responses: [ + // { + // author: { + // id: "", + // nickName: "Commenter Name 4", + // image: { + // src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + // alt: "..." + // } + // }, + // comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors." + // }, + // { + // author: { + // id: "", + // nickName: "Commenter Name 3", + // image: { + // src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + // alt: "..." + // } + // }, + // comment: "When you put money directly to a problem, it makes a good headline." + // } + // ] + // }, + // { + // author: { + // id: "", + // nickName: "Commenter Name 2", + // image: { + // src: `${process.env.REACT_APP_FRONTEND}/Image/50x50/ced4da/6c757d`, + // alt: "..." + // } + // }, + // comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence." + // } - ], + // ], isLoading: false } diff --git a/src/ClientApp/src/store/reducers/ShopRelated.ts b/src/ClientApp/src/store/reducers/ShopRelated.ts index 5d228d0..c1b3f3c 100644 --- a/src/ClientApp/src/store/reducers/ShopRelated.ts +++ b/src/ClientApp/src/store/reducers/ShopRelated.ts @@ -1,28 +1,45 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' -import { GetShopRelatedRequestModel } from '../../models/requests' -import { GetShopRelatedResponseModel } from '../../models/responses' +import { IParams, IRequest, IResponse } from '../../interfaces' +import { IRelatedProduct } from '../../pages/Shop/Item/RelatedProducts' + import { Get } from '../../restClient' -export interface ShopRelatedState extends GetShopRelatedResponseModel { +// Resquest +interface IGetShopRelatedPathParams extends IParams {} + +interface IGetShopRelatedSearchParams extends IParams {} + +interface IGetShopRelatedRequestModel extends IRequest { } + +// Response +interface IGetShopRelatedResponseModel extends IResponse { + items: IRelatedProduct [] +} + + +export interface ShopRelatedState extends IGetShopRelatedResponseModel { isLoading: boolean } -interface RequestAction extends GetShopRelatedRequestModel { +interface RequestAction extends IGetShopRelatedRequestModel { type: 'REQUEST_SHOP_RELATED' } -interface ReceiveAction extends GetShopRelatedResponseModel { +interface ReceiveAction extends IGetShopRelatedResponseModel { type: 'RECEIVE_SHOP_RELATED' } type KnownAction = RequestAction | ReceiveAction export const actionCreators = { - requestShopRelated: (props?: GetShopRelatedRequestModel): AppThunkAction => (dispatch, getState) => { + requestShopRelated: (props?: IGetShopRelatedRequestModel): AppThunkAction => (dispatch, getState) => { - Get>('https://localhost:7151/api/ShopRelated', props?.pathParams, props?.searchParams) + if(process.env.REACT_APP_LOCAL_ONLY == 'Y') + return + + Get>('https://localhost:7151/api/ShopRelated', props?.pathParams, props?.searchParams) .then(response => response) .then(data => { if(data) @@ -39,7 +56,7 @@ const unloadedState: ShopRelatedState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale", "best offer" ], title: "Shop item title", brandName: "Brand & Name", @@ -48,7 +65,7 @@ const unloadedState: ShopRelatedState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -63,7 +80,7 @@ const unloadedState: ShopRelatedState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale", "best offer" ], title: "Shop item title", brandName: "Brand & Name", @@ -72,7 +89,7 @@ const unloadedState: ShopRelatedState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -87,7 +104,7 @@ const unloadedState: ShopRelatedState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale", "best offer" ], title: "Shop item title", brandName: "Brand & Name", @@ -96,7 +113,7 @@ const unloadedState: ShopRelatedState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), @@ -111,7 +128,7 @@ const unloadedState: ShopRelatedState = { id: '', slug: "shop-catalog-item", sku: "SKU-0", - image: { src: `${process.env.REACT_APP_API}/Image/450x300/dee2e6/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/450x300/dee2e6/6c757d`, alt: "..." }, badges: [ "sale", "best offer" ], title: "Shop item title", brandName: "Brand & Name", @@ -120,7 +137,7 @@ const unloadedState: ShopRelatedState = { text: "", author: { id: '', - image: { src: `${process.env.REACT_APP_API}/Image/40x40/ced4da/6c757d`, alt: "..." }, + image: { src: `${process.env.REACT_APP_FRONTEND}/Image/40x40/ced4da/6c757d`, alt: "..." }, nickName: "Admin" }, created: (new Date).toString(), diff --git a/src/ClientApp/src/store/reducers/WeatherForecasts.ts b/src/ClientApp/src/store/reducers/WeatherForecasts.ts index 4d52f83..53763fd 100644 --- a/src/ClientApp/src/store/reducers/WeatherForecasts.ts +++ b/src/ClientApp/src/store/reducers/WeatherForecasts.ts @@ -1,6 +1,17 @@ import { Action, Reducer } from 'redux' import { AppThunkAction } from '../' + + +// export interface GetWeatherForecastResponseModel extends ResponseModel { +// date: string, +// temperatireC: number, +// temperatureF: number, +// summary?: string +// } + + + // ----------------- // STATE - This defines the type of data maintained in the Redux store. diff --git a/src/DomainObjects/Abstractions/PersonBase.cs b/src/DomainObjects/Abstractions/PersonBase.cs index 66194b9..ace6d37 100644 --- a/src/DomainObjects/Abstractions/PersonBase.cs +++ b/src/DomainObjects/Abstractions/PersonBase.cs @@ -2,5 +2,5 @@ public abstract class PersonBase : DomainObjectBase { public Guid Id { get; set; } - public Image? Image { get; set; } + public MediaAttachment? Image { get; set; } } diff --git a/src/DomainObjects/Image.cs b/src/DomainObjects/Image.cs deleted file mode 100644 index 1f9cdc3..0000000 --- a/src/DomainObjects/Image.cs +++ /dev/null @@ -1,17 +0,0 @@ -using DomainObjects.Abstractions; - -namespace DomainObjects; - -public class Image : DomainObjectBase { - public string Src { get; set; } - - public string Alt { get; set; } - - public override int GetHashCode() { - int hash = 17; - hash = hash * 23 + Src.GetHashCode(); - hash = hash * 23 + Alt.GetHashCode(); - return hash; - } -} - diff --git a/src/DomainObjects/PageSections/TitleSection.cs b/src/DomainObjects/PageSections/TitleSection.cs index 685ed15..35bf433 100644 --- a/src/DomainObjects/PageSections/TitleSection.cs +++ b/src/DomainObjects/PageSections/TitleSection.cs @@ -3,7 +3,7 @@ namespace DomainObjects.PageSections; public class TitleSection : PageSectionBase { - public Image? Image { get; set; } + public MediaAttachment? Image { get; set; } public Link? PrimaryLink { get; set; } public Link? SecondaryLink { get; set; } public string? PostedOnBy { get; set; } diff --git a/src/ImageProvider/ImageProvider.cs b/src/ImageProvider/ImageProvider.cs index 86e7a85..6a1a6af 100644 --- a/src/ImageProvider/ImageProvider.cs +++ b/src/ImageProvider/ImageProvider.cs @@ -83,8 +83,6 @@ namespace ImageProvider { image.Mutate(ctx => { ctx.BackgroundColor(backgroundColor); ctx.ApplyScalingWaterMark(font, $"{width}x{height}", foregroundColor, 5, false); - //x.Fill(, new Rectangle(0, 0, width, height)); - //x.DrawText($"{width}x{height}", font, Color.Black, new PointF(10, 10)); }); using var stream = new MemoryStream(); diff --git a/src/ReverseProxy/appsettings.json b/src/ReverseProxy/appsettings.json index 33f6967..3d65dc6 100644 --- a/src/ReverseProxy/appsettings.json +++ b/src/ReverseProxy/appsettings.json @@ -18,7 +18,7 @@ "route2": { "ClusterId": "cluster1", "Match": { - "Path": "image/{**catchall}" + "Path": "Image/{**catchall}" }, "Transforms": [ { "PathPattern": "/api/Image/{**catchall}" } diff --git a/src/WeatherForecast/Models/Content/Responses/MediaAttachmentResponseModel.cs b/src/WeatherForecast/Models/Content/Responses/MediaAttachmentResponseModel.cs index 20daf17..2802edf 100644 --- a/src/WeatherForecast/Models/Content/Responses/MediaAttachmentResponseModel.cs +++ b/src/WeatherForecast/Models/Content/Responses/MediaAttachmentResponseModel.cs @@ -1,23 +1,25 @@ using Core.Abstractions.Models; using Core.Enumerations; +using DomainObjects; using DomainObjects.Enumerations; +using DomainObjects.L10n; namespace WeatherForecast.Models.Content.Responses { /// /// /// - public class MediaAttachmentL10nRequestModel : RequestModelBase { + public class MediaAttachmentL10nReponsetModel : ResponseModelBase { /// /// /// - public Locales Locale { get; set; } = Locales.Unknown; + public Locales Locale { get; set; } /// /// /// - public string Alt { get; set; } = string.Empty; + public string Alt { get; set; } /// /// @@ -33,6 +35,18 @@ namespace WeatherForecast.Models.Content.Responses { /// /// public string? Description { get; set; } + + /// + /// + /// + /// + public MediaAttachmentL10nReponsetModel(MediaAttachmentL10n l10n) { + Locale = l10n.Locale; + Alt = l10n.Alt; + Target = l10n.Target; + Title = l10n.Title; + Description = l10n.Description; + } } /// @@ -53,7 +67,18 @@ namespace WeatherForecast.Models.Content.Responses { /// /// /// - public List L10n { get; set; } = new List(); + public List L10n { get; set; } = new List(); + + /// + /// + /// + /// + public MediaAttachmentResponseModel(MediaAttachment mediaAttachment) { + Src= mediaAttachment.Src; + MediaType= mediaAttachment.MediaType; + + L10n = mediaAttachment.L10n.Select(x => new MediaAttachmentL10nReponsetModel(x)).ToList(); + } } } diff --git a/src/WeatherForecast/Models/Content/Responses/PageSections/TestimonialsSectionModel.cs b/src/WeatherForecast/Models/Content/Responses/PageSections/TestimonialsSectionModel.cs index 9612058..1c6ad30 100644 --- a/src/WeatherForecast/Models/Content/Responses/PageSections/TestimonialsSectionModel.cs +++ b/src/WeatherForecast/Models/Content/Responses/PageSections/TestimonialsSectionModel.cs @@ -19,8 +19,6 @@ namespace WeatherForecast.Models.Content.Responses.PageSections { /// public MediaAttachmentResponseModel? Image { get; set; } - - /// /// /// @@ -39,8 +37,8 @@ namespace WeatherForecast.Models.Content.Responses.PageSections { FullName = reviewer.FullName; Position = reviewer.Position; - //if (reviewer.Image != null) - // Image = new ImageModel(reviewer.Image); + if (reviewer.Image != null) + Image = new MediaAttachmentResponseModel(reviewer.Image); } } diff --git a/src/docker-compose.override.yml b/src/docker-compose.override.yml index 77b6eec..0227358 100644 --- a/src/docker-compose.override.yml +++ b/src/docker-compose.override.yml @@ -31,13 +31,6 @@ services: networks: - "my-network" - #clientapp: - # environment: - # - ASPNETCORE_ENVIRONMENT=Development - # ports: - # - "3000:3000" - # networks: - # - "my-network" mongo: image: mongo diff --git a/src/docker-compose.yml b/src/docker-compose.yml index dc5fa0e..494ba20 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -10,10 +10,4 @@ services: image: ${DOCKER_REGISTRY-}reverseproxy build: context: . - dockerfile: ReverseProxy/Dockerfile - #clientapp: - # image: ${DOCKER_REGISTRY-}clientapp - # build: - # context: . - # dockerfile: ClientApp/Dockerfile - + dockerfile: ReverseProxy/Dockerfile \ No newline at end of file diff --git a/src/docker-compose/mongo/data/db/WiredTiger.turtle b/src/docker-compose/mongo/data/db/WiredTiger.turtle index 226b4c3..f637578 100644 --- a/src/docker-compose/mongo/data/db/WiredTiger.turtle +++ b/src/docker-compose/mongo/data/db/WiredTiger.turtle @@ -3,4 +3,4 @@ WiredTiger 10.0.2: (December 21, 2021) WiredTiger version major=10,minor=0,patch=2 file:WiredTiger.wt -access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.124102=(addr="018381e487d81b1f8481e4bf0e4b478581e4ba5850aa808080e301ffc0e3010fc0",order=124102,time=1680030284,size=81920,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=29,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=372999,run_write_gen=372970)),checkpoint_backup_info=,checkpoint_lsn=(73,16000) +access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.126190=(addr="018381e452c7a52b8481e4d7b9659f8581e46348db2e808080e3023fc0e3010fc0",order=126190,time=1680809094,size=81920,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=1524,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=379335,run_write_gen=377166)),checkpoint_backup_info=,checkpoint_lsn=(78,597248) diff --git a/src/docker-compose/mongo/data/db/WiredTiger.wt b/src/docker-compose/mongo/data/db/WiredTiger.wt index d1d8f79..0ea1470 100644 Binary files a/src/docker-compose/mongo/data/db/WiredTiger.wt and b/src/docker-compose/mongo/data/db/WiredTiger.wt differ diff --git a/src/docker-compose/mongo/data/db/collection-2--4715807334585891142.wt b/src/docker-compose/mongo/data/db/collection-2--4715807334585891142.wt index f0aa21b..39b24c4 100644 Binary files a/src/docker-compose/mongo/data/db/collection-2--4715807334585891142.wt and b/src/docker-compose/mongo/data/db/collection-2--4715807334585891142.wt differ diff --git a/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt b/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt index ae5546c..b734e63 100644 Binary files a/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt and b/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt differ diff --git a/src/docker-compose/mongo/data/db/collection-6--4748285384625290805.wt b/src/docker-compose/mongo/data/db/collection-6--4748285384625290805.wt index 2cc4fca..6e5fa29 100644 Binary files a/src/docker-compose/mongo/data/db/collection-6--4748285384625290805.wt and b/src/docker-compose/mongo/data/db/collection-6--4748285384625290805.wt differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-11-29T17-35-19Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-11-29T17-35-19Z-00000 deleted file mode 100644 index a98e90c..0000000 Binary files a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-11-29T17-35-19Z-00000 and /dev/null differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-12-01T18-13-51Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-12-01T18-13-51Z-00000 deleted file mode 100644 index a866c22..0000000 Binary files a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2022-12-01T18-13-51Z-00000 and /dev/null differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-28T18-55-45Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-28T18-55-45Z-00000 index 5da2a89..c2e86a8 100644 Binary files a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-28T18-55-45Z-00000 and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-28T18-55-45Z-00000 differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-29T18-08-22Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-29T18-08-22Z-00000 new file mode 100644 index 0000000..2d6196a Binary files /dev/null and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-03-29T18-08-22Z-00000 differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-04T09-36-16Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-04T09-36-16Z-00000 new file mode 100644 index 0000000..33d9cb5 Binary files /dev/null and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-04T09-36-16Z-00000 differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-05T10-21-02Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-05T10-21-02Z-00000 new file mode 100644 index 0000000..5668e4e Binary files /dev/null and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-05T10-21-02Z-00000 differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-06T07-31-26Z-00000 b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-06T07-31-26Z-00000 new file mode 100644 index 0000000..7294fbc Binary files /dev/null and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.2023-04-06T07-31-26Z-00000 differ diff --git a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.interim b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.interim index cbdfe7d..c597fe9 100644 Binary files a/src/docker-compose/mongo/data/db/diagnostic.data/metrics.interim and b/src/docker-compose/mongo/data/db/diagnostic.data/metrics.interim differ diff --git a/src/docker-compose/mongo/data/db/index-3--4715807334585891142.wt b/src/docker-compose/mongo/data/db/index-3--4715807334585891142.wt index 6f5d1ce..9ef33ad 100644 Binary files a/src/docker-compose/mongo/data/db/index-3--4715807334585891142.wt and b/src/docker-compose/mongo/data/db/index-3--4715807334585891142.wt differ diff --git a/src/docker-compose/mongo/data/db/index-5--4715807334585891142.wt b/src/docker-compose/mongo/data/db/index-5--4715807334585891142.wt index e129422..cf9f647 100644 Binary files a/src/docker-compose/mongo/data/db/index-5--4715807334585891142.wt and b/src/docker-compose/mongo/data/db/index-5--4715807334585891142.wt differ diff --git a/src/docker-compose/mongo/data/db/index-6--4715807334585891142.wt b/src/docker-compose/mongo/data/db/index-6--4715807334585891142.wt index 0b2c9e8..16ad9eb 100644 Binary files a/src/docker-compose/mongo/data/db/index-6--4715807334585891142.wt and b/src/docker-compose/mongo/data/db/index-6--4715807334585891142.wt differ diff --git a/src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000073 b/src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000078 similarity index 99% rename from src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000073 rename to src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000078 index aaacc05..318f504 100644 Binary files a/src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000073 and b/src/docker-compose/mongo/data/db/journal/WiredTigerLog.0000000078 differ diff --git a/src/docker-compose/mongo/data/db/sizeStorer.wt b/src/docker-compose/mongo/data/db/sizeStorer.wt index b25e0e0..d00d6a5 100644 Binary files a/src/docker-compose/mongo/data/db/sizeStorer.wt and b/src/docker-compose/mongo/data/db/sizeStorer.wt differ diff --git a/src/launchSettings.json b/src/launchSettings.json index 81f2c0a..a679c14 100644 --- a/src/launchSettings.json +++ b/src/launchSettings.json @@ -7,8 +7,7 @@ "mongo": "StartWithoutDebugging", "mongo-express": "StartWithoutDebugging", "reverseproxy": "StartWithoutDebugging", - "weatherforecast": "StartDebugging", - // "clientapp": "StartDebugging" + "weatherforecast": "StartDebugging" } } }