@ebubekirylmaz/link-test 1.2.43 → 1.2.45

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 (73) hide show
  1. package/package.json +9 -6
  2. package/src/Platform.jsx +14 -15
  3. package/src/config/schemas.js +1 -0
  4. package/src/context/Context.js +98 -0
  5. package/src/context/reducer.js +590 -10
  6. package/src/hooks/index.js +2 -1
  7. package/src/hooks/use-beta.js +8 -0
  8. package/src/layouts/auth/modern.jsx +4 -2
  9. package/src/layouts/common/account-popover.jsx +1 -2
  10. package/src/lib/APIDialogAction/APIDialogAction.jsx +109 -0
  11. package/src/lib/APIDialogAction/index.js +1 -0
  12. package/src/lib/APIDialogAction/styles.js +6 -0
  13. package/src/lib/APIParams/APIParams.jsx +57 -0
  14. package/src/lib/APIParams/index.js +1 -0
  15. package/src/lib/APIPath/APIPath.jsx +82 -0
  16. package/src/lib/APIPath/index.js +1 -0
  17. package/src/lib/APIPath/styles.js +19 -0
  18. package/src/lib/APITree/APITree.jsx +409 -0
  19. package/src/lib/APITree/Arrow.jsx +21 -0
  20. package/src/lib/APITree/DeleteMethodDialog.jsx +41 -0
  21. package/src/lib/APITree/index.js +1 -0
  22. package/src/lib/APITree/styles.js +19 -0
  23. package/src/lib/APITypes/APITypes.jsx +141 -0
  24. package/src/lib/APITypes/TypeEditor.jsx +46 -0
  25. package/src/lib/APITypes/TypeList.jsx +180 -0
  26. package/src/lib/APITypes/index.js +1 -0
  27. package/src/lib/BlankTreeMessage/BlankTreeMessage.jsx +39 -0
  28. package/src/lib/BlankTreeMessage/index.js +1 -0
  29. package/src/lib/DialogTootip/DialogTooltip.jsx +67 -0
  30. package/src/lib/DialogTootip/index.js +1 -0
  31. package/src/lib/DialogTootip/styles.js +9 -0
  32. package/src/lib/Flow/connectors/DynamicConnector.jsx +179 -107
  33. package/src/lib/Flow/core/Flow.jsx +2 -0
  34. package/src/lib/Flow/core/FlowNode.jsx +2 -0
  35. package/src/lib/Flow/core/FlowViewport.jsx +41 -9
  36. package/src/lib/Flow/hooks/useNodeStyle.js +14 -0
  37. package/src/lib/Flow/nodes/FlowNodeView.jsx +105 -21
  38. package/src/lib/Flow/styles.js +4 -0
  39. package/src/lib/NewApiBody/NewAPIBody.jsx +97 -0
  40. package/src/lib/NewApiBody/ParamView.jsx +38 -0
  41. package/src/lib/NucDialog/NucDialog.jsx +108 -0
  42. package/src/lib/NucDialog/index.js +1 -0
  43. package/src/lib/ParamTable/ParamTable.jsx +133 -0
  44. package/src/lib/ParamTable/TypeMenu.jsx +102 -0
  45. package/src/lib/ParamTable/defaults.js +47 -0
  46. package/src/lib/ParamTable/index.js +1 -0
  47. package/src/lib/ParamTable/styles.js +12 -0
  48. package/src/lib/ResourceMenu/AlertMassage.jsx +28 -0
  49. package/src/lib/ResourceMenu/DeleteResourceDialog.jsx +60 -0
  50. package/src/lib/ResourceMenu/ResourceMenu.jsx +156 -0
  51. package/src/lib/ResourceMenu/index.js +1 -0
  52. package/src/lib/ResourceMenu/styles.js +5 -0
  53. package/src/lib/Schema/Schema.jsx +204 -0
  54. package/src/lib/Schema/index.js +1 -0
  55. package/src/lib/SchemaEditor/SchemaEditor.jsx +258 -0
  56. package/src/lib/SchemaEditor/SchemaEditor.test.js +193 -0
  57. package/src/lib/SchemaEditor/SchemaPropertyEditor.jsx +135 -0
  58. package/src/lib/SchemaEditor/SchemaUtils.js +152 -0
  59. package/src/lib/SchemaEditor/index.js +1 -0
  60. package/src/lib/ToggleableMenu/ToggleableMenu.jsx +35 -0
  61. package/src/lib/ToggleableMenu/index.js +1 -0
  62. package/src/lib/index.js +14 -0
  63. package/src/pages/Callback.jsx +6 -8
  64. package/src/pages/LoginPage.jsx +3 -12
  65. package/src/stories/APITree.stories.jsx +429 -0
  66. package/src/stories/FlowChart.stories.jsx +1 -1
  67. package/src/templates/ActionTemplate.js +24 -0
  68. package/src/widgets/Login/CognitoLogin.jsx +212 -41
  69. package/src/widgets/Login/DemoLogin.jsx +9 -7
  70. package/src/widgets/Login/amplifyAuth.js +6 -6
  71. package/src/widgets/Login/amplifyConfig.js +3 -0
  72. package/src/widgets/LoginForm/LoginForm.jsx +8 -3
  73. package/src/widgets/SettingsDialog.jsx +171 -7
