├── .editorconfig ├── .env.example ├── .eslintrc.json ├── .gitignore ├── .markdownlint.json ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── netlify.toml ├── next-sitemap.config.js ├── next.config.js ├── package.json ├── postcss.config.mjs ├── public ├── .htaccess ├── images │ ├── 404.png │ ├── aboutUs.png │ ├── avatar-sm.png │ ├── avatar.png │ ├── banner.png │ ├── call-to-action.png │ ├── category-1.png │ ├── category-2.png │ ├── favicon.png │ ├── image-placeholder.png │ ├── logo-darkmode.png │ ├── logo.png │ ├── no-search-found.png │ ├── payment │ │ ├── bkash.png │ │ ├── express.png │ │ ├── mastercard.png │ │ ├── nagad.png │ │ ├── upay.png │ │ └── visa.png │ ├── product-1.png │ ├── product_image404.jpg │ ├── quote.svg │ ├── service-1.png │ ├── service-2.png │ ├── service-3.png │ ├── staff │ │ └── staff.png │ └── visa.png ├── products_export_1.csv ├── robots.txt └── sitemap.xml ├── scripts └── removeDarkmode.js ├── src ├── app │ ├── (auth) │ │ ├── login │ │ │ └── page.tsx │ │ └── sign-up │ │ │ └── page.tsx │ ├── [regular] │ │ └── page.tsx │ ├── about │ │ └── page.tsx │ ├── api │ │ └── customer │ │ │ ├── login │ │ │ └── route.ts │ │ │ └── sign-up │ │ │ └── route.ts │ ├── contact │ │ └── page.tsx │ ├── layout.tsx │ ├── not-found.tsx │ ├── page.tsx │ └── products │ │ ├── [slug] │ │ └── page.tsx │ │ └── page.tsx ├── config │ ├── config.json │ ├── menu.json │ ├── social.json │ └── theme.json ├── content │ ├── about │ │ └── _index.md │ ├── contact │ │ └── _index.md │ ├── pages │ │ ├── privacy-policy.md │ │ └── terms-services.md │ └── sections │ │ ├── call-to-action.md │ │ └── payments-and-delivery.md ├── hooks │ └── useLoadMore.ts ├── layouts │ ├── components │ │ ├── Breadcrumbs.tsx │ │ ├── CollectionsSlider.tsx │ │ ├── Expandable.tsx │ │ ├── HeroSlider.tsx │ │ ├── Logo.tsx │ │ ├── NavUser.tsx │ │ ├── Pagination.tsx │ │ ├── Price.tsx │ │ ├── SearchBar.tsx │ │ ├── Share.tsx │ │ ├── Social.tsx │ │ ├── ThemeSwitcher.tsx │ │ ├── cart │ │ │ ├── AddToCart.tsx │ │ │ ├── Cart.tsx │ │ │ ├── CartModal.tsx │ │ │ ├── CloseCart.tsx │ │ │ ├── DeleteItemButton.tsx │ │ │ ├── EditItemQuantityButton.tsx │ │ │ └── OpenCart.tsx │ │ ├── filter │ │ │ ├── DropdownMenu.tsx │ │ │ └── FilterDropdownItem.tsx │ │ ├── loadings │ │ │ ├── LoadingDots.tsx │ │ │ └── skeleton │ │ │ │ ├── SkeletonCards.tsx │ │ │ │ ├── SkeletonCategory.tsx │ │ │ │ ├── SkeletonDescription.tsx │ │ │ │ ├── SkeletonFeaturedProducts.tsx │ │ │ │ ├── SkeletonProductGallery.tsx │ │ │ │ ├── SkeletonProductThumb.tsx │ │ │ │ └── SkeletonProducts.tsx │ │ ├── product │ │ │ ├── PaymentSlider.tsx │ │ │ ├── ProductGallery.tsx │ │ │ ├── ProductLayouts.tsx │ │ │ ├── ShowTags.tsx │ │ │ ├── Tabs.tsx │ │ │ ├── VariantDropDown.tsx │ │ │ └── VariantSelector.tsx │ │ └── rangeSlider │ │ │ ├── RangeSlider.tsx │ │ │ └── rangeSlider.css │ ├── helpers │ │ ├── DynamicIcon.tsx │ │ ├── ImageFallback.tsx │ │ ├── MDXContent.tsx │ │ └── TwSizeIndicator.tsx │ ├── partials │ │ ├── CallToAction.tsx │ │ ├── FeaturedProducts.tsx │ │ ├── Footer.tsx │ │ ├── Header.tsx │ │ ├── PageHeader.tsx │ │ ├── PostSidebar.tsx │ │ ├── ProductCardView.tsx │ │ ├── ProductFilters.tsx │ │ ├── ProductListView.tsx │ │ ├── Providers.tsx │ │ ├── SeoMeta.tsx │ │ └── Testimonials.tsx │ └── shortcodes │ │ ├── Accordion.tsx │ │ ├── Button.tsx │ │ ├── Notice.tsx │ │ ├── Tab.tsx │ │ ├── Tabs.tsx │ │ ├── Video.tsx │ │ ├── Youtube.tsx │ │ └── all.tsx ├── lib │ ├── constants.ts │ ├── contentParser.ts │ ├── shopify │ │ ├── fragments │ │ │ ├── cart.ts │ │ │ ├── image.ts │ │ │ ├── product.ts │ │ │ └── seo.ts │ │ ├── index.ts │ │ ├── mutations │ │ │ ├── cart.ts │ │ │ └── customer.ts │ │ ├── queries │ │ │ ├── cart.ts │ │ │ ├── collection.ts │ │ │ ├── menu.ts │ │ │ ├── page.ts │ │ │ ├── product.ts │ │ │ └── vendor.ts │ │ └── types.ts │ ├── taxonomyParser.ts │ ├── typeGuards.ts │ ├── utils.ts │ └── utils │ │ ├── cartActions.ts │ │ ├── dateFormat.ts │ │ ├── readingTime.ts │ │ ├── similarItems.ts │ │ ├── sortFunctions.ts │ │ ├── taxonomyFilter.ts │ │ └── textConverter.ts ├── styles │ ├── base.css │ ├── buttons.css │ ├── components.css │ ├── main.css │ ├── navigation.css │ ├── safe.css │ ├── search.css │ └── utilities.css ├── tailwind-plugin │ ├── tw-bs-grid.js │ └── tw-theme.js └── types │ └── index.d.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | ; https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SHOPIFY_API_SECRET_KEY="" 2 | SHOPIFY_STOREFRONT_ACCESS_TOKEN="" 3 | SHOPIFY_STORE_DOMAIN="[your-shopify-store-subdomain].myshopify.com" -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .json/ 7 | .pnp.js 8 | yarn.lock 9 | package-lock.json 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": false, 3 | "MD013": false 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["bradlc.vscode-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.mdx": "markdown" 4 | }, 5 | "tailwindCSS.experimental.configFile": "src/styles/main.css" 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 - Present, Zeon Studio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = ".next" 3 | command = "yarn build" 4 | 5 | [functions] 6 | included_files = ["src/content/**"] 7 | 8 | [build.environment] 9 | NODE_VERSION = "22" 10 | # __NEXT_PRIVATE_PREBUNDLED_REACT = "next" 11 | # NEXT_FORCE_EDGE_IMAGES = true 12 | 13 | # [[headers]] 14 | # for = "/*" # This defines which paths this specific [[headers]] block will cover. 15 | 16 | # [headers.values] 17 | # X-Frame-Options = "DENY" 18 | # X-XSS-Protection = "1; mode=block" 19 | # Referrer-Policy = "same-origin" 20 | # Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload" 21 | -------------------------------------------------------------------------------- /next-sitemap.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next-sitemap').IConfig} */ 2 | 3 | module.exports = { 4 | siteUrl: process.env.SITE_URL || "https://example.com", 5 | generateRobotsTxt: true, 6 | }; 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const config = require("./src/config/config.json"); 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | reactStrictMode: true, 6 | basePath: config.base_path !== "/" ? config.base_path : "", 7 | trailingSlash: config.site.trailing_slash, 8 | transpilePackages: ["next-mdx-remote"], 9 | output: "standalone", 10 | images: { 11 | remotePatterns: [ 12 | { protocol: "https", hostname: "cdn.shopify.com", pathname: "/**" }, 13 | ], 14 | }, 15 | eslint: { ignoreDuringBuilds: true }, 16 | }; 17 | 18 | module.exports = nextConfig; 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commerceplate", 3 | "version": "2.2.5", 4 | "description": "Shopify Storefront Boilerplate with Next.js, Tailwind CSS, and Shopify SDK", 5 | "author": "zeon.studio", 6 | "license": "MIT", 7 | "packageManager": "yarn@1.22.19", 8 | "scripts": { 9 | "dev": "next dev --turbopack", 10 | "build": "next build", 11 | "postbuild": "next-sitemap", 12 | "start": "next start", 13 | "lint": "next lint", 14 | "format": "prettier -w ./src", 15 | "remove-darkmode": "node scripts/removeDarkmode.js && yarn format" 16 | }, 17 | "engines": { 18 | "node": ">=18.0.0" 19 | }, 20 | "dependencies": { 21 | "@justinribeiro/lite-youtube": "^1.7.1", 22 | "date-fns": "^4.1.0", 23 | "github-slugger": "^2.0.0", 24 | "gray-matter": "^4.0.3", 25 | "js-cookie": "^3.0.5", 26 | "marked": "^15.0.8", 27 | "next": "15.3.0", 28 | "next-mdx-remote": "^5.0.0", 29 | "next-sitemap": "^4.2.3", 30 | "next-themes": "^0.4.6", 31 | "react": "19.1.0", 32 | "react-dom": "19.1.0", 33 | "react-gravatar": "^2.6.3", 34 | "react-icons": "^5.5.0", 35 | "remark-gfm": "^4.0.1", 36 | "swiper": "^11.2.6" 37 | }, 38 | "devDependencies": { 39 | "@tailwindcss/forms": "^0.5.10", 40 | "@tailwindcss/postcss": "^4.1.3", 41 | "@tailwindcss/typography": "^0.5.16", 42 | "@types/js-cookie": "^3.0.6", 43 | "@types/marked": "^5.0.2", 44 | "@types/node": "^22.14.1", 45 | "@types/react": "19.1.0", 46 | "@types/react-dom": "19.1.2", 47 | "@types/react-gravatar": "^2.6.14", 48 | "eslint": "^9.24.0", 49 | "eslint-config-next": "15.3.0", 50 | "postcss": "^8.5.3", 51 | "prettier": "^3.5.3", 52 | "prettier-plugin-tailwindcss": "^0.6.11", 53 | "tailwindcss": "^4.1.3", 54 | "typescript": "5.8.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | 7 | export default config; 8 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | ##### Optimize default expiration time - BEGIN 2 | 3 | 4 | ## Enable expiration control 5 | ExpiresActive On 6 | 7 | ## CSS and JS expiration: 1 week after request 8 | ExpiresByType text/css "now plus 1 week" 9 | ExpiresByType application/javascript "now plus 1 week" 10 | ExpiresByType application/x-javascript "now plus 1 week" 11 | 12 | ## Image files expiration: 1 month after request 13 | ExpiresByType image/bmp "now plus 1 month" 14 | ExpiresByType image/gif "now plus 1 month" 15 | ExpiresByType image/jpeg "now plus 1 month" 16 | ExpiresByType image/webp "now plus 1 month" 17 | ExpiresByType image/jp2 "now plus 1 month" 18 | ExpiresByType image/pipeg "now plus 1 month" 19 | ExpiresByType image/png "now plus 1 month" 20 | ExpiresByType image/svg+xml "now plus 1 month" 21 | ExpiresByType image/tiff "now plus 1 month" 22 | ExpiresByType image/x-icon "now plus 1 month" 23 | ExpiresByType image/ico "now plus 1 month" 24 | ExpiresByType image/icon "now plus 1 month" 25 | ExpiresByType text/ico "now plus 1 month" 26 | ExpiresByType application/ico "now plus 1 month" 27 | ExpiresByType image/vnd.wap.wbmp "now plus 1 month" 28 | 29 | ## Font files expiration: 1 month after request 30 | ExpiresByType application/x-font-ttf "now plus 1 month" 31 | ExpiresByType application/x-font-opentype "now plus 1 month" 32 | ExpiresByType application/x-font-woff "now plus 1 month" 33 | ExpiresByType font/woff2 "now plus 1 month" 34 | ExpiresByType image/svg+xml "now plus 1 month" 35 | 36 | ## Audio files expiration: 1 month after request 37 | ExpiresByType audio/ogg "now plus 1 month" 38 | ExpiresByType application/ogg "now plus 1 month" 39 | ExpiresByType audio/basic "now plus 1 month" 40 | ExpiresByType audio/mid "now plus 1 month" 41 | ExpiresByType audio/midi "now plus 1 month" 42 | ExpiresByType audio/mpeg "now plus 1 month" 43 | ExpiresByType audio/mp3 "now plus 1 month" 44 | ExpiresByType audio/x-aiff "now plus 1 month" 45 | ExpiresByType audio/x-mpegurl "now plus 1 month" 46 | ExpiresByType audio/x-pn-realaudio "now plus 1 month" 47 | ExpiresByType audio/x-wav "now plus 1 month" 48 | 49 | ## Movie files expiration: 1 month after request 50 | ExpiresByType application/x-shockwave-flash "now plus 1 month" 51 | ExpiresByType x-world/x-vrml "now plus 1 month" 52 | ExpiresByType video/x-msvideo "now plus 1 month" 53 | ExpiresByType video/mpeg "now plus 1 month" 54 | ExpiresByType video/mp4 "now plus 1 month" 55 | ExpiresByType video/quicktime "now plus 1 month" 56 | ExpiresByType video/x-la-asf "now plus 1 month" 57 | ExpiresByType video/x-ms-asf "now plus 1 month" 58 | 59 | ##### Optimize default expiration time - END 60 | 61 | ##### 1 Month for most static resources 62 | 63 | Header set Cache-Control "max-age=2592000, public" 64 | 65 | 66 | ##### Enable gzip compression for resources 67 | 68 | mod_gzip_on Yes 69 | mod_gzip_dechunk Yes 70 | mod_gzip_item_include file .(html?|txt|css|js|php)$ 71 | mod_gzip_item_include handler ^cgi-script$ 72 | mod_gzip_item_include mime ^text/.* 73 | mod_gzip_item_include mime ^application/x-javascript.* 74 | mod_gzip_item_exclude mime ^image/.* 75 | mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* 76 | 77 | 78 | ##### Or, compress certain file types by extension: 79 | 80 | SetOutputFilter DEFLATE 81 | 82 | 83 | ##### Set Header Vary: Accept-Encoding 84 | 85 | 86 | Header append Vary: Accept-Encoding 87 | 88 | -------------------------------------------------------------------------------- /public/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/404.png -------------------------------------------------------------------------------- /public/images/aboutUs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/aboutUs.png -------------------------------------------------------------------------------- /public/images/avatar-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/avatar-sm.png -------------------------------------------------------------------------------- /public/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/avatar.png -------------------------------------------------------------------------------- /public/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/banner.png -------------------------------------------------------------------------------- /public/images/call-to-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/call-to-action.png -------------------------------------------------------------------------------- /public/images/category-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/category-1.png -------------------------------------------------------------------------------- /public/images/category-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/category-2.png -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/favicon.png -------------------------------------------------------------------------------- /public/images/image-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/image-placeholder.png -------------------------------------------------------------------------------- /public/images/logo-darkmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/logo-darkmode.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/logo.png -------------------------------------------------------------------------------- /public/images/no-search-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/no-search-found.png -------------------------------------------------------------------------------- /public/images/payment/bkash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/bkash.png -------------------------------------------------------------------------------- /public/images/payment/express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/express.png -------------------------------------------------------------------------------- /public/images/payment/mastercard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/mastercard.png -------------------------------------------------------------------------------- /public/images/payment/nagad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/nagad.png -------------------------------------------------------------------------------- /public/images/payment/upay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/upay.png -------------------------------------------------------------------------------- /public/images/payment/visa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/payment/visa.png -------------------------------------------------------------------------------- /public/images/product-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/product-1.png -------------------------------------------------------------------------------- /public/images/product_image404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/product_image404.jpg -------------------------------------------------------------------------------- /public/images/quote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/service-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/service-1.png -------------------------------------------------------------------------------- /public/images/service-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/service-2.png -------------------------------------------------------------------------------- /public/images/service-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/service-3.png -------------------------------------------------------------------------------- /public/images/staff/staff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/staff/staff.png -------------------------------------------------------------------------------- /public/images/visa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeon-studio/commerceplate/8b14c7b788364e11bfbe526ad0dc7aeddef3d35f/public/images/visa.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # * 2 | User-agent: * 3 | Allow: / 4 | 5 | # Host 6 | Host: https://example.com 7 | 8 | # Sitemaps 9 | Sitemap: https://example.com/sitemap.xml 10 | -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /scripts/removeDarkmode.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const rootDirs = ["src/app", "src/hooks", "src/layouts", "src/styles"]; 5 | const configFiles = [ 6 | { 7 | filePath: "tailwind.config.js", 8 | patterns: ["darkmode:\\s*{[^}]*},", 'darkMode:\\s*"class",'], 9 | }, 10 | { filePath: "src/config/theme.json", patterns: ["colors.darkmode"] }, 11 | ]; 12 | 13 | rootDirs.forEach(removeDarkModeFromPages); 14 | configFiles.forEach(removeDarkMode); 15 | 16 | function removeDarkModeFromFiles(filePath, regexPatterns) { 17 | const fileContent = fs.readFileSync(filePath, "utf8"); 18 | let updatedContent = fileContent; 19 | regexPatterns.forEach((pattern) => { 20 | const regex = new RegExp(pattern, "g"); 21 | updatedContent = updatedContent.replace(regex, ""); 22 | }); 23 | fs.writeFileSync(filePath, updatedContent, "utf8"); 24 | } 25 | 26 | function removeDarkModeFromPages(directoryPath) { 27 | const files = fs.readdirSync(directoryPath); 28 | 29 | files.forEach((file) => { 30 | const filePath = path.join(directoryPath, file); 31 | const stats = fs.statSync(filePath); 32 | if (stats.isDirectory()) { 33 | removeDarkModeFromPages(filePath); 34 | } else if (stats.isFile()) { 35 | removeDarkModeFromFiles(filePath, [ 36 | '(?:(?!["])\\S)*dark:(?:(?![,;"])\\S)*', 37 | ]); 38 | } 39 | }); 40 | } 41 | 42 | function removeDarkMode(configFile) { 43 | const { filePath, patterns } = configFile; 44 | if (filePath === "tailwind.config.js") { 45 | removeDarkModeFromFiles(filePath, patterns); 46 | } else { 47 | const contentFile = JSON.parse(fs.readFileSync(filePath, "utf8")); 48 | patterns.forEach((pattern) => deleteNestedProperty(contentFile, pattern)); 49 | fs.writeFileSync(filePath, JSON.stringify(contentFile)); 50 | } 51 | } 52 | 53 | function deleteNestedProperty(obj, propertyPath) { 54 | const properties = propertyPath.split("."); 55 | let currentObj = obj; 56 | for (let i = 0; i < properties.length - 1; i++) { 57 | const property = properties[i]; 58 | if (currentObj.hasOwnProperty(property)) { 59 | currentObj = currentObj[property]; 60 | } else { 61 | return; // Property not found, no need to continue 62 | } 63 | } 64 | delete currentObj[properties[properties.length - 1]]; 65 | } 66 | -------------------------------------------------------------------------------- /src/app/(auth)/login/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { CustomerError } from "@/lib/shopify/types"; 4 | import Link from "next/link"; 5 | import { useRouter } from "next/navigation"; 6 | import { ChangeEvent, FormEvent, useState } from "react"; 7 | import { BiLoaderAlt } from "react-icons/bi"; 8 | import { FormData } from "../sign-up/page"; 9 | 10 | const Login = () => { 11 | const router = useRouter(); 12 | const [formData, setFormData] = useState({ 13 | email: "", 14 | password: "", 15 | }); 16 | 17 | const [loading, setLoading] = useState(false); 18 | const [errorMessages, setErrorMessages] = useState([]); 19 | 20 | const handleChange = (e: ChangeEvent): void => { 21 | setFormData({ 22 | ...formData, 23 | [e.target.name]: e.target.value, 24 | }); 25 | }; 26 | 27 | const handleLogin = async (e: FormEvent): Promise => { 28 | e.preventDefault(); 29 | 30 | try { 31 | setLoading(true); 32 | 33 | const response = await fetch("/api/customer/login", { 34 | method: "POST", 35 | headers: { 36 | "Content-Type": "application/json", 37 | }, 38 | body: JSON.stringify(formData), 39 | }); 40 | 41 | const responseData = await response.json(); 42 | 43 | if (response.ok) { 44 | setErrorMessages([]); 45 | const data = responseData; 46 | localStorage.setItem("user", JSON.stringify(data)); 47 | router.push("/"); 48 | } else { 49 | const errors = responseData.errors || []; 50 | setErrorMessages(errors); 51 | } 52 | } catch (error) { 53 | console.error("Error during login:", error); 54 | } finally { 55 | setLoading(false); 56 | } 57 | }; 58 | 59 | return ( 60 |
61 |
62 |
63 |
64 |
65 |

