@authhero/react-admin 0.15.0 → 0.17.0
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/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/src/App.spec.tsx +17 -10
- package/src/components/roles/edit.tsx +20 -0
- package/src/components/settings/edit.tsx +29 -1
- package/src/components/users/edit.tsx +69 -44
- package/vite.config.ts +11 -2
- package/src/components/TenantsAppBar.tsx +0 -21
- package/src/components/tenants/edit.tsx +0 -54
- package/src/components/themes/edit.tsx +0 -200
- package/src/components/themes/index.ts +0 -2
- package/src/components/themes/list.tsx +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @authhero/react-admin
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c8c83e3: Add a admin:organizations permission to hande organizations in the control_plane
|
|
8
|
+
|
|
9
|
+
## 0.16.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 17d73eb: Change name of organization flag and add OR support in lucence queries
|
|
14
|
+
- e542773: Fixes for syncing resources servers and global roles
|
|
15
|
+
|
|
3
16
|
## 0.15.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/package.json
CHANGED
package/src/App.spec.tsx
CHANGED
|
@@ -3,8 +3,8 @@ import { test, vi, expect } from "vitest";
|
|
|
3
3
|
import { App } from "./App";
|
|
4
4
|
|
|
5
5
|
// Mock all the react-admin components and dependencies
|
|
6
|
-
vi.mock(
|
|
7
|
-
const actual = await importOriginal() as any;
|
|
6
|
+
vi.mock("react-admin", async (importOriginal) => {
|
|
7
|
+
const actual = (await importOriginal()) as any;
|
|
8
8
|
return {
|
|
9
9
|
...actual,
|
|
10
10
|
Admin: ({ children }: any) => <div data-testid="admin">{children}</div>,
|
|
@@ -13,24 +13,31 @@ vi.mock('react-admin', async (importOriginal) => {
|
|
|
13
13
|
};
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
vi.mock(
|
|
17
|
-
getDataproviderForTenant: () =>
|
|
16
|
+
vi.mock("./dataProvider", () => ({
|
|
17
|
+
getDataproviderForTenant: () =>
|
|
18
|
+
Promise.resolve(() => Promise.resolve({ data: [] })),
|
|
18
19
|
getDataprovider: () => Promise.resolve(() => Promise.resolve({ data: [] })),
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
|
-
vi.mock(
|
|
22
|
+
vi.mock("./authProvider", () => ({
|
|
22
23
|
getAuthProvider: () => ({}),
|
|
23
24
|
}));
|
|
24
25
|
|
|
25
|
-
vi.mock(
|
|
26
|
-
getSelectedDomainFromStorage: () => ({ url:
|
|
26
|
+
vi.mock("./utils/domainUtils", () => ({
|
|
27
|
+
getSelectedDomainFromStorage: () => ({ url: "test.com", clientId: "test" }),
|
|
27
28
|
getDomainFromStorage: () => [],
|
|
28
29
|
buildUrlWithProtocol: (url: string) => `https://${url}`,
|
|
29
30
|
}));
|
|
30
31
|
|
|
31
|
-
vi.mock(
|
|
32
|
+
vi.mock("react-router-dom", () => ({
|
|
32
33
|
useNavigate: () => vi.fn(),
|
|
33
|
-
useLocation: () => ({ pathname:
|
|
34
|
+
useLocation: () => ({ pathname: "/" }),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// Mock color picker to avoid CSS import issues in tests
|
|
38
|
+
vi.mock("react-admin-color-picker", () => ({
|
|
39
|
+
ColorInput: () => null,
|
|
40
|
+
ColorField: () => null,
|
|
34
41
|
}));
|
|
35
42
|
|
|
36
43
|
test.skip("should pass", async () => {
|
|
@@ -38,5 +45,5 @@ test.skip("should pass", async () => {
|
|
|
38
45
|
render(<App tenantId="test" />);
|
|
39
46
|
|
|
40
47
|
// Just check that something renders
|
|
41
|
-
expect(screen.getByTestId(
|
|
48
|
+
expect(screen.getByTestId("admin")).toBeTruthy();
|
|
42
49
|
}, 10000);
|
|
@@ -268,6 +268,26 @@ const AddRolePermissionButton = () => {
|
|
|
268
268
|
</li>
|
|
269
269
|
)}
|
|
270
270
|
/>
|
|
271
|
+
{!loadingPermissions && availablePermissions.length > 0 && (
|
|
272
|
+
<Box sx={{ mt: 1, display: "flex", gap: 1 }}>
|
|
273
|
+
<Button
|
|
274
|
+
size="small"
|
|
275
|
+
variant="outlined"
|
|
276
|
+
onClick={() => setSelectedPermissions([...availablePermissions])}
|
|
277
|
+
disabled={selectedPermissions.length === availablePermissions.length}
|
|
278
|
+
>
|
|
279
|
+
Select All ({availablePermissions.length})
|
|
280
|
+
</Button>
|
|
281
|
+
<Button
|
|
282
|
+
size="small"
|
|
283
|
+
variant="outlined"
|
|
284
|
+
onClick={() => setSelectedPermissions([])}
|
|
285
|
+
disabled={selectedPermissions.length === 0}
|
|
286
|
+
>
|
|
287
|
+
Clear Selection
|
|
288
|
+
</Button>
|
|
289
|
+
</Box>
|
|
290
|
+
)}
|
|
271
291
|
</Box>
|
|
272
292
|
|
|
273
293
|
{!loadingPermissions && availablePermissions.length === 0 && (
|
|
@@ -9,9 +9,32 @@ import {
|
|
|
9
9
|
} from "react-admin";
|
|
10
10
|
import { Stack } from "@mui/material";
|
|
11
11
|
|
|
12
|
+
// Recursively remove null/undefined values from an object
|
|
13
|
+
function removeNullValues(obj: Record<string, unknown>): Record<string, unknown> {
|
|
14
|
+
const result: Record<string, unknown> = {};
|
|
15
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
16
|
+
if (value === null || value === undefined) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
20
|
+
const cleaned = removeNullValues(value as Record<string, unknown>);
|
|
21
|
+
if (Object.keys(cleaned).length > 0) {
|
|
22
|
+
result[key] = cleaned;
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
result[key] = value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
12
31
|
export function SettingsEdit() {
|
|
32
|
+
const transform = (data: Record<string, unknown>) => {
|
|
33
|
+
return removeNullValues(data);
|
|
34
|
+
};
|
|
35
|
+
|
|
13
36
|
return (
|
|
14
|
-
<Edit>
|
|
37
|
+
<Edit transform={transform}>
|
|
15
38
|
<TabbedForm>
|
|
16
39
|
<TabbedForm.Tab label="General">
|
|
17
40
|
<Stack spacing={2}>
|
|
@@ -215,6 +238,11 @@ export function SettingsEdit() {
|
|
|
215
238
|
source="flags.mfa_show_factor_list_on_enrollment"
|
|
216
239
|
label="MFA Show Factor List on Enrollment"
|
|
217
240
|
/>
|
|
241
|
+
<BooleanInput
|
|
242
|
+
source="flags.inherit_global_permissions_in_organizations"
|
|
243
|
+
label="Inherit Tenant Permissions in Organizations"
|
|
244
|
+
helperText="When enabled, tenant-level permissions will be inherited when users request organization-scoped tokens"
|
|
245
|
+
/>
|
|
218
246
|
</Stack>
|
|
219
247
|
</TabbedForm.Tab>
|
|
220
248
|
|
|
@@ -988,85 +988,105 @@ const UserRolesTable = ({
|
|
|
988
988
|
|
|
989
989
|
return (
|
|
990
990
|
<Box sx={{ mt: 2 }}>
|
|
991
|
-
<table
|
|
992
|
-
<thead>
|
|
993
|
-
<tr
|
|
994
|
-
<
|
|
995
|
-
|
|
991
|
+
<Box component="table" sx={{ width: "100%", borderCollapse: "collapse" }}>
|
|
992
|
+
<Box component="thead" sx={{ bgcolor: "action.hover" }}>
|
|
993
|
+
<tr>
|
|
994
|
+
<Box
|
|
995
|
+
component="th"
|
|
996
|
+
sx={{
|
|
996
997
|
padding: "12px",
|
|
997
998
|
textAlign: "left",
|
|
998
|
-
borderBottom:
|
|
999
|
+
borderBottom: 1,
|
|
1000
|
+
borderColor: "divider",
|
|
999
1001
|
}}
|
|
1000
1002
|
>
|
|
1001
1003
|
Role
|
|
1002
|
-
</
|
|
1003
|
-
<
|
|
1004
|
-
|
|
1004
|
+
</Box>
|
|
1005
|
+
<Box
|
|
1006
|
+
component="th"
|
|
1007
|
+
sx={{
|
|
1005
1008
|
padding: "12px",
|
|
1006
1009
|
textAlign: "left",
|
|
1007
|
-
borderBottom:
|
|
1010
|
+
borderBottom: 1,
|
|
1011
|
+
borderColor: "divider",
|
|
1008
1012
|
}}
|
|
1009
1013
|
>
|
|
1010
1014
|
Description
|
|
1011
|
-
</
|
|
1012
|
-
<
|
|
1013
|
-
|
|
1015
|
+
</Box>
|
|
1016
|
+
<Box
|
|
1017
|
+
component="th"
|
|
1018
|
+
sx={{
|
|
1014
1019
|
padding: "12px",
|
|
1015
1020
|
textAlign: "left",
|
|
1016
|
-
borderBottom:
|
|
1021
|
+
borderBottom: 1,
|
|
1022
|
+
borderColor: "divider",
|
|
1017
1023
|
}}
|
|
1018
1024
|
>
|
|
1019
1025
|
Organization
|
|
1020
|
-
</
|
|
1021
|
-
<
|
|
1022
|
-
|
|
1026
|
+
</Box>
|
|
1027
|
+
<Box
|
|
1028
|
+
component="th"
|
|
1029
|
+
sx={{
|
|
1023
1030
|
padding: "12px",
|
|
1024
1031
|
textAlign: "left",
|
|
1025
|
-
borderBottom:
|
|
1032
|
+
borderBottom: 1,
|
|
1033
|
+
borderColor: "divider",
|
|
1026
1034
|
}}
|
|
1027
1035
|
>
|
|
1028
1036
|
ID
|
|
1029
|
-
</
|
|
1030
|
-
<
|
|
1031
|
-
|
|
1037
|
+
</Box>
|
|
1038
|
+
<Box
|
|
1039
|
+
component="th"
|
|
1040
|
+
sx={{
|
|
1032
1041
|
padding: "12px",
|
|
1033
1042
|
textAlign: "left",
|
|
1034
|
-
borderBottom:
|
|
1043
|
+
borderBottom: 1,
|
|
1044
|
+
borderColor: "divider",
|
|
1035
1045
|
}}
|
|
1036
1046
|
>
|
|
1037
1047
|
Actions
|
|
1038
|
-
</
|
|
1048
|
+
</Box>
|
|
1039
1049
|
</tr>
|
|
1040
|
-
</
|
|
1050
|
+
</Box>
|
|
1041
1051
|
<tbody>
|
|
1042
1052
|
{roles.map((role) => (
|
|
1043
|
-
<
|
|
1053
|
+
<Box
|
|
1054
|
+
component="tr"
|
|
1044
1055
|
key={`${role.id}-${role.organization_id || "global"}`}
|
|
1045
|
-
|
|
1056
|
+
sx={{ borderBottom: 1, borderColor: "divider" }}
|
|
1046
1057
|
>
|
|
1047
|
-
<td
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1058
|
+
<Box component="td" sx={{ padding: "12px" }}>
|
|
1059
|
+
{role.name}
|
|
1060
|
+
</Box>
|
|
1061
|
+
<Box component="td" sx={{ padding: "12px" }}>
|
|
1062
|
+
{role.description || "-"}
|
|
1063
|
+
</Box>
|
|
1064
|
+
<Box component="td" sx={{ padding: "12px" }}>
|
|
1065
|
+
<Box
|
|
1066
|
+
component="span"
|
|
1067
|
+
sx={{
|
|
1068
|
+
color:
|
|
1069
|
+
role.role_context === "global"
|
|
1070
|
+
? "text.secondary"
|
|
1071
|
+
: "primary.main",
|
|
1053
1072
|
fontStyle:
|
|
1054
1073
|
role.role_context === "global" ? "italic" : "normal",
|
|
1055
1074
|
}}
|
|
1056
1075
|
>
|
|
1057
1076
|
{role.organization_name}
|
|
1058
|
-
</
|
|
1059
|
-
</
|
|
1060
|
-
<
|
|
1061
|
-
|
|
1077
|
+
</Box>
|
|
1078
|
+
</Box>
|
|
1079
|
+
<Box
|
|
1080
|
+
component="td"
|
|
1081
|
+
sx={{
|
|
1062
1082
|
padding: "12px",
|
|
1063
1083
|
fontFamily: "monospace",
|
|
1064
1084
|
fontSize: "0.9em",
|
|
1065
1085
|
}}
|
|
1066
1086
|
>
|
|
1067
1087
|
{role.id}
|
|
1068
|
-
</
|
|
1069
|
-
<td
|
|
1088
|
+
</Box>
|
|
1089
|
+
<Box component="td" sx={{ padding: "12px" }}>
|
|
1070
1090
|
<IconButton
|
|
1071
1091
|
onClick={() => handleRemoveRole(role)}
|
|
1072
1092
|
color="error"
|
|
@@ -1075,21 +1095,26 @@ const UserRolesTable = ({
|
|
|
1075
1095
|
>
|
|
1076
1096
|
<DeleteIcon />
|
|
1077
1097
|
</IconButton>
|
|
1078
|
-
</
|
|
1079
|
-
</
|
|
1098
|
+
</Box>
|
|
1099
|
+
</Box>
|
|
1080
1100
|
))}
|
|
1081
1101
|
{roles.length === 0 && (
|
|
1082
1102
|
<tr>
|
|
1083
|
-
<
|
|
1103
|
+
<Box
|
|
1104
|
+
component="td"
|
|
1084
1105
|
colSpan={5}
|
|
1085
|
-
|
|
1106
|
+
sx={{
|
|
1107
|
+
padding: "24px",
|
|
1108
|
+
textAlign: "center",
|
|
1109
|
+
color: "text.secondary",
|
|
1110
|
+
}}
|
|
1086
1111
|
>
|
|
1087
1112
|
No roles assigned
|
|
1088
|
-
</
|
|
1113
|
+
</Box>
|
|
1089
1114
|
</tr>
|
|
1090
1115
|
)}
|
|
1091
1116
|
</tbody>
|
|
1092
|
-
</
|
|
1117
|
+
</Box>
|
|
1093
1118
|
</Box>
|
|
1094
1119
|
);
|
|
1095
1120
|
};
|
package/vite.config.ts
CHANGED
|
@@ -12,10 +12,11 @@ export default defineConfig({
|
|
|
12
12
|
host: true,
|
|
13
13
|
},
|
|
14
14
|
base: "./",
|
|
15
|
-
//
|
|
15
|
+
// @ts-expect-error
|
|
16
16
|
test: {
|
|
17
17
|
environment: "jsdom", // Set JSDOM as the default test environment
|
|
18
18
|
globals: true, // Make test globals available
|
|
19
|
+
css: true, // Enable CSS processing for tests
|
|
19
20
|
env: {
|
|
20
21
|
VITE_AUTH0_API_URL: "http://localhost:3000",
|
|
21
22
|
VITE_AUTH0_DOMAIN: "test.auth0.com",
|
|
@@ -23,7 +24,15 @@ export default defineConfig({
|
|
|
23
24
|
server: {
|
|
24
25
|
deps: {
|
|
25
26
|
// Workaround for React Admin ES module issues
|
|
26
|
-
inline: [
|
|
27
|
+
inline: [
|
|
28
|
+
"ra-ui-materialui",
|
|
29
|
+
"ra-core",
|
|
30
|
+
"react-admin",
|
|
31
|
+
"@mui/material",
|
|
32
|
+
"@mui/icons-material",
|
|
33
|
+
"react-admin-color-picker",
|
|
34
|
+
"react-color",
|
|
35
|
+
],
|
|
27
36
|
},
|
|
28
37
|
},
|
|
29
38
|
},
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Create a custom AppBar specifically for TenantsApp
|
|
2
|
-
import { AppBar as ReactAdminAppBar, TitlePortal } from "react-admin";
|
|
3
|
-
import { Box } from "@mui/material";
|
|
4
|
-
|
|
5
|
-
interface TenantsAppBarProps {
|
|
6
|
-
domainSelectorButton?: React.ReactNode;
|
|
7
|
-
[key: string]: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function TenantsAppBar(props: TenantsAppBarProps) {
|
|
11
|
-
const { domainSelectorButton, ...rest } = props;
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<ReactAdminAppBar {...rest}>
|
|
15
|
-
<TitlePortal />
|
|
16
|
-
<Box sx={{ display: "flex", alignItems: "center", flex: 1, justifyContent: "flex-end" }}>
|
|
17
|
-
{domainSelectorButton}
|
|
18
|
-
</Box>
|
|
19
|
-
</ReactAdminAppBar>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DateField,
|
|
3
|
-
Edit,
|
|
4
|
-
FieldTitle,
|
|
5
|
-
Labeled,
|
|
6
|
-
SelectInput,
|
|
7
|
-
TabbedForm,
|
|
8
|
-
TextInput,
|
|
9
|
-
} from "react-admin";
|
|
10
|
-
import { ColorInput } from "react-admin-color-picker";
|
|
11
|
-
|
|
12
|
-
export function TenantsEdit() {
|
|
13
|
-
return (
|
|
14
|
-
<Edit>
|
|
15
|
-
<TabbedForm>
|
|
16
|
-
<TabbedForm.Tab label="Info">
|
|
17
|
-
<TextInput source="id" />
|
|
18
|
-
<TextInput source="name" />
|
|
19
|
-
<TextInput source="audience" label="Audience" />
|
|
20
|
-
<TextInput source="support_url" label="Support Url" />
|
|
21
|
-
<Labeled label={<FieldTitle source="created_at" />}>
|
|
22
|
-
<DateField source="created_at" showTime={true} />
|
|
23
|
-
</Labeled>
|
|
24
|
-
<Labeled label={<FieldTitle source="updated_at" />}>
|
|
25
|
-
<DateField source="updated_at" showTime={true} />
|
|
26
|
-
</Labeled>
|
|
27
|
-
</TabbedForm.Tab>
|
|
28
|
-
<TabbedForm.Tab label="Communication">
|
|
29
|
-
<TextInput source="sender_email" />
|
|
30
|
-
<TextInput source="sender_name" />
|
|
31
|
-
</TabbedForm.Tab>
|
|
32
|
-
<TabbedForm.Tab label="Style">
|
|
33
|
-
<SelectInput
|
|
34
|
-
source="language"
|
|
35
|
-
label="Languages"
|
|
36
|
-
choices={[
|
|
37
|
-
{ id: "en", name: "English" },
|
|
38
|
-
{ id: "nb", name: "Norwegian" },
|
|
39
|
-
{ id: "sv", name: "Swedish" },
|
|
40
|
-
{ id: "it", name: "Italian" },
|
|
41
|
-
{ id: "pl", name: "Polish" },
|
|
42
|
-
{ id: "da", name: "Danish" },
|
|
43
|
-
{ id: "cs", name: "Czech" },
|
|
44
|
-
{ id: "fi", name: "Finnish" },
|
|
45
|
-
]}
|
|
46
|
-
/>
|
|
47
|
-
<ColorInput source="primary_color" label="Primary Color" />
|
|
48
|
-
<ColorInput source="secondary_color" label="Secondary Color" />
|
|
49
|
-
<TextInput source="logo" label="Logo" />
|
|
50
|
-
</TabbedForm.Tab>
|
|
51
|
-
</TabbedForm>
|
|
52
|
-
</Edit>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Edit,
|
|
3
|
-
TextInput,
|
|
4
|
-
NumberInput,
|
|
5
|
-
BooleanInput,
|
|
6
|
-
SelectInput,
|
|
7
|
-
SimpleForm,
|
|
8
|
-
} from "react-admin";
|
|
9
|
-
import { ColorInput } from "react-admin-color-picker";
|
|
10
|
-
|
|
11
|
-
export function ThemesEdit() {
|
|
12
|
-
return (
|
|
13
|
-
<Edit>
|
|
14
|
-
<SimpleForm>
|
|
15
|
-
<TextInput source="displayName" label="Display Name" />
|
|
16
|
-
|
|
17
|
-
{/* Colors Section */}
|
|
18
|
-
<h3 style={{ marginTop: 24, marginBottom: 16 }}>Colors</h3>
|
|
19
|
-
<ColorInput source="colors.primary_button" label="Primary Button" />
|
|
20
|
-
<ColorInput
|
|
21
|
-
source="colors.primary_button_label"
|
|
22
|
-
label="Primary Button Label"
|
|
23
|
-
/>
|
|
24
|
-
<ColorInput
|
|
25
|
-
source="colors.secondary_button_border"
|
|
26
|
-
label="Secondary Button Border"
|
|
27
|
-
/>
|
|
28
|
-
<ColorInput
|
|
29
|
-
source="colors.secondary_button_label"
|
|
30
|
-
label="Secondary Button Label"
|
|
31
|
-
/>
|
|
32
|
-
<ColorInput source="colors.base_focus_color" label="Base Focus Color" />
|
|
33
|
-
<ColorInput source="colors.base_hover_color" label="Base Hover Color" />
|
|
34
|
-
<ColorInput source="colors.body_text" label="Body Text" />
|
|
35
|
-
<SelectInput
|
|
36
|
-
source="colors.captcha_widget_theme"
|
|
37
|
-
label="Captcha Widget Theme"
|
|
38
|
-
choices={[{ id: "auto", name: "Auto" }]}
|
|
39
|
-
/>
|
|
40
|
-
<ColorInput source="colors.error" label="Error" />
|
|
41
|
-
<ColorInput source="colors.header" label="Header" />
|
|
42
|
-
<ColorInput source="colors.icons" label="Icons" />
|
|
43
|
-
<ColorInput source="colors.input_background" label="Input Background" />
|
|
44
|
-
<ColorInput source="colors.input_border" label="Input Border" />
|
|
45
|
-
<ColorInput
|
|
46
|
-
source="colors.input_filled_text"
|
|
47
|
-
label="Input Filled Text"
|
|
48
|
-
/>
|
|
49
|
-
<ColorInput
|
|
50
|
-
source="colors.input_labels_placeholders"
|
|
51
|
-
label="Input Labels/Placeholders"
|
|
52
|
-
/>
|
|
53
|
-
<ColorInput
|
|
54
|
-
source="colors.links_focused_components"
|
|
55
|
-
label="Links/Focused Components"
|
|
56
|
-
/>
|
|
57
|
-
<ColorInput source="colors.success" label="Success" />
|
|
58
|
-
<ColorInput
|
|
59
|
-
source="colors.widget_background"
|
|
60
|
-
label="Widget Background"
|
|
61
|
-
/>
|
|
62
|
-
<ColorInput source="colors.widget_border" label="Widget Border" />
|
|
63
|
-
|
|
64
|
-
{/* Borders Section */}
|
|
65
|
-
<h3 style={{ marginTop: 24, marginBottom: 16 }}>Borders</h3>
|
|
66
|
-
<NumberInput
|
|
67
|
-
source="borders.button_border_radius"
|
|
68
|
-
label="Button Border Radius"
|
|
69
|
-
/>
|
|
70
|
-
<NumberInput
|
|
71
|
-
source="borders.button_border_weight"
|
|
72
|
-
label="Button Border Weight"
|
|
73
|
-
/>
|
|
74
|
-
<SelectInput
|
|
75
|
-
source="borders.buttons_style"
|
|
76
|
-
label="Buttons Style"
|
|
77
|
-
choices={[
|
|
78
|
-
{ id: "pill", name: "Pill" },
|
|
79
|
-
{ id: "rounded", name: "Rounded" },
|
|
80
|
-
{ id: "sharp", name: "Sharp" },
|
|
81
|
-
]}
|
|
82
|
-
/>
|
|
83
|
-
<NumberInput
|
|
84
|
-
source="borders.input_border_radius"
|
|
85
|
-
label="Input Border Radius"
|
|
86
|
-
/>
|
|
87
|
-
<NumberInput
|
|
88
|
-
source="borders.input_border_weight"
|
|
89
|
-
label="Input Border Weight"
|
|
90
|
-
/>
|
|
91
|
-
<SelectInput
|
|
92
|
-
source="borders.inputs_style"
|
|
93
|
-
label="Inputs Style"
|
|
94
|
-
choices={[
|
|
95
|
-
{ id: "pill", name: "Pill" },
|
|
96
|
-
{ id: "rounded", name: "Rounded" },
|
|
97
|
-
{ id: "sharp", name: "Sharp" },
|
|
98
|
-
]}
|
|
99
|
-
/>
|
|
100
|
-
<BooleanInput
|
|
101
|
-
source="borders.show_widget_shadow"
|
|
102
|
-
label="Show Widget Shadow"
|
|
103
|
-
/>
|
|
104
|
-
<NumberInput
|
|
105
|
-
source="borders.widget_border_weight"
|
|
106
|
-
label="Widget Border Weight"
|
|
107
|
-
/>
|
|
108
|
-
<NumberInput
|
|
109
|
-
source="borders.widget_corner_radius"
|
|
110
|
-
label="Widget Corner Radius"
|
|
111
|
-
/>
|
|
112
|
-
|
|
113
|
-
{/* Fonts Section */}
|
|
114
|
-
<h3 style={{ marginTop: 24, marginBottom: 16 }}>Fonts</h3>
|
|
115
|
-
<TextInput source="fonts.font_url" label="Font URL" fullWidth />
|
|
116
|
-
<NumberInput
|
|
117
|
-
source="fonts.reference_text_size"
|
|
118
|
-
label="Reference Text Size"
|
|
119
|
-
/>
|
|
120
|
-
|
|
121
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Body Text</h4>
|
|
122
|
-
<BooleanInput source="fonts.body_text.bold" label="Bold" />
|
|
123
|
-
<NumberInput source="fonts.body_text.size" label="Size" />
|
|
124
|
-
|
|
125
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Button Text</h4>
|
|
126
|
-
<BooleanInput source="fonts.buttons_text.bold" label="Bold" />
|
|
127
|
-
<NumberInput source="fonts.buttons_text.size" label="Size" />
|
|
128
|
-
|
|
129
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Input Labels</h4>
|
|
130
|
-
<BooleanInput source="fonts.input_labels.bold" label="Bold" />
|
|
131
|
-
<NumberInput source="fonts.input_labels.size" label="Size" />
|
|
132
|
-
|
|
133
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Links</h4>
|
|
134
|
-
<BooleanInput source="fonts.links.bold" label="Bold" />
|
|
135
|
-
<NumberInput source="fonts.links.size" label="Size" />
|
|
136
|
-
<SelectInput
|
|
137
|
-
source="fonts.links_style"
|
|
138
|
-
label="Links Style"
|
|
139
|
-
choices={[
|
|
140
|
-
{ id: "normal", name: "Normal" },
|
|
141
|
-
{ id: "underlined", name: "Underlined" },
|
|
142
|
-
]}
|
|
143
|
-
/>
|
|
144
|
-
|
|
145
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Subtitle</h4>
|
|
146
|
-
<BooleanInput source="fonts.subtitle.bold" label="Bold" />
|
|
147
|
-
<NumberInput source="fonts.subtitle.size" label="Size" />
|
|
148
|
-
|
|
149
|
-
<h4 style={{ marginTop: 16, marginBottom: 8 }}>Title</h4>
|
|
150
|
-
<BooleanInput source="fonts.title.bold" label="Bold" />
|
|
151
|
-
<NumberInput source="fonts.title.size" label="Size" />
|
|
152
|
-
|
|
153
|
-
{/* Page Background Section */}
|
|
154
|
-
<h3 style={{ marginTop: 24, marginBottom: 16 }}>Page Background</h3>
|
|
155
|
-
<ColorInput
|
|
156
|
-
source="page_background.background_color"
|
|
157
|
-
label="Background Color"
|
|
158
|
-
/>
|
|
159
|
-
<TextInput
|
|
160
|
-
source="page_background.background_image_url"
|
|
161
|
-
label="Background Image URL"
|
|
162
|
-
fullWidth
|
|
163
|
-
/>
|
|
164
|
-
<SelectInput
|
|
165
|
-
source="page_background.page_layout"
|
|
166
|
-
label="Page Layout"
|
|
167
|
-
choices={[{ id: "center", name: "Center" }]}
|
|
168
|
-
/>
|
|
169
|
-
|
|
170
|
-
{/* Widget Section */}
|
|
171
|
-
<h3 style={{ marginTop: 24, marginBottom: 16 }}>Widget</h3>
|
|
172
|
-
<SelectInput
|
|
173
|
-
source="widget.header_text_alignment"
|
|
174
|
-
label="Header Text Alignment"
|
|
175
|
-
choices={[{ id: "center", name: "Center" }]}
|
|
176
|
-
/>
|
|
177
|
-
<NumberInput source="widget.logo_height" label="Logo Height" />
|
|
178
|
-
<SelectInput
|
|
179
|
-
source="widget.logo_position"
|
|
180
|
-
label="Logo Position"
|
|
181
|
-
choices={[
|
|
182
|
-
{ id: "center", name: "Center" },
|
|
183
|
-
{ id: "left", name: "Left" },
|
|
184
|
-
{ id: "none", name: "None" },
|
|
185
|
-
{ id: "right", name: "Right" },
|
|
186
|
-
]}
|
|
187
|
-
/>
|
|
188
|
-
<TextInput source="widget.logo_url" label="Logo URL" fullWidth />
|
|
189
|
-
<SelectInput
|
|
190
|
-
source="widget.social_buttons_layout"
|
|
191
|
-
label="Social Buttons Layout"
|
|
192
|
-
choices={[
|
|
193
|
-
{ id: "bottom", name: "Bottom" },
|
|
194
|
-
{ id: "top", name: "Top" },
|
|
195
|
-
]}
|
|
196
|
-
/>
|
|
197
|
-
</SimpleForm>
|
|
198
|
-
</Edit>
|
|
199
|
-
);
|
|
200
|
-
}
|