@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.
@@ -1,5 +1,5 @@
1
- import { ref, computed } from 'vue';
2
- import { useRuntimeConfig } from '#imports';
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
- const config = useRuntimeConfig();
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 { ref, computed } from 'vue';
2
- import { useRuntimeConfig, useRoute } from '#imports';
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
- const config = useRuntimeConfig();
64
- const route = useRoute();
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
- // Get subdomain from current host or route
64
+ // Отримати subdomain з host працює і на SSR, і на клієнті
67
65
  const getSubdomain = () => {
68
- if (process.client) {
69
- const hostname = window.location.hostname;
70
- const parts = hostname.split('.');
71
- // Assuming format: subdomain.domain.com
72
- if (parts.length >= 3) {
73
- return parts[0];
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 (process.client && colors) {
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 (process.client && fonts) {
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 (process.client && customCSS) {
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 (process.client) {
180
+ if (import.meta.client) {
172
181
  if (siteName) {
173
182
  document.title = siteName;
174
183
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crm-market/template-shared",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "Shared Nuxt 3 layer for CRM Market store templates (layouts, components, composables, pages)",
6
6
  "publishConfig": {
@@ -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,4 +1,3 @@
1
- import { useRuntimeConfig } from '#imports';
2
1
  import { getApiBaseUrl } from './api';
3
2
 
4
3
  /**
@@ -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
- });