afront 1.0.19 → 1.0.20

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 (60) hide show
  1. package/build-prod/index.html +1 -1
  2. package/build-prod/offline.html +1024 -0
  3. package/build-prod/service-worker.js +1 -0
  4. package/build-prod/static/css/main-b7c0f6b3ea505263e9a2.css +1 -0
  5. package/build-prod/static/js/3-b7f6c4256e55d2c173ac.js +1 -0
  6. package/build-prod/static/js/41-d729186c867a4a7f5f49.js +1 -0
  7. package/build-prod/static/js/525-506bbc7da1b98d130181.js +1 -0
  8. package/build-prod/static/js/573-8fcda455788ea4aa45cd.js +1 -0
  9. package/build-prod/static/js/99-ccf46a26c15904ecb674.js +1 -0
  10. package/build-prod/static/js/main-34c866ebbc6fbc291b85.js +1 -0
  11. package/build-prod-ssr/3.ssr.prod.js +1 -1
  12. package/build-prod-ssr/41.ssr.prod.js +1 -1
  13. package/build-prod-ssr/525.ssr.prod.js +1 -0
  14. package/build-prod-ssr/573.ssr.prod.js +1 -1
  15. package/build-prod-ssr/ssr.prod.js +1 -1
  16. package/build-prod-ssr/static/css/main-b7c0f6b3ea505263e9a2.css +1 -0
  17. package/build-prod-static/index.html +1 -1
  18. package/build-prod-static/offline.html +1024 -0
  19. package/build-prod-static/service-worker.js +1 -0
  20. package/build-prod-static/static/css/main-b7c0f6b3ea505263e9a2.css +1 -0
  21. package/build-prod-static/static/js/23-c9124d3cac34021e3406.js +1 -0
  22. package/build-prod-static/static/js/303-0d29064b1a912a7441ef.js +1 -0
  23. package/build-prod-static/static/js/636-adf7ca5d4e262a755df3.js +1 -0
  24. package/build-prod-static/static/js/863-2c35feb0663baeecb6f8.js +1 -0
  25. package/build-prod-static/static/js/main-02c8c189d5bec6c0a7f1.js +1 -0
  26. package/install.js +2 -2
  27. package/package.json +5 -4
  28. package/server.js +2 -0
  29. package/src/ARoutes/AFRoutes.js +22 -5
  30. package/src/Api/api.config.js +266 -0
  31. package/src/Api/login.service.js +44 -0
  32. package/src/App.js +10 -9
  33. package/src/Components/Loading/LoadingIndicator.js +12 -0
  34. package/src/Components/Loading/LoadingIndicator.module.css +34 -0
  35. package/src/Components/Loading/LoadingSpinner.js +27 -0
  36. package/src/Components/Loading/LoadingSpinner.module.css +100 -0
  37. package/src/Components/RequireAuth.js +30 -0
  38. package/src/PageNotFound.js +1 -1
  39. package/src/Pages/Signup.js +230 -0
  40. package/src/Routes/ARoutes.js +47 -6
  41. package/src/Routes/ARoutesStatic.js +83 -0
  42. package/src/Static/appStatic.js +10 -20
  43. package/src/Static/indexStatic.js +3 -0
  44. package/src/Utils/LoadingContext.js +5 -0
  45. package/src/index.js +0 -4
  46. package/webpack.build-prod.js +13 -0
  47. package/webpack.dev.js +18 -0
  48. package/webpack.prod.js +11 -2
  49. package/webpack.ssr.prod.js +4 -0
  50. package/build-prod/static/css/main-7f7c4e72ce002df48179.css +0 -1
  51. package/build-prod/static/js/3-985f8801e97b3b2025e2.js +0 -1
  52. package/build-prod/static/js/41-a66fae84fd39cfb1e349.js +0 -1
  53. package/build-prod/static/js/573-f7791eb491c75b0ccfb7.js +0 -1
  54. package/build-prod/static/js/main-1cbe42ef1eb3d942625c.js +0 -1
  55. package/build-prod-ssr/static/css/main-7f7c4e72ce002df48179.css +0 -1
  56. package/build-prod-static/static/css/main-7f7c4e72ce002df48179.css +0 -1
  57. package/build-prod-static/static/js/23-9e69ffb20f982ca1ba75.js +0 -1
  58. package/build-prod-static/static/js/303-1b6136e5efb4925c5f98.js +0 -1
  59. package/build-prod-static/static/js/636-7f7bf68f9765bc200b86.js +0 -1
  60. package/build-prod-static/static/js/main-6c6f40c44813930620ed.js +0 -1
