my-fleetbo-react-v1 1.0.2

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.
Files changed (40) hide show
  1. package/README.md +16 -0
  2. package/eslint.config.js +33 -0
  3. package/globals.d.ts +139 -0
  4. package/index.html +14 -0
  5. package/jsconfig.json +11 -0
  6. package/package.json +39 -0
  7. package/public/branding/ic_launcher-playstore.png +0 -0
  8. package/public/branding/logo.png +0 -0
  9. package/public/native/iOS/Hello.text +1 -0
  10. package/src/@fleetbo/components/common/Loader.jsx +45 -0
  11. package/src/@fleetbo/components/common/PageConfig.js +21 -0
  12. package/src/@fleetbo/components/layout/Navigation.js +4 -0
  13. package/src/@fleetbo/components/layout/ProtectedLayout.jsx +14 -0
  14. package/src/@fleetbo/config/fleetboConfig.js +3 -0
  15. package/src/@fleetbo/config/fleetboEngine.js +93 -0
  16. package/src/@fleetbo/config/systemProtocol.js +223 -0
  17. package/src/@fleetbo/hooks/useLoadingTimeout.js +18 -0
  18. package/src/@fleetbo/hooks/useModalLauncher.js +22 -0
  19. package/src/@fleetbo/hooks/useStartupEffect.js +111 -0
  20. package/src/@fleetbo/index.js +7 -0
  21. package/src/@fleetbo/utils/FormatDate.js +52 -0
  22. package/src/@fleetbo/utils/getToken.js +12 -0
  23. package/src/App.jsx +119 -0
  24. package/src/app/assets/css/App.css +91 -0
  25. package/src/app/assets/css/Auth.css +50 -0
  26. package/src/app/assets/css/Form.css +113 -0
  27. package/src/app/assets/images/avatar.png +0 -0
  28. package/src/app/assets/images/logo.png +0 -0
  29. package/src/app/core/AuthRouter.jsx +36 -0
  30. package/src/app/core/NotFound.jsx +18 -0
  31. package/src/app/mocks/Login.jsx +89 -0
  32. package/src/app/mocks/SampleTab1.jsx +60 -0
  33. package/src/app/mocks/SampleTab2.jsx +64 -0
  34. package/src/app/mocks/SampleTab3.jsx +64 -0
  35. package/src/app/tabs/Tab1.jsx +40 -0
  36. package/src/app/tabs/Tab2.jsx +25 -0
  37. package/src/app/tabs/Tab3.jsx +23 -0
  38. package/src/app/tabs/Welcome.jsx +174 -0
  39. package/src/main.jsx +11 -0
  40. package/vite.config.js +32 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * === Fleetbo Developer Guide: The Authentication Gateway (Login.jsx) ===
