@crm-market/template-shared 1.0.5 → 1.0.7
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/composables/useCategories.ts +10 -7
- package/composables/useCheckout.ts +0 -2
- package/composables/useProducts.ts +0 -2
- package/composables/useSiteConfig.ts +29 -20
- package/package.json +1 -1
- package/plugins/site-init.ts +33 -0
- package/utils/image.ts +0 -1
- package/plugins/site-init.client.ts +0 -29
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { useState } from '#imports';
|
|
3
3
|
import { getApiBaseUrl } from '../utils/api';
|
|
4
4
|
import { useSiteConfig } from './useSiteConfig';
|
|
5
5
|
|
|
@@ -15,16 +15,19 @@ interface Category {
|
|
|
15
15
|
order?: number;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const categories = ref<Category[]>([]);
|
|
19
|
-
const loading = ref(false);
|
|
20
|
-
const error = ref<string | null>(null);
|
|
21
|
-
|
|
22
18
|
export const useCategories = () => {
|
|
23
|
-
|
|
19
|
+
// useState — SSR-safe, дані передаються клієнту через гідрацію
|
|
20
|
+
const categories = useState<Category[]>('categories', () => []);
|
|
21
|
+
const loading = useState<boolean>('categories-loading', () => false);
|
|
22
|
+
const error = useState<string | null>('categories-error', () => null);
|
|
23
|
+
|
|
24
24
|
const { publicApiToken } = useSiteConfig();
|
|
25
25
|
|
|
26
26
|
// Fetch all categories
|
|
27
27
|
const fetchCategories = async () => {
|
|
28
|
+
// Якщо вже завантажено — не перезавантажуємо
|
|
29
|
+
if (categories.value.length > 0) return categories.value;
|
|
30
|
+
|
|
28
31
|
loading.value = true;
|
|
29
32
|
error.value = null;
|
|
30
33
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ref } from 'vue';
|
|
2
|
-
import { useRuntimeConfig } from '#imports';
|
|
3
2
|
import { useCart } from './useCart';
|
|
4
3
|
import { useSiteConfig } from './useSiteConfig';
|
|
5
4
|
|
|
@@ -33,7 +32,6 @@ const error = ref<string | null>(null);
|
|
|
33
32
|
const orderId = ref<string | null>(null);
|
|
34
33
|
|
|
35
34
|
export const useCheckout = () => {
|
|
36
|
-
const config = useRuntimeConfig();
|
|
37
35
|
const { cart, clearCart } = useCart();
|
|
38
36
|
const { organizationId } = useSiteConfig();
|
|
39
37
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ref, computed } from 'vue';
|
|
2
|
-
import { useRuntimeConfig } from '#imports';
|
|
3
2
|
import { getApiBaseUrl } from '../utils/api';
|
|
4
3
|
import { useSiteConfig } from './useSiteConfig';
|
|
5
4
|
|
|
@@ -33,7 +32,6 @@ const error = ref<string | null>(null);
|
|
|
33
32
|
const currentProduct = ref<Product | null>(null);
|
|
34
33
|
|
|
35
34
|
export const useProducts = () => {
|
|
36
|
-
const config = useRuntimeConfig();
|
|
37
35
|
const { publicApiToken } = useSiteConfig();
|
|
38
36
|
|
|
39
37
|
// Fetch all products with optional filters
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { useState, useRequestHeaders } from '#imports';
|
|
3
3
|
import { getApiBaseUrl } from '../utils/api';
|
|
4
4
|
|
|
5
5
|
interface ColorScheme {
|
|
@@ -55,29 +55,38 @@ interface SiteConfig {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const siteConfig = ref<SiteConfig | null>(null);
|
|
59
|
-
const loading = ref(false);
|
|
60
|
-
const error = ref<string | null>(null);
|
|
61
|
-
|
|
62
58
|
export const useSiteConfig = () => {
|
|
63
|
-
|
|
64
|
-
const
|
|
59
|
+
// useState — SSR-safe, дані передаються клієнту через гідрацію
|
|
60
|
+
const siteConfig = useState<SiteConfig | null>('site-config', () => null);
|
|
61
|
+
const loading = useState<boolean>('site-config-loading', () => false);
|
|
62
|
+
const error = useState<string | null>('site-config-error', () => null);
|
|
65
63
|
|
|
66
|
-
//
|
|
64
|
+
// Отримати subdomain з host — працює і на SSR, і на клієнті
|
|
67
65
|
const getSubdomain = () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
let hostname = '';
|
|
67
|
+
|
|
68
|
+
if (import.meta.server) {
|
|
69
|
+
// SSR: отримуємо host з request headers
|
|
70
|
+
const headers = useRequestHeaders(['host', 'x-forwarded-host']);
|
|
71
|
+
hostname = (headers['x-forwarded-host'] || headers['host'] || '').split(':')[0];
|
|
72
|
+
} else {
|
|
73
|
+
// Client: з window.location
|
|
74
|
+
hostname = window.location.hostname;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const parts = hostname.split('.');
|
|
78
|
+
// Формат: subdomain.domain.com або subdomain.domain.com.ua
|
|
79
|
+
if (parts.length >= 3) {
|
|
80
|
+
return parts[0];
|
|
75
81
|
}
|
|
76
82
|
return null;
|
|
77
83
|
};
|
|
78
84
|
|
|
79
85
|
// Fetch site configuration from API
|
|
80
86
|
const fetchConfig = async (subdomain?: string) => {
|
|
87
|
+
// Якщо вже завантажено — не перезавантажуємо
|
|
88
|
+
if (siteConfig.value && !subdomain) return siteConfig.value;
|
|
89
|
+
|
|
81
90
|
loading.value = true;
|
|
82
91
|
error.value = null;
|
|
83
92
|
|
|
@@ -113,7 +122,7 @@ export const useSiteConfig = () => {
|
|
|
113
122
|
|
|
114
123
|
const { colors, fonts, customCSS } = siteConfig.value.customization;
|
|
115
124
|
|
|
116
|
-
if (
|
|
125
|
+
if (import.meta.client && colors) {
|
|
117
126
|
const root = document.documentElement;
|
|
118
127
|
|
|
119
128
|
if (colors.primary) {
|
|
@@ -132,7 +141,7 @@ export const useSiteConfig = () => {
|
|
|
132
141
|
}
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
if (
|
|
144
|
+
if (import.meta.client && fonts) {
|
|
136
145
|
const root = document.documentElement;
|
|
137
146
|
|
|
138
147
|
if (fonts.headingFont) {
|
|
@@ -150,7 +159,7 @@ export const useSiteConfig = () => {
|
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
// Inject custom CSS
|
|
153
|
-
if (
|
|
162
|
+
if (import.meta.client && customCSS) {
|
|
154
163
|
let styleEl = document.getElementById('custom-site-css');
|
|
155
164
|
if (!styleEl) {
|
|
156
165
|
styleEl = document.createElement('style');
|
|
@@ -168,7 +177,7 @@ export const useSiteConfig = () => {
|
|
|
168
177
|
const { siteName, siteDescription, logoUrl } = siteConfig.value;
|
|
169
178
|
const seo = siteConfig.value.customization?.content?.seo;
|
|
170
179
|
|
|
171
|
-
if (
|
|
180
|
+
if (import.meta.client) {
|
|
172
181
|
if (siteName) {
|
|
173
182
|
document.title = siteName;
|
|
174
183
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from '#app';
|
|
2
|
+
import { useSiteConfig } from '../composables/useSiteConfig';
|
|
3
|
+
import { useCategories } from '../composables/useCategories';
|
|
4
|
+
|
|
5
|
+
export default defineNuxtPlugin(async () => {
|
|
6
|
+
const { fetchConfig, siteConfig, error, applyCustomization } = useSiteConfig();
|
|
7
|
+
const { fetchCategories } = useCategories();
|
|
8
|
+
|
|
9
|
+
// SSR: завантажуємо конфіг і категорії — дані передаються клієнту через useState
|
|
10
|
+
// Client: якщо дані вже є від SSR (гідрація) — fetchConfig пропускає повторний запит
|
|
11
|
+
try {
|
|
12
|
+
await fetchConfig();
|
|
13
|
+
await fetchCategories();
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.error('Failed to initialize site:', e);
|
|
16
|
+
|
|
17
|
+
if (import.meta.client && error.value) {
|
|
18
|
+
console.error('Site configuration error:', error.value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Client-only: застосувати CSS кастомізацію після гідрації
|
|
23
|
+
if (import.meta.client && siteConfig.value) {
|
|
24
|
+
applyCustomization();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Client-only: завантажити кошик з localStorage
|
|
28
|
+
if (import.meta.client) {
|
|
29
|
+
const { useCart } = await import('../composables/useCart');
|
|
30
|
+
const { loadCart } = useCart();
|
|
31
|
+
loadCart();
|
|
32
|
+
}
|
|
33
|
+
});
|
package/utils/image.ts
CHANGED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { defineNuxtPlugin } from '#app';
|
|
2
|
-
import { useSiteConfig } from '../composables/useSiteConfig';
|
|
3
|
-
import { useCart } from '../composables/useCart';
|
|
4
|
-
import { useCategories } from '../composables/useCategories';
|
|
5
|
-
|
|
6
|
-
export default defineNuxtPlugin(async () => {
|
|
7
|
-
const { fetchConfig, error } = useSiteConfig();
|
|
8
|
-
const { loadCart } = useCart();
|
|
9
|
-
const { fetchCategories } = useCategories();
|
|
10
|
-
|
|
11
|
-
// Initialize cart from localStorage
|
|
12
|
-
loadCart();
|
|
13
|
-
|
|
14
|
-
// Fetch site configuration
|
|
15
|
-
try {
|
|
16
|
-
await fetchConfig();
|
|
17
|
-
|
|
18
|
-
// Load categories after config is loaded
|
|
19
|
-
await fetchCategories();
|
|
20
|
-
} catch (e) {
|
|
21
|
-
console.error('Failed to initialize site:', e);
|
|
22
|
-
|
|
23
|
-
// If site config fails to load, redirect to error page or show message
|
|
24
|
-
if (process.client && error.value) {
|
|
25
|
-
// You can customize this behavior
|
|
26
|
-
console.error('Site configuration error:', error.value);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|