├── .env ├── .env.example ├── .gitignore ├── .lintstagedrc ├── .nvmrc ├── README.md ├── components ├── AppFooter.vue ├── AppHeader.vue ├── BottomNavigation.vue ├── CartSidebar.vue ├── CategoryPageHeader.vue ├── Checkout │ ├── CartPreview.vue │ ├── VsfPaymentProvider.vue │ └── VsfShippingProvider.vue ├── FeaturedProduct.vue ├── FiltersSidebar.vue ├── HeaderNavigation.vue ├── InstagramFeed.vue ├── LocaleSelector.vue ├── LoginModal.vue ├── MobileMenuSidebar.vue ├── MobileStoreBanner.vue ├── MyAccount │ ├── BillingAddressForm.vue │ ├── PasswordResetForm.vue │ ├── ProfileUpdateForm.vue │ └── ShippingAddressForm.vue ├── NewsletterModal.vue ├── Notification.vue ├── README.md ├── RelatedProducts.vue ├── SearchResults.vue ├── Tab.vue ├── Tabs.vue ├── TopBar.vue ├── UserBillingAddress.vue ├── UserShippingAddress.vue ├── WishlistSidebar.vue ├── checkout │ ├── UserBillingAddresses.vue │ └── UserShippingAddresses.vue └── utils │ └── LoadWhenVisible.vue ├── composables ├── index.ts ├── useUiHelpers │ └── index.ts ├── useUiNotification │ └── index.ts └── useUiState.ts ├── helpers ├── README.md ├── afterloadforci.js ├── article.ts ├── cacheControl.js ├── category │ ├── getCategoryPath.js │ ├── getCategorySearchParameters.js │ └── index.js ├── filters │ ├── getFiltersForUrl.js │ └── getFiltersFromUrl.js └── utils.ts ├── integrations └── refresh-caching.client.js ├── jest.config.js ├── lang ├── config.js ├── de.js └── en.js ├── layouts ├── account.vue ├── blank.vue ├── default.vue └── error.vue ├── middleware.config.js ├── middleware ├── README.md └── authenticated.js ├── modules ├── cms │ ├── build.ts │ ├── enums │ │ └── SortBy.ts │ ├── module.ts │ ├── pages │ │ ├── articles │ │ │ └── _handle.vue │ │ └── blogs │ │ │ ├── _handle.vue │ │ │ └── index.vue │ └── runtime.ts └── integrations.js ├── nuxt.config.js ├── package.json ├── pages ├── Category.vue ├── Checkout.vue ├── Checkout │ ├── Billing.vue │ ├── Payment.vue │ ├── Shipping.vue │ └── ThankYou.vue ├── ContactUs.vue ├── Home.vue ├── MyAccount.vue ├── MyAccount │ ├── AdressBook.vue │ ├── BillingDetails.vue │ ├── LoyaltyCard.vue │ ├── MyNewsletter.vue │ ├── MyProfile.vue │ ├── MyReviews.vue │ ├── OrderHistory.vue │ └── ShippingDetails.vue ├── Posts.vue ├── Product.vue ├── README.md ├── ResetPassword.vue └── TermsAndConditions.vue ├── plugins └── scrollToTop.client.js ├── server-middleware └── custom-features.js ├── static ├── README.md ├── country-state.json ├── error │ └── error.webp ├── favicon.ico ├── homepage │ ├── apple.png │ ├── bannerA.webp │ ├── bannerB.webp │ ├── bannerC.webp │ ├── bannerD.png │ ├── bannerE.webp │ ├── bannerF.webp │ ├── bannerG.webp │ ├── bannerH.webp │ ├── google.png │ ├── imageAd.webp │ ├── imageAm.webp │ ├── imageBd.webp │ ├── imageBm.webp │ ├── imageCd.webp │ ├── imageCm.webp │ ├── imageDd.webp │ ├── imageDm.webp │ ├── newsletter.webp │ ├── productA.webp │ ├── productB.webp │ └── productC.webp ├── icon.png ├── icons │ ├── android-icon-144x144.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-92x92.png │ ├── empty-cart.webp │ ├── facebook.webp │ ├── google.webp │ ├── langs │ │ ├── de.webp │ │ └── en.webp │ ├── logo.webp │ ├── pinterest.webp │ ├── twitter.webp │ └── youtube.webp ├── productpage │ ├── productA.jpg │ ├── productB.jpg │ └── productM.jpg └── thankyou │ ├── bannerD.png │ └── bannerM.png ├── store └── index.js ├── tests └── e2e │ ├── cypress.json │ ├── fixtures │ └── example.json │ ├── integration │ └── search.spec.js │ ├── plugins │ └── index.js │ └── support │ ├── commands.js │ └── index.js ├── tsconfig.json └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | SHOPIFY_DOMAIN=vsf-next-pwa.myshopify.com 2 | SHOPIFY_STOREFRONT_TOKEN=03f21475b97c18fa05c0ab452c368af4 3 | BASE_URL=localhost:3001 -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SHOPIFY_STOREFRONT_TOKEN= 2 | SHOPIFY_DOMAIN= 3 | BASE_URL= 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | server 3 | middleware.config.js 4 | .nuxt 5 | _theme 6 | node_modules 7 | tests 8 | .history 9 | package-json.lock 10 | sw.js 11 | .env 12 | # Logs 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | 18 | # NPM config 19 | .npmrc 20 | 21 | # Yarn Integrity file 22 | .yarn-integrity 23 | 24 | # Rollup generate output 25 | lib 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | 30 | # Editor directories and files 31 | .idea 32 | .vscode 33 | 34 | # OS generated files 35 | .DS_STORE 36 | 37 | !scripts/lib 38 | 39 | packages/theme/.template 40 | 41 | tests/e2e/report 42 | 43 | docs/.vuepress/dist 44 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,jsx}": [ 3 | "eslint --fix" 4 | ], 5 | "*.{ts,tsx}": [ 6 | "eslint --fix" 7 | ], 8 | "*.{vue}": [ 9 | "eslint --fix" 10 | ], 11 | } 12 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12.22.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alokai Shopify Template 2 | 3 | ### Stay connected 4 | 5 | [![GitHub Repo stars](https://img.shields.io/github/stars/vuestorefront/vue-storefront?style=social)](https://github.com/vuestorefront/vue-storefront) 6 | [![Twitter Follow](https://img.shields.io/twitter/follow/vuestorefront?style=social)](https://twitter.com/vuestorefront) 7 | [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UCkm1F3Cglty3CE1QwKQUhhg?style=social)](https://www.youtube.com/c/VueStorefront) 8 | [![Discord](https://img.shields.io/discord/770285988244750366?label=join%20discord&logo=Discord&logoColor=white)](https://discord.vuestorefront.io) 9 | 10 | Alokai template for Shopify. 11 | 12 | ## Setup 13 | 14 | 1. Create a `.env` inline with `middleware.config.js` file and fill the following required variables 15 | 16 | ```bash 17 | SHOPIFY_STOREFRONT_TOKEN= 18 | SHOPIFY_DOMAIN= # example: vsf-next-pwa.myshopify.com 19 | BASE_URL= # example: localhost:3001 20 | ``` 21 | 22 | 2. Run the project 23 | 24 | ``` bash 25 | # install dependencies 26 | $ yarn install 27 | 28 | # serve with hot reload at localhost:3000 29 | $ yarn dev 30 | 31 | # build for production and launch server 32 | $ yarn build 33 | $ yarn start 34 | 35 | # generate static project 36 | $ yarn generate 37 | ``` 38 | 39 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org) / [Alokai Docs](https://docs.vuestorefront.io/v2/). 40 | 41 | ## Contributing 42 | 43 | This repository is autogenerated. If you want to contribute to Shopify integration please use https://github.com/vuestorefront/shopify. 44 | -------------------------------------------------------------------------------- /components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 114 | 115 | -------------------------------------------------------------------------------- /components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 248 | 249 | 294 | -------------------------------------------------------------------------------- /components/BottomNavigation.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 91 | 92 | -------------------------------------------------------------------------------- /components/CategoryPageHeader.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 124 | 125 | 247 | -------------------------------------------------------------------------------- /components/Checkout/CartPreview.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /components/Checkout/VsfPaymentProvider.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 62 | 63 | 76 | -------------------------------------------------------------------------------- /components/Checkout/VsfShippingProvider.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 69 | 70 | 83 | -------------------------------------------------------------------------------- /components/FiltersSidebar.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 170 | 171 | 242 | -------------------------------------------------------------------------------- /components/HeaderNavigation.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 59 | 60 | 75 | -------------------------------------------------------------------------------- /components/InstagramFeed.vue: -------------------------------------------------------------------------------- 1 | 27 | 56 | 108 | -------------------------------------------------------------------------------- /components/LocaleSelector.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 109 | 110 | 159 | -------------------------------------------------------------------------------- /components/MobileMenuSidebar.vue: -------------------------------------------------------------------------------- 1 | 21 | 66 | -------------------------------------------------------------------------------- /components/MobileStoreBanner.vue: -------------------------------------------------------------------------------- 1 | 42 | 57 | 90 | -------------------------------------------------------------------------------- /components/MyAccount/PasswordResetForm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /components/MyAccount/ProfileUpdateForm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /components/MyAccount/ShippingAddressForm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /components/NewsletterModal.vue: -------------------------------------------------------------------------------- 1 | 54 | 90 | 91 | 117 | -------------------------------------------------------------------------------- /components/Notification.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 39 | 40 | 95 | -------------------------------------------------------------------------------- /components/README.md: -------------------------------------------------------------------------------- 1 | Put here theme-specific components to override default ones -------------------------------------------------------------------------------- /components/RelatedProducts.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 180 | 181 | 214 | -------------------------------------------------------------------------------- /components/Tab.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /components/Tabs.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 98 | -------------------------------------------------------------------------------- /components/TopBar.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | 38 | -------------------------------------------------------------------------------- /components/UserBillingAddress.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | 61 | -------------------------------------------------------------------------------- /components/UserShippingAddress.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | -------------------------------------------------------------------------------- /components/WishlistSidebar.vue: -------------------------------------------------------------------------------- 1 | 76 | 126 | 127 | 205 | -------------------------------------------------------------------------------- /components/checkout/UserBillingAddresses.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 78 | 79 | 89 | -------------------------------------------------------------------------------- /components/checkout/UserShippingAddresses.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 79 | 80 | 97 | -------------------------------------------------------------------------------- /components/utils/LoadWhenVisible.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /composables/index.ts: -------------------------------------------------------------------------------- 1 | import useUiHelpers from './useUiHelpers'; 2 | import useUiState from './useUiState'; 3 | import useUiNotification from './useUiNotification'; 4 | 5 | export { 6 | useUiHelpers, 7 | useUiState, 8 | useUiNotification 9 | }; 10 | -------------------------------------------------------------------------------- /composables/useUiHelpers/index.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from '@nuxtjs/composition-api'; 2 | import { Category } from '@vue-storefront/shopify-api'; 3 | import { AgnosticFacet } from '@vue-storefront/core'; 4 | 5 | const nonFilters = ['page', 'sort', 'term', 'itemsPerPage']; 6 | 7 | const getContext = () => { 8 | const vm = getCurrentInstance(); 9 | return vm.root.proxy; 10 | }; 11 | 12 | const reduceFilters = (query) => (prev, curr) => { 13 | const makeArray = Array.isArray(query[curr]) || nonFilters.includes(curr); 14 | 15 | return { 16 | ...prev, 17 | [curr]: makeArray ? query[curr] : [query[curr]] 18 | }; 19 | }; 20 | 21 | const getFiltersDataFromUrl = (context, onlyFilters) => { 22 | const { query } = context.$router.history.current; 23 | 24 | return Object.keys(query) 25 | .filter(f => onlyFilters ? !nonFilters.includes(f) : nonFilters.includes(f)) 26 | .reduce(reduceFilters(query), {}); 27 | }; 28 | 29 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 30 | const useUiHelpers = () => { 31 | const context = getContext(); 32 | 33 | const getFacetsFromURL = () => { 34 | const { query, params } = context.$router.currentRoute; 35 | const categorySlug = Object.keys(params).reduce((prev, curr) => params[curr] || prev, params.slug_1); 36 | 37 | return { 38 | rootCatSlug: params.slug_1, 39 | categorySlug, 40 | page: parseInt(query.page as string, 10) || 1, 41 | sort: query.sort || 'latest', 42 | filters: getFiltersDataFromUrl(context, true), 43 | itemsPerPage: parseInt(query.itemsPerPage as string, 12) || 20, 44 | term: query.term 45 | }; 46 | }; 47 | 48 | const getCatLink = (category: Category): string => { 49 | return `/c/${category.slug}`; 50 | }; 51 | 52 | const changeSorting = (sort: string) => { 53 | const { query } = context.$router.currentRoute 54 | context.$router.push({ query: { ...query, sort } }); 55 | }; 56 | 57 | const changeFilters = (filters: any) => { 58 | context.$router.push({ 59 | query: { 60 | ...getFiltersDataFromUrl(context, false), 61 | ...filters 62 | } 63 | }); 64 | }; 65 | 66 | const changeItemsPerPage = (itemsPerPage: number) => { 67 | context.$router.push({ 68 | query: { 69 | ...getFiltersDataFromUrl(context, false), 70 | itemsPerPage 71 | } 72 | }); 73 | }; 74 | 75 | const changeSearchTerm = (term: string) => { 76 | context.$router.push({ 77 | query: { 78 | ...getFiltersDataFromUrl(context, false), 79 | term: term || undefined 80 | } 81 | }); 82 | }; 83 | 84 | const isFacetColor = (facet: AgnosticFacet): boolean => facet.id === 'color'; 85 | 86 | const isFacetCheckbox = (): boolean => false; 87 | 88 | const formatDate = (date: string) => { 89 | const monthsArray = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 90 | const toFormatDate = new Date(date); 91 | return monthsArray[toFormatDate.getMonth()] + ' ' + toFormatDate.getDate() + ', ' + toFormatDate.getFullYear() + ' at ' + toFormatDate.getHours() + ':' + toFormatDate.getMinutes(); 92 | } 93 | 94 | return { 95 | getFacetsFromURL, 96 | getCatLink, 97 | changeSorting, 98 | changeFilters, 99 | changeItemsPerPage, 100 | changeSearchTerm, 101 | isFacetColor, 102 | isFacetCheckbox, 103 | formatDate 104 | }; 105 | }; 106 | 107 | export default useUiHelpers; 108 | -------------------------------------------------------------------------------- /composables/useUiNotification/index.ts: -------------------------------------------------------------------------------- 1 | import { computed, reactive } from '@nuxtjs/composition-api'; 2 | 3 | interface UiNotification { 4 | message: string; 5 | action: { text: string; onClick: (...args: any) => void }; 6 | type: 'danger' | 'success' | 'info'; 7 | icon: string; 8 | persist: boolean; 9 | id: symbol; 10 | dismiss: () => void; 11 | } 12 | 13 | interface Notifications { 14 | notifications: Array; 15 | } 16 | 17 | const state = reactive({ 18 | notifications: [] 19 | }); 20 | const maxVisibleNotifications = 1; 21 | const timeToLive = 3000; 22 | 23 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 24 | const useUiNotification = () => { 25 | const send = (notification: UiNotification) => { 26 | const id = Symbol('notification'); 27 | 28 | const dismiss = () => { 29 | const index = state.notifications.findIndex(notification => notification.id === id); 30 | 31 | if (index !== -1) state.notifications.splice(index, 1); 32 | }; 33 | 34 | const newNotification = { 35 | ...notification, 36 | id, 37 | dismiss 38 | }; 39 | 40 | state.notifications.push(newNotification); 41 | if (state.notifications.length > maxVisibleNotifications) state.notifications.shift(); 42 | 43 | if (!notification.persist) { 44 | setTimeout(dismiss, timeToLive); 45 | } 46 | }; 47 | 48 | return { 49 | send, 50 | notifications: computed(() => state.notifications) 51 | }; 52 | }; 53 | 54 | export default useUiNotification; 55 | -------------------------------------------------------------------------------- /composables/useUiState.ts: -------------------------------------------------------------------------------- 1 | import { reactive, computed } from '@nuxtjs/composition-api'; 2 | 3 | const state = reactive({ 4 | isCartSidebarOpen: false, 5 | isWishlistSidebarOpen: false, 6 | isLoginModalOpen: false, 7 | isCategoryGridView: true, 8 | isFilterSidebarOpen: false, 9 | isNavigationSidebarOpen: false, 10 | isMobileMenuOpen: false, 11 | articlesPerPage: '5' 12 | }); 13 | 14 | const useUiState = () => { 15 | const isMobileMenuOpen = computed(() => state.isMobileMenuOpen); 16 | const toggleMobileMenu = () => { 17 | state.isMobileMenuOpen = !state.isMobileMenuOpen; 18 | }; 19 | 20 | const isCartSidebarOpen = computed(() => state.isCartSidebarOpen); 21 | const toggleCartSidebar = () => { 22 | state.isCartSidebarOpen = !state.isCartSidebarOpen; 23 | }; 24 | 25 | const isWishlistSidebarOpen = computed(() => state.isWishlistSidebarOpen); 26 | const toggleWishlistSidebar = () => { 27 | state.isWishlistSidebarOpen = !state.isWishlistSidebarOpen; 28 | }; 29 | 30 | const isNavigationSidebarOpen = computed(() => state.isNavigationSidebarOpen); 31 | const toggleNavigationSidebar = () => { 32 | state.isNavigationSidebarOpen = !state.isNavigationSidebarOpen; 33 | }; 34 | 35 | const isLoginModalOpen = computed(() => state.isLoginModalOpen); 36 | const toggleLoginModal = () => { 37 | state.isLoginModalOpen = !state.isLoginModalOpen; 38 | }; 39 | 40 | const isCategoryGridView = computed(() => state.isCategoryGridView); 41 | const toggleCategoryGridView = () => { 42 | state.isCategoryGridView = !state.isCategoryGridView; 43 | }; 44 | 45 | const isFilterSidebarOpen = computed(() => state.isFilterSidebarOpen); 46 | const toggleFilterSidebar = () => { 47 | state.isFilterSidebarOpen = !state.isFilterSidebarOpen; 48 | }; 49 | 50 | const articlesPerPage = computed(() => state.articlesPerPage) 51 | const setArticlesPerPage = (perPage: string) => { 52 | state.articlesPerPage = perPage 53 | } 54 | 55 | return { 56 | isCartSidebarOpen, 57 | isWishlistSidebarOpen, 58 | isLoginModalOpen, 59 | isCategoryGridView, 60 | isFilterSidebarOpen, 61 | isNavigationSidebarOpen, 62 | isMobileMenuOpen, 63 | toggleMobileMenu, 64 | toggleCartSidebar, 65 | toggleWishlistSidebar, 66 | toggleLoginModal, 67 | toggleCategoryGridView, 68 | toggleFilterSidebar, 69 | toggleNavigationSidebar, 70 | articlesPerPage, 71 | setArticlesPerPage 72 | }; 73 | }; 74 | 75 | export default useUiState; 76 | -------------------------------------------------------------------------------- /helpers/README.md: -------------------------------------------------------------------------------- 1 | Put here platform-specific, non-agnostic functions that overwrite default code. 2 | -------------------------------------------------------------------------------- /helpers/afterloadforci.js: -------------------------------------------------------------------------------- 1 | // callback - the function to run after onLoad 2 | // delay - wait X milliseconds after onLoad 3 | export const afterLoad = (callback, delay = 10) => { 4 | // missed the load event, run now 5 | if (document.readyState === 'complete') { 6 | setTimeout(() => callback(), delay); 7 | } else { 8 | // eslint-disable-next-line func-names 9 | window.addEventListener('load', () => { 10 | setTimeout(() => callback(), delay); 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /helpers/article.ts: -------------------------------------------------------------------------------- 1 | import { formatDate } from "./utils" 2 | 3 | export const getArticleImage = (article) => { 4 | return article?.image?.url 5 | } 6 | 7 | export const getArticlePublishedAt = (article) => { 8 | return article?.publishedAt && formatDate(article?.publishedAt) 9 | } 10 | 11 | export const getArticleLink = (article) => { 12 | return { 13 | name: 'articles-handle', 14 | params: { handle: article.handle }, 15 | query: { id: article.id } 16 | }; 17 | }; -------------------------------------------------------------------------------- /helpers/cacheControl.js: -------------------------------------------------------------------------------- 1 | const cacheControl = (values) => ({ res }) => { 2 | if (!process.server) return; 3 | 4 | const cacheControlValue = Object.entries(values) 5 | .map(([key, value]) => `${key}=${value}`) 6 | .join(','); 7 | 8 | res.setHeader('Cache-Control', cacheControlValue); 9 | }; 10 | 11 | export default cacheControl; 12 | -------------------------------------------------------------------------------- /helpers/category/getCategoryPath.js: -------------------------------------------------------------------------------- 1 | export function getCategoryPath(category, context = this) { 2 | return `/c/${context.$route.params.slug_1}/${category.slug}`; 3 | } 4 | -------------------------------------------------------------------------------- /helpers/category/getCategorySearchParameters.js: -------------------------------------------------------------------------------- 1 | export const getCategorySearchParameters = (context) => { 2 | const { params } = context.root.$route; 3 | const lastSlug = Object.keys(params).reduce((prev, curr) => params[curr] || prev, params.slug_1); 4 | 5 | return { 6 | slug: lastSlug 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /helpers/category/index.js: -------------------------------------------------------------------------------- 1 | import { getCategorySearchParameters } from './getCategorySearchParameters'; 2 | import { getCategoryPath } from './getCategoryPath'; 3 | 4 | // TODO: remove, use faceting instead 5 | export { 6 | getCategorySearchParameters, 7 | getCategoryPath 8 | }; 9 | -------------------------------------------------------------------------------- /helpers/filters/getFiltersForUrl.js: -------------------------------------------------------------------------------- 1 | export const getFiltersForUrl = (filters) => { 2 | return Object.entries(filters || {}).reduce((prev, [name, filter]) => { 3 | prev[name] = filter.options.filter(option => option.selected).map(option => option.value); 4 | return prev; 5 | }, {}); 6 | }; 7 | -------------------------------------------------------------------------------- /helpers/filters/getFiltersFromUrl.js: -------------------------------------------------------------------------------- 1 | export const getFiltersFromUrl = (context, filters) => { 2 | const { query } = context.root.$route; 3 | const filtersFromQuery = Object.entries(query).filter(([name]) => !['page', 'items'].includes(name)); 4 | filtersFromQuery.forEach(([name, values]) => { 5 | if (!filters[name]) { 6 | return; 7 | } 8 | 9 | if (!Array.isArray(values)) { 10 | values = [values]; 11 | } 12 | filters[name].options.forEach(option => { 13 | if (values.includes(option.value)) { 14 | option.selected = true; 15 | } 16 | }); 17 | }); 18 | return filters; 19 | }; 20 | -------------------------------------------------------------------------------- /helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from '@nuxtjs/composition-api' 2 | 3 | export const formatDate = (date: string) => { 4 | const { app } = useContext() 5 | const monthsArray = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 6 | const toFormatDate = new Date(date); 7 | return app.i18n.t(monthsArray[toFormatDate.getMonth()]) + ' ' + toFormatDate.getDate() + ', ' + toFormatDate.getFullYear() + ' at ' + toFormatDate.getHours() + ':' + toFormatDate.getMinutes(); 8 | } -------------------------------------------------------------------------------- /integrations/refresh-caching.client.js: -------------------------------------------------------------------------------- 1 | export default async({ $cookies, $config }) => { 2 | if ('serviceWorker' in navigator) { 3 | if ($config.appVersion !== $cookies.get('AppVer')) { 4 | await navigator.serviceWorker.getRegistrations().then((registrations) => { 5 | for (const registration of registrations) { 6 | registration.unregister(); 7 | } 8 | }).catch((err) => { 9 | console.log('Service Worker registration failed: ', err); 10 | }); 11 | caches.keys().then(cacheNames => { 12 | cacheNames.forEach(cacheName => { 13 | caches.delete(cacheName); 14 | }); 15 | }).then(() => { 16 | $cookies.set('AppVer', $config.appVersion, { maxAge: 60 * 60 * 24 * 365 }); 17 | // register service worker 18 | navigator.serviceWorker.register('/sw.js'); 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleNameMapper: { 3 | '^@/(.*)$': '/$1', 4 | '^~/(.*)$': '/$1', 5 | '^vue$': 'vue/dist/vue.common.js' 6 | }, 7 | moduleFileExtensions: [ 8 | 'ts', 9 | 'js', 10 | 'vue', 11 | 'json' 12 | ], 13 | transform: { 14 | '^.+\\.ts$': 'ts-jest', 15 | '^.+\\.js$': 'babel-jest', 16 | '.*\\.(vue)$': 'vue-jest' 17 | }, 18 | testMatch: ['/**/__tests__/**/*spec.[jt]s?(x)', '!**/e2e/**'], 19 | collectCoverage: true, 20 | collectCoverageFrom: [ 21 | '/components/**/*.vue', 22 | '/pages/**/*.vue' 23 | ], 24 | testEnvironment: 'jsdom' 25 | } -------------------------------------------------------------------------------- /lang/config.js: -------------------------------------------------------------------------------- 1 | const locales = [ 2 | { 3 | code: 'en', 4 | file: 'en.js', 5 | name: 'en', 6 | label: 'English', 7 | shopId: 121, 8 | country: { 9 | name: 'US', 10 | label: 'United States' 11 | }, 12 | currency: { 13 | name: 'USD', 14 | label: 'Dollar' 15 | } 16 | } 17 | ]; 18 | 19 | export default { 20 | locales, 21 | defaultLocale: locales[0].code, 22 | lazy: true, 23 | langDir: 'lang/', 24 | vueI18n: { 25 | fallbackLocale: locales[0].code 26 | }, 27 | detectBrowserLanguage: { 28 | cookieKey: 'vsf-locale', 29 | alwaysRedirect: true 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lang/de.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export default { 4 | 'Categories': 'Kategorien', 5 | 'Filters': 'Filters', 6 | 'Sort by': 'Sortieren nach', 7 | 'Products found': 'Produkte gefunden', 8 | 'About us': 'Über uns', 9 | 'Who we are': 'Wer wir sind', 10 | 'Quality in the details': 'Qualität im Detail', 11 | 'Customer Reviews': 'Kundenbewertungen', 12 | 'Departments': 'Abteilungen', 13 | 'Women fashion': 'Damenmode', 14 | 'Men fashion': 'Herrenmode', 15 | 'Kidswear': 'Kinderkleidung', 16 | 'Home': 'Zuhause', 17 | 'Help': 'Hilfe', 18 | 'Customer service': 'Kundendienst', 19 | 'Size guide': 'Größentabelle', 20 | 'Contact us': 'Kontaktiere uns', 21 | 'Payment & Delivery': 'Zahlung & Lieferung', 22 | 'Purchase terms': 'Kaufbedingungen', 23 | 'Guarantee': 'Garantie', 24 | 'Description': 'Beschreibung', 25 | 'Read reviews': 'Bewertungen lesen', 26 | 'Additional Information': 'Zusätzliche Information', 27 | 'Save for later': 'Für später speichern', 28 | 'Add to compare': 'Hinzufügen zum vergleichen', 29 | 'Match it with': 'Kombiniere es mit', 30 | 'Share your look': 'Teile deinen Look', 31 | 'Product description': 'Das Karissa V-Neck Tee hat eine halb taillierte Form schmeichelhaft für jede Figur. Sie können mit ins Fitnessstudio gehen Vertrauen, während es Kurven umarmt und häufiges "Problem" verbirgt Bereiche. Finden Sie atemberaubende Cocktailkleider für Frauen und feiern Sie Kleider.', 32 | 'Brand': 'Marke', 33 | 'Instruction1': 'Um mich kümmern', 34 | 'Instruction2': 'Nur hier für die Pflegehinweise?', 35 | 'Instruction3': 'Ja, das haben wir uns gedacht', 36 | 'Items': 'Gegenstände', 37 | 'View': 'Ansicht', 38 | 'Show on page': 'Auf Seite anzeigen', 39 | 'Done': 'Fertig', 40 | 'Clear all': 'Alles löschen', 41 | 'Empty': 'Sieht so aus, als hätten Sie der Tasche noch keine Artikel hinzugefügt. Beginnen Sie mit dem Einkaufen, um es auszufüllen.', 42 | 'Help & FAQs': 'Hilfe & FAQs', 43 | 'Download': 'Laden Sie unsere Anwendung herunter', 44 | 'Find out more': 'Finde mehr heraus', 45 | 'Login': 'Anmeldung', 46 | 'Forgotten password?': 'Passwort vergessen?', 47 | 'No account': `Sie haben noch keinen Account?`, 48 | 'Register today': 'Melde dich noch heute an', 49 | 'Go to checkout': 'Zur Kasse gehen', 50 | 'Go back shopping': 'Zurück einkaufen', 51 | 'Personal details': 'Persönliche Daten', 52 | 'Edit': 'Bearbeiten', 53 | 'Shipping details': 'Versanddetails', 54 | 'Billing address': 'Rechnungsadresse', 55 | 'Same as shipping address': 'Wie Versandadresse', 56 | 'Payment method': 'Zahlungsmethode', 57 | 'Apply': 'Übernehmen', 58 | 'Update password': 'Passwort aktualisieren', 59 | 'Update personal data': 'Persönliche Daten aktualisieren', 60 | 'Item': 'Artikel', 61 | 'Go back': 'Go back', 62 | 'Continue to shipping': 'Weiter zum Versand', 63 | 'I agree to': 'Ich stimme zu', 64 | 'Terms and conditions': 'Allgemeine Geschäftsbedingungen', 65 | 'Pay for order': 'Für Bestellung bezahlen', 66 | 'Log into your account': 'In dein Konto einloggen', 67 | 'or fill the details below': 'oder füllen Sie die Details unten', 68 | 'Enjoy your free account': 'Enjoy these perks with your free account!', 69 | 'Continue to payment': 'Weiter zur Zahlung', 70 | 'Order No.': 'Bestellnummer', 71 | 'Successful placed order': 'Sie haben die Bestellung erfolgreich aufgegeben. Sie können den Status Ihres Bestellen Sie über unsere Lieferstatusfunktion. Sie erhalten eine Bestellung Bestätigungs-E-Mail mit Details Ihrer Bestellung und einem Link zum Verfolgen der Bestellung Fortschritt.', 72 | 'Info after order': 'Sie können sich mit E-Mail und definiertem Passwort in Ihrem Konto anmelden vorhin. Überprüfen Sie in Ihrem Konto Ihre Profildaten Transaktionsverlauf, Abonnement für Newsletter bearbeiten.', 73 | 'Allow order notifications': 'Bestellbenachrichtigungen zulassen', 74 | 'Feedback': 'Ihr Feedback ist uns wichtig. Lassen Sie uns wissen, was wir verbessern können.', 75 | 'Send my feedback': 'Senden Sie mein Feedback', 76 | 'Go back to shop': 'Zurück zum Einkaufen', 77 | 'Read all reviews': 'Alle Bewertungen lesen', 78 | 'Color': 'Farbe', 79 | 'Contact details updated': 'Halten Sie Ihre Adressen und Kontaktdaten auf dem neuesten Stand.', 80 | 'Manage billing addresses': 'Alle gewünschten Rechnungsadressen verwalten (Arbeitsplatz, Privatadresse ...) Auf diese Weise müssen Sie die Rechnungsadresse nicht bei jeder Bestellung manuell eingeben.', 81 | 'Change': 'Änderungsänderung', 82 | 'Delete': 'Löschen', 83 | 'Add new address': 'Neue Adresse hinzufügen', 84 | 'Set up newsletter': 'Richten Sie Ihren Newsletter ein und wir senden Ihnen wöchentlich Informationen zu neuen Produkten und Trends aus den von Ihnen ausgewählten Bereichen', 85 | 'Sections that interest you': 'Abschnitte, die Sie interessieren', 86 | 'Save changes': 'Änderungen speichern', 87 | 'Read and understand': 'Ich habe das gelesen und verstanden', 88 | 'Privacy': 'Datenschutz', 89 | 'Cookies Policy': 'Cookie-Richtlinie', 90 | 'Commercial information': 'und erklären sich damit einverstanden, personalisierte Handelsinformationen vom Markennamen per E-Mail zu erhalten', 91 | 'Feel free to edit': 'Fühlen Sie sich frei, Ihre unten stehenden Daten zu bearbeiten, damit Ihr Konto immer auf dem neuesten Stand ist', 92 | 'Use your personal data': 'Bei Markennamen legen wir großen Wert auf Datenschutzfragen und verpflichten uns, die persönlichen Daten unserer Benutzer zu schützen. Erfahren Sie mehr darüber, wie wir Ihre persönlichen Daten pflegen und verwenden', 93 | 'Privacy Policy': 'Datenschutzrichtlinie', 94 | 'Change password your account': 'Wenn Sie das Passwort ändern möchten, um auf Ihr Konto zuzugreifen, geben Sie die folgenden Informationen ein', 95 | 'Your current email address is': 'Ihre aktuelle E-Mail-Adresse lautet', 96 | 'Product': 'Produkt', 97 | 'Details and status orders': 'Überprüfen Sie die Details und den Status Ihrer Bestellungen im Online-Shop. Sie können Ihre Bestellung auch stornieren oder eine Rücksendung anfordern. ', 98 | 'You currently have no orders': 'Sie haben derzeit keine Bestellungen', 99 | 'Start shopping': 'Einkaufen starten', 100 | 'Download': 'Herunterladen', 101 | 'Download all': 'Alle herunterladen', 102 | 'View details': 'Details anzeigen', 103 | 'Manage shipping addresses': 'Alle gewünschten Versandadressen verwalten (Arbeitsplatz, Privatadresse ...) Auf diese Weise müssen Sie die Versandadresse nicht bei jeder Bestellung manuell eingeben.', 104 | 'Quantity': 'Menge', 105 | 'Price': 'Preis', 106 | 'Back to homepage': 'Zurück zur Homepage', 107 | 'Select shipping method': 'Versandart auswählen', 108 | 'Review my order': 'Meine Bestellung überprüfen', 109 | 'Select payment method': 'Zahlungsmethode auswählen', 110 | 'Make an order': 'Bestellung aufgeben', 111 | 'or': 'oder', 112 | 'login in to your account': 'Anmelden bei Ihrem Konto', 113 | 'Create an account': 'Konto erstellen', 114 | 'Your bag is empty': 'Ihre Tasche ist leer', 115 | 'Cancel': 'Abbrechen', 116 | 'See all': 'Alle anzeigen', 117 | 'See all results': 'Alle Ergebnisse anzeigen', 118 | 'We haven’t found any results for given phrase': 'Wir haben keine Ergebnisse für die angegebene Phrase gefunden', 119 | 'You haven’t searched for items yet': 'Sie haben noch nicht nach Artikeln gesucht.', 120 | 'Let’s start now – we’ll help you': 'Fangen wir jetzt an - wir helfen Ihnen.', 121 | 'Search results': 'Suchergebnisse', 122 | 'Product suggestions': 'Produktvorschläge', 123 | 'Search for items': 'Nach Artikeln suchen', 124 | 'Enter promo code': 'Geben Sie den Promo-Code ein', 125 | 'Shipping method': 'Versandart', 126 | 'Continue to billing': 'Weiter zur Abrechnung', 127 | 'Payment methods': 'Zahlungsmethoden', 128 | 'Shipping address': 'Lieferanschrift', 129 | 'Subtotal': 'Zwischensumme', 130 | 'Shipping': 'Versand', 131 | 'Total price': 'Gesamtpreis', 132 | 'Payment': 'Zahlung', 133 | 'Order summary': 'Bestellübersicht', 134 | 'Products': 'Produkte', 135 | 'Total': 'Gesamt', 136 | 'Reset Password': 'Passwort Zurücksetzen', 137 | 'Save Password': 'Passwort Speichern', 138 | 'Back to home': 'Zurück Zur Startseite', 139 | 'Forgot Password': 'Wenn Sie Ihr Passwort vergessen haben, können Sie es zurücksetzen.', 140 | 'Thank You Inbox': 'Wenn die Nachricht nicht in Ihrem Posteingang ankommt, versuchen Sie es mit einer anderen E-Mail-Adresse, mit der Sie sich möglicherweise registriert haben.', 141 | 'Sign in': 'Einloggen', 142 | 'Register': 'Registrieren', 143 | 'Password Changed': 'Passwort erfolgreich geändert. Sie können nun zur Startseite zurückkehren und sich anmelden.', 144 | 'Password': 'Passwort', 145 | 'Repeat Password': 'Wiederhole das Passwort', 146 | 'Forgot Password Modal Email': 'E-Mail, mit der Sie sich anmelden:', 147 | forgotPasswordConfirmation: 'Vielen Dank! Wenn ein Konto mit der E-Mail-Adresse {0} registriert ist, finden Sie in Ihrem Posteingang eine Nachricht mit einem Link zum Zurücksetzen des Passworts.', 148 | subscribeToNewsletterModalContent: 149 | 'Wenn Sie sich für den Newsletter angemeldet haben, erhalten Sie spezielle Angebote und Nachrichten von VSF per E-Mail. Wir werden Ihre E-Mail zu keinem Zeitpunkt an Dritte verkaufen oder weitergeben. Bitte beachten Sie unsere {0}.', 150 | 'Subscribe': 'Abonnieren', 151 | 'Subscribe to newsletter': 'Anmeldung zum Newsletter', 152 | 'Email address': 'E-Mail Adresse', 153 | 'I confirm subscription': 'Ich bestätige das Abonnement', 154 | 'You can unsubscribe at any time': 'Sie können sich jederzeit abmelden', 155 | 'show more': 'mehr anzeigen', 156 | 'hide': 'ausblenden', 157 | 'Change to grid view': 'Zur Rasteransicht wechseln', 158 | 'Change to list view': 'Zur Listenansicht wechseln', 159 | 'Delivery': 'Lieferung', 160 | 'Pickup in store': 'Abholung im Laden', 161 | 'Free': 'Kostenlos', 162 | 'Select Channel': 'Kanal auswählen', 163 | 'Remove coupon': 'Gutschein entfernen', 164 | 'This promo code is invalid': 'Dieser Gutscheincode ist ungültig', 165 | 'Search results': 'Search results', 166 | 'Out of Stock': 'Ausverkauft', 167 | 'You haven\'t searched for items yet': 'You haven\'t searched for items yet', 168 | 'Let\'s start now - we\'ll help you': 'Let\'s start now - we\'ll help you', 169 | 'on': 'on', 170 | 'Add to Cart': 'Añadir al carrito' 171 | }; -------------------------------------------------------------------------------- /lang/en.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export default { 4 | 'Categories': 'Categories', 5 | 'Filters': 'Filters', 6 | 'Sort by': 'Sort by', 7 | 'Products found': 'Products found', 8 | 'About us': 'About us', 9 | 'Who we are': 'Who we are', 10 | 'Quality in the details': 'Quality in the details', 11 | 'Customer Reviews': 'Customer Reviews', 12 | 'Departments': 'Departments', 13 | 'Women fashion': 'Women fashion', 14 | 'Men fashion': 'Men fashion', 15 | 'Kidswear': 'Kidswear', 16 | 'Home': 'Home', 17 | 'Help': 'Help', 18 | 'Customer service': 'Customer service', 19 | 'Size guide': 'Size guide', 20 | 'Contact us': 'Contact us', 21 | 'Payment & Delivery': 'Payment & Delivery', 22 | 'Purchase terms': 'Purchase terms', 23 | 'Guarantee': 'Guarantee', 24 | 'Description': 'Description', 25 | 'Read reviews': 'Read reviews', 26 | 'Additional Information': 'Additional Information', 27 | 'Save for later': 'Save for later', 28 | 'Add to compare': 'Add to compare', 29 | 'Match it with': 'Match it with', 30 | 'Share your look': 'Share your look', 31 | 'Product description': `The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses.`, 32 | 'Brand': 'Brand', 33 | 'Out of Stock': 'Out of Stock', 34 | 'Instruction1': 'Take care of me', 35 | 'Instruction2': 'Just here for the care instructions?', 36 | 'Instruction3': 'Yeah, we thought so', 37 | 'Items': 'Items', 38 | 'View': 'View', 39 | 'Show on page': 'Show on page', 40 | 'Done': 'Done', 41 | 'Clear all': 'Clear all', 42 | 'Empty': 'Looks like you haven’t added any items to the bag yet. Start shopping to fill it in.', 43 | 'Help & FAQs': 'Help & FAQs', 44 | 'Download': 'Download our application.', 45 | 'Find out more': 'Find out more', 46 | 'Login': 'Login', 47 | 'Forgotten password?': 'Forgotten password?', 48 | 'No account': `Don't have an account yet?`, 49 | 'Register today': 'Register today', 50 | 'Go to checkout': 'Go to checkout', 51 | 'Go back shopping': 'Go back shopping', 52 | 'Personal details': 'Personal details', 53 | 'Edit': 'Edit', 54 | 'Shipping details': 'Shipping details', 55 | 'Billing address': 'Billing address', 56 | 'Same as shipping address': 'Same as shipping address', 57 | 'Payment method': 'Payment method', 58 | 'Apply': 'Apply', 59 | 'Update password': 'Update password', 60 | 'Update personal data': 'Update personal data', 61 | 'Item': 'Item', 62 | 'Go back': 'Go back', 63 | 'Continue to shipping': 'Continue to shipping', 64 | 'I agree to': 'I agree to', 65 | 'Terms and conditions': 'Terms and conditions', 66 | 'Pay for order': 'Pay for order', 67 | 'Log into your account': 'Log into your account', 68 | 'or fill the details below': 'or fill the details below', 69 | 'Enjoy your free account': 'Enjoy these perks with your free account!', 70 | 'Continue to payment': 'Continue to payment', 71 | 'Order No.': 'Order No.', 72 | 'Successful placed order': 'You have successfully placed the order. You can check status of your order by using our delivery status feature. You will receive an order confirmation e-mail with details of your order and a link to track its progress.', 73 | 'Info after order': 'You can log to your account using e-mail and password defined earlier. On your account you can edit your profile data, check history of transactions, edit subscription to newsletter.', 74 | 'Allow order notifications': 'Allow order notifications', 75 | 'Feedback': 'Your feedback is important to us. Let us know what we could improve.', 76 | 'Send my feedback': 'Send my feedback', 77 | 'Go back to shop': 'Go back to shop', 78 | 'Read all reviews': 'Read all reviews', 79 | 'Color': 'Color', 80 | 'Contact details updated': 'Keep your addresses and contact details updated.', 81 | 'Manage billing addresses': 'Manage all the billing addresses you want (work place, home address...) This way you won"t have to enter the billing address manually with each order.', 82 | 'Change': 'Change', 83 | 'Delete': 'Delete', 84 | 'Add new address': 'Add new address', 85 | 'Set up newsletter': 'Set up your newsletter and we will send you information about new products and trends from the sections you selected every week.', 86 | 'Sections that interest you': 'Sections that interest you', 87 | 'Save changes': 'Save changes', 88 | 'Read and understand': 'I have read and understand the', 89 | 'Privacy': 'Privacy', 90 | 'Cookies Policy': 'Cookies Policy', 91 | 'Commercial information': 'and agree to receive personalized commercial information from Brand name by email', 92 | 'Feel free to edit': 'Feel free to edit any of your details below so your account is always up to date', 93 | 'Use your personal data': 'At Brand name, we attach great importance to privacy issues and are committed to protecting the personal data of our users. Learn more about how we care and use your personal data in the', 94 | 'Privacy Policy': 'Privacy Policy', 95 | 'Change password your account': 'If you want to change the password to access your account, enter the following information', 96 | 'Your current email address is': 'Your current email address is', 97 | 'Product': 'Product', 98 | 'Details and status orders': 'Check the details and status of your orders in the online store. You can also cancel your order or request a return.', 99 | 'You currently have no orders': 'You currently have no orders', 100 | 'Start shopping': 'Start shopping', 101 | 'Download': 'Download', 102 | 'Download all': 'Download all', 103 | 'View details': 'View details', 104 | 'Manage shipping addresses': 'Manage all the shipping addresses you want (work place, home address...) This way you won"t have to enter the shipping address manually with each order.', 105 | 'Quantity': 'Quantity', 106 | 'Price': 'Price', 107 | 'Back to homepage': 'Back to homepage', 108 | 'Select shipping method': 'Select shipping method', 109 | 'Review my order': 'Review my order', 110 | 'Select payment method': 'Select payment method', 111 | 'Make an order': 'Make an order', 112 | 'or': 'or', 113 | 'login in to your account': 'login in to your account', 114 | 'Create an account': 'Create an account', 115 | 'Your bag is empty': 'Your bag is empty', 116 | 'Cancel': 'Cancel', 117 | 'See all': 'See all', 118 | 'See all results': 'See all results', 119 | 'We haven’t found any results for given phrase': 'We haven’t found any results for given phrase', 120 | 'You haven’t searched for items yet': 'You haven’t searched for items yet.', 121 | 'Let’s start now – we’ll help you': 'Let’s start now – we’ll help you.', 122 | 'Search results': 'Search results', 123 | 'Product suggestions': 'Product suggestions', 124 | 'Search for items': 'Search for items', 125 | 'Enter promo code': 'Enter promo code', 126 | 'Shipping method': 'Shipping method', 127 | 'Continue to billing': 'Continue to billing', 128 | 'Payment methods': 'Payment methods', 129 | 'Shipping address': 'Shipping address', 130 | 'Subtotal': 'Subtotal', 131 | 'Shipping': 'Shipping', 132 | 'Total price': 'Total price', 133 | 'Payment': 'Payment', 134 | 'Order summary': 'Order summary', 135 | 'Products': 'Products', 136 | 'Total': 'Total', 137 | 'Reset Password': 'Reset Password', 138 | 'Save Password': 'Save Password', 139 | 'Back to home': 'Back to home', 140 | 'Forgot Password': 'If you can’t remember your password, you can reset it.', 141 | 'Thank You Inbox': 'If the message is not arriving in your inbox, try another email address you might’ve used to register.', 142 | 'Sign in': 'Sign in', 143 | 'Register': 'Register', 144 | 'Password Changed': 'Password successfuly changed. You can now go back to homepage and sign in.', 145 | 'Password': 'Password', 146 | 'Repeat Password': 'Repeat Password', 147 | 'Forgot Password Modal Email': 'Email you are using to sign in:', 148 | forgotPasswordConfirmation: 'Thanks! If there is an account registered with the {0} email, you will find message with a password reset link in your inbox.', 149 | subscribeToNewsletterModalContent: 150 | 'After signing up for the newsletter, you will receive special offers and messages from VSF via email. We will not sell or distribute your email to any third party at any time. Please see our {0}.', 151 | 'Subscribe': 'Subscribe', 152 | 'Subscribe to newsletter': 'Subscribe to newsletter', 153 | 'Email address': 'Email address', 154 | 'I confirm subscription': 'I confirm subscription', 155 | 'You can unsubscribe at any time': 'You can unsubscribe at any time', 156 | 'show more': 'show more', 157 | 'hide': 'hide', 158 | 'Change to grid view': 'Change to grid view', 159 | 'Change to list view': 'Change to list view', 160 | 'Delivery': 'Delivery', 161 | 'Pickup in store': 'Pickup in store', 162 | 'Free': 'Free', 163 | 'Select Channel': 'Select Channel', 164 | 'Remove coupon': 'Remove coupon', 165 | 'This promo code is invalid': 'This promo code is invalid', 166 | 'Search results': 'Search results', 167 | 'You haven\'t searched for items yet': 'You haven\'t searched for items yet', 168 | 'Let\'s start now - we\'ll help you': 'Let\'s start now - we\'ll help you', 169 | 'on': 'on', 170 | 'January': 'January', 171 | 'February': 'February', 172 | 'March': 'March', 173 | 'April': 'April', 174 | 'May': 'May', 175 | 'June': 'June', 176 | 'July': 'July', 177 | 'August': 'August', 178 | 'September': 'September', 179 | 'October': 'October', 180 | 'November': 'November', 181 | 'December': 'December', 182 | 'Men Fashion': 'Men Fashion', 183 | 'Social': 'Social', 184 | 'Add to Cart': 'Add to Cart' 185 | }; -------------------------------------------------------------------------------- /layouts/account.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | 34 | 50 | -------------------------------------------------------------------------------- /layouts/blank.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 88 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 83 | 84 | 147 | -------------------------------------------------------------------------------- /layouts/error.vue: -------------------------------------------------------------------------------- 1 | 28 | 38 | 96 | -------------------------------------------------------------------------------- /middleware.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | integrations: { 3 | shopify: { 4 | location: '@vue-storefront/shopify-api/server', 5 | configuration: { 6 | api: { 7 | domain: process.env.SHOPIFY_DOMAIN, 8 | storefrontAccessToken: process.env.SHOPIFY_STOREFRONT_TOKEN, 9 | apiVersion: "2022-01" 10 | }, 11 | cms: { 12 | blogs: '/blogs', 13 | articles: '/articles' 14 | }, 15 | currency: 'USD', 16 | country: 'US' 17 | } 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /middleware/authenticated.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | // eslint-disable-next-line func-names 3 | export default (context) => { 4 | const appKey = context.$config.appKey; 5 | const token = context.$cookies.get(appKey + '_token'); 6 | // check if user not logged In 7 | if (!token) { 8 | context.app.router.push('/'); 9 | context.redirect('/'); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /modules/cms/build.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nuxt/types' 2 | 3 | const CMSBuildModule: Module = async function () { 4 | 5 | 6 | // this.nuxt.hook('generate:before', async () => { 7 | const middlewareConfig = await import(this.options.rootDir + '/middleware.config.js') 8 | const userConfig = middlewareConfig.integrations.shopify.configuration 9 | 10 | this.nuxt.options.publicRuntimeConfig.cms = userConfig?.cms ?? { blogs: '/blogs', articles: '/articles' } 11 | 12 | this.nuxt.options.router.extendRoutes = (routes, resolve) => { 13 | const prefixBlogs = userConfig?.cms?.blogs 14 | const prefixArticles = userConfig?.cms?.articles 15 | 16 | routes.push({ 17 | path: prefixBlogs, 18 | name: 'blogs', 19 | component: resolve(__dirname, './pages/blogs/index.vue') 20 | }) 21 | 22 | routes.push({ 23 | path: prefixBlogs + '/:handle', 24 | name: 'blogs-handle', 25 | component: resolve(__dirname, './pages/blogs/_handle.vue') 26 | }) 27 | 28 | routes.push({ 29 | path: prefixArticles + '/:handle', 30 | name: 'articles-handle', 31 | component: resolve(__dirname, './pages/articles/_handle.vue') 32 | }) 33 | 34 | return routes 35 | } 36 | // }); 37 | 38 | 39 | } 40 | 41 | export default CMSBuildModule -------------------------------------------------------------------------------- /modules/cms/enums/SortBy.ts: -------------------------------------------------------------------------------- 1 | export enum SortBy { 2 | Latest = 'latest', 3 | Oldest = 'oldest' 4 | } -------------------------------------------------------------------------------- /modules/cms/module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nuxt/types' 2 | 3 | const CMSModule: Module = async function () { 4 | const middlewareConfig = await import(this.options.rootDir + '/middleware.config.js') 5 | 6 | const userConfig = middlewareConfig.integrations.shopify.configuration 7 | 8 | this.options.publicRuntimeConfig = Object.assign(this.options.publicRuntimeConfig, { 9 | routes: userConfig?.routes ?? { blogs: '/blogs', articles: '/articles' } 10 | }) 11 | 12 | this.options.router.extendRoutes = (routes, resolve) => { 13 | const prefixBlogs = userConfig?.routes?.blogs 14 | const prefixArticles = userConfig?.routes?.articles 15 | 16 | routes.push({ 17 | path: prefixBlogs, 18 | name: 'blogs', 19 | component: resolve(__dirname, './pages/blogs/index.vue') 20 | }) 21 | 22 | routes.push({ 23 | path: prefixBlogs + '/:handle', 24 | name: 'blogs-handle', 25 | component: resolve(__dirname, './pages/blogs/_handle.vue') 26 | }) 27 | 28 | routes.push({ 29 | path: prefixArticles + '/:handle', 30 | name: 'articles-handle', 31 | component: resolve(__dirname, './pages/articles/_handle.vue') 32 | }) 33 | 34 | return routes 35 | } 36 | } 37 | 38 | export default CMSModule -------------------------------------------------------------------------------- /modules/cms/pages/articles/_handle.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /modules/cms/pages/blogs/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 27 | -------------------------------------------------------------------------------- /modules/cms/runtime.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nuxt/types' 2 | 3 | const CMSRuntimeModule: Module = async function () { 4 | const middlewareConfig = await import(this.options.rootDir + '/middleware.config.js') 5 | const userConfig = middlewareConfig.integrations.shopify.configuration 6 | 7 | this.nuxt.options.publicRuntimeConfig.cms = userConfig?.cms ?? { blogs: '/blogs', articles: '/articles' } 8 | } 9 | 10 | export default CMSRuntimeModule -------------------------------------------------------------------------------- /modules/integrations.js: -------------------------------------------------------------------------------- 1 | import { resolve, join } from 'path'; 2 | import { readdirSync, statSync } from 'fs'; 3 | 4 | export default function () { 5 | this.nuxt.hook('build:before', () => { 6 | const folder = resolve(__dirname, '../integrations'); 7 | const files = readdirSync(folder); 8 | 9 | files.forEach((file) => { 10 | const filename = resolve(folder, file); 11 | const stat = statSync(filename); 12 | 13 | if (stat.isFile()) { 14 | const { dst } = this.addTemplate(filename); 15 | this.options.plugins.push(join(this.options.buildDir, dst)); 16 | } 17 | }); 18 | }); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch'); 2 | import webpack from 'webpack'; 3 | const platformENV = process.env.NODE_ENV !== 'production' ? 'http' : 'https' 4 | const config = { 5 | server: { 6 | port: process.env.APP_PORT || 3001, 7 | host: '0.0.0.0' 8 | }, 9 | publicRuntimeConfig: { 10 | appKey: 'vsf2spcon', 11 | appVersion: Date.now(), 12 | middlewareUrl: `${platformENV}://${process.env.BASE_URL}/api/` 13 | }, 14 | privateRuntimeConfig: { 15 | storeURL: process.env.SHOPIFY_DOMAIN, 16 | storeToken: process.env.SHOPIFY_STOREFRONT_TOKEN 17 | }, 18 | serverMiddleware: [ 19 | { path: '/custom', handler: '~/server-middleware/custom-features.js' } 20 | ], 21 | head: { 22 | title: 'Shopify | Vue Storefront Next', 23 | meta: [ 24 | { charset: 'utf-8' }, 25 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 26 | { name: 'theme-color', content: '#5ece7b' }, 27 | { 28 | hid: 'description', 29 | name: 'description', 30 | content: process.env.npm_package_description || '' 31 | } 32 | ], 33 | link: [ 34 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, 35 | { 36 | rel: 'preconnect', 37 | href: 'https://fonts.gstatic.com', 38 | crossorigin: 'crossorigin' 39 | }, 40 | { 41 | rel: 'preload', 42 | href: 'https://fonts.googleapis.com/css?family=Raleway:300,400,400i,500,600,700|Roboto:300,300i,400,400i,500,700&display=swap', 43 | as: 'style' 44 | }, 45 | { 46 | rel: 'stylesheet', 47 | href: 'https://fonts.googleapis.com/css?family=Raleway:300,400,400i,500,600,700|Roboto:300,300i,400,400i,500,700&display=swap', 48 | media: 'print', 49 | onload: "this.media='all'" 50 | } 51 | ] 52 | }, 53 | loading: { color: '#fff' }, 54 | plugins: ["~/plugins/scrollToTop.client.js"], 55 | buildModules: [ 56 | // to core 57 | './modules/cms/build', 58 | '@nuxtjs/composition-api/module', 59 | '@nuxtjs/pwa', 60 | '@nuxtjs/device', 61 | '@nuxt/typescript-build', 62 | '@nuxtjs/style-resources', 63 | [ 64 | '@vue-storefront/nuxt', 65 | { 66 | useRawSource: { 67 | dev: ['@vue-storefront/shopify', '@vue-storefront/core'], 68 | prod: ['@vue-storefront/shopify', '@vue-storefront/core'] 69 | } 70 | } 71 | ], 72 | ['@vue-storefront/nuxt-theme'], 73 | [ 74 | '@vue-storefront/shopify/nuxt', 75 | { 76 | i18n: { 77 | useNuxtI18nConfig: true 78 | } 79 | } 80 | ] 81 | ], 82 | modules: [ 83 | '@nuxtjs/i18n', 84 | 'cookie-universal-nuxt', 85 | 'vue-scrollto/nuxt', 86 | '@vue-storefront/middleware/nuxt', 87 | '@nuxtjs/sitemap', 88 | './modules/cms/runtime', 89 | '@nuxt/image' 90 | ], 91 | device: { 92 | refreshOnResize: true 93 | }, 94 | i18n: { 95 | currency: 'USD', 96 | country: 'US', 97 | countries: [ 98 | { name: 'US', label: 'United States' }, 99 | { name: 'DE', label: 'Germany' } 100 | ], 101 | currencies: [ 102 | { name: 'EUR', label: 'Euro' }, 103 | { name: 'USD', label: 'Dollar' } 104 | ], 105 | locales: [ 106 | { 107 | code: 'en', 108 | alias: 'us', 109 | label: 'English', 110 | file: 'en.js', 111 | iso: 'en' 112 | }, 113 | { 114 | code: 'de', 115 | alias: 'de', 116 | label: 'German', 117 | file: 'de.js', 118 | iso: 'de' 119 | } 120 | ], 121 | defaultLocale: 'en', 122 | lazy: true, 123 | seo: true, 124 | langDir: 'lang/', 125 | vueI18n: { 126 | fallbackLocale: 'en', 127 | numberFormats: { 128 | en: { 129 | currency: { 130 | style: 'currency', 131 | currency: 'USD', 132 | currencyDisplay: 'symbol' 133 | }, 134 | decimal: { 135 | style: 'decimal', 136 | minimumFractionDigits: 2, 137 | maximumFractionDigits: 2 138 | }, 139 | percent: { 140 | style: 'percent', 141 | useGrouping: false 142 | } 143 | }, 144 | de: { 145 | currency: { 146 | style: 'currency', 147 | currency: 'EUR', 148 | currencyDisplay: 'symbol' 149 | }, 150 | decimal: { 151 | style: 'decimal', 152 | minimumFractionDigits: 2, 153 | maximumFractionDigits: 2 154 | }, 155 | percent: { 156 | style: 'percent', 157 | useGrouping: false 158 | } 159 | } 160 | } 161 | }, 162 | detectBrowserLanguage: { 163 | cookieKey: 'vsf-locale' 164 | } 165 | }, 166 | styleResources: { 167 | scss: [ 168 | require.resolve('@storefront-ui/shared/styles/_helpers.scss', { 169 | paths: [process.cwd()] 170 | }) 171 | ] 172 | }, 173 | build: { 174 | transpile: ['vee-validate/dist/rules', 'storefront-ui'], 175 | plugins: [ 176 | new webpack.DefinePlugin({ 177 | 'process.VERSION': JSON.stringify({ 178 | // eslint-disable-next-line global-require 179 | version: require('./package.json').version, 180 | lastCommit: process.env.LAST_COMMIT || '' 181 | }) 182 | }) 183 | ], 184 | extend(config) { 185 | config.resolve.extensions.push('.mjs'); 186 | 187 | config.module.rules.push({ 188 | test: /\.mjs$/, 189 | include: /node_modules/, 190 | type: 'javascript/auto' 191 | }); 192 | }, 193 | extractCSS: { 194 | ignoreOrder: true 195 | } 196 | }, 197 | pwa: { 198 | manifest: { 199 | name: 'VSF Next: Shopify APP', 200 | lang: 'en', 201 | shortName: 'SPVSF2', 202 | startUrl: '/', 203 | display: 'standalone', 204 | backgroundColor: '#5ece7b', 205 | themeColor: '#5ece7b', 206 | description: 'This is the Shopify PWA app for VSF Next', 207 | icons: [ 208 | { 209 | src: '/icons/android-icon-48x48.png', 210 | sizes: '48x48', 211 | type: 'image/png' 212 | }, 213 | { 214 | src: '/icons/android-icon-72x72.png', 215 | sizes: '72x72', 216 | type: 'image/png' 217 | }, 218 | { 219 | src: '/icons/android-icon-96x96.png', 220 | sizes: '96x96', 221 | type: 'image/png' 222 | }, 223 | { 224 | src: '/icons/android-icon-144x144.png', 225 | sizes: '144x144', 226 | type: 'image/png' 227 | }, 228 | { 229 | src: '/icons/android-icon-168x168.png', 230 | sizes: '168x168', 231 | type: 'image/png' 232 | }, 233 | { 234 | src: '/icons/android-icon-192x192.png', 235 | sizes: '192x192', 236 | type: 'image/png' 237 | }, 238 | { 239 | src: '/icons/android-icon-512x512.png', 240 | sizes: '512x512', 241 | type: 'image/png' 242 | } 243 | ] 244 | }, 245 | meta: { 246 | name: 'VSF Next: Shopify APP', 247 | author: 'Aureate labs', 248 | backgroundColor: '#5ece7b', 249 | description: 250 | 'This is the Shopify PWA app for VSF Next - Developed by Aureate labs', 251 | themeColor: '#5ece7b', 252 | ogHost: 'shopify-pwa.aureatelabs.com' 253 | }, 254 | icon: { 255 | iconSrc: 'src/static/android-icon-512x512.png' 256 | }, 257 | workbox: { 258 | offlineStrategy: 'StaleWhileRevalidate', 259 | runtimeCaching: [ 260 | { 261 | // Match any request that ends with .png, .jpg, .jpeg or .svg. 262 | urlPattern: /\.(?:png|jpg|jpeg|svg|woff|woff2)$/, 263 | // Apply a cache-first strategy. 264 | handler: 'CacheFirst', 265 | options: { 266 | // Use a custom cache name. 267 | cacheName: 'SPVSF2Assets', 268 | 269 | // Only cache 100 images. 270 | expiration: { 271 | maxEntries: 100 272 | } 273 | } 274 | }, 275 | { 276 | urlPattern: /^\/(?:(c)?(\/.*)?)$/, 277 | handler: 'StaleWhileRevalidate', 278 | strategyOptions: { 279 | cacheName: 'SPVSF2cached', 280 | cacheExpiration: { 281 | maxEntries: 200, 282 | maxAgeSeconds: 3600 283 | } 284 | } 285 | } 286 | ] 287 | } 288 | } 289 | }; 290 | 291 | export default config; 292 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue-storefront/shopify-theme", 3 | "version": "1.1.2", 4 | "scripts": { 5 | "dev": "nuxt", 6 | "build": "nuxt build --modern=client", 7 | "build:analyze": "nuxt build -a --modern=client", 8 | "start": "nuxt start --modern=client", 9 | "generate": "nuxt generate", 10 | "test": "jest", 11 | "test:e2e:open": "cypress open --config-file tests/e2e/cypress.json", 12 | "test:e2e:run": "cypress run --config-file tests/e2e/cypress.json", 13 | "test:e2e": "start-server-and-test dev http://localhost:3000 test:e2e:run" 14 | }, 15 | "dependencies": { 16 | "@nuxt/image": "0.6.0", 17 | "@nuxtjs/pwa": "^3.3.5", 18 | "@nuxtjs/sitemap": "^2.4.0", 19 | "@nuxtjs/style-resources": "^1.0.0", 20 | "@storefront-ui/vue": "^0.12.0", 21 | "@vue-storefront/shopify": "^1.1.2", 22 | "@vue-storefront/middleware": "2.5.6", 23 | "@vue-storefront/nuxt": "2.5.6", 24 | "@vue-storefront/nuxt-theme": "2.5.6", 25 | "cookie-universal-nuxt": "^2.1.3", 26 | "core-js": "^3.19.1", 27 | "isomorphic-fetch": "^2.2.1", 28 | "nuxt": "^2.13.3", 29 | "@nuxtjs/i18n": "^7.2.1", 30 | "vee-validate": "^3.2.3", 31 | "vue-scrollto": "^2.17.1", 32 | "@nuxtjs/device": "^2.1.0", 33 | "ipx": "0.9.1" 34 | }, 35 | "devDependencies": { 36 | "@nuxt/types": "^2.13.3", 37 | "@nuxt/typescript-build": "^2.0.0", 38 | "@vue/test-utils": "^1.0.0-beta.27", 39 | "babel-jest": "^24.1.0", 40 | "cypress": "^9.1.0", 41 | "ejs": "^3.0.2", 42 | "jest": "^27.4.3", 43 | "lint-staged": "^11.1.2", 44 | "start-server-and-test": "^1.14.0", 45 | "vue-jest": "^4.0.0-0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pages/Checkout.vue: -------------------------------------------------------------------------------- 1 | 32 | 73 | 74 | 111 | -------------------------------------------------------------------------------- /pages/Checkout/Billing.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/Checkout/Billing.vue -------------------------------------------------------------------------------- /pages/Checkout/Payment.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/Checkout/Payment.vue -------------------------------------------------------------------------------- /pages/Checkout/Shipping.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/Checkout/Shipping.vue -------------------------------------------------------------------------------- /pages/Checkout/ThankYou.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/Checkout/ThankYou.vue -------------------------------------------------------------------------------- /pages/ContactUs.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 33 | -------------------------------------------------------------------------------- /pages/Home.vue: -------------------------------------------------------------------------------- 1 | 52 | 219 | 220 | 322 | -------------------------------------------------------------------------------- /pages/MyAccount.vue: -------------------------------------------------------------------------------- 1 | 47 | 152 | 153 | 177 | -------------------------------------------------------------------------------- /pages/MyAccount/AdressBook.vue: -------------------------------------------------------------------------------- 1 | 92 | 232 | 233 | 335 | -------------------------------------------------------------------------------- /pages/MyAccount/BillingDetails.vue: -------------------------------------------------------------------------------- 1 | 72 | 137 | 138 | 215 | -------------------------------------------------------------------------------- /pages/MyAccount/LoyaltyCard.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/MyAccount/LoyaltyCard.vue -------------------------------------------------------------------------------- /pages/MyAccount/MyNewsletter.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 54 | 55 | 100 | -------------------------------------------------------------------------------- /pages/MyAccount/MyReviews.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/MyAccount/MyReviews.vue -------------------------------------------------------------------------------- /pages/MyAccount/ShippingDetails.vue: -------------------------------------------------------------------------------- 1 | 72 | 137 | 138 | 214 | -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /pages/ResetPassword.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/pages/ResetPassword.vue -------------------------------------------------------------------------------- /pages/TermsAndConditions.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /plugins/scrollToTop.client.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | Vue.mixin({ 4 | methods: { 5 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 6 | scrollToTop() { 7 | if (process.client) { 8 | window.scrollTo(0, 0); 9 | } 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /server-middleware/custom-features.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const app = require('express')(); 3 | const axios = require('axios'); 4 | module.exports = { 5 | path: '/', 6 | handler: app 7 | }; 8 | app.use(bodyParser.json()); 9 | 10 | app.get('/cart.js', (req, res) => { 11 | const options = { 12 | method: 'GET', 13 | url: `https://${process.env.SHOPIFY_DOMAIN}` + req.originalUrl 14 | }; 15 | axios.request(options).then((response) => { 16 | res.status(200).send(response.data); 17 | }).catch((error) => { 18 | console.error(error); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /static/error/error.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/error/error.webp -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/favicon.ico -------------------------------------------------------------------------------- /static/homepage/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/apple.png -------------------------------------------------------------------------------- /static/homepage/bannerA.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerA.webp -------------------------------------------------------------------------------- /static/homepage/bannerB.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerB.webp -------------------------------------------------------------------------------- /static/homepage/bannerC.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerC.webp -------------------------------------------------------------------------------- /static/homepage/bannerD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerD.png -------------------------------------------------------------------------------- /static/homepage/bannerE.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerE.webp -------------------------------------------------------------------------------- /static/homepage/bannerF.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerF.webp -------------------------------------------------------------------------------- /static/homepage/bannerG.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerG.webp -------------------------------------------------------------------------------- /static/homepage/bannerH.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/bannerH.webp -------------------------------------------------------------------------------- /static/homepage/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/google.png -------------------------------------------------------------------------------- /static/homepage/imageAd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageAd.webp -------------------------------------------------------------------------------- /static/homepage/imageAm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageAm.webp -------------------------------------------------------------------------------- /static/homepage/imageBd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageBd.webp -------------------------------------------------------------------------------- /static/homepage/imageBm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageBm.webp -------------------------------------------------------------------------------- /static/homepage/imageCd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageCd.webp -------------------------------------------------------------------------------- /static/homepage/imageCm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageCm.webp -------------------------------------------------------------------------------- /static/homepage/imageDd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageDd.webp -------------------------------------------------------------------------------- /static/homepage/imageDm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/imageDm.webp -------------------------------------------------------------------------------- /static/homepage/newsletter.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/newsletter.webp -------------------------------------------------------------------------------- /static/homepage/productA.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/productA.webp -------------------------------------------------------------------------------- /static/homepage/productB.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/productB.webp -------------------------------------------------------------------------------- /static/homepage/productC.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/homepage/productC.webp -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icon.png -------------------------------------------------------------------------------- /static/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /static/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /static/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /static/icons/android-icon-92x92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/android-icon-92x92.png -------------------------------------------------------------------------------- /static/icons/empty-cart.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/empty-cart.webp -------------------------------------------------------------------------------- /static/icons/facebook.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/facebook.webp -------------------------------------------------------------------------------- /static/icons/google.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/google.webp -------------------------------------------------------------------------------- /static/icons/langs/de.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/langs/de.webp -------------------------------------------------------------------------------- /static/icons/langs/en.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/langs/en.webp -------------------------------------------------------------------------------- /static/icons/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/logo.webp -------------------------------------------------------------------------------- /static/icons/pinterest.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/pinterest.webp -------------------------------------------------------------------------------- /static/icons/twitter.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/twitter.webp -------------------------------------------------------------------------------- /static/icons/youtube.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/icons/youtube.webp -------------------------------------------------------------------------------- /static/productpage/productA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/productpage/productA.jpg -------------------------------------------------------------------------------- /static/productpage/productB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/productpage/productB.jpg -------------------------------------------------------------------------------- /static/productpage/productM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/productpage/productM.jpg -------------------------------------------------------------------------------- /static/thankyou/bannerD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/thankyou/bannerD.png -------------------------------------------------------------------------------- /static/thankyou/bannerM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront/template-shopify/224b5f5aacbf509556e0f8e8fb21446f3e69ddbf/static/thankyou/bannerM.png -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | cartItems: [], 3 | cartTotal: 0, 4 | }) 5 | 6 | export const mutations = { 7 | SET_CARTTOTAL(state, totalItems){ 8 | state.cartTotal = totalItems 9 | }, 10 | SET_CART_ITEMS(state, cartItems){ 11 | state.cartItems = cartItems 12 | } 13 | } 14 | export const actions = { 15 | async nuxtServerInit({ commit }, context) { 16 | // Do something on server initialised 17 | } 18 | } -------------------------------------------------------------------------------- /tests/e2e/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "fixturesFolder": "tests/e2e/fixtures", 4 | "integrationFolder": "tests/e2e/integration", 5 | "pluginsFile": "tests/e2e/plugins/index.js", 6 | "supportFile": "tests/e2e/support/index.js", 7 | "video": false, 8 | "viewportHeight": 1080, 9 | "viewportWidth": 1920, 10 | "screenshotOnRunFailure": true, 11 | "screenshotsFolder": "tests/e2e/report/assets/screenshots" 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /tests/e2e/integration/search.spec.js: -------------------------------------------------------------------------------- 1 | describe('[shopify-theme] search header', () => { 2 | it ('returns result upon search', () => { 3 | cy.visit('http://localhost:3000') 4 | 5 | cy.get('.sf-search-bar__input').type('blouse') 6 | 7 | cy.get('.results-listing').children().should('have.length', 2) 8 | }) 9 | }) -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "esnext", 8 | "esnext.asynciterable", 9 | "dom" 10 | ], 11 | "esModuleInterop": true, 12 | "allowJs": true, 13 | "sourceMap": true, 14 | "strict": false, 15 | "noEmit": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "~/*": [ 19 | "./*" 20 | ], 21 | "@/*": [ 22 | "./*" 23 | ] 24 | }, 25 | "types": [ 26 | "@types/node", 27 | "@nuxt/types", 28 | "@nuxtjs/i18n", 29 | "@nuxt/image" 30 | ] 31 | }, 32 | "exclude": ["_theme"] 33 | } 34 | --------------------------------------------------------------------------------