diff --git a/spa/package-lock.json b/spa/package-lock.json index 320b702..b809f09 100644 --- a/spa/package-lock.json +++ b/spa/package-lock.json @@ -11,6 +11,8 @@ "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.11", "ofetch": "^1.4.1", + "pinia": "^3.0.3", + "swiper": "^11.2.10", "vue": "^3.5.17", "vue-router": "^4.5.1", "vue-tg": "^0.9.0-beta.10" @@ -1163,6 +1165,30 @@ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, "node_modules/@vue/reactivity": { "version": "3.5.17", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", @@ -1251,6 +1277,15 @@ "postcss": "^8.1.0" } }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/browserslist": { "version": "4.25.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", @@ -1314,6 +1349,21 @@ "node": ">=18" } }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -1482,6 +1532,24 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -1749,6 +1817,12 @@ "node": ">= 18" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -1816,6 +1890,12 @@ "ufo": "^1.5.4" } }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1834,6 +1914,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pinia": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -1869,6 +1979,12 @@ "dev": true, "license": "MIT" }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, "node_modules/rollup": { "version": "4.44.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", @@ -1917,6 +2033,46 @@ "node": ">=0.10.0" } }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/swiper": { + "version": "11.2.10", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz", + "integrity": "sha512-RMeVUUjTQH+6N3ckimK93oxz6Sn5la4aDlgPzB+rBrG/smPdCTicXyhxa+woIpopz+jewEloiEE3lKo1h9w2YQ==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/tailwindcss": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", diff --git a/spa/package.json b/spa/package.json index 98bf9ce..e852fd5 100644 --- a/spa/package.json +++ b/spa/package.json @@ -12,6 +12,8 @@ "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.11", "ofetch": "^1.4.1", + "pinia": "^3.0.3", + "swiper": "^11.2.10", "vue": "^3.5.17", "vue-router": "^4.5.1", "vue-tg": "^0.9.0-beta.10" diff --git a/spa/src/App.vue b/spa/src/App.vue index 9ffedc8..64fc69e 100644 --- a/spa/src/App.vue +++ b/spa/src/App.vue @@ -1,21 +1,21 @@ - + diff --git a/spa/src/ShoppingCart.js b/spa/src/ShoppingCart.js new file mode 100644 index 0000000..db4b314 --- /dev/null +++ b/spa/src/ShoppingCart.js @@ -0,0 +1,63 @@ +import {reactive} from "vue"; + +class ShoppingCart { + constructor() { + this.items = reactive([]); + this.storageKey = 'shoppingCart'; + this.storage = Telegram.WebApp.DeviceStorage; + + this._load() + .then(items => { + this.items = items; + console.log(items); + }) + .catch(error => console.log(error)); + } + + async addItem(productId, productName, quantity, options = {}) { + this.items.push({ productId: productId, productName: productName, quantity, options }); + this._save(this.items); + } + + has(productId) { + const item = this.getItem(productId); + console.log(item); + return this.getItem(productId) !== null; + } + + getItem(productId) { + return this.items.find(item => item.productId === productId) ?? null; + } + + getItems() { + return this.items; + } + + clear() { + this.storage.deleteItem(this.storageKey) + } + + async _load() { + return new Promise((resolve, reject) => { + this.storage.getItem(this.storageKey, (error, value) => { + if (error) { + console.error(error); + reject([]); + } + + try { + resolve(value ? JSON.parse(value) : []); + } catch (error) { + console.error(error); + reject([]); + } + }); + }); + } + + _save(items) { + this.storage.setItem(this.storageKey, JSON.stringify(items)); + } +} + +export default ShoppingCart; diff --git a/spa/src/components/DevConsoleOverlay.vue b/spa/src/components/DevConsoleOverlay.vue new file mode 100644 index 0000000..409aa9a --- /dev/null +++ b/spa/src/components/DevConsoleOverlay.vue @@ -0,0 +1,93 @@ + + + + [{{ log.type.toUpperCase() }}] {{ log.message }} + + + + + \ No newline at end of file diff --git a/spa/src/components/ProductImageSwiper.vue b/spa/src/components/ProductImageSwiper.vue new file mode 100644 index 0000000..4cb4edd --- /dev/null +++ b/spa/src/components/ProductImageSwiper.vue @@ -0,0 +1,74 @@ + + + + + + + + + + + + diff --git a/spa/src/main.js b/spa/src/main.js index 1726c15..8af99c4 100644 --- a/spa/src/main.js +++ b/spa/src/main.js @@ -3,9 +3,12 @@ import App from './App.vue' import './style.css' import { VueTelegramPlugin } from 'vue-tg'; import { router } from './router'; +import { createPinia } from 'pinia'; +const pinia = createPinia(); const app = createApp(App); app + .use(pinia) .use(router) .use(VueTelegramPlugin); app.mount('#app'); @@ -20,6 +23,4 @@ theme.onChange(() => { }); document.documentElement.setAttribute('data-theme', theme.colorScheme.value); -tg.ready(); - - +window.Telegram.WebApp.ready(); diff --git a/spa/src/router.js b/spa/src/router.js index a95edc3..d824521 100644 --- a/spa/src/router.js +++ b/spa/src/router.js @@ -3,12 +3,14 @@ import Home from './views/Home.vue'; import Product from './views/Product.vue'; import CategoriesList from "./views/CategoriesList.vue"; import ProductsList from "./views/ProductsList.vue"; +import Cart from "./views/Cart.vue"; const routes = [ {path: '/', name: 'home', component: Home}, {path: '/product/:id', name: 'product.show', component: Product}, {path: '/categories', name: 'categories', component: CategoriesList}, {path: '/category/:id', name: 'category.show', component: ProductsList}, + {path: '/cart', name: 'cart.show', component: Cart}, ]; export const router = createRouter({ diff --git a/spa/src/stores/CartStore.js b/spa/src/stores/CartStore.js new file mode 100644 index 0000000..bce632e --- /dev/null +++ b/spa/src/stores/CartStore.js @@ -0,0 +1,27 @@ +import {defineStore} from "pinia"; + +export const useCartStore = defineStore('cart', { + state: () => ({ + items: [], + }), + + actions: { + getProduct(productId) { + return this.items.find(item => parseInt(item.productId) === parseInt(productId)) ?? null; + }, + + hasProduct(productId) { + return this.getProduct(productId) !== null; + }, + + addProduct(productId, productName, price, quantity = 1, options = []) { + this.items.push({ + productId: productId, + productName: productName, + price: price, + quantity: quantity, + options: options, + }); + }, + }, +}); \ No newline at end of file diff --git a/spa/src/views/Cart.vue b/spa/src/views/Cart.vue new file mode 100644 index 0000000..37b62ae --- /dev/null +++ b/spa/src/views/Cart.vue @@ -0,0 +1,69 @@ + + + Корзина + + + + + {{ item.productName }} + {{ item.price }} + + − + {{ item.quantity }} + + + + + + + Удалить + + + + + + Ваша корзина пуста + + + + + \ No newline at end of file diff --git a/spa/src/views/Product.vue b/spa/src/views/Product.vue index b086d2f..9b47969 100644 --- a/spa/src/views/Product.vue +++ b/spa/src/views/Product.vue @@ -44,22 +44,59 @@
{{ item.price }}
Ваша корзина пуста