package/server.js CHANGED
@@ -7,6 +7,8 @@ const app = express();
7
7
  const PORT = process.env.PORT;
8
8
  const HOST = process.env.HOST;
9
9
 
10
+ app.set("trust proxy", 1);
11
+
10
12
  // Set up rate limiter: maximum of 100 requests per 15 minutes
11
13
  const limiter = RateLimit({
12
14
  windowMs: 15 * 60 * 1000, // 15 minutes
@@ -1,11 +1,28 @@
1
1
  import { lazy } from "react";
2
2
 
3
-
4
3
  const routes = [
5
- { path: "/", element: lazy(() => import("../Pages/Home.js")) },
6
- { path: "/support", element: lazy(() => import("../Pages/Support.js")) },
7
- { path: "/*", element: lazy(() => import("../PageNotFound.js")) }, // Page Not Found route
4
+ {
5
+ path: "/",
6
+ element: lazy(() => import("../Pages/Home.js")),
7
+ withHeaderFooter: true,
8
+ },
9
+ {
10
+ path: "/signup",
11
+ element: lazy(() => import("../Pages/Signup.js")),
12
+ withHeaderFooter: false,
13
+ },
14
+ {
15
+ path: "/support/s",
16
+ element: lazy(() => import("../Pages/Support.js")),
17
+ protected: true,
18
+ withHeaderFooter: true,
19
+ },
20
+ {
21
+ path: "/*",
22
+ element: lazy(() => import("../PageNotFound.js")),
23
+ withHeaderFooter: true,
24
+ }, // Page Not Found route
8
25
  // Add More Pages
9
26
  ];
10
27
 
11
- export default routes;
28
+ export default routes;
@@ -0,0 +1,266 @@
1
+ // Lightweight fetch-based API instance with interceptors and retry helper
2
+ const apiBase =
3
+ (typeof process !== "undefined" &&
4
+ process.env &&
5
+ process.env.REACT_APP_API_BASE) ||
6
+ (typeof window !== "undefined" &&
7
+ window.__env &&
8
+ window.__env.REACT_APP_API_BASE) ||
9
+ "http://localhost:4000/api/v1";
10
+
11
+ function isAbsoluteUrl(u) {
12
+ return /^https?:\/\//i.test(u);
13
+ }
14
+
15
+ function buildUrl(base, url, params) {
16
+ const full = isAbsoluteUrl(url) ? url : `${base}${url}`;
17
+ if (!params) return full;
18
+ const search = new URLSearchParams(params).toString();
19
+ return search ? `${full}${full.includes("?") ? "&" : "?"}${search}` : full;
20
+ }
21
+
22
+ function createInstance({
23
+ baseURL = apiBase,
24
+ timeout = 60000,
25
+ headers = { "Content-Type": "application/json" },
26
+ withCredentials = true,
27
+ } = {}) {
28
+ const requestHandlers = [];
29
+ const responseHandlers = [];
30
+
31
+ const interceptors = {
32
+ request: {
33
+ use: (fn) => {
34
+ requestHandlers.push(fn);
35
+ return requestHandlers.length - 1;
36
+ },
37
+ },
38
+ response: {
39
+ use: (onFulfilled, onRejected) => {
40
+ responseHandlers.push({ onFulfilled, onRejected });
41
+ return responseHandlers.length - 1;
42
+ },
43
+ },
44
+ };
45
+
46
+ async function runRequestHandlers(cfg) {
47
+ let c = { ...cfg };
48
+ for (const h of requestHandlers) {
49
+ // handlers may be sync or async
50
+ // allow them to modify/return the config
51
+ // if they return undefined, assume they mutated in place
52
+ // otherwise use returned value
53
+ // eslint-disable-next-line no-await-in-loop
54
+ const out = await h(c);
55
+ if (typeof out !== "undefined") c = out;
56
+ }
57
+ return c;
58
+ }
59
+
60
+ async function runResponseHandlers(resOrErr, success = true) {
61
+ let acc = resOrErr;
62
+ for (const h of responseHandlers) {
63
+ try {
64
+ if (success && typeof h.onFulfilled === "function") {
65
+ // eslint-disable-next-line no-await-in-loop
66
+ const out = await h.onFulfilled(acc);
67
+ if (typeof out !== "undefined") acc = out;
68
+ } else if (!success && typeof h.onRejected === "function") {
69
+ // eslint-disable-next-line no-await-in-loop
70
+ const out = await h.onRejected(acc);
71
+ if (typeof out !== "undefined") acc = out;
72
+ }
73
+ } catch (e) {
74
+ // if a handler throws, treat as rejection
75
+ acc = e;
76
+ success = false;
77
+ }
78
+ }
79
+ if (success) return acc;
80
+ throw acc;
81
+ }
82
+
83
+ async function request(cfg) {
84
+ const finalCfg = await runRequestHandlers({
85
+ baseURL,
86
+ timeout,
87
+ headers: { ...headers },
88
+ withCredentials,
89
+ ...cfg,
90
+ });
91
+
92
+ const url = buildUrl(
93
+ finalCfg.baseURL || baseURL,
94
+ finalCfg.url || finalCfg.path || "/",
95
+ finalCfg.params
96
+ );
97
+
98
+ const controller = new AbortController();
99
+ const to = finalCfg.timeout || timeout;
100
+ const timer = setTimeout(() => controller.abort(), to);
101
+
102
+ const fetchOpts = {
103
+ method: (finalCfg.method || "GET").toUpperCase(),
104
+ headers: finalCfg.headers || {},
105
+ signal: controller.signal,
106
+ };
107
+ if (finalCfg.withCredentials || finalCfg.withCredentials === undefined)
108
+ fetchOpts.credentials = "include";
109
+ if (finalCfg.body !== undefined && finalCfg.body !== null) {
110
+ fetchOpts.body = finalCfg.body;
111
+ }
112
+
113
+ try {
114
+ // log request for convenience
115
+ // allow user interceptors to already log as requested
116
+ const raw = await fetch(url, fetchOpts);
117
+ clearTimeout(timer);
118
+
119
+ // try to parse JSON, fallback to text
120
+ let data = null;
121
+ const ct = raw.headers.get("content-type") || "";
122
+ if (ct.includes("application/json")) {
123
+ try {
124
+ data = await raw.json();
125
+ } catch (e) {
126
+ data = null;
127
+ }
128
+ } else {
129
+ try {
130
+ data = await raw.text();
131
+ } catch (e) {
132
+ data = null;
133
+ }
134
+ }
135
+
136
+ const response = { ok: raw.ok, status: raw.status, data, raw };
137
+ return await runResponseHandlers(response, true);
138
+ } catch (err) {
139
+ clearTimeout(timer);
140
+ // network or abort
141
+ return await runResponseHandlers(err, false);
142
+ }
143
+ }
144
+
145
+ // convenience methods
146
+ const instance = {
147
+ interceptors,
148
+ request,
149
+ get: (url, cfg = {}) => request({ method: "GET", url, ...cfg }),
150
+ post: (url, body, cfg = {}) =>
151
+ request({
152
+ method: "POST",
153
+ url,
154
+ body:
155
+ typeof body === "object" && !(body instanceof FormData)
156
+ ? JSON.stringify(body)
157
+ : body,
158
+ headers: {
159
+ ...(cfg.headers || {}),
160
+ "Content-Type":
161
+ body instanceof FormData ? undefined : "application/json",
162
+ },
163
+ ...cfg,
164
+ }),
165
+ put: (url, body, cfg = {}) =>
166
+ request({
167
+ method: "PUT",
168
+ url,
169
+ body:
170
+ typeof body === "object" && !(body instanceof FormData)
171
+ ? JSON.stringify(body)
172
+ : body,
173
+ headers: {
174
+ ...(cfg.headers || {}),
175
+ "Content-Type":
176
+ body instanceof FormData ? undefined : "application/json",
177
+ },
178
+ ...cfg,
179
+ }),
180
+ patch: (url, body, cfg = {}) =>
181
+ request({
182
+ method: "PATCH",
183
+ url,
184
+ body:
185
+ typeof body === "object" && !(body instanceof FormData)
186
+ ? JSON.stringify(body)
187
+ : body,
188
+ headers: {
189
+ ...(cfg.headers || {}),
190
+ "Content-Type":
191
+ body instanceof FormData ? undefined : "application/json",
192
+ },
193
+ ...cfg,
194
+ }),
195
+ delete: (url, cfg = {}) => request({ method: "DELETE", url, ...cfg }),
196
+ };
197
+
198
+ return instance;
199
+ }
200
+
201
+ export function requestWithRetry(
202
+ fn,
203
+ { retries = 3, retryDelay = 1000, retryOn = (err) => true } = {}
204
+ ) {
205
+ return new Promise(async (resolve, reject) => {
206
+ let attempt = 0;
207
+ while (attempt < retries) {
208
+ try {
209
+ // fn should be a function returning a promise
210
+ const res = await fn();
211
+ return resolve(res);
212
+ } catch (err) {
213
+ attempt += 1;
214
+ if (attempt >= retries || !retryOn(err)) return reject(err);
215
+ // exponential backoff
216
+ // eslint-disable-next-line no-await-in-loop
217
+ await new Promise((r) => setTimeout(r, retryDelay * attempt));
218
+ }
219
+ }
220
+ reject(new Error("Retries exhausted"));
221
+ });
222
+ }
223
+
224
+ export const instance = createInstance({
225
+ baseURL: apiBase,
226
+ timeout: 60000,
227
+ headers: { "Content-Type": "application/json" },
228
+ withCredentials: true,
229
+ });
230
+
231
+ // expose defaults for logging/compatibility
232
+ instance.defaults = { baseURL: apiBase, timeout: 60000 };
233
+
234
+ // request logging interceptor
235
+ instance.interceptors.request.use((config) => {
236
+ try {
237
+ console.log(
238
+ `API Request -> ${(
239
+ (config.method || "").toString() || "GET"
240
+ ).toUpperCase()} ${config.url || config.path} (base: ${
241
+ instance.defaults.baseURL
242
+ }) (timeout: ${config.timeout || instance.defaults.timeout}ms)`
243
+ );
244
+ } catch (e) {
245
+ /* ignore */
246
+ }
247
+ return config;
248
+ });
249
+
250
+ // response / error logging interceptor
251
+ instance.interceptors.response.use(
252
+ (response) => response,
253
+ (error) => {
254
+ if (!error || !error.status) {
255
+ console.error(
256
+ "Network or CORS error when calling API:",
257
+ error && error.message ? error.message : error
258
+ );
259
+ } else {
260
+ console.warn("API response error:", error.status, error.data);
261
+ }
262
+ return Promise.reject(error);
263
+ }
264
+ );
265
+
266
+ export default instance;
@@ -0,0 +1,44 @@
1
+ import api from "./api.config.js";
2
+
3
+ const BASE = "/auth";
4
+
5
+ export async function login({ email, password }) {
6
+ return api.post(
7
+ `${BASE}/login`,
8
+ { email, password },
9
+ { withCredentials: true }
10
+ );
11
+ }
12
+
13
+ export async function signup({
14
+ email,
15
+ password,
16
+ name,
17
+ accountType,
18
+ companyName,
19
+ }) {
20
+ return api.post(
21
+ `${BASE}/signup`,
22
+ { email, password, name, accountType, companyName },
23
+ { withCredentials: true }
24
+ );
25
+ }
26
+
27
+ export async function checkSignup({ email }) {
28
+ return api.post(`${BASE}/check`, { email }, { withCredentials: true });
29
+ }
30
+
31
+ export async function logout() {
32
+ try {
33
+ await api.post(`${BASE}/logout`, null, { withCredentials: true });
34
+ } catch (e) {
35
+ /* ignore */
36
+ }
37
+ try {
38
+ localStorage.setItem("auth-event", String(Date.now()));
39
+ } catch (e) {}
40
+ window.dispatchEvent(new Event("auth-changed"));
41
+ return { ok: true };
42
+ }
43
+
44
+ export default { login, signup, logout, checkSignup };
package/src/App.js CHANGED
@@ -1,7 +1,6 @@
1
- import React from "react";
1
+ import React, { useState } from "react";
2
2
  import ARoutes from "./Routes/ARoutes";
3
- import Header from "./Components/Header/Header.js";
4
- import Footer from "./Components/Footer/Footer.js";
3
+ import LoadingContext from "./Utils/LoadingContext";
5
4
 
6
5
  const isProduction = process.env.NODE_ENV === "production";
7
6
 
@@ -9,20 +8,22 @@ const Tail = () => (
9
8
  <asggen
10
9
  style={{ display: "none" }}
11
10
  dangerouslySetInnerHTML={{
12
- __html: `Rendering Asggen DOM...</asggen></asggenapp></body></html>`,
11
+ __html: `Rendering Asggen DOM...</asggen></asggenapp><script>window.addEventListener('load', () => { const loadingElement = document.getElementById('loading'); if (loadingElement) { setTimeout(() => { loadingElement.style.display = 'none'; }, 00);}});</script><script>if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker .register("/service-worker.js") .then((registration) => { registration.scope }) .catch((error) => { console.error("Service Worker registration failed:", error); }); }); }</script></body></html>`,
13
12
  }}
14
13
  />
15
14
  );
16
15
 
17
16
  function App({ context }) {
17
+ const [loading, setLoading] = useState(false);
18
18
  return (
19
19
  <>
20
- <Header />
21
- <ARoutes context={context} />
22
- <Footer />
23
- {isProduction ? <Tail /> : null}
20
+ <LoadingContext.Provider value={{ loading, setLoading }}>
21
+ {loading && <LoadingIndicator />}
22
+ <ARoutes context={context} />
23
+ {isProduction ? <Tail /> : null}
24
+ </LoadingContext.Provider>
24
25
  </>
25
26
  );
26
27
  }
27
28
 
28
- export default App;
29
+ export default App;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import * as styles from "./LoadingIndicator.module.css";
3
+
4
+ const LoadingIndicator = () => {
5
+ return (
6
+ <div className={styles.loadingIndicator}>
7
+ <div className={styles.loadingLine}></div>
8
+ </div>
9
+ );
10
+ };
11
+
12
+ export default LoadingIndicator;
@@ -0,0 +1,34 @@
1
+ .loadingIndicator {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 0.25rem;
7
+ background-color: #4287f5;
8
+ z-index: 9998;
9
+ overflow: hidden;
10
+ }
11
+
12
+ .loadingLine {
13
+ width: 100%;
14
+ height: 100%;
15
+ background: linear-gradient(90deg, transparent, #84f9f1, transparent);
16
+ position: absolute;
17
+ animation: loadingAnimation 0.5s ease-in-out infinite;
18
+ }
19
+
20
+ @keyframes loadingAnimation {
21
+ 0% {
22
+ transform: translateX(-100%);
23
+ opacity: 0;
24
+ }
25
+
26
+ 50% {
27
+ opacity: 1;
28
+ }
29
+
30
+ 100% {
31
+ transform: translateX(100%);
32
+ opacity: 0;
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import * as styles from "./LoadingSpinner.module.css";
3
+
4
+ const LoadingSpinner = ({ size }) => {
5
+ const scaleFactor = size / 80;
6
+ return (
7
+ <div
8
+ style={{ transform: `scale(${scaleFactor})` }}
9
+ className={styles.ldsSpinner}
10
+ >
11
+ <div></div>
12
+ <div></div>
13
+ <div></div>
14
+ <div></div>
15
+ <div></div>
16
+ <div></div>
17
+ <div></div>
18
+ <div></div>
19
+ <div></div>
20
+ <div></div>
21
+ <div></div>
22
+ <div></div>
23
+ </div>
24
+ );
25
+ };
26
+
27
+ export default LoadingSpinner;
@@ -0,0 +1,100 @@
1
+ .ldsSpinner,
2
+ .ldsSpinner div,
3
+ .ldsSpinner div:after {
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ .ldsSpinner {
8
+ color: currentColor;
9
+ display: inline-block;
10
+ position: relative;
11
+ width: 80px;
12
+ height: 80px;
13
+ }
14
+
15
+ .ldsSpinner div {
16
+ transform-origin: 40px 40px;
17
+ animation: ldsSpinner 1.2s linear infinite;
18
+ }
19
+
20
+ .ldsSpinner div:after {
21
+ content: " ";
22
+ display: block;
23
+ position: absolute;
24
+ top: 3.2px;
25
+ left: 36.8px;
26
+ width: 6.4px;
27
+ height: 17.6px;
28
+ border-radius: 20%;
29
+ background: currentColor;
30
+ }
31
+
32
+ .ldsSpinner div:nth-child(1) {
33
+ transform: rotate(0deg);
34
+ animation-delay: -1.1s;
35
+ }
36
+
37
+ .ldsSpinner div:nth-child(2) {
38
+ transform: rotate(30deg);
39
+ animation-delay: -1s;
40
+ }
41
+
42
+ .ldsSpinner div:nth-child(3) {
43
+ transform: rotate(60deg);
44
+ animation-delay: -0.9s;
45
+ }
46
+
47
+ .ldsSpinner div:nth-child(4) {
48
+ transform: rotate(90deg);
49
+ animation-delay: -0.8s;
50
+ }
51
+
52
+ .ldsSpinner div:nth-child(5) {
53
+ transform: rotate(120deg);
54
+ animation-delay: -0.7s;
55
+ }
56
+
57
+ .ldsSpinner div:nth-child(6) {
58
+ transform: rotate(150deg);
59
+ animation-delay: -0.6s;
60
+ }
61
+
62
+ .ldsSpinner div:nth-child(7) {
63
+ transform: rotate(180deg);
64
+ animation-delay: -0.5s;
65
+ }
66
+
67
+ .ldsSpinner div:nth-child(8) {
68
+ transform: rotate(210deg);
69
+ animation-delay: -0.4s;
70
+ }
71
+
72
+ .ldsSpinner div:nth-child(9) {
73
+ transform: rotate(240deg);
74
+ animation-delay: -0.3s;
75
+ }
76
+
77
+ .ldsSpinner div:nth-child(10) {
78
+ transform: rotate(270deg);
79
+ animation-delay: -0.2s;
80
+ }
81
+
82
+ .ldsSpinner div:nth-child(11) {
83
+ transform: rotate(300deg);
84
+ animation-delay: -0.1s;
85
+ }
86
+
87
+ .ldsSpinner div:nth-child(12) {
88
+ transform: rotate(330deg);
89
+ animation-delay: 0s;
90
+ }
91
+
92
+ @keyframes ldsSpinner {
93
+ 0% {
94
+ opacity: 1;
95
+ }
96
+
97
+ 100% {
98
+ opacity: 0;
99
+ }
100
+ }
@@ -0,0 +1,30 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Navigate, Outlet, useLocation } from "react-router";
3
+ import auth from "../Api/login.service";
4
+
5
+ export default function RequireAuth({ children }) {
6
+ const location = useLocation();
7
+ const [status, setStatus] = useState("checking");
8
+
9
+ useEffect(() => {
10
+ let mounted = true;
11
+ (async () => {
12
+ try {
13
+ const res = await auth.getCurrentUser();
14
+ if (mounted && res?.data?.user) setStatus("authed");
15
+ else if (mounted) setStatus("unauth");
16
+ } catch (e) {
17
+ if (mounted) setStatus("unauth");
18
+ }
19
+ })();
20
+ return () => {
21
+ mounted = false;
22
+ };
23
+ }, []);
24
+
25
+ if (status === "checking") return null;
26
+ if (status === "unauth")
27
+ return <Navigate to="/login" state={{ from: location }} replace />;
28
+ // If children are provided, render them; otherwise render nested routes via Outlet
29
+ return children ? children : <Outlet />;
30
+ }
@@ -17,4 +17,4 @@ const PageNotFound = () => {
17
17
  );
18
18
  };
19
19
 
20
- export default PageNotFound;
20
+ export default PageNotFound;