umwd-components 0.1.836 → 0.1.838

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.
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ /*
3
+ * UMWD-Components
4
+ * @copyright Jelle Paulus
5
+ * @license MIT
6
+ */
7
+
8
+ import { jsx, Fragment } from 'react/jsx-runtime';
9
+ import { isModuleEnabled, isModuleFeatureEnabled } from '../../../lib/module-config/config.js';
10
+
11
+ /**
12
+ * Component that conditionally renders children based on module/feature enablement
13
+ */
14
+ function ModuleGuard({ module, feature, fallback = null, children, }) {
15
+ const moduleEnabled = isModuleEnabled(module);
16
+ if (!moduleEnabled) {
17
+ return jsx(Fragment, { children: fallback });
18
+ }
19
+ if (feature) {
20
+ const featureEnabled = isModuleFeatureEnabled(module, feature);
21
+ if (!featureEnabled) {
22
+ return jsx(Fragment, { children: fallback });
23
+ }
24
+ }
25
+ return jsx(Fragment, { children: children });
26
+ }
27
+ /**
28
+ * Render prop component that provides module/feature status to children
29
+ */
30
+ function ConditionalModule({ module, feature, children, }) {
31
+ const moduleEnabled = isModuleEnabled(module);
32
+ const featureEnabled = feature
33
+ ? isModuleFeatureEnabled(module, feature)
34
+ : true;
35
+ const isEnabled = moduleEnabled && featureEnabled;
36
+ return jsx(Fragment, { children: children(isEnabled) });
37
+ }
38
+
39
+ export { ConditionalModule, ModuleGuard };
@@ -223,3 +223,6 @@ export { MinioItemList } from './components/common/media/minio/MinioItemList.js'
223
223
  export { MinioDisplay } from './components/common/media/minio/MinioDisplay.js';
224
224
  export { contactFormAction } from './data/actions/e-commerce/lead/contactFormAction.js';
225
225
  export { ShippingStatusIndicator } from './components/e-commerce/opo/ShippingStatusIndicator.js';
