PluginProbe
Ultimate Store Kit / 3.1.3
Ultimate Store Kit v3.1.3
3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 92 releases
ultimate-store-kit / src / admin / App.js

App.js in Ultimate Store Kit 3.1.3, at src/admin/App.js

320 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useState, useCallback, useEffect } from '@wordpress/element';
2 import { applyFilters, addAction, removeAction } from '@wordpress/hooks';
3 import { __ } from '@wordpress/i18n';
4 import Header from './components/Header';
5 import Sidebar from './components/Sidebar';
6 import Welcome from './pages/Welcome';
7 import WidgetsPage from './pages/WidgetsPage';
8 import OtherSettings from './pages/OtherSettings';
9 import GetPro from './pages/GetPro';
10 import AboutInfo from './pages/AboutInfo';
11 import ProModulePlaceholder from './pages/ProModulePlaceholder';
12 import { appShell, bodyRow, mainContent } from './tw';
13 import Toast from './components/Toast';
14 import { getPlaceholderModules } from './utils';
15
16 const adminData = window.ultimateStoreKitAdminData || {};
17
18 const slugify = (label) =>
19 label.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
20
21 const getSettingsGroups = () => {
22 const widgets = adminData.widgets?.ultimate_store_kit_other_settings || [];
23 const groups = [];
24 widgets.forEach((w) => {
25 if (w.type === 'start_group') {
26 groups.push({ id: `settings-${slugify(w.label)}`, label: w.label });
27 }
28 });
29 return groups;
30 };
31
32 const settingsGroups = getSettingsGroups();
33
34 const getPageFromHash = () => {
35 const hash = window.location.hash.replace('#', '');
36 const pageName = hash.split('?')[0];
37
38 const placeholderIds = getPlaceholderModules().map((p) => p.id);
39 const proPages = applyFilters('usk.admin.pages', {});
40 const proPageIds = Object.keys(proPages);
41
42 const corePages = [
43 'welcome',
44 'widgets',
45 'woocommerce-widgets',
46 'edd-widgets',
47 'other-widgets',
48 'get-pro',
49 'about',
50 ...settingsGroups.map((g) => g.id),
51 ...placeholderIds,
52 ...proPageIds,
53 ];
54
55 const validPages = applyFilters('usk.admin.validPages', corePages);
56 return validPages.includes(pageName) ? pageName : 'welcome';
57 };
58
59 const App = () => {
60 const [activePage, setActivePage] = useState(getPageFromHash());
61 const [settings, setSettings] = useState(adminData.savedSettings || {});
62 const [isProActive, setIsProActive] = useState(!!adminData.isPro);
63 const [saving, setSaving] = useState(false);
64 const [notification, setNotification] = useState(null);
65 const [isSidebarOpen, setIsSidebarOpen] = useState(false);
66 const [isDesktop, setIsDesktop] = useState(() => window.innerWidth > 1024);
67
68 // Allow pro (or any extension) to trigger toasts via doAction('usk.admin.notify', {...})
69 useEffect(() => {
70 const handleNotify = (data) => setNotification(data);
71 addAction('usk.admin.notify', 'usk-core', handleNotify);
72 return () => removeAction('usk.admin.notify', 'usk-core');
73 }, []);
74
75 // Listen for license activation/deactivation from Pro's LicensePage
76 useEffect(() => {
77 const handleLicenseChanged = (active) => {
78 setIsProActive(active);
79 window.ultimateStoreKitAdminData.isPro = active;
80 };
81 addAction('usk.admin.licenseChanged', 'usk-core', handleLicenseChanged);
82 return () => removeAction('usk.admin.licenseChanged', 'usk-core');
83 }, []);
84
85 useEffect(() => {
86 if (notification) {
87 const timer = setTimeout(() => setNotification(null), 3000);
88 return () => clearTimeout(timer);
89 }
90 }, [notification]);
91
92 useEffect(() => {
93 const handleHashChange = () => {
94 setActivePage(getPageFromHash());
95 };
96
97 window.addEventListener('hashchange', handleHashChange);
98 return () => window.removeEventListener('hashchange', handleHashChange);
99 }, []);
100
101 useEffect(() => {
102 // When pro plugin is installed, Get Pro is replaced by License.
103 // Redirect stale get-pro links to license (if registered) or welcome.
104 if (activePage === 'get-pro') {
105 const proPages = applyFilters('usk.admin.pages', {});
106 if (proPages.license || isProActive) {
107 const target = proPages.license ? 'license' : 'welcome';
108 setActivePage(target);
109 window.location.hash = `#${target}`;
110 }
111 }
112 }, [isProActive, activePage]);
113
114 useEffect(() => {
115 setIsSidebarOpen(false);
116 }, [activePage]);
117
118 useEffect(() => {
119 document.body.style.overflow = isSidebarOpen ? 'hidden' : '';
120 return () => {
121 document.body.style.overflow = '';
122 };
123 }, [isSidebarOpen]);
124
125 useEffect(() => {
126 const handleResize = () => {
127 const desktop = window.innerWidth > 1024;
128 setIsDesktop(desktop);
129 if (desktop) {
130 setIsSidebarOpen(false);
131 }
132 };
133 window.addEventListener('resize', handleResize);
134 return () => window.removeEventListener('resize', handleResize);
135 }, []);
136
137 const handleToggleSidebar = useCallback((event) => {
138 if (event?.preventDefault) event.preventDefault();
139 if (event?.stopPropagation) event.stopPropagation();
140 setIsSidebarOpen((prev) => !prev);
141 }, []);
142
143 const saveSettings = useCallback(
144 (section, sectionSettings) => {
145 setSaving(true);
146
147 fetch(adminData.restUrl + 'settings', {
148 method: 'POST',
149 headers: {
150 'Content-Type': 'application/json',
151 'X-WP-Nonce': adminData.restNonce,
152 },
153 body: JSON.stringify({ section, settings: sectionSettings }),
154 })
155 .then((res) => {
156 if (!res.ok) throw res;
157 return res.json();
158 })
159 .then((data) => {
160 setSettings((prev) => ({
161 ...prev,
162 [section]: sectionSettings,
163 }));
164 setNotification({
165 type: 'success',
166 message:
167 data?.message ||
168 __('Settings saved successfully.', 'ultimate-store-kit'),
169 });
170 })
171 .catch(() => {
172 setNotification({
173 type: 'error',
174 message: __(
175 'An error occurred while saving.',
176 'ultimate-store-kit'
177 ),
178 });
179 })
180 .finally(() => {
181 setSaving(false);
182 });
183 },
184 []
185 );
186
187 const renderPage = () => {
188 const widgets = adminData.widgets || {};
189
190 // Check pro-registered pages first
191 const proPages = applyFilters('usk.admin.pages', {});
192 if (proPages[activePage]) {
193 // If user doesn't have an active license, show placeholder for module pages
194 if (!isProActive) {
195 const placeholders = getPlaceholderModules();
196 const placeholder = placeholders.find((p) => p.id === activePage);
197 if (placeholder) {
198 return <ProModulePlaceholder module={placeholder} />;
199 }
200 }
201 const ProPageComponent = proPages[activePage];
202 return <ProPageComponent />;
203 }
204
205 // Check placeholder modules (upsell for non-pro users)
206 const placeholders = getPlaceholderModules();
207 const placeholder = placeholders.find((p) => p.id === activePage);
208 if (placeholder) {
209 return <ProModulePlaceholder module={placeholder} />;
210 }
211
212 switch (activePage) {
213 case 'welcome':
214 return (
215 <Welcome
216 widgets={widgets}
217 settings={settings}
218 isPro={isProActive}
219 />
220 );
221 case 'widgets':
222 case 'woocommerce-widgets':
223 case 'edd-widgets':
224 case 'other-widgets': {
225 const widgetTypeMap = {
226 'woocommerce-widgets': 'wc',
227 'edd-widgets': 'edd',
228 'other-widgets': 'other',
229 'widgets': 'wc'
230 };
231 return (
232 <WidgetsPage
233 allWidgets={widgets}
234 allSettings={settings}
235 onSave={saveSettings}
236 saving={saving}
237 isPro={isProActive}
238 widgetType={widgetTypeMap[activePage]}
239 />
240 );
241 }
242 case 'get-pro':
243 return isProActive ? (
244 <Welcome
245 widgets={widgets}
246 settings={settings}
247 isPro={isProActive}
248 />
249 ) : (
250 <GetPro isPro={isProActive} />
251 );
252 case 'about':
253 return <AboutInfo />;
254 default: {
255 const settingsGroup = settingsGroups.find((g) => g.id === activePage);
256 if (settingsGroup) {
257 return (
258 <OtherSettings
259 widgets={widgets.ultimate_store_kit_other_settings || []}
260 section="ultimate_store_kit_other_settings"
261 settings={settings.ultimate_store_kit_other_settings || {}}
262 onSave={saveSettings}
263 saving={saving}
264 isPro={isProActive}
265 activeGroup={settingsGroup.label}
266 />
267 );
268 }
269 return <Welcome widgets={widgets} settings={settings} />;
270 }
271 }
272 };
273
274 return (
275 <div className={appShell}>
276 <Toast notification={notification} onDismiss={() => setNotification(null)} />
277 <Header
278 version={adminData.version}
279 isPro={isProActive}
280 isSidebarOpen={isSidebarOpen}
281 isDesktop={isDesktop}
282 onToggleSidebar={handleToggleSidebar}
283 />
284 <div className="bg-slate-50">
285 <div className={`${bodyRow}`}>
286 <Sidebar
287 activePage={activePage}
288 onNavigate={setActivePage}
289 isPro={isProActive}
290 isOpen={isSidebarOpen}
291 isDesktop={isDesktop}
292 onClose={() => setIsSidebarOpen(false)}
293 settingsGroups={settingsGroups}
294 />
295 <div className={`${mainContent} flex flex-col`}>
296 <div className="flex-1">{renderPage()}</div>
297 </div>
298 </div>
299 </div>
300 <footer className="p-5 bg-white py-4 text-center text-sm text-slate-500 rounded-bl-lg rounded-br-lg">
301 {__(
302 'Ultimate Store Kit Addon made with love by',
303 'ultimate-store-kit'
304 )}{' '}
305 <a
306 target="_blank"
307 rel="noopener noreferrer"
308 href="https://bdthemes.com"
309 className="text-uks-brand no-underline hover:underline"
310 >
311 BdThemes
312 </a>
313 . {__('All rights reserved.', 'ultimate-store-kit')}
314 </footer>
315 </div>
316 );
317 };
318
319 export default App;
320