From 789d59b0c9f644a99decaf9ed8bffaf6a2a2ea0b Mon Sep 17 00:00:00 2001 From: Yakovenko Valeriia Date: Tue, 17 Mar 2026 14:12:02 +0100 Subject: [PATCH] fix: added page PageProductCardFull and Addresses --- bo/components.d.ts | 10 ++ bo/src/app.config.ts | 9 +- bo/src/components/TopBar.vue | 6 + .../admin}/ProductDetailView.vue | 0 .../admin}/ProductsView.vue | 0 bo/src/components/customer/PageAddresses.vue | 154 ++++++++++++++++ .../customer/PageProductCardFull.vue | 164 ++++++++++++++++++ .../components/ProductCustomization.vue | 16 ++ .../customer/components/ProductVariants.vue | 57 ++++++ bo/src/router/index.ts | 16 +- bo/src/stores/address.ts | 136 +++++++++++++++ 11 files changed, 556 insertions(+), 12 deletions(-) rename bo/src/{views/TranslateProduct => components/admin}/ProductDetailView.vue (100%) rename bo/src/{views/TranslateProduct => components/admin}/ProductsView.vue (100%) create mode 100644 bo/src/components/customer/PageAddresses.vue create mode 100644 bo/src/components/customer/PageProductCardFull.vue create mode 100644 bo/src/components/customer/components/ProductCustomization.vue create mode 100644 bo/src/components/customer/components/ProductVariants.vue create mode 100644 bo/src/stores/address.ts diff --git a/bo/components.d.ts b/bo/components.d.ts index 655bc41..d6733d5 100644 --- a/bo/components.d.ts +++ b/bo/components.d.ts @@ -16,8 +16,16 @@ declare module 'vue' { En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default'] En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default'] LangSwitch: typeof import('./src/components/inner/langSwitch.vue')['default'] + PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default'] + PageProductCardFull: typeof import('./src/components/customer/PageProductCardFull.vue')['default'] Pl_PrivacyPolicyView: typeof import('./src/components/terms/pl_PrivacyPolicyView.vue')['default'] Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default'] + ProductCardFull: typeof import('./src/components/customer/ProductCardFull.vue')['default'] + ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default'] + ProductDetailView: typeof import('./src/components/admin/ProductDetailView.vue')['default'] + ProductsView: typeof import('./src/components/admin/ProductsView.vue')['default'] + ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default'] + 'ProductСustomization': typeof import('./src/components/customer/components/ProductСustomization.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] ThemeSwitch: typeof import('./src/components/inner/themeSwitch.vue')['default'] @@ -32,6 +40,8 @@ declare module 'vue' { UFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default'] UIcon: typeof import('./node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default'] UInput: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default'] + UInputNumber: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue')['default'] + UModal: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default'] UPagination: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue')['default'] USelect: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Select.vue')['default'] USelectMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] diff --git a/bo/src/app.config.ts b/bo/src/app.config.ts index b9baa6c..6bcbbf3 100644 --- a/bo/src/app.config.ts +++ b/bo/src/app.config.ts @@ -22,6 +22,13 @@ export const uiOptions: NuxtUIOptions = { error: 'text-red-600!' }, }, + inputNumber: { + slots: { + base: 'text-(--black) dark:text-white border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! pt-2 px-1! w-auto!', + increment: 'border-0! pe-0! ps-0!', + decrement: 'border-0! pe-0! ps-0!' + }, + }, select: { slots: { base: 'w-full! cursor-pointer border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0!', @@ -43,7 +50,7 @@ export const uiOptions: NuxtUIOptions = { } }, - table: { + table: { slots: { base: 'border! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! bg-(--second-light) dark:bg-(--main-dark)', tr: 'border-b! border-(--border-light)! dark:border-(--border-dark)! outline-0! ring-0! text-(--black)! dark:text-white!', diff --git a/bo/src/components/TopBar.vue b/bo/src/components/TopBar.vue index eb632f7..10917ba 100644 --- a/bo/src/components/TopBar.vue +++ b/bo/src/components/TopBar.vue @@ -22,6 +22,12 @@ const authStore = useAuthStore() product detail + + ProductCardFull + + + Addresses +
diff --git a/bo/src/views/TranslateProduct/ProductDetailView.vue b/bo/src/components/admin/ProductDetailView.vue similarity index 100% rename from bo/src/views/TranslateProduct/ProductDetailView.vue rename to bo/src/components/admin/ProductDetailView.vue diff --git a/bo/src/views/TranslateProduct/ProductsView.vue b/bo/src/components/admin/ProductsView.vue similarity index 100% rename from bo/src/views/TranslateProduct/ProductsView.vue rename to bo/src/components/admin/ProductsView.vue diff --git a/bo/src/components/customer/PageAddresses.vue b/bo/src/components/customer/PageAddresses.vue new file mode 100644 index 0000000..02c6f92 --- /dev/null +++ b/bo/src/components/customer/PageAddresses.vue @@ -0,0 +1,154 @@ + + + diff --git a/bo/src/components/customer/PageProductCardFull.vue b/bo/src/components/customer/PageProductCardFull.vue new file mode 100644 index 0000000..3341aca --- /dev/null +++ b/bo/src/components/customer/PageProductCardFull.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/bo/src/components/customer/components/ProductCustomization.vue b/bo/src/components/customer/components/ProductCustomization.vue new file mode 100644 index 0000000..4470502 --- /dev/null +++ b/bo/src/components/customer/components/ProductCustomization.vue @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/bo/src/components/customer/components/ProductVariants.vue b/bo/src/components/customer/components/ProductVariants.vue new file mode 100644 index 0000000..feb1895 --- /dev/null +++ b/bo/src/components/customer/components/ProductVariants.vue @@ -0,0 +1,57 @@ + + + diff --git a/bo/src/router/index.ts b/bo/src/router/index.ts index a0fe04e..eed2bb9 100644 --- a/bo/src/router/index.ts +++ b/bo/src/router/index.ts @@ -5,8 +5,6 @@ import { getSettings } from './settings' import { useAuthStore } from '@/stores/auth' import Default from '@/layouts/default.vue' -// Helper: read the non-HTTPOnly is_authenticated cookie set by the backend. -// The backend sets it to "1" on login and removes it on logout. function isAuthenticated(): boolean { if (typeof document === 'undefined') return false return document.cookie.split('; ').some((c) => c === 'is_authenticated=1') @@ -31,8 +29,10 @@ const router = createRouter({ component: Default, children: [ { path: '', component: () => import('../views/RepoChartView.vue'), name: 'home' }, - { path: 'products', component: () => import('../views/customer/ProductsView.vue'), name: 'products' }, - { path: 'products-datail/', component: () => import('../views/customer/ProductDetailView.vue'), name: 'product-detail' }, + { path: 'products', component: () => import('../components/admin/ProductsView.vue'), name: 'products' }, + { path: 'products-datail/', component: () => import('../components/admin/ProductDetailView.vue'), name: 'product-detail' }, + { path: 'product-card-full/', component: () => import('../components/customer/PageProductCardFull.vue'), name: 'product-card-full' }, + { path: 'addresses', component: () => import('../components/customer/PageAddresses.vue'), name: 'addresses' }, ], }, { @@ -51,34 +51,28 @@ const router = createRouter({ ], }) -// Navigation guard: language handling + auth protection router.beforeEach((to, from, next) => { const locale = to.params.locale as string const localeLang = langs.find((x) => x.iso_code == locale) - // Check if the locale is valid if (locale && langs.length > 0) { const authStore = useAuthStore() - console.log(authStore.isAuthenticated,to, from) + console.log(authStore.isAuthenticated, to, from) // if() const validLocale = langs.find((l) => l.lang_code === locale) if (validLocale) { currentLang.value = localeLang - - // Auth guard: if the route does NOT have meta.guest = true, require authentication if (!to.meta?.guest && !isAuthenticated()) { return next({ name: 'login', params: { locale } }) } return next() } else if (locale) { - // Invalid locale - redirect to default language return next(`/${currentLang.value?.iso_code}${to.path.replace(`/${locale}`, '') || '/'}`) } } - // No locale in URL - redirect to default language if (!locale && to.path !== '/') { return next(`/${currentLang.value?.iso_code}${to.path}`) } diff --git a/bo/src/stores/address.ts b/bo/src/stores/address.ts new file mode 100644 index 0000000..d76c713 --- /dev/null +++ b/bo/src/stores/address.ts @@ -0,0 +1,136 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export interface AddressFormData { + street: string + zipCode: string + city: string + country: string +} + +export interface Address { + id: number + street: string + zipCode: string + city: string + country: string +} + +export const useAddressStore = defineStore('address', () => { + const addresses = ref([]) + const loading = ref(false) + const error = ref(null) + + const currentPage = ref(1) + const pageSize = 10 + const totalItems = computed(() => filteredAddresses.value.length) + const totalPages = computed(() => Math.ceil(totalItems.value / pageSize)) + + const searchQuery = ref('') + + function initMockData() { + addresses.value = [ + { + id: 1, + street: 'Main Street 123', + zipCode: '10-001', + city: 'New York', + country: 'United States' + }, + { + id: 2, + street: 'Oak Avenue 123', + zipCode: '90-001', + city: 'Los Angeles', + country: 'United States' + }, + { + id: 3, + street: 'Pine Road 123 ', + zipCode: '60-601', + city: 'Chicago', + country: 'United States' + } + ] + } + + const filteredAddresses = computed(() => { + if (!searchQuery.value) { + return addresses.value + } + + const query = searchQuery.value.toLowerCase() + + return addresses.value.filter(addr => + addr.street.toLowerCase().includes(query) || + addr.city.toLowerCase().includes(query) || + addr.country.toLowerCase().includes(query) || + addr.zipCode.toLowerCase().includes(query) + ) + }) + const paginatedAddresses = computed(() => { + const start = (currentPage.value - 1) * pageSize + const end = start + pageSize + return filteredAddresses.value.slice(start, end) + }) + + function getAddressById(id: number): Address | undefined { + return addresses.value.find(addr => addr.id === id) + } + + function addAddress(formData: AddressFormData): Address { + const now = new Date().toISOString() + + const newAddress: Address = { + id: Date.now(), + ...formData, + } + + addresses.value.push(newAddress) + + return newAddress + } + + function deleteAddress(id: number): boolean { + const index = addresses.value.findIndex(addr => addr.id === id) + if (index === -1) return false + + addresses.value.splice(index, 1) + + return true + } + + function setPage(page: number) { + currentPage.value = page + } + + function setSearchQuery(query: string) { + searchQuery.value = query + currentPage.value = 1 + } + + function resetPagination() { + currentPage.value = 1 + } + + initMockData() + + return { + addresses, + loading, + error, + currentPage, + pageSize, + totalItems, + totalPages, + searchQuery, + filteredAddresses, + paginatedAddresses, + getAddressById, + addAddress, + deleteAddress, + setPage, + setSearchQuery, + resetPagination + } +})