@crm-market/template-shared 1.0.5 → 1.0.6

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, useRuntimeConfig } from '#imports';
3
3
  import { getApiBaseUrl } from '../utils/api';
4
4
  import { useSiteConfig } from './useSiteConfig';
5
5
 
@@ -15,16 +15,20 @@ 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 = () => {
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
+
23
24
  const config = useRuntimeConfig();
24
25
  const { publicApiToken } = useSiteConfig();
25
26
 
26
27
  // Fetch all categories
27
28
  const fetchCategories = async () => {
29
+ // Якщо вже завантажено — не перезавантажуємо
30
+ if (categories.value.length > 0) return categories.value;
31
+
28
32
  loading.value = true;
29
33
  error.value = null;
30
34
 
@@ -1,5 +1,5 @@
1
- import { ref, computed } from 'vue';
2
- import { useRuntimeConfig, useRoute } from '#imports';
1
+ import { computed } from 'vue';
2
+ import { useState, useRuntimeConfig, useRoute, useRequestHeaders } from '#imports';
3
3
  import { getApiBaseUrl } from '../utils/api';
4
4
 
5
5
  interface ColorScheme {
@@ -55,29 +55,40 @@ 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 = () => {
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);
63
+
63
64
  const config = useRuntimeConfig();
64
- const route = useRoute();
65
65
 
66
- // Get subdomain from current host or route
66
+ // Отримати subdomain з host працює і на SSR, і на клієнті
67
67
  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
- }
68
+ let hostname = '';
69
+
70
+ if (import.meta.server) {
71
+ // SSR: отримуємо host з request headers
72
+ const headers = useRequestHeaders(['host', 'x-forwarded-host']);
73
+ hostname = (headers['x-forwarded-host'] || headers['host'] || '').split(':')[0];
74
+ } else {
75
+ // Client: з window.location
76
+ hostname = window.location.hostname;
77
+ }
78
+
79
+ const parts = hostname.split('.');
80
+ // Формат: subdomain.domain.com або subdomain.domain.com.ua
81
+ if (parts.length >= 3) {
82
+ return parts[0];
75
83
  }
76
84
  return null;
77
85
  };
78
86
 
79
87
  // Fetch site configuration from API
80
88
  const fetchConfig = async (subdomain?: string) => {
89
+ // Якщо вже завантажено — не перезавантажуємо
90
+ if (siteConfig.value && !subdomain) return siteConfig.value;
91
+
81
92
  loading.value = true;
82
93
  error.value = null;
83
94
 
@@ -113,7 +124,7 @@ export const useSiteConfig = () => {
113
124
 
114
125
  const { colors, fonts, customCSS } = siteConfig.value.customization;
115
126
 
116
- if (process.client && colors) {
127
+ if (import.meta.client && colors) {
117
128
  const root = document.documentElement;
118
129
 
119
130
  if (colors.primary) {
@@ -132,7 +143,7 @@ export const useSiteConfig = () => {
132
143
  }
133
144
  }
134
145
 
135
- if (process.client && fonts) {
146
+ if (import.meta.client && fonts) {
136
147
  const root = document.documentElement;
137
148
 
138
149
  if (fonts.headingFont) {
@@ -150,7 +161,7 @@ export const useSiteConfig = () => {
150
161
  }
151
162
 
152
163
  // Inject custom CSS
153
- if (process.client && customCSS) {
164
+ if (import.meta.client && customCSS) {
154
165
  let styleEl = document.getElementById('custom-site-css');
155
166
  if (!styleEl) {
156
167
  styleEl = document.createElement('style');
@@ -168,7 +179,7 @@ export const useSiteConfig = () => {
168
179
  const { siteName, siteDescription, logoUrl } = siteConfig.value;
169
180
  const seo = siteConfig.value.customization?.content?.seo;
170
181
 
171
- if (process.client) {
182
+ if (import.meta.client) {
172
183
  if (siteName) {
173
184
  document.title = siteName;
174
185
  }
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.6",
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
+ });
@@ -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
- });