226
+ export { clearConfigCache, getConfigFilePath, getEnabledModules, getFullConfig, getModuleConfig, isModuleEnabled, isModuleFeatureEnabled, validateCurrentConfig } from './lib/module-config/config.js';
227
+ export { filterNavigationByModules } from './lib/module-config/navigation-filter.js';
228
+ export { ConditionalModule, ModuleGuard } from './components/common/module-config/ModuleGuard.js';
@@ -0,0 +1,231 @@
1
+ /*
2
+ * UMWD-Components
3
+ * @copyright Jelle Paulus
4
+ * @license MIT
5
+ */
6
+
7
+ import { readFileSync } from 'fs';
8
+ import { join } from 'path';
9
+
10
+ let cachedConfig = null;
11
+ /**
12
+ * Get the path to the umwdconfig.json file
13
+ * This function works across different environments (frontend, backend, CMS)
14
+ */
15
+ function getConfigPath() {
16
+ // Start from the current working directory
17
+ let currentPath = process.cwd();
18
+ // For apps in subdirectories, traverse up to find the root config
19
+ const possiblePaths = [
20
+ join(currentPath, "umwdconfig.json"), // Root level
21
+ join(currentPath, "..", "umwdconfig.json"), // One level up (for frontend/backend/cms)
22
+ join(currentPath, "..", "..", "umwdconfig.json"), // Two levels up
23
+ join(currentPath, "..", "..", "..", "umwdconfig.json"), // Three levels up
24
+ ];
25
+ for (const path of possiblePaths) {
26
+ try {
27
+ // Check if file exists by trying to read it
28
+ readFileSync(path, "utf8");
29
+ return path;
30
+ }
31
+ catch {
32
+ // File doesn't exist, continue to next path
33
+ continue;
34
+ }
35
+ }
36
+ throw new Error("umwdconfig.json not found. Please ensure the configuration file exists in the project root.\n" +
37
+ `Searched paths:\n${possiblePaths.map((p) => ` - ${p}`).join("\n")}`);
38
+ }
39
+ /**
40
+ * Validate the UMWD configuration object
41
+ */
42
+ function validateConfig(config) {
43
+ const errors = [];
44
+ if (!config || typeof config !== "object") {
45
+ return {
46
+ isValid: false,
47
+ errors: [
48
+ { field: "root", message: "Configuration must be a valid object" },
49
+ ],
50
+ };
51
+ }
52
+ if (!config.modules || typeof config.modules !== "object") {
53
+ errors.push({
54
+ field: "modules",
55
+ message: "modules field is required and must be an object",
56
+ });
57
+ }
58
+ else {
59
+ // Validate required modules
60
+ const requiredModules = ["pageBuilder", "ecommerce"];
61
+ for (const module of requiredModules) {
62
+ if (!config.modules[module]) {
63
+ errors.push({
64
+ field: `modules.${module}`,
65
+ message: `${module} module configuration is required`,
66
+ });
67
+ }
68
+ else if (typeof config.modules[module] !== "object") {
69
+ errors.push({
70
+ field: `modules.${module}`,
71
+ message: `${module} module must be an object`,
72
+ });
73
+ }
74
+ else if (typeof config.modules[module].enabled !== "boolean") {
75
+ errors.push({
76
+ field: `modules.${module}.enabled`,
77
+ message: `${module}.enabled must be a boolean`,
78
+ });
79
+ }
80
+ }
81
+ // Validate optional features within modules
82
+ Object.keys(config.modules).forEach((moduleName) => {
83
+ const module = config.modules[moduleName];
84
+ if (module && module.features && typeof module.features !== "object") {
85
+ errors.push({
86
+ field: `modules.${moduleName}.features`,
87
+ message: `${moduleName}.features must be an object`,
88
+ });
89
+ }
90
+ else if (module && module.features) {
91
+ Object.keys(module.features).forEach((featureName) => {
92
+ if (typeof module.features[featureName] !== "boolean") {
93
+ errors.push({
94
+ field: `modules.${moduleName}.features.${featureName}`,
95
+ message: `${moduleName}.features.${featureName} must be a boolean`,
96
+ });
97
+ }
98
+ });
99
+ }
100
+ });
101
+ }
102
+ return {
103
+ isValid: errors.length === 0,
104
+ errors,
105
+ };
106
+ }
107
+ /**
108
+ * Load and parse the UMWD configuration file
109
+ */
110
+ function loadConfig(useCache = true) {
111
+ if (useCache && cachedConfig) {
112
+ return cachedConfig;
113
+ }
114
+ try {
115
+ const configPath = getConfigPath();
116
+ const configContent = readFileSync(configPath, "utf8");
117
+ const config = JSON.parse(configContent);
118
+ const validation = validateConfig(config);
119
+ if (!validation.isValid) {
120
+ const errorMessages = validation.errors
121
+ .map((error) => `${error.field}: ${error.message}`)
122
+ .join(", ");
123
+ throw new Error(`Invalid UMWD configuration: ${errorMessages}`);
124
+ }
125
+ cachedConfig = config;
126
+ return cachedConfig;
127
+ }
128
+ catch (error) {
129
+ if (error instanceof SyntaxError) {
130
+ throw new Error("Invalid JSON in umwdconfig.json file. Please check the file syntax.");
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ /**
136
+ * Get the configuration for a specific module
137
+ */
138
+ function getModuleConfig(moduleName) {
139
+ const config = loadConfig();
140
+ if (!config.modules[moduleName]) {
141
+ throw new Error(`Module '${moduleName}' not found in configuration`);
142
+ }
143
+ return config.modules[moduleName];
144
+ }
145
+ /**
146
+ * Check if a specific module is enabled
147
+ */
148
+ function isModuleEnabled(moduleName) {
149
+ try {
150
+ const moduleConfig = getModuleConfig(moduleName);
151
+ return moduleConfig ? moduleConfig.enabled : false;
152
+ }
153
+ catch {
154
+ // If module doesn't exist or config is invalid, assume it's disabled
155
+ return false;
156
+ }
157
+ }
158
+ /**
159
+ * Check if a specific feature within a module is enabled
160
+ */
161
+ function isModuleFeatureEnabled(moduleName, featureName) {
162
+ try {
163
+ const moduleConfig = getModuleConfig(moduleName);
164
+ // If module doesn't exist or is disabled, all features are disabled
165
+ if (!moduleConfig || !moduleConfig.enabled) {
166
+ return false;
167
+ }
168
+ // If no features are defined, assume the feature is enabled (if module is enabled)
169
+ if (!moduleConfig.features) {
170
+ return true;
171
+ }
172
+ // Check if the specific feature is enabled
173
+ return moduleConfig.features[featureName] === true;
174
+ }
175
+ catch {
176
+ // If module doesn't exist or config is invalid, assume feature is disabled
177
+ return false;
178
+ }
179
+ }
180
+ /**
181
+ * Get the full configuration object
182
+ */
183
+ function getFullConfig() {
184
+ return loadConfig();
185
+ }
186
+ /**
187
+ * Get all enabled modules
188
+ */
189
+ function getEnabledModules() {
190
+ const config = loadConfig();
191
+ return Object.keys(config.modules).filter((moduleName) => {
192
+ const moduleConfig = config.modules[moduleName];
193
+ return moduleConfig && moduleConfig.enabled;
194
+ });
195
+ }
196
+ /**
197
+ * Validate the current configuration without throwing
198
+ */
199
+ function validateCurrentConfig() {
200
+ try {
201
+ const configPath = getConfigPath();
202
+ const configContent = readFileSync(configPath, "utf8");
203
+ const config = JSON.parse(configContent);
204
+ return validateConfig(config);
205
+ }
206
+ catch (error) {
207
+ return {
208
+ isValid: false,
209
+ errors: [
210
+ {
211
+ field: "file",
212
+ message: error instanceof Error ? error.message : "Unknown error",
213
+ },
214
+ ],
215
+ };
216
+ }
217
+ }
218
+ /**
219
+ * Clear the configuration cache (useful for testing or dynamic config reloading)
220
+ */
221
+ function clearConfigCache() {
222
+ cachedConfig = null;
223
+ }
224
+ /**
225
+ * Get the path to the configuration file (useful for debugging)
226
+ */
227
+ function getConfigFilePath() {
228
+ return getConfigPath();
229
+ }
230
+
231
+ export { clearConfigCache, getConfigFilePath, getEnabledModules, getFullConfig, getModuleConfig, isModuleEnabled, isModuleFeatureEnabled, validateCurrentConfig };
@@ -0,0 +1,58 @@
1
+ /*
2
+ * UMWD-Components
3
+ * @copyright Jelle Paulus
4
+ * @license MIT
5
+ */
6
+
7
+ import { isModuleEnabled, isModuleFeatureEnabled } from './config.js';
8
+
9
+ /**
10
+ * Filter navigation items based on enabled modules and features
11
+ */
12
+ function filterNavigationByModules(navigation) {
13
+ return navigation
14
+ .filter((item) => {
15
+ // Check if this is an e-commerce related navigation item
16
+ if (item.href && isECommerceRoute(item.href)) {
17
+ if (!isModuleEnabled("ecommerce")) {
18
+ return false;
19
+ }
20
+ // Check for specific e-commerce features
21
+ if (item.href.includes("/user/orders") ||
22
+ item.href.includes("/user/returns")) {
23
+ return isModuleFeatureEnabled("ecommerce", "orders");
24
+ }
25
+ if (item.href.includes("/shop")) {
26
+ return isModuleFeatureEnabled("ecommerce", "shopping");
27
+ }
28
+ }
29
+ return true;
30
+ })
31
+ .map((item) => {
32
+ // Process children for items that passed the filter
33
+ if (item.children) {
34
+ return {
35
+ ...item,
36
+ children: filterNavigationByModules(item.children),
37
+ };
38
+ }
39
+ return item;
40
+ });
41
+ }
42
+ /**
43
+ * Check if a route is e-commerce related
44
+ */
45
+ function isECommerceRoute(href) {
46
+ const ecommercePatterns = [
47
+ "/shop",
48
+ "/user/orders",
49
+ "/user/returns",
50
+ "/user/profile",
51
+ "/cart",
52
+ "/checkout",
53
+ "/check-out",
54
+ ];
55
+ return ecommercePatterns.some((pattern) => href.startsWith(pattern));
56
+ }
57
+
58
+ export { filterNavigationByModules };