3
+ *
4
+ * This component is the ignition key for your application.
5
+ * It establishes the secure link with the Fleetbo Native Engine.
6
+ */
7
+
8
+ import React, { useState } from 'react';
9
+ import 'app/assets/css/Auth.css';
10
+ import logo from 'app/assets/images/logo.png';
11
+ import { PageConfig } from '@fleetbo';
12
+
13
+ const DEFAULT_TAB = 'tab1';
14
+
15
+ const Login = ({ sessionData: sessionDataProp }) => {
16
+ const sessionData = sessionDataProp || (() => {
17
+ try {
18
+ const s = localStorage.getItem('fleetbo_session');
19
+ return s ? JSON.parse(s) : null;
20
+ } catch (e) { return null; }
21
+ })();
22
+
23
+ const [phoneNumber, setPhoneNumber] = useState('');
24
+ const [loadingLog, setLoadingLog] = useState(false);
25
+
26
+ const handleVerifyPhoneNumber = async () => {
27
+ if (!phoneNumber.trim()) return;
28
+ setLoadingLog(true);
29
+ try {
30
+ const result = await Fleetbo.logTester(phoneNumber.trim());
31
+ if (result && result.success) {
32
+ await Fleetbo.log(DEFAULT_TAB);
33
+ } else {
34
+ alert("Unauthorized.\n\nPlease check your phone number.");
35
+ }
36
+ } catch (error) {
37
+ alert("Connection error during verification.");
38
+ } finally {
39
+ setLoadingLog(false);
40
+ }
41
+ };
42
+
43
+ return (
44
+ <>
45
+ <PageConfig navbar="none" />
46
+
47
+ <div className="login-passerelle-container" style={{ animation: 'fadeIn 0.5s ease-in-out' }}>
48
+ <div className="login-passerelle-box">
49
+ {sessionData ? (
50
+ <div className="w-100 d-flex flex-column align-items-center text-center" style={{ maxWidth: '340px' }}>
51
+ <img className="mb-3" src={logo} alt="logo" style={{ width: '50px', height: '50px' }} />
52
+ <h3 className="fw-bold mb-1" style={{ fontSize: '19px', color: '#4c8a69' }}>
53
+ {sessionData.appName || "Fleetbo"}
54
+ </h3>
55
+ <p className="text-muted mb-4" style={{ fontSize: '13px' }}>
56
+ {sessionData.description || "Mobile Cloud OS"}
57
+ </p>
58
+
59
+ <input
60
+ type="tel"
61
+ className="form-control p-3 text-center mb-3 w-100"
62
+ style={{ height: '55px', borderRadius: '10px' }}
63
+ placeholder="Enter your phone number"
64
+ value={phoneNumber}
65
+ onChange={(e) => setPhoneNumber(e.target.value)}
66
+ />
67
+ <button
68
+ onClick={handleVerifyPhoneNumber}
69
+ className="btn p-3 fs-6 btn-success w-100"
70
+ style={{ borderRadius: '14px', height: '55px', backgroundColor: '#4c8a69', border: 'none', fontWeight: 'bold' }}
71
+ disabled={loadingLog}
72
+ >
73
+ {loadingLog ? (
74
+ <><span className="spinner-border spinner-border-sm me-2" />Checking...</>
75
+ ) : (
76
+ "Login"
77
+ )}
78
+ </button>
79
+ </div>
80
+ ) : (
81
+ <p className="text-danger">Failed to load app data.</p>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </>
86
+ );
87
+ };
88
+
89
+ export default Login;
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+
3
+ export default function SampleTab() {
4
+ return (
5
+ <div
6
+ className="d-flex flex-column justify-content-center align-items-center w-100"
7
+ style={{
8
+ minHeight: '100vh',
9
+ backgroundColor: '#F8F9FA',
10
+ padding: '24px',
11
+ paddingBottom: '80px'
12
+ }}
13
+ >
14
+ <h1
15
+ style={{
16
+ fontSize: '28px',
17
+ fontWeight: 'bold',
18
+ color: '#212529',
19
+ margin: '0 0 16px 0',
20
+ textAlign: 'center'
21
+ }}
22
+ >
23
+ Tab 1 - Mock
24
+ </h1>
25
+
26
+ <p
27
+ style={{
28
+ fontSize: '16px',
29
+ color: '#6C757D',
30
+ textAlign: 'center',
31
+ lineHeight: '24px',
32
+ margin: '0 0 40px 0',
33
+ maxWidth: '400px'
34
+ }}
35
+ >
36
+ This is a Fleetbo View. The interface is rendered natively at 120 FPS.<br />
37
+ Open the Compose panel, describe your feature in plain language, and Alex forges the Fleetbo View instantly.
38
+ </p>
39
+
40
+ <div
41
+ className="d-flex align-items-center justify-content-center text-white"
42
+ style={{
43
+ backgroundColor: '#0E904D',
44
+ borderRadius: '12px',
45
+ width: '100%',
46
+ maxWidth: '400px',
47
+ height: '56px',
48
+ cursor: 'default',
49
+ fontWeight: 'bold',
50
+ fontSize: '16px',
51
+ transition: 'transform 0.1s'
52
+ }}
53
+ onClick={() => Fleetbo.emit('PING_JS', {})}
54
+ >
55
+ EMIT SIGNAL TO JS
56
+ </div>
57
+ </div>
58
+ );
59
+ }
60
+ // ⚡ Forged by Alex on 2026-03-28 at 15:04:20
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+
3
+ export default function SampleTab() {
4
+ return (
5
+ <div
6
+ className="d-flex flex-column justify-content-center align-items-center w-100"
7
+ style={{
8
+ minHeight: '100vh',
9
+ backgroundColor: '#F8F9FA',
10
+ padding: '24px',
11
+ paddingBottom: '80px'
12
+ }}
13
+ >
14
+ <h1
15
+ style={{
16
+ fontSize: '28px',
17
+ fontWeight: 'bold',
18
+ color: '#212529',
19
+ margin: '0 0 16px 0',
20
+ textAlign: 'center'
21
+ }}
22
+ >
23
+ Tab 2 - Mock
24
+ </h1>
25
+
26
+ <p
27
+ style={{
28
+ fontSize: '16px',
29
+ color: '#6C757D',
30
+ textAlign: 'center',
31
+ lineHeight: '24px',
32
+ margin: '0 0 40px 0',
33
+ maxWidth: '400px'
34
+ }}
35
+ >
36
+ This is a Fleetbo View. The interface is rendered natively at 120 FPS.<br />
37
+ Open the Compose panel, describe your feature in plain language, and Alex forges the Fleetbo View instantly.
38
+ </p>
39
+
40
+ <div
41
+ className="d-flex align-items-center justify-content-center text-white"
42
+ style={{
43
+ backgroundColor: '#0E904D',
44
+ borderRadius: '12px',
45
+ width: '100%',
46
+ maxWidth: '400px',
47
+ height: '56px',
48
+ cursor: 'default',
49
+ fontWeight: 'bold',
50
+ fontSize: '16px',
51
+ transition: 'transform 0.1s'
52
+ }}
53
+ onTouchStart={(e) => e.currentTarget.style.transform = 'scale(0.96)'}
54
+ onTouchEnd={(e) => e.currentTarget.style.transform = 'scale(1)'}
55
+ onMouseDown={(e) => e.currentTarget.style.transform = 'scale(0.96)'}
56
+ onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
57
+ onClick={() => Fleetbo.emit('PING_JS', {})}
58
+ >
59
+ EMIT SIGNAL TO JS
60
+ </div>
61
+ </div>
62
+ );
63
+ }
64
+ // ⚡ Forged by Alex on 2026-03-28 at 15:04:20
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+
3
+ export default function SampleTab() {
4
+ return (
5
+ <div
6
+ className="d-flex flex-column justify-content-center align-items-center w-100"
7
+ style={{
8
+ minHeight: '100vh',
9
+ backgroundColor: '#F8F9FA',
10
+ padding: '24px',
11
+ paddingBottom: '80px'
12
+ }}
13
+ >
14
+ <h1
15
+ style={{
16
+ fontSize: '28px',
17
+ fontWeight: 'bold',
18
+ color: '#212529',
19
+ margin: '0 0 16px 0',
20
+ textAlign: 'center'
21
+ }}
22
+ >
23
+ Tab 3 - Mock
24
+ </h1>
25
+
26
+ <p
27
+ style={{
28
+ fontSize: '16px',
29
+ color: '#6C757D',
30
+ textAlign: 'center',
31
+ lineHeight: '24px',
32
+ margin: '0 0 40px 0',
33
+ maxWidth: '400px'
34
+ }}
35
+ >
36
+ This is a Fleetbo View. The interface is rendered natively at 120 FPS.<br />
37
+ Open the Compose panel, describe your feature in plain language, and Alex forges the Fleetbo View instantly.
38
+ </p>
39
+
40
+ <div
41
+ className="d-flex align-items-center justify-content-center text-white"
42
+ style={{
43
+ backgroundColor: '#de4751ff',
44
+ borderRadius: '12px',
45
+ width: '100%',
46
+ maxWidth: '400px',
47
+ height: '56px',
48
+ cursor: 'default',
49
+ fontWeight: 'bold',
50
+ fontSize: '16px',
51
+ transition: 'transform 0.1s'
52
+ }}
53
+ onTouchStart={(e) => e.currentTarget.style.transform = 'scale(0.96)'}
54
+ onTouchEnd={(e) => e.currentTarget.style.transform = 'scale(1)'}
55
+ onMouseDown={(e) => e.currentTarget.style.transform = 'scale(0.96)'}
56
+ onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
57
+ onClick={() => Fleetbo.emit('LOGOUT', {})}
58
+ >
59
+ Log Out
60
+ </div>
61
+ </div>
62
+ );
63
+ }
64
+ // ⚡ Forged by Alex on 2026-03-28 at 15:04:20
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tab1.jsx — Fleetbo JS in Action
3
+ *
4
+ * JS = Brain (orchestrates) | Native = Muscle (executes) | Alex = Architect (forges)
5
+ *
6
+ * Two patterns:
7
+ * Manager → List + Create in one module. Fleetbo.openView('GuestManager', true)
8
+ * Atomic → One action, closes. await Fleetbo.exec('Scanner', 'open', {})
9
+ *
10
+ * Alex (npm run alex):
11
+ * "Forge a guest manager with list and photo" → Alex builds
12
+ * "A guest manager with list and photo" → Alex stays conversational
13
+ * Rule: use "forge", "create", "modify" to trigger code generation.
14
+ *
15
+ * Quick ref:
16
+ * Fleetbo.openView('Module', true) → Native tab
17
+ * Fleetbo.exec('Module', 'action', {}) → Native overlay
18
+ * Fleetbo.add(db, col, json) → Write doc
19
+ * Fleetbo.getDocsG(db, col) → Read docs
20
+ * Fleetbo.delete(db, col, id) → Delete doc
21
+ */
22
+
23
+ import { useEffect, useState } from 'react';
24
+ import { PageConfig } from '@fleetbo';
25
+
26
+ export default function Tab1() {
27
+ const [navMode, setNavMode] = useState("show");
28
+ useEffect(() => {
29
+ Fleetbo.openView('SampleTab1', true, {
30
+ emit: async (action, payload) => {
31
+ if (action === 'HIDE_NAVBAR') setNavMode("none");
32
+ if (action === 'SHOW_NAVBAR') setNavMode("show");
33
+ if (action === 'PING_JS') {
34
+ // Example : Fleetbo.exec('ModuleName', 'action', {}); Generate by Alex
35
+ }
36
+ }
37
+ });
38
+ }, []);
39
+ return (<> <PageConfig navbar={navMode} /> </>);
40
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Fleetbo Tab Redirect or Not
3
+ *
4
+ * This tab is handled by a native module (or its mock in dev).
5
+ * If React renders this route (e.g. on reload), we redirect to the mock.
6
+ */
7
+ import { useEffect, useState } from 'react';
8
+ import { PageConfig } from '@fleetbo';
9
+
10
+ export default function Tab2() {
11
+ const [navMode, setNavMode] = useState("show");
12
+ useEffect(() => {
13
+ Fleetbo.openView('SampleTab2', true, {
14
+ emit: async (action, payload) => {
15
+ if (action === 'HIDE_NAVBAR') setNavMode("none");
16
+ if (action === 'SHOW_NAVBAR') setNavMode("show");
17
+ if (action === 'PING_JS') {
18
+ console.log("Signal reçu du Métal !");
19
+ // Example: Fleetbo.exec('MyModuleName', 'open', {});
20
+ }
21
+ }
22
+ });
23
+ }, []);
24
+ return (<> <PageConfig navbar={navMode} /> </>);
25
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Fleetbo Tab Redirect or Not
3
+ * This tab is handled by a fleetbo module (or its mock in dev).
4
+ */
5
+ import { useEffect, useState } from 'react';
6
+ import { PageConfig } from '@fleetbo';
7
+
8
+ export default function Tab3() {
9
+ const [navMode, setNavMode] = useState("show");
10
+ useEffect(() => {
11
+ Fleetbo.openView('SampleTab3', true, {
12
+ emit: async (action, payload) => {
13
+ if (action === 'HIDE_NAVBAR') setNavMode("none");
14
+ if (action === 'SHOW_NAVBAR') setNavMode("show");
15
+ if (action === 'LOGOUT') { Fleetbo.logout(); }
16
+ //if (action === 'OPEN_EDIT_PROFILE') {
17
+ // Example: Fleetbo.exec('EditProfileModule', 'open', {});
18
+ //}
19
+ }
20
+ });
21
+ }, []);
22
+ return (<> <PageConfig navbar={navMode} /> </>);
23
+ }
@@ -0,0 +1,174 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+
4
+ const navItems = [
5
+ { id: 'Tab1', view: 'tab1', label: 'Tab 1' },
6
+ { id: 'Tab2', view: 'tab2', label: 'Tab 2' },
7
+ { id: 'Tab3', view: 'tab3', label: 'Tab 3' },
8
+ ];
9
+
10
+ const getNavbarType = () => {
11
+ const params = new URLSearchParams(window.location.search);
12
+ if (params.get('type') === 'header') return 'header';
13
+ if (params.get('type') === 'footer') return 'footer';
14
+ const savedType = localStorage.getItem("navbar");
15
+ return savedType || 'footer';
16
+ };
17
+
18
+ const getInitialTab = () => {
19
+ const params = new URLSearchParams(window.location.search);
20
+ const activeRoute = params.get('activeRoute');
21
+ if (activeRoute) {
22
+ const matchedTab = navItems.find(item => !item.isNative && activeRoute.includes(`/${item.view}`));
23
+ if (matchedTab) return matchedTab.id;
24
+ }
25
+ return localStorage.getItem("activeTab") || 'Tab1';
26
+ };
27
+
28
+ const Welcome = () => {
29
+ const location = useLocation();
30
+
31
+ const [navbarType, setNavbarType] = useState(getNavbarType);
32
+ const [activeTab, setActiveTab] = useState(getInitialTab);
33
+
34
+ useEffect(() => {
35
+ const handleMessage = (event) => {
36
+ if (!event.data) return;
37
+ const { type, route: eventRoute, activeTab: msgActiveTab, navbarMode } = event.data;
38
+
39
+ // Reset navbar
40
+ if (type === 'FLEETBO_FORCE_LOGOUT') {
41
+ setActiveTab('Tab1');
42
+ localStorage.setItem("activeTab", 'Tab1');
43
+ return;
44
+ }
45
+
46
+ if (type === 'FLEETBO_UPDATE_ACTIVE_TAB' && msgActiveTab) {
47
+ setActiveTab(msgActiveTab);
48
+ localStorage.setItem("activeTab", msgActiveTab);
49
+ } else if (type === 'FLEETBO_NAVIGATE_INTERNAL' && eventRoute) {
50
+ const matchedTab = navItems.find(item => !item.isNative && eventRoute.includes(`/${item.view}`));
51
+ if (matchedTab) {
52
+ setActiveTab(matchedTab.id);
53
+ localStorage.setItem("activeTab", matchedTab.id);
54
+ }
55
+ } else if (type === 'SET_NAVBAR_TYPE' && navbarMode) {
56
+ setNavbarType(navbarMode);
57
+ localStorage.setItem("navbar", navbarMode);
58
+ }
59
+ };
60
+
61
+ const handleStorageChange = (e) => {
62
+ if (e.key === 'activeTab') {
63
+ setActiveTab(e.newValue || 'Tab1');
64
+ }
65
+
66
+ if (e.key === null && !localStorage.getItem('activeTab')) {
67
+ setActiveTab('Tab1');
68
+ }
69
+ };
70
+
71
+ window.addEventListener('message', handleMessage);
72
+ window.addEventListener('storage', handleStorageChange);
73
+
74
+ if (window.top !== window.self) {
75
+ window.top.postMessage({ type: 'FLEETBO_REQUEST_ENGINE' }, '*');
76
+ }
77
+
78
+ return () => {
79
+ window.removeEventListener('message', handleMessage);
80
+ window.removeEventListener('storage', handleStorageChange);
81
+ };
82
+ }, []);
83
+
84
+ const handleTabClick = (item) => {
85
+ setActiveTab(item.id);
86
+
87
+ localStorage.setItem("activeTab", item.id);
88
+ localStorage.setItem("activeTabView", item.view);
89
+ localStorage.setItem("activeTabNative", item.isNative ? "true" : "false");
90
+
91
+ if (window.Fleetbo) {
92
+ window.Fleetbo.openView(item.view, !!item.isNative);
93
+ } else {
94
+ console.warn("Fleetbo engine not found");
95
+ }
96
+ };
97
+
98
+ const getIconClass = (id, isActive) => {
99
+ if (id === 'Tab1') return isActive ? 'fa-solid fa-house' : 'fa-solid fa-house';
100
+ if (id === 'Tab2') return isActive ? 'fa-solid fa-compass' : 'fa-regular fa-compass';
101
+ if (id === 'Tab3') return isActive ? 'fa-solid fa-user' : 'fa-regular fa-user';
102
+ return 'fa-solid fa-circle';
103
+ };
104
+
105
+ // ==========================================
106
+ // STYLES INLINE
107
+ // ==========================================
108
+ const containerStyle = {
109
+ position: 'fixed',
110
+ left: 0,
111
+ right: 0,
112
+ height: '70px',
113
+ paddingBottom: 'env(safe-area-inset-bottom)',
114
+ boxSizing: 'border-box',
115
+ display: 'flex',
116
+ justifyContent: 'space-around',
117
+ alignItems: 'center',
118
+ backgroundColor: 'rgba(247, 246, 246, 0.95)',
119
+ zIndex: 1000,
120
+ ...(navbarType === 'header'
121
+ ? { top: 0, borderBottom: '1px solid #eee' }
122
+ : { bottom: 0, borderTop: '1px solid #eee' }
123
+ )
124
+ };
125
+
126
+ const baseStyles = {
127
+ button: {
128
+ flex: 1,
129
+ background: 'transparent',
130
+ border: 'none',
131
+ padding: '8px',
132
+ cursor: 'default',
133
+ display: 'flex',
134
+ flexDirection: 'column',
135
+ alignItems: 'center',
136
+ justifyContent: 'center',
137
+ color: '#999',
138
+ },
139
+ activeButton: {
140
+ color: '#0E904D',
141
+ fontWeight: 'bold'
142
+ },
143
+ label: {
144
+ fontSize: '12px',
145
+ marginTop: '4px'
146
+ }
147
+ };
148
+
149
+ return (
150
+ <div style={containerStyle}>
151
+ {navItems.map((item) => {
152
+ const isActive = activeTab === item.id;
153
+ return (
154
+ <button
155
+ key={item.id}
156
+ onClick={() => handleTabClick(item)}
157
+ style={{
158
+ ...baseStyles.button,
159
+ ...(isActive ? baseStyles.activeButton : {})
160
+ }}
161
+ >
162
+ <i
163
+ className={getIconClass(item.id, isActive)}
164
+ style={{ fontSize: '20px', marginBottom: '2px' }}
165
+ ></i>
166
+ <span style={baseStyles.label}>{item.label}</span>
167
+ </button>
168
+ );
169
+ })}
170
+ </div>
171
+ );
172
+ };
173
+
174
+ export default Welcome;
package/src/main.jsx ADDED
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { HashRouter } from 'react-router-dom';
4
+ import './app/assets/css/App.css';
5
+ import App from './App';
6
+
7
+ ReactDOM.createRoot(document.getElementById('root')).render(
8
+ <HashRouter>
9
+ <App />
10
+ </HashRouter>
11
+ );
package/vite.config.js ADDED
@@ -0,0 +1,32 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import path from 'path';
4
+
5
+ export default defineConfig({
6
+ plugins: [react({
7
+ include: '**/*.{jsx,js}',
8
+ })],
9
+
10
+ base: './',
11
+
12
+ define: {
13
+ 'process.env.VITE_FLEETBO_ENTERPRISE_ID': JSON.stringify(process.env.VITE_FLEETBO_ENTERPRISE_ID),
14
+ 'process.env.VITE_FLEETBO_KEY_APP': JSON.stringify(process.env.VITE_FLEETBO_KEY_APP),
15
+ },
16
+
17
+ resolve: {
18
+ alias: {
19
+ '@fleetbo': path.resolve(__dirname, 'src/@fleetbo'),
20
+ 'app': path.resolve(__dirname, './src/app'),
21
+ },
22
+ },
23
+
24
+ server: {
25
+ port: 3000,
26
+ host: '0.0.0.0',
27
+ },
28
+
29
+ build: {
30
+ outDir: 'build',
31
+ },
32
+ });