├── src ├── entities │ ├── article │ │ └── card │ │ │ ├── types.ts │ │ │ ├── index.ts │ │ │ └── ui.vue │ ├── modal │ │ ├── index.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── store.ts │ ├── person │ │ ├── index.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── store.ts │ ├── product │ │ └── card │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── ui.vue │ ├── screen │ │ ├── index.ts │ │ └── model │ │ │ ├── types.ts │ │ │ ├── index.ts │ │ │ └── store.ts │ └── catalog │ │ ├── index.ts │ │ └── model │ │ ├── index.ts │ │ ├── types.ts │ │ └── store.ts ├── shims-vue.d.ts ├── pages │ └── home │ │ ├── index.ts │ │ └── ui.vue ├── shared │ ├── button │ │ ├── index.ts │ │ ├── button.spec.js │ │ └── ui.vue │ ├── logo │ │ ├── index.ts │ │ └── ui.vue │ ├── avatar │ │ ├── index.ts │ │ └── ui.vue │ ├── badge │ │ ├── index.ts │ │ └── ui.vue │ ├── content │ │ ├── index.ts │ │ └── ui.vue │ ├── field │ │ ├── index.ts │ │ └── ui.vue │ ├── rating │ │ ├── index.ts │ │ └── ui.vue │ ├── container │ │ ├── index.ts │ │ └── ui.vue │ ├── modal │ │ ├── index.ts │ │ └── ui.vue │ ├── typography │ │ ├── index.ts │ │ └── ui.vue │ └── icon │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue ├── features │ ├── like │ │ ├── index.ts │ │ └── ui.vue │ ├── header │ │ ├── navigation │ │ │ ├── index.ts │ │ │ └── ui.vue │ │ ├── dropdown-menu │ │ │ ├── index.ts │ │ │ └── ui.vue │ │ └── user-menu │ │ │ ├── model │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── store.ts │ │ │ ├── index.ts │ │ │ └── ui.vue │ ├── main-carousel │ │ ├── index.ts │ │ └── ui.vue │ ├── modals │ │ └── modal-sign-in │ │ │ ├── index.ts │ │ │ └── ui.vue │ └── special-offer │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue ├── widgets │ ├── footer │ │ ├── index.ts │ │ └── ui.vue │ ├── header │ │ ├── index.ts │ │ └── ui.vue │ ├── modals │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue │ ├── special-offers │ │ ├── index.ts │ │ └── ui.vue │ ├── article-cards │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue │ ├── bottom-tab-navigator │ │ ├── index.ts │ │ └── ui.vue │ ├── shops-map │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue │ └── product-cards │ │ ├── index.ts │ │ ├── types.ts │ │ └── ui.vue ├── app │ ├── helpers │ │ ├── index.ts │ │ └── formatCurrency.ts │ ├── logo.svg │ ├── base.css │ ├── stores │ │ └── counter.ts │ ├── router │ │ └── index.ts │ ├── App.vue │ └── main.css ├── assets │ ├── avatar.png │ ├── article-1.png │ ├── article-2.png │ ├── article-3.png │ ├── mock-map.png │ ├── product-1.png │ ├── slide-1-bg.png │ ├── special-offer-1.png │ ├── special-offer-2.png │ ├── slide-1-bg-mobile.png │ └── slide-1-bg-tablet.png └── main.ts ├── env.d.ts ├── public ├── favicon.ico └── footer-bg-pattern.png ├── .vscode └── extensions.json ├── .prettierrc.json ├── tsconfig.vitest.json ├── tsconfig.json ├── tsconfig.app.json ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── vitest.config.ts ├── Dockerfile ├── .eslintrc.cjs ├── index.html ├── .github └── workflows │ └── build.yml ├── package.json └── README.md /src/entities/article/card/types.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue'; -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/pages/home/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Home } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/button/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from './ui.vue'; -------------------------------------------------------------------------------- /src/shared/logo/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Logo } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/entities/modal/index.ts: -------------------------------------------------------------------------------- 1 | export { useModalStore } from './model'; 2 | 3 | -------------------------------------------------------------------------------- /src/entities/person/index.ts: -------------------------------------------------------------------------------- 1 | export { usePersonStore } from './model'; 2 | -------------------------------------------------------------------------------- /src/features/like/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Like } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/avatar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Avatar } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/badge/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Badge } from "./ui.vue"; 2 | -------------------------------------------------------------------------------- /src/shared/content/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Content } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/field/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Field } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/rating/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Rating } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/widgets/footer/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Footer } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/widgets/header/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Header } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/widgets/modals/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Modals } from "./ui.vue"; 2 | -------------------------------------------------------------------------------- /src/app/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export { formatCurrency } from "./formatCurrency"; 2 | -------------------------------------------------------------------------------- /src/entities/modal/model/index.ts: -------------------------------------------------------------------------------- 1 | export { useModalStore } from './store'; 2 | 3 | -------------------------------------------------------------------------------- /src/shared/container/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Container } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/shared/modal/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Modal } from './ui.vue'; 2 | 3 | -------------------------------------------------------------------------------- /src/shared/typography/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Typography } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/entities/article/card/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ArticleCard } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/entities/product/card/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProductCard } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/entities/screen/index.ts: -------------------------------------------------------------------------------- 1 | export { useScreenStore, type Platform } from './model'; 2 | -------------------------------------------------------------------------------- /src/features/header/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Navigation } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/features/main-carousel/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MainCarousel } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/widgets/special-offers/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SpecialOffers } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/entities/catalog/index.ts: -------------------------------------------------------------------------------- 1 | export { useCatalogStore, type CatalogSection } from './model'; 2 | -------------------------------------------------------------------------------- /src/features/header/dropdown-menu/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DropdownMenu } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/features/modals/modal-sign-in/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ModalSignIn } from "./ui.vue"; 2 | -------------------------------------------------------------------------------- /src/widgets/article-cards/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ArticleCards } from './ui.vue'; 2 | 3 | -------------------------------------------------------------------------------- /src/assets/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/avatar.png -------------------------------------------------------------------------------- /src/entities/modal/model/types.ts: -------------------------------------------------------------------------------- 1 | type ModalStore = string[]; 2 | 3 | export { type ModalStore }; 4 | -------------------------------------------------------------------------------- /src/widgets/bottom-tab-navigator/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BottomTabNavigator } from './ui.vue'; 2 | -------------------------------------------------------------------------------- /src/assets/article-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/article-1.png -------------------------------------------------------------------------------- /src/assets/article-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/article-2.png -------------------------------------------------------------------------------- /src/assets/article-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/article-3.png -------------------------------------------------------------------------------- /src/assets/mock-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/mock-map.png -------------------------------------------------------------------------------- /src/assets/product-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/product-1.png -------------------------------------------------------------------------------- /src/assets/slide-1-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/slide-1-bg.png -------------------------------------------------------------------------------- /public/footer-bg-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/public/footer-bg-pattern.png -------------------------------------------------------------------------------- /src/assets/special-offer-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/special-offer-1.png -------------------------------------------------------------------------------- /src/assets/special-offer-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/special-offer-2.png -------------------------------------------------------------------------------- /src/shared/icon/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Icon } from './ui.vue'; 2 | export { type IconType } from './types'; 3 | -------------------------------------------------------------------------------- /src/widgets/shops-map/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ShopsMap } from './ui.vue'; 2 | export { type Shop } from './types'; -------------------------------------------------------------------------------- /src/app/helpers/formatCurrency.ts: -------------------------------------------------------------------------------- 1 | const formatCurrency = (v: number) => v.toFixed(2); 2 | 3 | export { formatCurrency }; -------------------------------------------------------------------------------- /src/assets/slide-1-bg-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/slide-1-bg-mobile.png -------------------------------------------------------------------------------- /src/assets/slide-1-bg-tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etopipec/severyanochka/HEAD/src/assets/slide-1-bg-tablet.png -------------------------------------------------------------------------------- /src/entities/person/model/index.ts: -------------------------------------------------------------------------------- 1 | export { usePersonStore } from './store'; 2 | export { type Person } from './types'; 3 | -------------------------------------------------------------------------------- /src/entities/screen/model/types.ts: -------------------------------------------------------------------------------- 1 | type Platform = 'desktop' | 'tablet' | 'mobile'; 2 | 3 | export { type Platform }; 4 | -------------------------------------------------------------------------------- /src/widgets/modals/types.ts: -------------------------------------------------------------------------------- 1 | interface ModalsProps { 2 | items: string[]; 3 | } 4 | 5 | export { type ModalsProps }; 6 | -------------------------------------------------------------------------------- /src/entities/screen/model/index.ts: -------------------------------------------------------------------------------- 1 | export { useScreenStore } from './store'; 2 | export { type Platform } from './types'; 3 | -------------------------------------------------------------------------------- /src/widgets/shops-map/types.ts: -------------------------------------------------------------------------------- 1 | type Shop = { 2 | name: string; 3 | checked: boolean; 4 | }; 5 | 6 | export { type Shop }; -------------------------------------------------------------------------------- /src/entities/catalog/model/index.ts: -------------------------------------------------------------------------------- 1 | export { useCatalogStore } from './store'; 2 | export { type CatalogSection } from './types'; 3 | -------------------------------------------------------------------------------- /src/features/header/user-menu/model/index.ts: -------------------------------------------------------------------------------- 1 | export { useUserMenuStore } from './store'; 2 | export { type Menu } from './types'; 3 | -------------------------------------------------------------------------------- /src/features/special-offer/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SpecialOffer } from './ui.vue'; 2 | export { type Offer } from "./types"; 3 | -------------------------------------------------------------------------------- /src/entities/person/model/types.ts: -------------------------------------------------------------------------------- 1 | interface Person { 2 | name: string; 3 | avatar: string; 4 | } 5 | 6 | export { type Person }; 7 | -------------------------------------------------------------------------------- /src/widgets/product-cards/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProductCards } from './ui.vue'; 2 | export { type CardsProps, type Card } from './types'; 3 | -------------------------------------------------------------------------------- /src/features/header/user-menu/index.ts: -------------------------------------------------------------------------------- 1 | export { default as UserMenu } from './ui.vue'; 2 | export { useUserMenuStore, type Menu } from './model'; 3 | -------------------------------------------------------------------------------- /src/features/header/user-menu/model/types.ts: -------------------------------------------------------------------------------- 1 | type Menu = { 2 | label: string; 3 | link?: string; 4 | action?: string; 5 | }[]; 6 | 7 | export { type Menu }; 8 | -------------------------------------------------------------------------------- /src/features/special-offer/types.ts: -------------------------------------------------------------------------------- 1 | interface Offer { 2 | title: string; 3 | description?: string; 4 | background: string; 5 | } 6 | 7 | export { type Offer }; 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "Vue.vscode-typescript-vue-plugin", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "lib": [], 7 | "types": ["node", "jsdom"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/features/modals/modal-sign-in/ui.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /src/widgets/article-cards/types.ts: -------------------------------------------------------------------------------- 1 | interface ArticlesProps { 2 | info: { 3 | title: string; 4 | listLinkText: string; 5 | listLinkHref: string; 6 | }; 7 | items: []; 8 | } 9 | 10 | export { type ArticlesProps }; 11 | 12 | -------------------------------------------------------------------------------- /src/entities/catalog/model/types.ts: -------------------------------------------------------------------------------- 1 | interface CatalogStore { 2 | sections: CatalogSection[]; 3 | } 4 | 5 | interface CatalogSection { 6 | label: string; 7 | link: string; 8 | } 9 | 10 | export { type CatalogSection, type CatalogStore }; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | }, 10 | { 11 | "path": "./tsconfig.vitest.json" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/entities/product/card/types.ts: -------------------------------------------------------------------------------- 1 | interface Card { 2 | id: number; 3 | img: string; 4 | name: string; 5 | price: number; 6 | priceWithSale: number; 7 | count?: number; 8 | sale?: number; 9 | isLiked?: boolean; 10 | } 11 | 12 | export { type Card }; 13 | -------------------------------------------------------------------------------- /src/app/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/shared/icon/types.ts: -------------------------------------------------------------------------------- 1 | type IconType = 2 | 'menu' | 3 | 'favorite' | 4 | 'orders' | 5 | 'cart' | 6 | 'chevron' | 7 | 'insta' | 8 | 'vk' | 9 | 'fb' | 10 | 'ok' | 11 | 'phone' | 12 | 'login' | 13 | 'plus' | 14 | 'minus'; 15 | 16 | export { type IconType }; 17 | -------------------------------------------------------------------------------- /src/widgets/product-cards/types.ts: -------------------------------------------------------------------------------- 1 | import { type Card } from "@/entities/product/card/types"; 2 | 3 | interface CardsProps { 4 | info: { 5 | title: string; 6 | listLinkText: string; 7 | listLinkHref: string; 8 | }; 9 | items: Card[]; 10 | } 11 | 12 | export { type CardsProps, type Card }; 13 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/widgets/modals/ui.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/app/base.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | outline: none; 6 | font-family: 'Rubik', sans-serif; 7 | font-weight: 400; 8 | } 9 | 10 | body { 11 | background-color: var(--main-page-background); 12 | } 13 | 14 | ul, ol { 15 | list-style: none; 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | color: unset; 21 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import 'vue3-carousel/dist/carousel.css'; 2 | import '@/app/main.css'; 3 | 4 | import { createApp } from 'vue'; 5 | import { createPinia } from 'pinia'; 6 | 7 | import App from '@/app/App.vue'; 8 | import router from '@/app/router'; 9 | 10 | const app = createApp(App); 11 | 12 | app.use(createPinia()); 13 | app.use(router); 14 | 15 | app.mount('#app'); 16 | -------------------------------------------------------------------------------- /src/app/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useCounterStore = defineStore('counter', () => { 5 | const count = ref(0) 6 | const doubleCount = computed(() => count.value * 2) 7 | function increment() { 8 | count.value++ 9 | } 10 | 11 | return { count, doubleCount, increment } 12 | }) 13 | -------------------------------------------------------------------------------- /src/app/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import { Home } from '@/pages/home'; 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(import.meta.env.BASE_URL), 6 | routes: [ 7 | { 8 | path: '/', 9 | name: 'home', 10 | component: Home, 11 | }, 12 | ] 13 | }) 14 | 15 | export default router 16 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | "types": ["node"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/shared/modal/ui.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/shared/avatar/ui.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /src/shared/container/ui.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig, 7 | defineConfig({ 8 | test: { 9 | environment: 'jsdom', 10 | exclude: [...configDefaults.exclude, 'e2e/*'], 11 | root: fileURLToPath(new URL('./', import.meta.url)) 12 | } 13 | }) 14 | ) 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine3.20 as build-stage 2 | 3 | WORKDIR /app 4 | 5 | RUN apk update \ 6 | && apk add git \ 7 | && git clone https://github.com/vadimkaKharitonenko/severyanochka.git . \ 8 | && git pull 9 | 10 | COPY package*.json ./ 11 | RUN npm install 12 | COPY . . 13 | RUN npm run build-only 14 | 15 | FROM nginx:stable-alpine as production-stage 16 | COPY --from=build-stage /app/dist /usr/share/nginx/html 17 | EXPOSE 80 18 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | }, 15 | rules: { 16 | 'vue/multi-word-component-names': 'off', 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/features/header/user-menu/model/store.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from 'vue'; 2 | import { defineStore } from 'pinia'; 3 | import { type Menu } from './types'; 4 | 5 | export const useUserMenuStore = defineStore('user-menu', () => { 6 | let menu = reactive([ 7 | { label: 'Профиль', link: '/profile' }, 8 | { label: 'Выйти', action: 'logout' }, 9 | ]); 10 | 11 | const setMenu = (newMenu: Menu) => menu = newMenu; 12 | 13 | return { menu, setMenu }; 14 | }) 15 | -------------------------------------------------------------------------------- /src/shared/badge/ui.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /src/entities/screen/model/store.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | import { defineStore } from 'pinia'; 3 | import { type Platform } from './types'; 4 | 5 | export const useScreenStore = defineStore('screen', () => { 6 | const platform = ref('desktop'); 7 | 8 | const setPlatform = (width: number) => { 9 | if (width >= 1208) return platform.value = 'desktop'; 10 | if (width >= 768) return platform.value = 'tablet'; 11 | return platform.value = 'mobile'; 12 | }; 13 | 14 | return { setPlatform, platform }; 15 | }) 16 | -------------------------------------------------------------------------------- /src/entities/person/model/store.ts: -------------------------------------------------------------------------------- 1 | import { ref, reactive } from 'vue'; 2 | import { defineStore } from 'pinia'; 3 | import avatarIMG from '@/assets/avatar.png'; 4 | import { type Person } from './types'; 5 | 6 | export const usePersonStore = defineStore('person', () => { 7 | const isAuth = ref(true); 8 | const person = reactive({ 9 | name: 'Алексей', 10 | avatar: avatarIMG, 11 | }); 12 | 13 | const setIsAuth = (value: boolean) => isAuth.value = value; 14 | 15 | return { isAuth, person, setIsAuth }; 16 | }) 17 | -------------------------------------------------------------------------------- /src/shared/content/ui.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 27 | -------------------------------------------------------------------------------- /src/app/App.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Северяночка 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/features/like/ui.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /src/entities/modal/model/store.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue'; 2 | import { defineStore } from 'pinia'; 3 | import { type ModalStore } from './types'; 4 | 5 | export const useModalStore = defineStore('modal', () => { 6 | const modals = ref([]); 7 | 8 | const modalItems = computed(() => modals.value); 9 | 10 | const addModal = (modalName: string) => { 11 | if (modalItems.value.includes(modalName)) return; 12 | modals.value = [...modals.value, modalName]; 13 | }; 14 | 15 | const closeModal = (modalName: string) => { 16 | modals.value = modals.value.filter(modal => modal != modalName); 17 | }; 18 | 19 | return { modalItems, modals, addModal, closeModal }; 20 | }) 21 | 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | checkout: 8 | runs-on: severyanochka-label 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | - name: Install deps 13 | run: npm install 14 | unit-test: 15 | needs: [checkout] 16 | runs-on: severyanochka-label 17 | steps: 18 | - name: Run unit tests 19 | run: npm run test:unit 20 | type-check: 21 | needs: [unit-test] 22 | runs-on: severyanochka-label 23 | steps: 24 | - name: Run type check 25 | run: npm run type-check 26 | deploy: 27 | needs: [type-check] 28 | runs-on: severyanochka-label 29 | steps: 30 | - name: Clear cache 31 | run: docker builder prune -a 32 | - name: Build docker image 33 | run: docker build -t severyanochka . 34 | - name: Stop current fronted container 35 | run: docker stop frontend 36 | - name: Run container 37 | run: docker run -it -p 8080:80 --rm -d --name frontend severyanochka 38 | -------------------------------------------------------------------------------- /src/shared/button/button.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { expect, it, describe } from "vitest"; 3 | import { Button } from './index'; 4 | 5 | describe("Button", () => { 6 | it('Render text', async () => { 7 | const wrapper = mount(Button, { 8 | props: { 9 | color: 'primary', 10 | decoration: 'default', 11 | size: 'L', 12 | disabled: false, 13 | }, 14 | slots: { 15 | default: 'Button Text', 16 | } 17 | }); 18 | 19 | expect(wrapper.find('.button__text').text()).toBe('Button Text'); 20 | }); 21 | 22 | it('Emit click', async () => { 23 | const click = () => console.log('isClicked'); 24 | 25 | const wrapper = mount(Button, { 26 | props: { 27 | color: 'primary', 28 | decoration: 'default', 29 | size: 'L', 30 | disabled: false, 31 | click, 32 | }, 33 | slots: { 34 | default: 'Button Text', 35 | } 36 | }); 37 | 38 | await wrapper.find('.button').trigger('click'); 39 | expect(wrapper.emitted().click).toBeTruthy(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/entities/catalog/model/store.ts: -------------------------------------------------------------------------------- 1 | import { reactive, computed } from 'vue'; 2 | import { defineStore } from 'pinia'; 3 | import { type CatalogSection, type CatalogStore } from './types'; 4 | 5 | export const useCatalogStore = defineStore('catalog', () => { 6 | const catalog = reactive({ 7 | sections: [ 8 | { label: 'Молоко, сыр, яйцо', link: '/section' }, 9 | { label: 'Напитки', link: '/section' }, 10 | { label: 'Бакалея', link: '/section' }, 11 | { label: 'Непродовольственные товары', link: '/section' }, 12 | { label: 'Хлеб', link: '/section' }, 13 | { label: 'Кондитерские изделия', link: '/section' }, 14 | { label: 'Здоровое питание', link: '/section' }, 15 | { label: 'Детское питание', link: '/section' }, 16 | { label: 'Фрукты и овощи', link: '/section' }, 17 | { label: 'Чай, кофе', link: '/section' }, 18 | { label: 'Зоотовары', link: '/section' }, 19 | { label: 'Мясо, птица, колбаса', link: '/section' }, 20 | { label: 'Замороженные продукты', link: '/section' }, 21 | ] 22 | }); 23 | 24 | const catalogSections = computed(() => catalog.sections); 25 | 26 | const setCatalogSections = (sections: CatalogSection[]) => catalog.sections = sections; 27 | 28 | return { setCatalogSections, catalogSections }; 29 | }) 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "severyanochka", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "run-p type-check \"build-only {@}\" --", 8 | "preview": "vite preview", 9 | "test:unit": "vitest", 10 | "build-only": "vite build", 11 | "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", 12 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 13 | "format": "prettier --write src/" 14 | }, 15 | "dependencies": { 16 | "pinia": "^2.1.7", 17 | "vue": "^3.3.4", 18 | "vue-router": "^4.2.5", 19 | "vue3-carousel": "^0.3.1" 20 | }, 21 | "devDependencies": { 22 | "@rushstack/eslint-patch": "^1.3.3", 23 | "@tsconfig/node18": "^18.2.2", 24 | "@types/jsdom": "^21.1.3", 25 | "@types/node": "^18.18.5", 26 | "@vitejs/plugin-vue": "^4.4.0", 27 | "@vue/eslint-config-prettier": "^8.0.0", 28 | "@vue/eslint-config-typescript": "^12.0.0", 29 | "@vue/test-utils": "^2.4.1", 30 | "@vue/tsconfig": "^0.4.0", 31 | "eslint": "^8.49.0", 32 | "eslint-plugin-vue": "^9.17.0", 33 | "jsdom": "^22.1.0", 34 | "npm-run-all2": "^6.1.1", 35 | "prettier": "^3.0.3", 36 | "typescript": "~5.2.0", 37 | "vite": "^4.4.11", 38 | "vitest": "^0.34.6", 39 | "vue-tsc": "^1.8.19" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/widgets/shops-map/ui.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 38 | 39 | -------------------------------------------------------------------------------- /src/features/header/navigation/ui.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 31 | 32 | -------------------------------------------------------------------------------- /src/widgets/special-offers/ui.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /src/features/header/dropdown-menu/ui.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | 34 | -------------------------------------------------------------------------------- /src/widgets/article-cards/ui.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 30 | 31 | 70 | -------------------------------------------------------------------------------- /src/app/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | :root { 4 | /* colors */ 5 | --main-page-background: #FBF8EC; 6 | --main-background: #F4F4F2; 7 | --main-on-background: #291E0F; 8 | --main-surface: #FFF; 9 | --main-on-surface: #414141; 10 | --main-primary: #F63; 11 | --main-on-primary: #FFF; 12 | --main-secondary: #70C05B; 13 | --main-on-secondary: #FFF; 14 | --main-footer: #F9F4E2; 15 | --grayscale-lightest: #F3F2F1; 16 | --grayscale-light: #BFBFBF; 17 | --grayscale-hard: #8F8F8F; 18 | --grayscale-hardest: #606060; 19 | --pallete-success: #008C49; 20 | --pallete-info: #1CB9FC; 21 | --pallete-warning: #FCA21C; 22 | --pallete-error: #D80000; 23 | --peach-puff: #FCD5BA; 24 | --secondary-muted: #E5FFDE; 25 | 26 | /* shadows */ 27 | --shadow-default-xs: 1px 2px 4px 0px rgba(0, 0, 0, 0.10); 28 | --shadow-default-s: 2px 4px 8px 0px rgba(0, 0, 0, 0.10); 29 | --shadow-default-m: 4px 8px 16px 0px rgba(0, 0, 0, 0.10); 30 | --shadow-default-l: 8px 16px 32px 0px rgba(0, 0, 0, 0.10); 31 | --shadow-default-xl: 16px 32px 64px 0px rgba(0, 0, 0, 0.10); 32 | --shadow-primary-xs: 1px 2px 4px 0px rgba(255, 102, 51, 0.20); 33 | --shadow-primary-s: 2px 4px 8px 0px rgba(255, 102, 51, 0.20); 34 | --shadow-primary-m: 4px 8px 16px 0px rgba(255, 102, 51, 0.20); 35 | --shadow-primary-l: 8px 16px 32px 0px rgba(255, 102, 51, 0.20); 36 | --shadow-primary-xl: 16px 32px 64px 0px rgba(255, 102, 51, 0.20); 37 | --shadow-secondary-xs: 1px 2px 4px 0px rgba(112, 192, 91, 0.20); 38 | --shadow-secondary-s: 2px 4px 8px 0px rgba(112, 192, 91, 0.20); 39 | --shadow-secondary-m: 4px 8px 16px 0px rgba(112, 192, 91, 0.20); 40 | --shadow-secondary-l: 8px 16px 32px 0px rgba(112, 192, 91, 0.20); 41 | --shadow-secondary-xl: 16px 32px 64px 0px rgba(112, 192, 91, 0.20); 42 | 43 | /* platform */ 44 | --tablet: 1207px; 45 | --mobile: 767px; 46 | } 47 | -------------------------------------------------------------------------------- /src/shared/typography/ui.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /src/features/special-offer/ui.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 23 | 24 | -------------------------------------------------------------------------------- /src/shared/rating/ui.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # severyanochka 2 | ## demo - [bidlocode.tw1.ru](http://bidlocode.tw1.ru/) 3 | 4 | This template should help get you started developing with Vue 3 in Vite. 5 | 6 | ## Recommended IDE Setup 7 | 8 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 9 | 10 | ## Type Support for `.vue` Imports in TS 11 | 12 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. 13 | 14 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: 15 | 16 | 1. Disable the built-in TypeScript Extension 17 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette 18 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` 19 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. 20 | 21 | ## Customize configuration 22 | 23 | See [Vite Configuration Reference](https://vitejs.dev/config/). 24 | 25 | ## Project Setup 26 | 27 | ```sh 28 | npm install 29 | ``` 30 | 31 | ### Compile and Hot-Reload for Development 32 | 33 | ```sh 34 | npm run dev 35 | ``` 36 | 37 | ### Type-Check, Compile and Minify for Production 38 | 39 | ```sh 40 | npm run build 41 | ``` 42 | 43 | ### Run Unit Tests with [Vitest](https://vitest.dev/) 44 | 45 | ```sh 46 | npm run test:unit 47 | ``` 48 | 49 | ### Lint with [ESLint](https://eslint.org/) 50 | 51 | ```sh 52 | npm run lint 53 | ``` 54 | -------------------------------------------------------------------------------- /src/entities/article/card/ui.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 86 | -------------------------------------------------------------------------------- /src/features/main-carousel/ui.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 43 | 44 | -------------------------------------------------------------------------------- /src/widgets/product-cards/ui.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 47 | 48 | 96 | -------------------------------------------------------------------------------- /src/shared/button/ui.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 37 | 38 | 126 | -------------------------------------------------------------------------------- /src/features/header/user-menu/ui.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 66 | 67 | -------------------------------------------------------------------------------- /src/widgets/bottom-tab-navigator/ui.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 50 | 51 | -------------------------------------------------------------------------------- /src/shared/field/ui.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 49 | 50 | -------------------------------------------------------------------------------- /src/entities/product/card/ui.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 75 | 76 | -------------------------------------------------------------------------------- /src/widgets/header/ui.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 93 | 94 | -------------------------------------------------------------------------------- /src/pages/home/ui.vue: -------------------------------------------------------------------------------- 1 | 241 | 242 | 273 | 274 | 280 | -------------------------------------------------------------------------------- /src/widgets/footer/ui.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 62 | 63 | -------------------------------------------------------------------------------- /src/shared/icon/ui.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /src/shared/logo/ui.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 59 | 60 | --------------------------------------------------------------------------------