Login

66 |

67 | Please fill your email and password to login 68 |

69 |
70 | 71 |
72 |
73 | 74 | 81 |
82 | 83 |
84 | 85 | 92 |
93 | 94 | {errorMessages.map((error: CustomerError) => ( 95 |

99 | * 100 | {error.code === "UNIDENTIFIED_CUSTOMER" 101 | ? `${error.message}` 102 | : "Invalid Email or Password"} 103 |

104 | ))} 105 | 106 | 116 |
117 | 118 |
119 |

120 | Don't have an account? 121 |

122 | 126 | Register 127 | 128 |
129 |
130 |
131 |
132 |
133 | ); 134 | }; 135 | 136 | export default Login; 137 | -------------------------------------------------------------------------------- /src/app/(auth)/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { CustomerError } from "@/lib/shopify/types"; 4 | import Link from "next/link"; 5 | import { useRouter } from "next/navigation"; 6 | import { ChangeEvent, FormEvent, useState } from "react"; 7 | import { BiLoaderAlt } from "react-icons/bi"; 8 | 9 | export interface FormData { 10 | firstName?: string; 11 | email: string; 12 | password: string; 13 | } 14 | 15 | const SignUp = () => { 16 | const router = useRouter(); 17 | const [formData, setFormData] = useState({ 18 | firstName: "", 19 | email: "", 20 | password: "", 21 | }); 22 | 23 | const [loading, setLoading] = useState(false); 24 | const [errorMessages, setErrorMessages] = useState([]); 25 | 26 | const handleChange = (e: ChangeEvent): void => { 27 | setFormData({ 28 | ...formData, 29 | [e.target.name]: e.target.value, 30 | }); 31 | }; 32 | 33 | const handleSignUp = async (e: FormEvent): Promise => { 34 | e.preventDefault(); 35 | 36 | try { 37 | setLoading(true); 38 | 39 | const response = await fetch("/api/customer/sign-up", { 40 | method: "POST", 41 | headers: { 42 | "Content-Type": "application/json", 43 | }, 44 | body: JSON.stringify(formData), 45 | }); 46 | 47 | const responseData = await response.json(); 48 | 49 | if (response.ok) { 50 | setErrorMessages([]); 51 | const data = responseData; 52 | localStorage.setItem("user", JSON.stringify(data)); 53 | router.push("/"); 54 | } else { 55 | const errors = responseData.errors || []; 56 | setErrorMessages(errors); 57 | } 58 | } catch (error) { 59 | console.error("Error during sign-up:", error); 60 | } finally { 61 | setLoading(false); 62 | } 63 | }; 64 | 65 | return ( 66 |
67 |
68 |
69 |
70 |
71 |

Create an account

72 |

73 | Create an account and start using... 74 |

75 |
76 | 77 |
78 |
79 | 80 | 87 |
88 | 89 |
90 | 91 | 98 |
99 | 100 |
101 | 102 | 109 |
110 | 111 | {errorMessages.map((error: CustomerError) => ( 112 |

116 | *{error.message} 117 |

118 | ))} 119 | 120 | 130 |
131 | 132 |
133 |

134 | I have read and agree to the 135 |

136 | 140 | Terms & Conditions 141 | 142 |
143 | 144 |
145 |

146 | Have an account? 147 |

148 | 152 | Login 153 | 154 |
155 |
156 |
157 |
158 |
159 | ); 160 | }; 161 | 162 | export default SignUp; 163 | -------------------------------------------------------------------------------- /src/app/[regular]/page.tsx: -------------------------------------------------------------------------------- 1 | import MDXContent from "@/helpers/MDXContent"; 2 | import { getSinglePage } from "@/lib/contentParser"; 3 | import PageHeader from "@/partials/PageHeader"; 4 | import SeoMeta from "@/partials/SeoMeta"; 5 | import { RegularPage } from "@/types"; 6 | import { notFound } from "next/navigation"; 7 | 8 | // Generate static params 9 | export const generateStaticParams = () => { 10 | const regularPages = getSinglePage("pages").map((page: RegularPage) => ({ 11 | regular: page.slug, 12 | })); 13 | return regularPages; 14 | }; 15 | 16 | // For all regular pages 17 | const RegularPages = async (props: { 18 | params: Promise<{ regular: string }>; 19 | }) => { 20 | const params = await props.params; 21 | const regularData = getSinglePage("pages"); 22 | const data = regularData.find( 23 | (page: RegularPage) => page.slug === params.regular, 24 | ); 25 | 26 | if (!data) return notFound(); 27 | 28 | const { frontmatter, content } = data; 29 | const { title, meta_title, description, image } = frontmatter; 30 | 31 | return ( 32 | <> 33 | 39 | 40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 | 48 | ); 49 | }; 50 | 51 | export default RegularPages; 52 | -------------------------------------------------------------------------------- /src/app/api/customer/login/route.ts: -------------------------------------------------------------------------------- 1 | import { getCustomerAccessToken, getUserDetails } from "@/lib/shopify"; 2 | import { cookies } from "next/headers"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function POST(req: NextRequest) { 6 | try { 7 | const input = await req.json(); 8 | const { token, customerLoginErrors } = await getCustomerAccessToken(input); 9 | if (customerLoginErrors.length > 0) { 10 | return NextResponse.json( 11 | { errors: customerLoginErrors }, 12 | { status: 400 }, 13 | ); 14 | } 15 | const cookieStore = await cookies(); 16 | cookieStore.set("token", token); 17 | 18 | const { customer } = await getUserDetails(token); 19 | 20 | return NextResponse.json({ ...customer, token }); 21 | } catch (error: any) { 22 | const { message, status } = error.error; 23 | return NextResponse.json( 24 | { errors: [{ code: "INTERNAL_ERROR", message }] }, 25 | { status }, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/api/customer/sign-up/route.ts: -------------------------------------------------------------------------------- 1 | import { createCustomer, getCustomerAccessToken } from "@/lib/shopify"; 2 | import { cookies } from "next/headers"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function POST(req: NextRequest) { 6 | try { 7 | const input = await req.json(); 8 | const { customer, customerCreateErrors } = await createCustomer(input); 9 | const { token } = await getCustomerAccessToken(input); 10 | 11 | if (customerCreateErrors.length > 0) { 12 | return NextResponse.json( 13 | { errors: customerCreateErrors }, 14 | { status: 400 }, 15 | ); 16 | } 17 | const cookieStore = await cookies(); 18 | cookieStore.set("token", token); 19 | 20 | return NextResponse.json({ ...customer, token }); 21 | } catch (error: any) { 22 | const { message, status } = error.error; 23 | return NextResponse.json( 24 | { errors: [{ code: "INTERNAL_ERROR", message }] }, 25 | { status }, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/contact/page.tsx: -------------------------------------------------------------------------------- 1 | import config from "@/config/config.json"; 2 | import { getListPage } from "@/lib/contentParser"; 3 | import { markdownify } from "@/lib/utils/textConverter"; 4 | import PageHeader from "@/partials/PageHeader"; 5 | import SeoMeta from "@/partials/SeoMeta"; 6 | import { ContactUsItem, RegularPage } from "@/types"; 7 | 8 | const Contact = async () => { 9 | const data: RegularPage = getListPage("contact/_index.md"); 10 | const { frontmatter } = data; 11 | const { title, description, meta_title, image, contact_meta } = frontmatter; 12 | const { contact_form_action } = config.params; 13 | 14 | return ( 15 | <> 16 | 22 | 23 |
24 |
25 |
26 | {contact_meta && 27 | contact_meta?.map((contact: ContactUsItem) => ( 28 |
32 |

36 |

37 |

38 | ))} 39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |

