@asaleh37/ui-base 25.9.17 → 25.9.24-1

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.
package/index.html CHANGED
@@ -2,6 +2,13 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
+ <link rel="manifest" href="/manifest.json" />
6
+ <meta name="mobile-web-app-capable" content="yes" />
7
+ <meta name="apple-mobile-web-app-capable" content="yes" />
8
+ <meta
9
+ name="apple-mobile-web-app-status-bar-style"
10
+ content="black-translucent"
11
+ />
5
12
  <link rel="icon" type="image/svg+xml" href="/logo.png" />
6
13
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
14
  <title>Loading...</title>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaleh37/ui-base",
3
- "version": "25.9.17",
3
+ "version": "25.9.24-1",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Ahmed Saleh Mohamed",
@@ -0,0 +1,21 @@
1
+ {
2
+ "short_name": "MyApp",
3
+ "name": "My React App",
4
+ "start_url": ".",
5
+ "display": "standalone",
6
+ "background_color": "#ffffff",
7
+ "theme_color": "#ffffff",
8
+ "orientation": "portrait",
9
+ "icons": [
10
+ {
11
+ "src": "icon-192x192.png",
12
+ "sizes": "192x192",
13
+ "type": "image/png"
14
+ },
15
+ {
16
+ "src": "icon-512x512.png",
17
+ "sizes": "512x512",
18
+ "type": "image/png"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,229 @@
1
+ import {
2
+ Box,
3
+ Button,
4
+ CircularProgress,
5
+ createTheme,
6
+ Paper,
7
+ TextField,
8
+ Typography,
9
+ } from "@mui/material";
10
+ import { useDispatch, useSelector } from "react-redux";
11
+ import { ThemeProvider } from "@emotion/react";
12
+ import { useEffect, useState } from "react";
13
+ import axios from "axios";
14
+ import { toast } from "react-toastify";
15
+ import { UserSessionActions } from "../../redux/features/common/UserSessionSlice";
16
+ import {
17
+ DARK_THEME_INITIAL_MAIN_COLOR,
18
+ DARK_THEME_INITIAL_SECANDARY_COLOR,
19
+ LIGHT_THEME_INITIAL_MAIN_COLOR,
20
+ LIGHT_THEME_INITIAL_SECANDARY_COLOR,
21
+ } from "../../util";
22
+ import { AppInfo } from "../../redux/features/common/AppInfoSlice";
23
+ const MobileLogin: React.FC = () => {
24
+ const appInfo: AppInfo = useSelector((state: any) => state.AppInfo.value);
25
+ const [username, setUsername] = useState("");
26
+ const [password, setPassword] = useState("");
27
+ const [isLoginInProcess, setIsLoginInProcess] = useState(false);
28
+ const UserSessionState = useSelector((state: any) => state.UserSession.value);
29
+ const dispatch = useDispatch();
30
+ const handleLogin = async () => {
31
+ if (username == null || username == "") {
32
+ toast.error("username is required to proceed");
33
+ return;
34
+ }
35
+ if (password == null || password == "") {
36
+ toast.error("password is required to proceed");
37
+ return;
38
+ }
39
+ setIsLoginInProcess(true);
40
+ let response: any = null;
41
+ try {
42
+ response = await axios.post(
43
+ `${appInfo.apiBaseUrl}/api/auth/login`,
44
+ {
45
+ username,
46
+ password,
47
+ },
48
+ {
49
+ withCredentials: true,
50
+ }
51
+ );
52
+ if (response.data != null && response.data !== "") {
53
+ setIsLoginInProcess(false);
54
+ const UserSession = {
55
+ ...response.data,
56
+ isAuthenticated: true,
57
+ };
58
+ if (response?.data?.token) {
59
+ localStorage.setItem("TOKEN", response.data.token);
60
+ }
61
+ dispatch(UserSessionActions.setAuthenticated(UserSession));
62
+ }
63
+ } catch (e: any) {
64
+ setIsLoginInProcess(false);
65
+ toast.error(
66
+ e?.response?.data ||
67
+ "failed to authenticate, contact your administrator"
68
+ );
69
+ }
70
+ };
71
+ const userSession = useSelector((state: any) => state.UserSession.value);
72
+ const loginTheme = createTheme({
73
+ components: {
74
+ MuiCssBaseline: {
75
+ styleOverrides: `
76
+ /* Custom Scrollbar */
77
+ * {
78
+ scrollbar-width: thin;
79
+ scrollbar-color: ${
80
+ appInfo.appTheme?.dark?.primaryColor ||
81
+ DARK_THEME_INITIAL_MAIN_COLOR
82
+ } #121212;
83
+ }
84
+
85
+ /* Webkit Browsers */
86
+ *::-webkit-scrollbar {
87
+ width: 12px;
88
+ height: 10px;
89
+ }
90
+ `,
91
+ },
92
+ },
93
+ palette: {
94
+ mode: appInfo?.loginScreenStyle?.themeMode || "dark",
95
+ primary: {
96
+ main:
97
+ appInfo?.loginScreenStyle?.themeMode === "light"
98
+ ? appInfo.appTheme?.light?.primaryColor ||
99
+ LIGHT_THEME_INITIAL_MAIN_COLOR
100
+ : appInfo.appTheme?.dark?.primaryColor ||
101
+ DARK_THEME_INITIAL_MAIN_COLOR,
102
+ },
103
+ secondary: {
104
+ main:
105
+ appInfo?.loginScreenStyle?.themeMode === "light"
106
+ ? appInfo.appTheme?.light?.secondaryColor ||
107
+ LIGHT_THEME_INITIAL_SECANDARY_COLOR
108
+ : appInfo.appTheme?.dark?.secondaryColor ||
109
+ DARK_THEME_INITIAL_SECANDARY_COLOR,
110
+ },
111
+ },
112
+ });
113
+ const checkUserSession = async () => {
114
+ if (appInfo?.apiBaseUrl) {
115
+ if (userSession.isAuthenticated == null) {
116
+ const token = localStorage.getItem("TOKEN");
117
+ try {
118
+ let response = await axios.get(
119
+ `${appInfo.apiBaseUrl}/api/auth/userInfo`,
120
+ {
121
+ withCredentials: true,
122
+ headers: { Authorization: `Bearer ${token}` },
123
+ }
124
+ );
125
+ if (response != null && response.data != null) {
126
+ const UserSession = {
127
+ ...response.data,
128
+ isAuthenticated: true,
129
+ };
130
+ dispatch(UserSessionActions.setAuthenticated(UserSession));
131
+ } else {
132
+ dispatch(UserSessionActions.setUnAuthenticated());
133
+ localStorage.removeItem("TOKEN");
134
+ }
135
+ } catch (error) {
136
+ dispatch(UserSessionActions.setUnAuthenticated());
137
+ }
138
+ }
139
+ }
140
+ };
141
+ useEffect(() => {
142
+ checkUserSession();
143
+ }, [appInfo, userSession.isAuthenticated]);
144
+
145
+ return (
146
+ <ThemeProvider theme={loginTheme}>
147
+ {UserSessionState.isAuthenticated == false ? (
148
+ <Paper
149
+ sx={{
150
+ display: "flex",
151
+ flexDirection: "column",
152
+ alignItems: "center",
153
+ justifyContent: "center",
154
+ height: "100vh",
155
+ width: "100%",
156
+ }}
157
+ >
158
+ <img src={appInfo?.appLogo} width={70} height={70} />
159
+ <Typography sx={{ m: 1 }} variant="caption" color="textSecondary">
160
+ {appInfo?.appName}
161
+ </Typography>
162
+ <Typography
163
+ sx={{
164
+ paddingRight: 1,
165
+ width: 200,
166
+ textAlign: "right",
167
+ fontSize: 10,
168
+ }}
169
+ variant="caption"
170
+ color="textSecondary"
171
+ >
172
+ V.{appInfo.appVersion}
173
+ </Typography>
174
+ <TextField
175
+ label="username"
176
+ sx={{ width: 200, m: 1 }}
177
+ value={username}
178
+ onChange={(event) => {
179
+ setUsername(event.target.value);
180
+ }}
181
+ onKeyDown={(event) => {
182
+ if (event.key === "Enter") {
183
+ handleLogin();
184
+ }
185
+ }}
186
+ />
187
+ <TextField
188
+ label="password"
189
+ sx={{ width: 200, m: 1 }}
190
+ value={password}
191
+ type="password"
192
+ onChange={(event) => {
193
+ setPassword(event.target.value);
194
+ }}
195
+ onKeyDown={(event) => {
196
+ if (event.key === "Enter") {
197
+ handleLogin();
198
+ }
199
+ }}
200
+ />
201
+ <Button
202
+ loading={isLoginInProcess}
203
+ onClick={handleLogin}
204
+ variant="contained"
205
+ color="primary"
206
+ sx={{ m: 1 }}
207
+ >
208
+ login
209
+ </Button>
210
+ </Paper>
211
+ ) : (
212
+ <Paper
213
+ sx={{
214
+ width: "100%",
215
+ height: "100vh",
216
+ display: "flex",
217
+ alignItems: "center",
218
+ justifyContent: "center",
219
+ }}
220
+ >
221
+ <CircularProgress sx={{ marginRight: 1 }} />
222
+ <div>You will be redirected shortly ... please wait</div>
223
+ </Paper>
224
+ )}
225
+ </ThemeProvider>
226
+ );
227
+ };
228
+
229
+ export default MobileLogin;
@@ -238,6 +238,7 @@ export type FormElementGroupProps = {
238
238
  formActions?: any;
239
239
  hiddenFields?: string[];
240
240
  disabledFields?: string[];
241
+ formValuesChangeCallBk?: FormValueChangeCallBk;
241
242
  };
242
243
 
243
244
  export type FormValueChangeCallBk = (
@@ -53,6 +53,7 @@ const FormElementGroup: React.FC<FormElementGroupProps> = (
53
53
  formActions={props.formActions}
54
54
  hiddenFields={props.hiddenFields}
55
55
  disabledFields={props.disabledFields}
56
+ formValuesChangeCallBk={props?.formValuesChangeCallBk}
56
57
  />
57
58
  );
58
59
  } else if (element.type === "field" && element.mode === "props") {
@@ -64,6 +65,7 @@ const FormElementGroup: React.FC<FormElementGroupProps> = (
64
65
  formActions={props.formActions}
65
66
  hiddenFields={props.hiddenFields}
66
67
  disabledFields={props.disabledFields}
68
+ formValuesChangeCallBk={props?.formValuesChangeCallBk}
67
69
  />
68
70
  );
69
71
  } else if (element.type === "field" && element.mode === "node") {
@@ -260,6 +260,7 @@ const TemplateForm: React.FC<TemplateFormProps> = (
260
260
  formActions={formActions}
261
261
  hiddenFields={hiddenFields}
262
262
  disabledFields={disabledFields}
263
+ formValuesChangeCallBk={props.formValuesChangeCallBk}
263
264
  />
264
265
  );
265
266
  } else if (
@@ -49,15 +49,6 @@ const ReportViewer: React.FC<ReportViewerProps> = (props) => {
49
49
  parameters: { reportCode: props.reportCode },
50
50
  successCallBkFn: (response: any) => {
51
51
  setReportInfo(response.data);
52
- if (
53
- props?.byPassParameterEntry === true ||
54
- response.data.reportParameters.length == 0
55
- ) {
56
- runReport(parametersValues);
57
- setReportViewerState("WAITING_RESULT");
58
- } else {
59
- setReportViewerState("WAITING_PARAMETER_INPUT");
60
- }
61
52
  },
62
53
  failureCallBkFn: (response) => {
63
54
  setErrorMessage(
@@ -68,6 +59,20 @@ const ReportViewer: React.FC<ReportViewerProps> = (props) => {
68
59
  });
69
60
  };
70
61
 
62
+ useEffect(() => {
63
+ if (reportInfo) {
64
+ if (
65
+ props?.byPassParameterEntry === true ||
66
+ reportInfo.reportParameters.length == 0
67
+ ) {
68
+ runReport(parametersValues);
69
+ setReportViewerState("WAITING_RESULT");
70
+ } else {
71
+ setReportViewerState("WAITING_PARAMETER_INPUT");
72
+ }
73
+ }
74
+ }, [reportInfo]);
75
+
71
76
  const runReport = async (params) => {
72
77
  setReportViewerState("WAITING_RESULT");
73
78
  if (reportInfo?.reportType === "Excel") {
@@ -98,7 +103,6 @@ const ReportViewer: React.FC<ReportViewerProps> = (props) => {
98
103
  successCallBkFn: (response: any) => {
99
104
  setReportViewerState("SHOWING_RESULT");
100
105
  const contentDisposition = response.headers["content-disposition"];
101
- debugger;
102
106
  let filename = "downloaded_file";
103
107
  if (
104
108
  contentDisposition &&
@@ -64,22 +64,13 @@ interface UseParameterPanelProps {
64
64
  }
65
65
 
66
66
  export const useParameterPanel = (props: UseParameterPanelProps) => {
67
- console.log("useParameterPanel", "useParameterPanel called");
68
67
  const [parametersValues, setParametersValues] = useState<any>({
69
68
  ...props?.initialParameterValues,
70
69
  });
71
- // const [panelElements, setPanelElements] = useState<Array<FormElementProps>>(
72
- // []
73
- // );
74
70
  const panelElements = getFormElementsFromParameters(
75
71
  props.parameters,
76
72
  parametersValues
77
73
  );
78
- // useEffect(() => {
79
- // setPanelElements(
80
- // getFormElementsFromParameters(props.parameters, parametersValues)
81
- // );
82
- // }, [props.parameters]);
83
74
 
84
75
  const ParameterPanel: React.FC<{
85
76
  searchBtnClickCallBk: (params: any) => Promise<void>;
@@ -146,7 +137,7 @@ export const useParameterPanel = (props: UseParameterPanelProps) => {
146
137
  setParametersValues({});
147
138
  }
148
139
  if (searchBtnClickCallBk) {
149
- await searchBtnClickCallBk(parametersValues);
140
+ await searchBtnClickCallBk(params);
150
141
  }
151
142
  },
152
143
  reloadData: async () => {},
@@ -17,6 +17,7 @@ import NoLicenseComponent from "../components/common/NoLicenseComponent";
17
17
  import { msalInstance } from "../components/msalConfig";
18
18
  import { PublicClientApplication } from "@azure/msal-browser";
19
19
  import AzureLogin from "../components/common/AzureLogin";
20
+ import MobileLogin from "../components/common/MobileLogin";
20
21
 
21
22
  const Main = styled("main", {
22
23
  shouldForwardProp: (prop) => prop !== "open",
@@ -89,8 +90,10 @@ const Layout: React.FC<LayoutProps> = ({ msalInstance }) => {
89
90
  </Main>
90
91
  ) : appInfo.authenticationMethod === "AZURE" ? (
91
92
  <AzureLogin msalInstance={msalInstance} />
92
- ) : (
93
+ ) : !isMobile ? (
93
94
  <Login />
95
+ ) : (
96
+ <MobileLogin />
94
97
  )}
95
98
  </BrowserRouter>
96
99
  );
@@ -6,7 +6,7 @@ import { cacheLtr, cacheRtl } from "../components/common/LayoutHandlers";
6
6
  import { SYSTEM_ROUTES } from "../routes";
7
7
  import { SystemRoute } from "../routes/types";
8
8
  import RouteWrapper from "./RouteWrapper";
9
- import { useAxios, useLoadingMask, useSession } from "../hooks";
9
+ import { useAxios, useIsMobile, useLoadingMask, useSession } from "../hooks";
10
10
  import { useEffect, useState } from "react";
11
11
  import { setStoreData } from "../redux/features/common/CommonStoreSlice";
12
12
 
@@ -57,6 +57,7 @@ const MainContent: React.FC = () => {
57
57
  loadCommonStores();
58
58
  }
59
59
  }, [commonStores.storeKeys, UserInfo?.currentOrganization]);
60
+ const isMobile = useIsMobile();
60
61
  return (
61
62
  <CacheProvider
62
63
  value={AppLayoutState.appDirection === "ltr" ? cacheLtr : cacheRtl}
@@ -70,7 +71,7 @@ const MainContent: React.FC = () => {
70
71
  justifyContent: "flex-start",
71
72
  flex: 1,
72
73
  overflow: "hidden",
73
- padding: 3,
74
+ padding: isMobile ? 1 : 3,
74
75
  }}
75
76
  >
76
77
  <Routes>
@@ -31,6 +31,7 @@ import { useIsMobile } from "../hooks/UseMobile";
31
31
  import useSession from "../hooks/UseSession";
32
32
  import { findNavigationItemById, NavigationItems } from "../navigationItems";
33
33
  import { DRAWER_WIDTH } from "../redux/features/common/AppLayoutSlice";
34
+ import { toggleSideBarState } from "../redux/features/common/SideBarSlice";
34
35
 
35
36
  function DotIcon() {
36
37
  return (
@@ -234,6 +235,9 @@ export default function NavigationTree() {
234
235
  const AppInfo = useSelector((state: any) => state.AppInfo.value);
235
236
  const dispatch = useDispatch();
236
237
  const isMobile = useIsMobile();
238
+ const toggleSideBar = () => {
239
+ dispatch(toggleSideBarState());
240
+ };
237
241
  const { isUserAuthorized, isCurrentOrganizationAuthorizedToModule } =
238
242
  useSession();
239
243
  const filterData = (data) => {
@@ -281,6 +285,9 @@ export default function NavigationTree() {
281
285
  navigationItem.children.length == 0)
282
286
  ) {
283
287
  navigate(navigationItem?.actionPayload?.path || "");
288
+ if (isMobile) {
289
+ toggleSideBar();
290
+ }
284
291
  }
285
292
  }}
286
293
  sx={{
@@ -167,20 +167,21 @@ const TopBar: React.FC = () => {
167
167
  >
168
168
  <FontAwesomeIcon icon="bars" />
169
169
  </IconButton>
170
- <AttachmentImageViewer
171
- showAsAvatar={true}
172
- attachmentCode="ORGANIZATION_LOGOS"
173
- refKey={UserSession?.value?.currentOrganization?.id + ""}
174
- onErrorImage="/logo.png"
175
- style={{
176
- marginRight: 1,
177
- marginLeft: 1,
178
- }}
179
- />
180
- {/* <Avatar
181
- src={AppInfo.appLogo}
182
- sx={{ marginRight: 1, marginLeft: 1 }}
183
- /> */}
170
+ {!isMobile ? (
171
+ <AttachmentImageViewer
172
+ showAsAvatar={true}
173
+ attachmentCode="ORGANIZATION_LOGOS"
174
+ refKey={UserSession?.value?.currentOrganization?.id + ""}
175
+ onErrorImage="/logo.png"
176
+ style={{
177
+ marginRight: 1,
178
+ marginLeft: 1,
179
+ }}
180
+ />
181
+ ) : (
182
+ <></>
183
+ )}
184
+
184
185
  <Typography variant="h6" noWrap component="div" sx={{ flex: 1 }}>
185
186
  {isMobile
186
187
  ? ""
@@ -260,14 +261,18 @@ const TopBar: React.FC = () => {
260
261
  ) : (
261
262
  <></>
262
263
  )}
263
- <div onClick={handleOpenUserMenu} style={{ cursor: "pointer" }}>
264
- <AttachmentImageViewer
265
- showAsAvatar={true}
266
- onErrorImage="/no_user.png"
267
- attachmentCode="EMPLOYEE_PHOTOS"
268
- refKey={UserSession.value?.id + ""}
269
- />
270
- </div>
264
+ {isMobile ? (
265
+ <div onClick={handleOpenUserMenu} style={{ cursor: "pointer" }}>
266
+ <AttachmentImageViewer
267
+ showAsAvatar={true}
268
+ onErrorImage="/no_user.png"
269
+ attachmentCode="EMPLOYEE_PHOTOS"
270
+ refKey={UserSession.value?.id + ""}
271
+ />
272
+ </div>
273
+ ) : (
274
+ <></>
275
+ )}
271
276
 
272
277
  <div
273
278
  style={{ marginLeft: 5, marginRight: 5, cursor: "pointer" }}
package/src/main.tsx CHANGED
@@ -3,7 +3,7 @@ import { BaseApp } from "./components";
3
3
 
4
4
  createRoot(document.getElementById("root")!).render(
5
5
  <BaseApp
6
- apiBaseUrl="http://localhost:8080/api-base"
6
+ apiBaseUrl="http://10.14.22.13:8080/api-base"
7
7
  enableUINotifications={false}
8
8
  appLogo={"/logo.png"}
9
9
  appName="UI Base Library"
@@ -12,7 +12,7 @@ createRoot(document.getElementById("root")!).render(
12
12
  backgroundImageNameInPublicFolder: "bg.jpg",
13
13
  }}
14
14
  appVersion="0.0"
15
- authenticationMethod="AZURE"
15
+ authenticationMethod="APP"
16
16
  azureConfiguration={{
17
17
  frontEndClientId: "c3bbbdbd-f392-4459-b3dd-2351cb07f924",
18
18
  tenantId: "9f136fef-4529-475f-98e6-d271eb04eb00",
@@ -32,11 +32,4 @@ export const findNavigationItemById = (
32
32
 
33
33
  export const NavigationItems: TreeViewBaseItem<ExtendedTreeItemProps>[] = [
34
34
  ...AdministrationItems,
35
- {
36
- id: "example",
37
- icon: "wind",
38
- action: "NAVIGATION",
39
- actionPayload: { path: "example" },
40
- label: "Example",
41
- },
42
35
  ];
@@ -2,7 +2,4 @@ import ExampleTrial from "../components/ExampleTrial";
2
2
  import { ADMINISTRATION_ROUTES } from "./administration";
3
3
  import { SystemRoute } from "./types";
4
4
 
5
- export const SYSTEM_ROUTES: Array<SystemRoute> = [
6
- ...ADMINISTRATION_ROUTES,
7
- { path: "example", component: ExampleTrial },
8
- ];
5
+ export const SYSTEM_ROUTES: Array<SystemRoute> = [...ADMINISTRATION_ROUTES];
package/vite.config.ts CHANGED
@@ -19,6 +19,6 @@ export default defineConfig({
19
19
  },
20
20
  },
21
21
  server: {
22
- port: 3000,
22
+ port: 8082,
23
23
  },
24
24
  });