@crm-market/template-shared 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/scss/_variables.scss +3 -3
- package/assets/scss/components/_header.scss +1 -1
- package/assets/scss/style.css +4 -4
- package/components/AboutUs/StaticContent.vue +32 -0
- package/components/Cart/CartEmpty.vue +42 -0
- package/components/Cart/CartItem.vue +145 -0
- package/components/Cart/CartSummary.vue +57 -0
- package/components/Cart/ProductQuantity.vue +23 -13
- package/components/Cart/index.vue +68 -150
- package/components/Categories/index.vue +88 -291
- package/components/Checkout/AddressFields.vue +74 -0
- package/components/Checkout/CheckoutForm.vue +75 -0
- package/components/Checkout/CityAutocomplete.vue +68 -0
- package/components/Checkout/DeliveryMethodCard.vue +66 -0
- package/components/Checkout/DeliveryOptions.vue +148 -0
- package/components/Checkout/OrderSummary.vue +116 -0
- package/components/Checkout/PaymentOptions.vue +76 -0
- package/components/Checkout/PickupPoints.vue +46 -0
- package/components/Checkout/PromoCode.vue +166 -0
- package/components/Checkout/index.vue +235 -173
- package/components/Checkout/types.ts +45 -0
- package/components/Common/PageBanner.vue +22 -20
- package/components/Common/ProductCard.vue +139 -115
- package/components/Common/Services.vue +4 -4
- package/components/Contact/ContactForm.vue +44 -74
- package/components/Contact/ContactInfo.vue +35 -61
- package/components/FAQ/index.vue +6 -119
- package/components/Layout/Copyright.vue +11 -9
- package/components/Layout/Footer.vue +49 -136
- package/components/Layout/HeaderBar.vue +174 -0
- package/components/Layout/HeaderLogo.vue +27 -0
- package/components/Layout/MiddleHeader.vue +1 -228
- package/components/Layout/MiddleHeaderThree.vue +1 -203
- package/components/Layout/MiddleHeaderTwo.vue +1 -239
- package/components/Layout/NavLinks.vue +23 -0
- package/components/Layout/Navbar.vue +38 -151
- package/components/Layout/NavbarStyleFour.vue +1 -332
- package/components/Layout/NavbarStyleThree.vue +1 -188
- package/components/Layout/NavbarStyleTwo.vue +1 -108
- package/components/Layout/Preloader.vue +5 -12
- package/components/Layout/ResponsiveNavbar.vue +32 -418
- package/components/Layout/SitewideBanner.vue +285 -0
- package/components/Layout/TopHeader.vue +17 -83
- package/components/Layout/layoutData.ts +83 -0
- package/components/PrivacyPolicy/index.vue +6 -110
- package/components/ProductDetails/ProductDetailsTab.vue +31 -321
- package/components/ProductDetails/ProductGallery.vue +45 -0
- package/components/ProductDetails/ProductQuantity.vue +8 -14
- package/components/ProductDetails/RecentlyViewed.vue +53 -289
- package/components/ProductDetails/RelatedProducts.vue +64 -0
- package/components/ProductDetails/index.vue +90 -230
- package/components/Products/index.vue +78 -16
- package/components/TermsConditions/index.vue +6 -110
- package/components/Wishlist/index.vue +62 -88
- package/composables/useCart.ts +133 -81
- package/composables/useCategories.ts +66 -29
- package/composables/useCategoryImages.ts +45 -0
- package/composables/useCheckout.ts +302 -95
- package/composables/useContactForm.ts +57 -0
- package/composables/useHomeContent.ts +220 -0
- package/composables/useI18n.ts +12 -24
- package/composables/useJsonLd.ts +162 -0
- package/composables/useProducts.ts +244 -81
- package/composables/useRecent.ts +30 -0
- package/composables/useSanitize.ts +76 -0
- package/composables/useSiteConfig.ts +51 -45
- package/composables/useStaticPages.ts +48 -0
- package/composables/useStoreOrigin.ts +32 -0
- package/composables/useTemplateSections.ts +51 -3
- package/composables/useThemeVars.ts +113 -0
- package/composables/useWishlist.ts +72 -0
- package/layouts/default.vue +80 -19
- package/locales/en.json +255 -86
- package/locales/uk.json +251 -82
- package/modules/template-schema.ts +35 -0
- package/nuxt.config.ts +8 -11
- package/package.json +1 -1
- package/pages/about-us.vue +9 -9
- package/pages/cart.vue +0 -1
- package/pages/categories.vue +0 -1
- package/pages/category/[slug].vue +84 -0
- package/pages/checkout.vue +0 -1
- package/pages/contact.vue +6 -6
- package/pages/delivery.vue +12 -0
- package/pages/faq.vue +6 -6
- package/pages/order-success.vue +113 -0
- package/pages/privacy-policy.vue +6 -6
- package/pages/product/[slug].vue +82 -0
- package/pages/product-details.vue +14 -6
- package/pages/products.vue +12 -1
- package/pages/returns.vue +12 -0
- package/pages/terms-conditions.vue +6 -6
- package/pages/wishlist.vue +2 -2
- package/utils/seo-urls.ts +70 -0
- package/utils/store-origin.ts +36 -0
- package/components/AboutUs/AboutUsTuan.vue +0 -25
- package/components/AboutUs/Statistics.vue +0 -42
- package/components/AboutUs/SubscribeToTheNewsletter.vue +0 -57
- package/components/AddAddress/index.vue +0 -68
- package/components/BestSellers/Products.vue +0 -1562
- package/components/BestSellers/RecentlyViewed.vue +0 -304
- package/components/ChangePassword/index.vue +0 -69
- package/components/Common/DashboardNavigation.vue +0 -35
- package/components/Dashboard/RecentOrder.vue +0 -173
- package/components/Dashboard/index.vue +0 -77
- package/components/EditAddress/index.vue +0 -117
- package/components/EditProfile/index.vue +0 -95
- package/components/FeaturedProduct/FeaturedProducts.vue +0 -304
- package/components/FeaturedProduct/Products.vue +0 -1562
- package/components/ForgotPassword/index.vue +0 -49
- package/components/Layout/FooterTwo.vue +0 -159
- package/components/Layout/LocationOption.vue +0 -178
- package/components/Layout/RTLSwitchBtn.vue +0 -40
- package/components/Login/index.vue +0 -89
- package/components/MyAccount/index.vue +0 -154
- package/components/NewArrivals/Products.vue +0 -1969
- package/components/NewArrivals/RecentlyViewed.vue +0 -304
- package/components/OrderDetails/index.vue +0 -75
- package/components/OrderHistory/index.vue +0 -195
- package/components/Products/RecentlyViewed.vue +0 -302
- package/components/Register/index.vue +0 -83
- package/components/TrendingProducts/FeaturedProducts.vue +0 -304
- package/components/TrendingProducts/Products.vue +0 -1564
- package/components/Wishlist/ProductQuantity.vue +0 -29
- package/layouts/inner.vue +0 -70
- package/locales/ar.json +0 -234
- package/locales/ru.json +0 -234
- package/pages/address.vue +0 -11
- package/pages/change-password.vue +0 -11
- package/pages/dashboard.vue +0 -11
- package/pages/edit-address.vue +0 -11
- package/pages/edit-profile.vue +0 -11
- package/pages/forgot-password.vue +0 -11
- package/pages/login.vue +0 -11
- package/pages/my-account.vue +0 -11
- package/pages/order-details.vue +0 -11
- package/pages/order-history.vue +0 -11
- package/pages/register.vue +0 -11
package/locales/uk.json
CHANGED
|
@@ -28,52 +28,8 @@
|
|
|
28
28
|
"productsStep": "Кроки товарів",
|
|
29
29
|
"adminPage": "Панель керування"
|
|
30
30
|
},
|
|
31
|
-
"auth": {
|
|
32
|
-
"login": "Увійти",
|
|
33
|
-
"register": "Реєстрація",
|
|
34
|
-
"loginDescription": "Якщо у вас є обліковий запис, увійдіть за допомогою імені користувача або електронної пошти.",
|
|
35
|
-
"usernameOrEmail": "Ім'я користувача або електронна пошта",
|
|
36
|
-
"username": "Ім'я користувача",
|
|
37
|
-
"email": "Електронна пошта",
|
|
38
|
-
"password": "Пароль",
|
|
39
|
-
"yourPassword": "Ваш пароль",
|
|
40
|
-
"remember": "Запам'ятати",
|
|
41
|
-
"lostPassword": "Забули пароль?",
|
|
42
|
-
"noAccount": "Немає облікового запису?",
|
|
43
|
-
"haveAccount": "Вже є обліковий запис?",
|
|
44
|
-
"forgotPassword": "Забули пароль",
|
|
45
|
-
"enterEmail": "Введіть вашу електронну пошту",
|
|
46
|
-
"changePassword": "Змінити пароль",
|
|
47
|
-
"oldPassword": "Старий пароль",
|
|
48
|
-
"newPassword": "Новий пароль",
|
|
49
|
-
"confirmPassword": "Підтвердити пароль",
|
|
50
|
-
"savePassword": "Зберегти пароль",
|
|
51
|
-
"logOut": "Вийти"
|
|
52
|
-
},
|
|
53
31
|
"account": {
|
|
54
|
-
"
|
|
55
|
-
"account": "Обліковий запис",
|
|
56
|
-
"yourCart": "Ваш кошик",
|
|
57
|
-
"viewCart": "Переглянути кошик",
|
|
58
|
-
"checkout": "Оформлення замовлення",
|
|
59
|
-
"dashboard": "Панель керування",
|
|
60
|
-
"yourAccount": "Ваш обліковий запис",
|
|
61
|
-
"yourOrders": "Ваші замовлення",
|
|
62
|
-
"editProfile": "Редагувати профіль",
|
|
63
|
-
"editAddress": "Редагувати адресу",
|
|
64
|
-
"orderHistory": "Історія замовлень",
|
|
65
|
-
"orderDetails": "Деталі замовлення",
|
|
66
|
-
"address": "Адреса",
|
|
67
|
-
"addAddress": "Додати адресу",
|
|
68
|
-
"billingAddress": "Платіжна адреса",
|
|
69
|
-
"navigation": "Навігація",
|
|
70
|
-
"myAccount": "Мій обліковий запис",
|
|
71
|
-
"country": "Країна",
|
|
72
|
-
"stateCounty": "Область",
|
|
73
|
-
"zip": "Індекс",
|
|
74
|
-
"phone": "Телефон",
|
|
75
|
-
"email": "Електронна пошта",
|
|
76
|
-
"password": "Пароль"
|
|
32
|
+
"checkout": "Оформлення замовлення"
|
|
77
33
|
},
|
|
78
34
|
"product": {
|
|
79
35
|
"addToCart": "Додати до кошика",
|
|
@@ -117,7 +73,21 @@
|
|
|
117
73
|
"newArrivals": "Новинки",
|
|
118
74
|
"wishlist": "Список бажань",
|
|
119
75
|
"productDetails": "Деталі товару",
|
|
120
|
-
"product": "Товар"
|
|
76
|
+
"product": "Товар",
|
|
77
|
+
"outOfStock": "Немає в наявності",
|
|
78
|
+
"inWishlist": "У списку бажань",
|
|
79
|
+
"brand": "Бренд",
|
|
80
|
+
"tags": "Теги",
|
|
81
|
+
"description": "Опис",
|
|
82
|
+
"specification": "Характеристики",
|
|
83
|
+
"noDescription": "Опис відсутній",
|
|
84
|
+
"notFound": "Товар не знайдено",
|
|
85
|
+
"notFoundText": "Можливо, товар видалено або посилання застаріло.",
|
|
86
|
+
"toCatalog": "До каталогу",
|
|
87
|
+
"recentlyViewed": "Нещодавно переглянуті",
|
|
88
|
+
"related": "Схожі товари",
|
|
89
|
+
"moreInCategory": "Більше в категорії",
|
|
90
|
+
"saleUntil": "Акція до {date}"
|
|
121
91
|
},
|
|
122
92
|
"cart": {
|
|
123
93
|
"cart": "Кошик",
|
|
@@ -127,9 +97,29 @@
|
|
|
127
97
|
"coupon": "Купон",
|
|
128
98
|
"applyCoupon": "Застосувати купон",
|
|
129
99
|
"updatePrice": "Оновити ціну",
|
|
130
|
-
"proceedToCheckout": "
|
|
100
|
+
"proceedToCheckout": "Оформити замовлення",
|
|
131
101
|
"cartEmpty": "Кошик порожній",
|
|
132
|
-
"shipping": "Доставка"
|
|
102
|
+
"shipping": "Доставка",
|
|
103
|
+
"empty": "Кошик порожній",
|
|
104
|
+
"emptyText": "Додайте товари з каталогу, щоб оформити замовлення.",
|
|
105
|
+
"toCatalog": "До каталогу",
|
|
106
|
+
"itemsCount": "Кількість товарів",
|
|
107
|
+
"loading": "Завантаження…",
|
|
108
|
+
"minQty": "Мінімум: {n}",
|
|
109
|
+
"decrease": "Зменшити кількість",
|
|
110
|
+
"increase": "Збільшити кількість",
|
|
111
|
+
"itemsSum": "Сума товарів",
|
|
112
|
+
"discount": "Знижка",
|
|
113
|
+
"shippingAtCheckout": "розраховується при оформленні",
|
|
114
|
+
"grandTotal": "Разом",
|
|
115
|
+
"continue": "Продовжити покупки",
|
|
116
|
+
"clear": "Очистити кошик",
|
|
117
|
+
"clearConfirm": "Видалити всі товари?",
|
|
118
|
+
"yes": "Так, очистити",
|
|
119
|
+
"cancel": "Скасувати",
|
|
120
|
+
"suggested": "Можливо, вам сподобається",
|
|
121
|
+
"itemsDiscount": "Знижка на товари",
|
|
122
|
+
"promoHint": "Маєте промокод? Його можна застосувати під час оформлення замовлення."
|
|
133
123
|
},
|
|
134
124
|
"checkout": {
|
|
135
125
|
"billingDetails": "Платіжні дані",
|
|
@@ -146,12 +136,91 @@
|
|
|
146
136
|
"phone": "Телефон",
|
|
147
137
|
"phoneNumber": "Номер телефону",
|
|
148
138
|
"additionalInfo": "Додаткова інформація",
|
|
149
|
-
"notes": "
|
|
139
|
+
"notes": "Побажання щодо замовлення (необовʼязково)",
|
|
150
140
|
"yourOrder": "Ваше замовлення",
|
|
151
141
|
"paymentOption": "Спосіб оплати",
|
|
152
142
|
"cardPayment": "Оплата карткою",
|
|
153
143
|
"cashOnDelivery": "Оплата при отриманні",
|
|
154
|
-
"placeOrder": "
|
|
144
|
+
"placeOrder": "Підтвердити замовлення",
|
|
145
|
+
"contactTitle": "Контактні дані",
|
|
146
|
+
"fullName": "ПІБ",
|
|
147
|
+
"fullNamePlaceholder": "Прізвище Імʼя По батькові",
|
|
148
|
+
"emailOptional": "Email *",
|
|
149
|
+
"comment": "Коментар до замовлення",
|
|
150
|
+
"deliveryTitle": "Доставка",
|
|
151
|
+
"free": "безкоштовно",
|
|
152
|
+
"noDelivery": "Способи доставки не налаштовано. Зверніться до продавця.",
|
|
153
|
+
"city": "Місто",
|
|
154
|
+
"cityPlaceholder": "Введіть назву міста",
|
|
155
|
+
"searching": "Пошук міст…",
|
|
156
|
+
"warehouse": "Відділення",
|
|
157
|
+
"warehousePlaceholder": "Оберіть відділення (введіть № або вулицю)",
|
|
158
|
+
"loadingWarehouses": "Завантаження відділень…",
|
|
159
|
+
"pickupDefault": "Самовивіз зі складу продавця — адресу уточнить менеджер.",
|
|
160
|
+
"courierPrice": "Вартість кур'єрської доставки",
|
|
161
|
+
"address": "Адреса",
|
|
162
|
+
"addressPlaceholder": "Вулиця, будинок, квартира",
|
|
163
|
+
"onlinePayment": "Оплата карткою онлайн",
|
|
164
|
+
"placing": "Оформлюємо…",
|
|
165
|
+
"backToCart": "Повернутися до кошика",
|
|
166
|
+
"close": "Закрити",
|
|
167
|
+
"paymentsDisabledTitle": "Оплата тимчасово недоступна",
|
|
168
|
+
"paymentsDisabledText": "Онлайн-оплата зараз не працює. Оберіть оплату при отриманні або спробуйте пізніше.",
|
|
169
|
+
"errCart": "Кошик порожній",
|
|
170
|
+
"errName": "Вкажіть ПІБ",
|
|
171
|
+
"errPhone": "Вкажіть телефон у форматі +380XXXXXXXXX",
|
|
172
|
+
"errEmail": "Невірний формат email",
|
|
173
|
+
"errDelivery": "Оберіть спосіб доставки",
|
|
174
|
+
"errCity": "Вкажіть місто",
|
|
175
|
+
"errWarehouse": "Оберіть відділення зі списку",
|
|
176
|
+
"errPickup": "Оберіть пункт самовивозу",
|
|
177
|
+
"errAddress": "Вкажіть адресу доставки",
|
|
178
|
+
"errPostcode": "Вкажіть поштовий індекс (5 цифр)",
|
|
179
|
+
"searchingShort": "Шукаємо…",
|
|
180
|
+
"nothingFound": "Нічого не знайдено",
|
|
181
|
+
"pickCityFirst": "Спершу оберіть місто",
|
|
182
|
+
"street": "Вулиця",
|
|
183
|
+
"streetPlaceholder": "Назва вулиці",
|
|
184
|
+
"house": "Будинок",
|
|
185
|
+
"apartment": "Квартира",
|
|
186
|
+
"postalHint": "5 цифр, його можна знайти на сайті Укрпошти",
|
|
187
|
+
"phoneHint": "Формат +380 XX XXX XX XX",
|
|
188
|
+
"emailHint": "На цю адресу надійде підтвердження замовлення",
|
|
189
|
+
"errEmailRequired": "Вкажіть email",
|
|
190
|
+
"codDesc": "Оплата під час отримання",
|
|
191
|
+
"onlineDesc": "Visa / Mastercard",
|
|
192
|
+
"chooseProvider": "Платіжний сервіс",
|
|
193
|
+
"sumGoods": "Товари",
|
|
194
|
+
"sumDelivery": "Доставка",
|
|
195
|
+
"sumTotal": "Разом",
|
|
196
|
+
"byCarrierTariffs": "за тарифами перевізника",
|
|
197
|
+
"errGeneric": "Не вдалося оформити замовлення. Спробуйте ще раз.",
|
|
198
|
+
"successPageTitle": "Замовлення",
|
|
199
|
+
"checkingPayment": "Перевіряємо оплату…",
|
|
200
|
+
"waitFewSeconds": "Зачекайте кілька секунд.",
|
|
201
|
+
"orderNumber": "Номер замовлення",
|
|
202
|
+
"toHome": "На головну",
|
|
203
|
+
"successTitle": "Дякуємо за замовлення!",
|
|
204
|
+
"successText": "Менеджер звʼяжеться з вами для підтвердження.",
|
|
205
|
+
"paidTitle": "Оплату отримано, дякуємо!",
|
|
206
|
+
"paidText": "Найближчим часом з вами звʼяжеться менеджер для підтвердження.",
|
|
207
|
+
"failedTitle": "Оплата не пройшла",
|
|
208
|
+
"failedText": "Спробуйте оплатити ще раз або зверніться до менеджера.",
|
|
209
|
+
"pendingText": "Замовлення прийнято. Оплата ще обробляється, менеджер звʼяжеться з вами.",
|
|
210
|
+
"loadingOptions": "Завантажуємо способи доставки та оплати…",
|
|
211
|
+
"promoCode": "Промокод",
|
|
212
|
+
"promoPlaceholder": "Введіть промокод",
|
|
213
|
+
"promoApply": "Застосувати",
|
|
214
|
+
"promoRemove": "Прибрати промокод",
|
|
215
|
+
"promoApplied": "Промокод {code}",
|
|
216
|
+
"promoInvalid": "Промокод недійсний",
|
|
217
|
+
"promoError": "Не вдалося перевірити промокод. Спробуйте ще раз.",
|
|
218
|
+
"promoRemoved": "Промокод більше не діє для цього кошика — його прибрано.",
|
|
219
|
+
"freeShipping": "Безкоштовна доставка",
|
|
220
|
+
"itemsDiscount": "Знижка на товари",
|
|
221
|
+
"orderDiscount": "Знижка",
|
|
222
|
+
"orderTotal": "Сума замовлення",
|
|
223
|
+
"promoRequiresBuyer": "Код перевіримо для вашого email/телефону"
|
|
155
224
|
},
|
|
156
225
|
"footer": {
|
|
157
226
|
"gotQuestion": "Є запитання? Телефонуйте цілодобово",
|
|
@@ -194,41 +263,141 @@
|
|
|
194
263
|
"enterName": "Введіть ваше ім'я",
|
|
195
264
|
"enterEmail": "Введіть вашу електронну пошту"
|
|
196
265
|
},
|
|
197
|
-
"order": {
|
|
198
|
-
"orderNumber": "Номер замовлення",
|
|
199
|
-
"orderCreatedAt": "Замовлення створено",
|
|
200
|
-
"backHistory": "Назад до історії",
|
|
201
|
-
"status": "Статус",
|
|
202
|
-
"paid": "Оплачено",
|
|
203
|
-
"pending": "Очікує",
|
|
204
|
-
"failed": "Невдале",
|
|
205
|
-
"trash": "Кошик",
|
|
206
|
-
"order": "Замовлення"
|
|
207
|
-
},
|
|
208
266
|
"common": {
|
|
209
|
-
"save": "Зберегти",
|
|
210
|
-
"saveChange": "Зберегти зміни",
|
|
211
|
-
"edit": "Редагувати",
|
|
212
|
-
"default": "За замовчуванням",
|
|
213
|
-
"clickHere": "Натисніть тут",
|
|
214
|
-
"changeImage": "Змінити зображення",
|
|
215
|
-
"location": "Місцезнаходження",
|
|
216
|
-
"searchLocation": "Знайдіть ваше місцезнаходження",
|
|
217
|
-
"selectLocation": "Оберіть місцезнаходження",
|
|
218
|
-
"deliverProducts": "Куди доставити ваші товари?",
|
|
219
267
|
"privacyPolicy": "Політика конфіденційності",
|
|
220
|
-
"termsConditions": "Умови та положення"
|
|
221
|
-
"helpCenter": "Центр допомоги"
|
|
268
|
+
"termsConditions": "Умови та положення"
|
|
222
269
|
},
|
|
223
|
-
"
|
|
224
|
-
"
|
|
225
|
-
"
|
|
226
|
-
|
|
227
|
-
|
|
270
|
+
"layout": {
|
|
271
|
+
"currency": "грн",
|
|
272
|
+
"top": {
|
|
273
|
+
"promo": "Акція"
|
|
274
|
+
},
|
|
275
|
+
"header": {
|
|
276
|
+
"allCategories": "Усі категорії",
|
|
277
|
+
"noCategories": "Категорії відсутні",
|
|
278
|
+
"searchPlaceholder": "Пошук товарів...",
|
|
279
|
+
"search": "Пошук",
|
|
280
|
+
"hotline": "Гаряча лінія",
|
|
281
|
+
"wishlist": "Список бажань",
|
|
282
|
+
"cart": "Кошик",
|
|
283
|
+
"remove": "Видалити з кошика",
|
|
284
|
+
"cartEmpty": "Кошик порожній",
|
|
285
|
+
"total": "Разом",
|
|
286
|
+
"viewCart": "Кошик",
|
|
287
|
+
"checkout": "Оформити"
|
|
288
|
+
},
|
|
289
|
+
"menu": {
|
|
290
|
+
"home": "Головна",
|
|
291
|
+
"catalog": "Каталог",
|
|
292
|
+
"categories": "Категорії",
|
|
293
|
+
"about": "Про нас",
|
|
294
|
+
"contact": "Контакти",
|
|
295
|
+
"toggle": "Меню",
|
|
296
|
+
"close": "Закрити",
|
|
297
|
+
"browseCategories": "Категорії товарів"
|
|
298
|
+
},
|
|
299
|
+
"footer": {
|
|
300
|
+
"categories": "Категорії",
|
|
301
|
+
"info": "Інформація",
|
|
302
|
+
"contacts": "Контакти",
|
|
303
|
+
"privacy": "Політика конфіденційності",
|
|
304
|
+
"terms": "Умови використання"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
"home": {
|
|
308
|
+
"favorite": "В обране",
|
|
309
|
+
"addToCart": "У кошик",
|
|
310
|
+
"inCart": "У кошику",
|
|
311
|
+
"outOfStock": "Немає в наявності",
|
|
312
|
+
"seeAll": "Усі",
|
|
313
|
+
"seeAllDeals": "Усі пропозиції",
|
|
314
|
+
"noProducts": "Товарів поки немає",
|
|
315
|
+
"specialsTitle": "Спеціальні пропозиції",
|
|
316
|
+
"topSellingTitle": "Новинки",
|
|
317
|
+
"dealsTitle": "Вигідні пропозиції",
|
|
318
|
+
"endOfOfferTitle": "Закінчення акції",
|
|
319
|
+
"shopNow": "Дивитись",
|
|
320
|
+
"popularCategories": "Популярні категорії",
|
|
321
|
+
"noCategories": "Категорій поки немає",
|
|
322
|
+
"bannerDefaultButton": "До каталогу",
|
|
323
|
+
"specialOffer": "Спеціальна пропозиція",
|
|
324
|
+
"loadError": "Не вдалося завантажити дані. Спробуйте пізніше."
|
|
325
|
+
},
|
|
326
|
+
"catalog": {
|
|
327
|
+
"filters": "Фільтри",
|
|
328
|
+
"title": "Категорії",
|
|
329
|
+
"subtitle": "Оберіть розділ, щоб переглянути товари",
|
|
330
|
+
"productsCount": "{count} товарів",
|
|
331
|
+
"allProducts": "Усі товари",
|
|
332
|
+
"empty": "Категорій поки немає",
|
|
333
|
+
"loadError": "Не вдалося завантажити категорії. Спробуйте пізніше.",
|
|
334
|
+
"retry": "Спробувати ще раз",
|
|
335
|
+
"subcategories": "Підкатегорії"
|
|
336
|
+
},
|
|
337
|
+
"pages": {
|
|
338
|
+
"about": {
|
|
339
|
+
"title": "Про нас"
|
|
340
|
+
},
|
|
341
|
+
"delivery": {
|
|
342
|
+
"title": "Доставка"
|
|
343
|
+
},
|
|
344
|
+
"returns": {
|
|
345
|
+
"title": "Повернення"
|
|
346
|
+
},
|
|
347
|
+
"faq": {
|
|
348
|
+
"title": "Питання та відповіді"
|
|
349
|
+
},
|
|
350
|
+
"privacy": {
|
|
351
|
+
"title": "Політика конфіденційності"
|
|
352
|
+
},
|
|
353
|
+
"terms": {
|
|
354
|
+
"title": "Умови використання"
|
|
355
|
+
},
|
|
356
|
+
"contact": {
|
|
357
|
+
"title": "Контакти",
|
|
358
|
+
"connect": "Зв'яжіться з нами",
|
|
359
|
+
"addressHours": "Адреса та графік",
|
|
360
|
+
"socials": "Ми в соцмережах",
|
|
361
|
+
"formTitle": "Напишіть нам",
|
|
362
|
+
"formDesc": "Залиште повідомлення — ми зв'яжемося з вами найближчим часом.",
|
|
363
|
+
"name": "Ваше ім'я",
|
|
364
|
+
"namePh": "Введіть ваше ім'я",
|
|
365
|
+
"contact": "Телефон або email",
|
|
366
|
+
"contactPh": "+380... або name@example.com",
|
|
367
|
+
"message": "Повідомлення",
|
|
368
|
+
"messagePh": "Напишіть повідомлення",
|
|
369
|
+
"send": "Надіслати",
|
|
370
|
+
"sending": "Надсилаємо...",
|
|
371
|
+
"sent": "Дякуємо! Ваше повідомлення надіслано, ми зв'яжемося з вами найближчим часом.",
|
|
372
|
+
"errRequired": "Заповніть ім'я, контакт і повідомлення.",
|
|
373
|
+
"errContact": "Вкажіть коректний телефон або email.",
|
|
374
|
+
"errSend": "Не вдалося надіслати повідомлення. Спробуйте ще раз.",
|
|
375
|
+
"email": "Email",
|
|
376
|
+
"emailPh": "you@example.com",
|
|
377
|
+
"phone": "Телефон",
|
|
378
|
+
"phonePh": "+380 __ ___ __ __",
|
|
379
|
+
"errEmail": "Вкажіть коректний email",
|
|
380
|
+
"errPhone": "Вкажіть коректний номер телефону"
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
"wishlist": {
|
|
384
|
+
"title": "Список бажань",
|
|
385
|
+
"loading": "Завантаження...",
|
|
386
|
+
"empty": "Список бажань порожній",
|
|
387
|
+
"emptyText": "Додавайте товари, натискаючи на серце, щоб повернутися до них пізніше.",
|
|
388
|
+
"toCatalog": "До каталогу",
|
|
389
|
+
"product": "Товар",
|
|
390
|
+
"removeItem": "Прибрати зі списку бажань"
|
|
228
391
|
},
|
|
229
|
-
"
|
|
230
|
-
"
|
|
231
|
-
"
|
|
232
|
-
"
|
|
392
|
+
"seo": {
|
|
393
|
+
"productsTitle": "Каталог товарів",
|
|
394
|
+
"categoriesTitle": "Категорії товарів",
|
|
395
|
+
"productsDescription": "Каталог магазину {site}: усі товари з цінами та швидкою доставкою.",
|
|
396
|
+
"categoriesDescription": "Усі категорії магазину {site} — оберіть потрібний розділ каталогу.",
|
|
397
|
+
"aboutDescription": "Про магазин {site}: хто ми і чим займаємось.",
|
|
398
|
+
"contactDescription": "Контакти магазину {site}: телефон, email, адреса та форма зворотного звʼязку.",
|
|
399
|
+
"deliveryDescription": "Умови та способи доставки замовлень у магазині {site}.",
|
|
400
|
+
"returnsDescription": "Умови повернення та обміну товарів у магазині {site}.",
|
|
401
|
+
"categoryDefault": "купуйте з доставкою в нашому інтернет-магазині."
|
|
233
402
|
}
|
|
234
403
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineNuxtModule } from '@nuxt/kit';
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Схема «Налаштувань шаблону» живе у репозиторії шаблону: <rootDir>/template.schema.json
|
|
7
|
+
* (шлях можна перевизначити змінною TEMPLATE_SCHEMA_PATH на етапі збірки).
|
|
8
|
+
* Файл вшивається в серверний бандл як віртуальний модуль '#template-schema'
|
|
9
|
+
* і віддається маршрутом GET /_template/schema.json — звідти staff імпортує схему в CRM.
|
|
10
|
+
*/
|
|
11
|
+
export default defineNuxtModule({
|
|
12
|
+
meta: { name: 'template-schema' },
|
|
13
|
+
setup(_options, nuxt) {
|
|
14
|
+
const file = process.env.TEMPLATE_SCHEMA_PATH
|
|
15
|
+
? resolve(process.env.TEMPLATE_SCHEMA_PATH)
|
|
16
|
+
: resolve(nuxt.options.rootDir, 'template.schema.json');
|
|
17
|
+
|
|
18
|
+
nuxt.hook('nitro:config', (config) => {
|
|
19
|
+
config.virtual = config.virtual || {};
|
|
20
|
+
config.virtual['#template-schema'] = () => {
|
|
21
|
+
if (!existsSync(file)) return 'export default null';
|
|
22
|
+
try {
|
|
23
|
+
// Валідуємо JSON на збірці: битий файл не повинен ламати сервер вітрини
|
|
24
|
+
return `export default ${JSON.stringify(JSON.parse(readFileSync(file, 'utf8')))}`;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.warn(`[template-schema] Некоректний JSON у ${file}:`, (e as Error).message);
|
|
27
|
+
return 'export default null';
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// dev: зміна файлу схеми перезбирає сервер
|
|
33
|
+
if (nuxt.options.dev) nuxt.options.watch.push(file);
|
|
34
|
+
},
|
|
35
|
+
});
|
package/nuxt.config.ts
CHANGED
|
@@ -9,21 +9,16 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
9
9
|
export default defineNuxtConfig({
|
|
10
10
|
ssr: true,
|
|
11
11
|
|
|
12
|
-
app: {
|
|
13
|
-
pageTransition: {
|
|
14
|
-
name: "fade",
|
|
15
|
-
mode: "out-in",
|
|
16
|
-
},
|
|
17
|
-
layoutTransition: {
|
|
18
|
-
name: "slide",
|
|
19
|
-
mode: "out-in",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
|
|
23
12
|
runtimeConfig: {
|
|
24
13
|
// SSR-only: внутрішній URL бекенду (Docker мережа або localhost)
|
|
25
14
|
apiBaseInternal: process.env.API_BASE_INTERNAL || '',
|
|
15
|
+
// SSR-only: магазин, зафіксований змінною середовища (деплой «один магазин на контейнер»).
|
|
16
|
+
// Якщо задано — конфіг магазину береться за ними, а не за Host запиту.
|
|
17
|
+
storeSubdomain: process.env.STORE_SUBDOMAIN || '',
|
|
18
|
+
storeDomain: process.env.STORE_DOMAIN || '',
|
|
26
19
|
public: {
|
|
20
|
+
// Базовий домен платформи для адрес <subdomain>.<baseDomain>
|
|
21
|
+
storeBaseDomain: process.env.STORE_BASE_DOMAIN || 'crm-market.com.ua',
|
|
27
22
|
apiBase: process.env.API_BASE_URL || 'http://localhost:3001/api',
|
|
28
23
|
templateType: process.env.TEMPLATE_TYPE || 'electronics',
|
|
29
24
|
},
|
|
@@ -47,6 +42,8 @@ export default defineNuxtConfig({
|
|
|
47
42
|
modules: [
|
|
48
43
|
// Auto-imports composables, utils, components для npm-пакету
|
|
49
44
|
resolve(__dirname, 'modules/auto-imports'),
|
|
45
|
+
// template.schema.json з кореня шаблону → GET /_template/schema.json
|
|
46
|
+
resolve(__dirname, 'modules/template-schema'),
|
|
50
47
|
(
|
|
51
48
|
_options: any,
|
|
52
49
|
nuxt: {
|
package/package.json
CHANGED
package/pages/about-us.vue
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { t } = useI18n();
|
|
3
|
+
const { pageHtml } = useStaticPages();
|
|
4
|
+
useHead({ title: () => t('pages.about.title') });
|
|
5
|
+
</script>
|
|
6
|
+
|
|
1
7
|
<template>
|
|
2
8
|
<div>
|
|
3
|
-
<CommonPageBanner :pageTitle="t('
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<AboutUsSubscribeToTheNewsletter />
|
|
9
|
+
<CommonPageBanner :pageTitle="t('pages.about.title')" />
|
|
10
|
+
<AboutUsStaticContent :html="pageHtml('about')" />
|
|
11
|
+
<ContactInfo />
|
|
7
12
|
</div>
|
|
8
13
|
</template>
|
|
9
|
-
|
|
10
|
-
<script setup lang="ts">
|
|
11
|
-
definePageMeta({ layout: 'inner' });
|
|
12
|
-
const { t } = useI18n();
|
|
13
|
-
</script>
|
package/pages/cart.vue
CHANGED
package/pages/categories.vue
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<CommonPageBanner v-if="category" :pageTitle="category.name" :crumbs="crumbs" />
|
|
4
|
+
<!-- key: при переході між категоріями каталог перевантажується з новим фільтром -->
|
|
5
|
+
<Products v-if="category" :key="category.id" :categoryId="category.id" />
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import { computed } from 'vue';
|
|
11
|
+
import { htmlToText } from '../../composables/useProducts';
|
|
12
|
+
|
|
13
|
+
const { t } = useI18n();
|
|
14
|
+
const route = useRoute();
|
|
15
|
+
const { categories, getCategoryById } = useCategories();
|
|
16
|
+
|
|
17
|
+
const slug = computed(() => String(route.params.slug || ''));
|
|
18
|
+
const categoryId = computed(() => parseIdFromSlug(slug.value));
|
|
19
|
+
|
|
20
|
+
// Категорії вантажить плагін site-init (SSR); на всякий випадок догружаємо, якщо порожньо
|
|
21
|
+
if (!categories.value.length) {
|
|
22
|
+
await useCategories().fetchCategories().catch(() => []);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const category = computed(() => (categoryId.value ? getCategoryById(categoryId.value) : undefined));
|
|
26
|
+
|
|
27
|
+
if (!category.value || category.value.isActive === false) {
|
|
28
|
+
if (import.meta.server) setResponseStatus(useRequestEvent()!, 404);
|
|
29
|
+
throw createError({ statusCode: 404, statusMessage: 'Категорію не знайдено', fatal: false });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Назву змінили — переспрямовуємо на канонічну адресу (301)
|
|
33
|
+
const canonical = categoryPath(category.value);
|
|
34
|
+
if (decodeURIComponent(route.path) !== canonical) {
|
|
35
|
+
await navigateTo({ path: canonical, query: route.query }, { redirectCode: 301, replace: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Хлібні крихти: Головна → (батьківська категорія) → Категорія
|
|
39
|
+
const crumbs = computed(() => {
|
|
40
|
+
const parent = category.value?.parentId ? getCategoryById(category.value.parentId) : undefined;
|
|
41
|
+
return parent ? [{ label: parent.name, to: categoryPath(parent) }] : [{ label: t('nav.products'), to: '/products' }];
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const { canonicalUrl } = useStoreOrigin();
|
|
45
|
+
const { siteName } = useSiteConfig();
|
|
46
|
+
const { fetchPage } = useProducts();
|
|
47
|
+
|
|
48
|
+
const title = computed(() => {
|
|
49
|
+
const c = category.value;
|
|
50
|
+
if (!c) return '';
|
|
51
|
+
return c.metaTitle || (siteName.value ? `${c.name} — ${siteName.value}` : c.name);
|
|
52
|
+
});
|
|
53
|
+
const description = computed(() => {
|
|
54
|
+
const c = category.value;
|
|
55
|
+
if (!c) return '';
|
|
56
|
+
const text = c.metaDescription || htmlToText(c.description) ||
|
|
57
|
+
`${c.name}${siteName.value ? ` — ${siteName.value}` : ''}: ${t('seo.categoryDefault')}`;
|
|
58
|
+
return text.length > 160 ? text.slice(0, 157).replace(/\s+\S*$/, '') + '...' : text;
|
|
59
|
+
});
|
|
60
|
+
const canonicalHref = computed(() =>
|
|
61
|
+
category.value ? canonicalUrl(categoryPath(category.value), route.query.page as string) : '');
|
|
62
|
+
|
|
63
|
+
useHead({
|
|
64
|
+
title: () => title.value,
|
|
65
|
+
link: () => (canonicalHref.value ? [{ key: 'canonical', rel: 'canonical', href: canonicalHref.value }] : []),
|
|
66
|
+
});
|
|
67
|
+
useSeoMeta({
|
|
68
|
+
description: () => description.value,
|
|
69
|
+
ogTitle: () => title.value,
|
|
70
|
+
ogDescription: () => description.value,
|
|
71
|
+
ogUrl: () => canonicalHref.value || undefined,
|
|
72
|
+
ogType: 'website',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Перші товари категорії для ItemList (SSR)
|
|
76
|
+
const { data: listing } = await useAsyncData(
|
|
77
|
+
() => `category-ld-${categoryId.value}`,
|
|
78
|
+
async () => (categoryId.value ? (await fetchPage({ categoryId: categoryId.value, limit: 20 })).items : []),
|
|
79
|
+
{ watch: [categoryId] },
|
|
80
|
+
);
|
|
81
|
+
const parentCategory = computed(() =>
|
|
82
|
+
category.value?.parentId ? getCategoryById(category.value.parentId) : undefined);
|
|
83
|
+
useCategoryJsonLd(category, parentCategory, () => listing.value);
|
|
84
|
+
</script>
|
package/pages/checkout.vue
CHANGED
package/pages/contact.vue
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { t } = useI18n();
|
|
3
|
+
useHead({ title: () => t('pages.contact.title') });
|
|
4
|
+
</script>
|
|
5
|
+
|
|
1
6
|
<template>
|
|
2
7
|
<div>
|
|
3
|
-
<CommonPageBanner :pageTitle="t('contact.
|
|
8
|
+
<CommonPageBanner :pageTitle="t('pages.contact.title')" />
|
|
4
9
|
<ContactInfo />
|
|
5
10
|
<ContactForm />
|
|
6
11
|
</div>
|
|
7
12
|
</template>
|
|
8
|
-
|
|
9
|
-
<script setup lang="ts">
|
|
10
|
-
definePageMeta({ layout: 'inner' });
|
|
11
|
-
const { t } = useI18n();
|
|
12
|
-
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { t } = useI18n();
|
|
3
|
+
const { pageHtml } = useStaticPages();
|
|
4
|
+
useHead({ title: () => t('pages.delivery.title') });
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div>
|
|
9
|
+
<CommonPageBanner :pageTitle="t('pages.delivery.title')" />
|
|
10
|
+
<AboutUsStaticContent :html="pageHtml('delivery')" />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
package/pages/faq.vue
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { t } = useI18n();
|
|
3
|
+
useHead({ title: () => t('pages.faq.title') });
|
|
4
|
+
</script>
|
|
5
|
+
|
|
1
6
|
<template>
|
|
2
7
|
<div>
|
|
3
|
-
<CommonPageBanner :pageTitle="t('
|
|
8
|
+
<CommonPageBanner :pageTitle="t('pages.faq.title')" />
|
|
4
9
|
<FAQ />
|
|
5
10
|
</div>
|
|
6
11
|
</template>
|
|
7
|
-
|
|
8
|
-
<script setup lang="ts">
|
|
9
|
-
definePageMeta({ layout: 'inner' });
|
|
10
|
-
const { t } = useI18n();
|
|
11
|
-
</script>
|