47 | We would love to hear from you! 48 |

49 | 50 |
55 |
56 |
57 | 60 | 68 |
69 | 70 |
71 | 74 | 81 |
82 |
83 | 84 |
85 |
86 | 89 | 97 |
98 | 99 |
100 | 103 | 111 |
112 |
113 | 114 |
115 | 118 | 126 |
127 | 128 |
129 | 132 |
133 |
134 |
135 |
136 |
137 | 138 | ); 139 | }; 140 | 141 | export default Contact; 142 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import Cart from "@/components/cart/Cart"; 2 | import OpenCart from "@/components/cart/OpenCart"; 3 | import config from "@/config/config.json"; 4 | import theme from "@/config/theme.json"; 5 | import TwSizeIndicator from "@/helpers/TwSizeIndicator"; 6 | import Footer from "@/partials/Footer"; 7 | import Header from "@/partials/Header"; 8 | import Providers from "@/partials/Providers"; 9 | import "@/styles/main.css"; 10 | 11 | export default function RootLayout({ 12 | children, 13 | }: { 14 | children: React.ReactNode; 15 | }) { 16 | // import google font css 17 | const pf = theme.fonts.font_family.primary; 18 | const sf = theme.fonts.font_family.secondary; 19 | 20 | return ( 21 | 22 | 23 | {/* responsive meta */} 24 | 28 | 29 | {/* favicon */} 30 | 31 | {/* theme meta */} 32 | 33 | 34 | 39 | 44 | 45 | {/* google font css */} 46 | 51 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 |
65 |
{children}
66 |