@@ -4,20 +4,56 @@ import { storage } from "@nucleoidjs/webstorage";
4
4
  import { useNavigate } from "react-router-dom";
5
5
  import { useState } from "react";
6
6
 
7
- import { Button, Stack, TextField, Typography } from "@mui/material";
7
+ import {
8
+ Box,
9
+ Button,
10
+ IconButton,
11
+ InputAdornment,
12
+ Stack,
13
+ TextField,
14
+ Typography,
15
+ alpha,
16
+ } from "@mui/material";
17
+ import {
18
+ CheckOutlined,
19
+ EmailOutlined,
20
+ LockOutlined,
21
+ MarkEmailReadOutlined,
22
+ Visibility,
23
+ VisibilityOff,
24
+ } from "@mui/icons-material";
8
25
  import { confirmSignup, getTokens, login, signup } from "./amplifyAuth";
9
26
 
27
+ const inputSx = {
28
+ "& .MuiOutlinedInput-root": {
29
+ fontSize: "1rem",
30
+ "& input": { py: 1.5 },
31
+ "&:hover fieldset": { borderColor: "primary.main" },
32
+ },
33
+ };
34
+
35
+ const primaryButtonSx = {
36
+ py: 1.5,
37
+ fontSize: "1rem",
38
+ fontWeight: 600,
39
+ textTransform: "none",
40
+ borderRadius: 1.5,
41
+ "&:active": { transform: "translateY(0px)" },
42
+ };
43
+
10
44
  export default function CognitoLogin() {
11
45
  const [mode, setMode] = useState("login");
12
46
 
13
- const [username, setUsername] = useState("");
14
47
  const [email, setEmail] = useState("");
15
48
  const [password, setPassword] = useState("");
49
+ const [confirmPassword, setConfirmPassword] = useState("");
16
50
  const [code, setCode] = useState("");
51
+ const [showPassword, setShowPassword] = useState(false);
52
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
17
53
 
18
54
  const navigate = useNavigate();
19
55
 
20
- const { appId } = config();
56
+ const { appId, credentials } = config();
21
57
 
22
58
  const handleLogin = async () => {
23
59
  try {
@@ -27,7 +63,9 @@ export default function CognitoLogin() {
27
63
  if (!tokens?.accessToken)
28
64
  throw new Error("No Cognito access token received");
29
65
 
30
- const res = await fetch("/api/oauth", {
66
+ const requestUrl = credentials.requestUrl || "/api/oauth";
67
+
68
+ const res = await fetch(requestUrl, {
31
69
  method: "POST",
32
70
  headers: { "Content-Type": "application/json" },
33
71
  body: JSON.stringify({
@@ -59,7 +97,7 @@ export default function CognitoLogin() {
59
97
 
60
98
  const handleSignup = async () => {
61
99
  try {
62
- await signup(email, password, password);
100
+ await signup(email, password);
63
101
  publish("GLOBAL_MESSAGE_POSTED", {
64
102
  status: true,
65
103
  message:
@@ -94,36 +132,128 @@ export default function CognitoLogin() {
94
132
  }
95
133
  };
96
134
 
135
+ const titles = {
136
+ login: { heading: "Sign in", sub: "Welcome back! Enter your credentials." },
137
+ signup: {
138
+ heading: "Create account",
139
+ sub: "Sign up to get started for free.",
140
+ },
141
+ confirm: {
142
+ heading: "Verify your email",
143
+ sub: "Enter the confirmation code sent to your inbox.",
144
+ },
145
+ };
146
+
147
+ const passwordAdornment = (
148
+ <InputAdornment position="end">
149
+ <IconButton
150
+ onClick={() => setShowPassword(!showPassword)}
151
+ edge="end"
152
+ size="small"
153
+ tabIndex={-1}
154
+ >
155
+ {showPassword ? <VisibilityOff /> : <Visibility />}
156
+ </IconButton>
157
+ </InputAdornment>
158
+ );
159
+
97
160
  return (
98
- <>
99
- <Typography variant="h4" gutterBottom>
100
- {mode === "login" && "Login"}
101
- {mode === "signup" && "Sign Up"}
102
- {mode === "confirm" && "Confirm Account"}
103
- </Typography>
161
+ <Stack spacing={3} sx={{ mb: 2 }}>
162
+ <Box sx={{ textAlign: "center" }}>
163
+ <Box
164
+ sx={{
165
+ width: 52,
166
+ height: 52,
167
+ borderRadius: 2,
168
+ display: "inline-flex",
169
+ alignItems: "center",
170
+ justifyContent: "center",
171
+ mb: 2,
172
+ bgcolor: (theme) => alpha(theme.palette.primary.main, 0.1),
173
+ color: "primary.main",
174
+ }}
175
+ >
176
+ {mode === "confirm" ? (
177
+ <MarkEmailReadOutlined sx={{ fontSize: 26 }} />
178
+ ) : (
179
+ <LockOutlined sx={{ fontSize: 26 }} />
180
+ )}
181
+ </Box>
182
+
183
+ <Typography variant="h4" fontWeight={700} gutterBottom>
184
+ {titles[mode].heading}
185
+ </Typography>
186
+ <Typography variant="body2" color="text.secondary">
187
+ {titles[mode].sub}
188
+ </Typography>
189
+ </Box>
104
190
 
105
191
  <Stack spacing={2}>
106
192
  <TextField
107
193
  label="Email"
108
194
  value={email}
109
195
  onChange={(e) => setEmail(e.target.value)}
196
+ fullWidth
197
+ InputProps={{
198
+ startAdornment: (
199
+ <InputAdornment position="start">
200
+ <EmailOutlined sx={{ color: "text.secondary", fontSize: 22 }} />
201
+ </InputAdornment>
202
+ ),
203
+ }}
204
+ sx={inputSx}
110
205
  />
111
206
 
112
- {mode === "signup" && (
207
+ {mode !== "confirm" && (
113
208
  <TextField
114
- type="password"
209
+ type={showPassword ? "text" : "password"}
115
210
  label="Password"
116
211
  value={password}
117
212
  onChange={(e) => setPassword(e.target.value)}
213
+ fullWidth
214
+ InputProps={{
215
+ startAdornment: (
216
+ <InputAdornment position="start">
217
+ <LockOutlined
218
+ sx={{ color: "text.secondary", fontSize: 22 }}
219
+ />
220
+ </InputAdornment>
221
+ ),
222
+ endAdornment: passwordAdornment,
223
+ }}
224
+ sx={inputSx}
118
225
  />
119
226
  )}
120
227
 
121
- {mode !== "confirm" && (
228
+ {mode === "signup" && (
122
229
  <TextField
123
- type="password"
124
- label="Password"
125
- value={password}
126
- onChange={(e) => setPassword(e.target.value)}
230
+ type={showConfirmPassword ? "text" : "password"}
231
+ label="Confirm Password"
232
+ value={confirmPassword}
233
+ onChange={(e) => setConfirmPassword(e.target.value)}
234
+ fullWidth
235
+ InputProps={{
236
+ startAdornment: (
237
+ <InputAdornment position="start">
238
+ <LockOutlined
239
+ sx={{ color: "text.secondary", fontSize: 22 }}
240
+ />
241
+ </InputAdornment>
242
+ ),
243
+ endAdornment: (
244
+ <InputAdornment position="end">
245
+ <IconButton
246
+ onClick={() => setShowConfirmPassword(!showConfirmPassword)}
247
+ edge="end"
248
+ size="small"
249
+ tabIndex={-1}
250
+ >
251
+ {showConfirmPassword ? <VisibilityOff /> : <Visibility />}
252
+ </IconButton>
253
+ </InputAdornment>
254
+ ),
255
+ }}
256
+ sx={inputSx}
127
257
  />
128
258
  )}
129
259
 
@@ -132,33 +262,74 @@ export default function CognitoLogin() {
132
262
  label="Confirmation Code"
133
263
  value={code}
134
264
  onChange={(e) => setCode(e.target.value)}
265
+ fullWidth
266
+ InputProps={{
267
+ startAdornment: (
268
+ <InputAdornment position="start">
269
+ <CheckOutlined
270
+ sx={{ color: "text.secondary", fontSize: 22 }}
271
+ />
272
+ </InputAdornment>
273
+ ),
274
+ }}
275
+ sx={inputSx}
135
276
  />
136
277
  )}
278
+ </Stack>
137
279
 
138
- {mode === "login" && (
139
- <>
140
- <Button variant="contained" onClick={handleLogin}>
141
- Login
142
- </Button>
143
- <Button onClick={() => setMode("signup")}>Create an account</Button>
144
- </>
145
- )}
146
-
147
- {mode === "signup" && (
148
- <>
149
- <Button variant="contained" onClick={handleSignup}>
150
- Sign Up
151
- </Button>
152
- <Button onClick={() => setMode("login")}>Back to login</Button>
153
- </>
154
- )}
280
+ {mode === "login" && (
281
+ <Stack spacing={1.5}>
282
+ <Button
283
+ variant="contained"
284
+ onClick={handleLogin}
285
+ size="large"
286
+ fullWidth
287
+ sx={primaryButtonSx}
288
+ >
289
+ Sign in
290
+ </Button>
291
+ <Button
292
+ onClick={() => setMode("signup")}
293
+ fullWidth
294
+ sx={{ textTransform: "none", fontWeight: 500 }}
295
+ >
296
+ Create an account
297
+ </Button>
298
+ </Stack>
299
+ )}
155
300
 
156
- {mode === "confirm" && (
157
- <Button variant="contained" onClick={handleConfirm}>
158
- Confirm
301
+ {mode === "signup" && (
302
+ <Stack spacing={1.5}>
303
+ <Button
304
+ variant="contained"
305
+ onClick={handleSignup}
306
+ size="large"
307
+ fullWidth
308
+ sx={primaryButtonSx}
309
+ >
310
+ Sign Up
159
311
  </Button>
160
- )}
161
- </Stack>
162
- </>
312
+ <Button
313
+ onClick={() => setMode("login")}
314
+ fullWidth
315
+ sx={{ textTransform: "none", fontWeight: 500 }}
316
+ >
317
+ Back to login
318
+ </Button>
319
+ </Stack>
320
+ )}
321
+
322
+ {mode === "confirm" && (
323
+ <Button
324
+ variant="contained"
325
+ onClick={handleConfirm}
326
+ size="large"
327
+ fullWidth
328
+ sx={primaryButtonSx}
329
+ >
330
+ Confirm
331
+ </Button>
332
+ )}
333
+ </Stack>
163
334
  );
164
335
  }
@@ -1,3 +1,7 @@
1
+ import config from "../../config/config";
2
+ import { storage } from "@nucleoidjs/webstorage";
3
+ import { useNavigate } from "react-router-dom";
4
+
1
5
  import {
2
6
  Box,
3
7
  Button,
@@ -16,20 +20,18 @@ import {
16
20
  } from "@mui/icons-material";
17
21
  import React, { useState } from "react";
18
22
 
19
- import config from "../../config/config";
20
- import { storage } from "@nucleoidjs/webstorage";
21
- import { useNavigate } from "react-router-dom";
22
-
23
23
  export default function DemoLogin() {
24
24
  const [username, setUsername] = useState("");
25
25
  const [password, setPassword] = useState("");
26
26
  const [showPassword, setShowPassword] = useState(false);
27
27
  const navigate = useNavigate();
28
28
 
29
- const { appId } = config();
29
+ const { appId, credentials } = config();
30
30
 
31
31
  async function handleLogin() {
32
- const res = await fetch("/api/oauth", {
32
+ const requestUrl = credentials.requestUrl || "/api/oauth";
33
+
34
+ const res = await fetch(requestUrl, {
33
35
  method: "POST",
34
36
  headers: { "Content-Type": "application/json" },
35
37
  body: JSON.stringify({
@@ -53,7 +55,7 @@ export default function DemoLogin() {
53
55
  }
54
56
 
55
57
  return (
56
- <Stack spacing={2.5}>
58
+ <Stack spacing={2.5} sx={{ mb: 2 }}>
57
59
  <Stack spacing={2}>
58
60
  <TextField
59
61
  label="Username"
@@ -6,13 +6,13 @@ import {
6
6
  signUp,
7
7
  } from "aws-amplify/auth";
8
8
 
9
- export async function login(username, password) {
10
- return signIn({ username, password });
9
+ export async function login(email, password) {
10
+ return signIn({ username: email, password });
11
11
  }
12
12
 
13
- export async function signup(username, password, email) {
13
+ export async function signup(email, password) {
14
14
  return signUp({
15
- username,
15
+ username: email,
16
16
  password,
17
17
  options: {
18
18
  userAttributes: {
@@ -22,9 +22,9 @@ export async function signup(username, password, email) {
22
22
  });
23
23
  }
24
24
 
25
- export async function confirmSignup(username, code) {
25
+ export async function confirmSignup(email, code) {
26
26
  return confirmSignUp({
27
- username,
27
+ username: email,
28
28
  confirmationCode: code,
29
29
  });
30
30
  }
@@ -14,6 +14,9 @@ export function configureAmplify() {
14
14
  userPoolId: credentials.userPoolId,
15
15
  userPoolClientId: credentials.clientId,
16
16
  region: credentials.region,
17
+ loginWith: {
18
+ email: true,
19
+ },
17
20
  },
18
21
  },
19
22
  });
@@ -18,12 +18,17 @@ const handleOAuthLogin = (
18
18
  };
19
19
 
20
20
  function LoginForm() {
21
- const { name, project } = config();
21
+ const { name, project, credentials } = config();
22
22
 
23
23
  const [email, setEmail] = useState("");
24
24
  const [password, setPassword] = useState("");
25
25
 
26
- const renderHead = (
26
+ const providerCheck =
27
+ credentials?.provider === "COGNITO" || credentials?.provider === "DEMO";
28
+
29
+ const hasContent = !providerCheck || !!project.nucleoid;
30
+
31
+ const renderHead = hasContent ? (
27
32
  <Stack spacing={2} sx={{ mb: 5 }}>
28
33
  <Typography variant="h4">Sign in to {name}</Typography>
29
34
  {project.nucleoid && (
@@ -34,7 +39,7 @@ function LoginForm() {
34
39
  </Stack>
35
40
  )}
36
41
  </Stack>
37
- );
42
+ ) : null;
38
43
 
39
44
  const renderForm = (
40
45
  <>
@@ -1,10 +1,19 @@
1
+ import Iconify from "../components/Iconify";
2
+ import config from "../config/config";
3
+ import { useEvent } from "@nucleoidai/react-event";
4
+ import useSettings from "../hooks/useSettings";
5
+ import { useSettingsContext } from "../components/settings/context";
6
+ import { useUser } from "../hooks/use-user";
7
+
1
8
  import {
2
9
  Avatar,
3
10
  Box,
11
+ Chip,
4
12
  FormControl,
5
13
  Grow,
6
14
  IconButton,
7
15
  InputLabel,
16
+ Link,
8
17
  List,
9
18
  ListItem,
10
19
  ListItemAvatar,
@@ -12,6 +21,7 @@ import {
12
21
  ListItemText,
13
22
  NativeSelect,
14
23
  Stack,
24
+ Switch,
15
25
  Tab,
16
26
  Tabs,
17
27
  TextField,
@@ -20,11 +30,17 @@ import {
20
30
  import { Button, Dialog, DialogActions, DialogContent } from "@mui/material";
21
31
  import React, { useEffect, useState } from "react";
22
32
 
23
- import Iconify from "../components/Iconify";
24
- import config from "../config/config";
25
- import { useEvent } from "@nucleoidai/react-event";
26
- import useSettings from "../hooks/useSettings";
27
- import { useUser } from "../hooks/use-user";
33
+ let pkg = {
34
+ name: "",
35
+ version: "",
36
+ description: "",
37
+ };
38
+
39
+ try {
40
+ pkg = require("../../../../../../package.json");
41
+ } catch (error) {
42
+ console.error("Failed to load package.json for About tab:", error);
43
+ }
28
44
 
29
45
  function a11yProps(index) {
30
46
  return {
@@ -42,6 +58,9 @@ const TabPanel = (props) => {
42
58
  const SettingsDialogTabs = ({ tabs }) => {
43
59
  const [value, setValue] = useState(0);
44
60
 
61
+ const hasPkgInfo =
62
+ pkg && (pkg.name || pkg.version || pkg.description) ? true : false;
63
+
45
64
  const handleChange = (event, newValue) => {
46
65
  setValue(newValue);
47
66
  };
@@ -95,13 +114,20 @@ const SettingsDialogTabs = ({ tabs }) => {
95
114
  sx={{ "& label": { color: "custom.grey" } }}
96
115
  {...a11yProps(1)}
97
116
  />
117
+ {hasPkgInfo && (
118
+ <Tab
119
+ label={"About"}
120
+ sx={{ "& label": { color: "custom.grey" } }}
121
+ {...a11yProps(2)}
122
+ />
123
+ )}
98
124
  {tabs?.map((tab, index) => (
99
125
  <Tab
100
126
  key={tab.label}
101
127
  iconPosition="start"
102
128
  label={tab.label}
103
129
  sx={{ "& label": { color: "custom.grey" } }}
104
- {...a11yProps(index + 2)}
130
+ {...a11yProps(index + (hasPkgInfo ? 3 : 2))}
105
131
  />
106
132
  ))}
107
133
  </Tabs>
@@ -112,8 +138,17 @@ const SettingsDialogTabs = ({ tabs }) => {
112
138
  <TabPanel value={value} index={1}>
113
139
  <Settings />
114
140
  </TabPanel>
141
+ {hasPkgInfo && (
142
+ <TabPanel value={value} index={2}>
143
+ <About />
144
+ </TabPanel>
145
+ )}
115
146
  {tabs?.map((tab, index) => (
116
- <TabPanel key={tab.label} value={value} index={index + 2}>
147
+ <TabPanel
148
+ key={tab.label}
149
+ value={value}
150
+ index={index + (hasPkgInfo ? 3 : 2)}
151
+ >
117
152
  <tab.panel />
118
153
  </TabPanel>
119
154
  ))}
@@ -249,6 +284,7 @@ const Permission = () => {
249
284
  const Settings = () => {
250
285
  const projectId = localStorage.getItem("projectId");
251
286
  const { settings, updateSettings } = useSettings(projectId);
287
+ const { beta, onUpdate } = useSettingsContext();
252
288
 
253
289
  const timeZones = [
254
290
  "Asia/Kolkata",
@@ -300,9 +336,137 @@ const Settings = () => {
300
336
  </NativeSelect>
301
337
  </FormControl>
302
338
  </ListItem>
339
+ <ListItem
340
+ sx={{
341
+ backgroundColor: "background.paper",
342
+ boxShadow: 1,
343
+ borderRadius: 1,
344
+ m: 1,
345
+ p: 2,
346
+ ":hover": { boxShadow: 3 },
347
+ transition: "all 0.2s ease-in-out",
348
+ }}
349
+ >
350
+ <ListItemText primary="Beta" secondary="Enable beta features" />
351
+ <Switch checked={beta} onChange={() => onUpdate("beta", !beta)} />
352
+ </ListItem>
303
353
  </List>
304
354
  </Stack>
305
355
  );
306
356
  };
307
357
 
358
+ const About = () => {
359
+ const iconSrc = config().template?.login?.icon || "";
360
+
361
+ const appName = pkg.name;
362
+ const version = pkg.version;
363
+ const description = pkg.description;
364
+
365
+ return (
366
+ <Stack direction="column" spacing={2} p={2}>
367
+ <Box
368
+ sx={{
369
+ p: 3,
370
+ borderRadius: 3,
371
+ border: "1px solid",
372
+ borderColor: "divider",
373
+ background:
374
+ "linear-gradient(135deg, rgba(255,255,255,0.06), rgba(255,255,255,0.01))",
375
+ }}
376
+ >
377
+ <Stack direction="row" spacing={2.5} alignItems="center">
378
+ <Avatar
379
+ src={iconSrc}
380
+ variant="rounded"
381
+ sx={{
382
+ width: 56,
383
+ height: 56,
384
+ borderRadius: 2,
385
+ bgcolor: "background.paper",
386
+ boxShadow: 1,
387
+ }}
388
+ >
389
+ <Iconify icon="solar:widget-bold-duotone" width={28} />
390
+ </Avatar>
391
+
392
+ <Box sx={{ flex: 1, minWidth: 0 }}>
393
+ <Typography variant="h5" fontWeight={600}>
394
+ {appName.toUpperCase()}
395
+ </Typography>
396
+
397
+ {description && (
398
+ <Typography
399
+ variant="body2"
400
+ color="text.secondary"
401
+ sx={{ mt: 0.5, maxWidth: 520 }}
402
+ >
403
+ {description}
404
+ </Typography>
405
+ )}
406
+
407
+ <Stack direction="row" spacing={1} sx={{ mt: 1 }}>
408
+ <Chip size="small" label={`v${version}`} />
409
+ </Stack>
410
+ </Box>
411
+ </Stack>
412
+
413
+ <Box
414
+ sx={{
415
+ my: 2,
416
+ borderBottom: "1px dashed",
417
+ borderColor: "divider",
418
+ }}
419
+ />
420
+
421
+ <Stack spacing={1.2}>
422
+ <InfoRow label="Version" value={version} />
423
+ <InfoRow label="Deployment" value="On-Premise" />
424
+ <InfoRow
425
+ label="Support"
426
+ value="support@greycollar.ai"
427
+ link="mailto:support@greycollar.ai"
428
+ />
429
+ <InfoRow
430
+ label="Documentation"
431
+ value="greycollar.ai/docs"
432
+ link="https://greycollar.ai/docs"
433
+ />
434
+ </Stack>
435
+
436
+ <Box
437
+ sx={{
438
+ mt: 3,
439
+ pt: 2,
440
+ borderTop: "1px dashed",
441
+ borderColor: "divider",
442
+ textAlign: "center",
443
+ }}
444
+ >
445
+ <Typography variant="caption" color="text.secondary">
446
+ © 2026 greycollar.ai. All rights reserved.
447
+ </Typography>
448
+ </Box>
449
+ </Box>
450
+ </Stack>
451
+ );
452
+ };
453
+
454
+ const InfoRow = ({ label, value, link }) => {
455
+ return (
456
+ <Stack direction="row" spacing={1.5} alignItems="center">
457
+ <Typography variant="body2" color="text.secondary" sx={{ minWidth: 110 }}>
458
+ {label}
459
+ </Typography>
460
+
461
+ {link ? (
462
+ <Link href={link} target="_blank" rel="noreferrer" underline="hover">
463
+ {value}
464
+ </Link>
465
+ ) : (
466
+ <Typography variant="body2">{value}</Typography>
467
+ )}
468
+ </Stack>
469
+ );
470
+ };
471
+
308
472
  export default SettingsDialog;