@rebasepro/admin 0.1.2 → 0.2.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/LICENSE +21 -6
- package/dist/{CollectionEditorDialog-ywdxhs1L.js → CollectionEditorDialog-BXIh2AXg.js} +40 -31
- package/dist/CollectionEditorDialog-BXIh2AXg.js.map +1 -0
- package/dist/{CollectionsStudioView-BDzMFzqH.js → CollectionsStudioView-jR8iz_ja.js} +6 -8
- package/dist/CollectionsStudioView-jR8iz_ja.js.map +1 -0
- package/dist/{ContentHomePage-0tHuEIm_.js → ContentHomePage-BQZWuOFb.js} +5 -7
- package/dist/ContentHomePage-BQZWuOFb.js.map +1 -0
- package/dist/{ExportCollectionAction-BIrq92To.js → ExportCollectionAction-CMdiiv1L.js} +36 -38
- package/dist/ExportCollectionAction-CMdiiv1L.js.map +1 -0
- package/dist/{ImportCollectionAction-h8yg_To8.js → ImportCollectionAction-C05lE0IW.js} +5 -7
- package/dist/ImportCollectionAction-C05lE0IW.js.map +1 -0
- package/dist/{PropertyEditView-BuZrNnBN.js → PropertyEditView-BB5xjnhZ.js} +261 -165
- package/dist/PropertyEditView-BB5xjnhZ.js.map +1 -0
- package/dist/{RolesView-CMPsaIXo.js → RolesView-CULIHWZ9.js} +22 -11
- package/dist/RolesView-CULIHWZ9.js.map +1 -0
- package/dist/{UsersView-BkeblMVT.js → UsersView-D7_AtJ44.js} +7 -71
- package/dist/UsersView-D7_AtJ44.js.map +1 -0
- package/dist/collection_editor/ui/collection_editor/LayoutModeSwitch.d.ts +2 -2
- package/dist/collection_editor/ui/collection_editor/properties/VectorPropertyField.d.ts +3 -0
- package/dist/collection_editor_ui.js +5 -5
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionListView.d.ts +18 -2
- package/dist/components/EntityCollectionView/FilterPresetsButton.d.ts +21 -0
- package/dist/components/EntityDetailView.d.ts +31 -0
- package/dist/components/EntityEditView.d.ts +3 -2
- package/dist/components/ReferenceTable/EntitySelectionTable.d.ts +1 -1
- package/dist/components/admin/CreationResultDialog.d.ts +5 -0
- package/dist/components/admin/RolesFilterSelect.d.ts +2 -0
- package/dist/components/admin/UserRolesSelectField.d.ts +2 -0
- package/dist/components/common/default_entity_actions.d.ts +7 -1
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/data_import/utils/data.d.ts +1 -1
- package/dist/data_import/utils/file_headers.d.ts +6 -1
- package/dist/data_import/utils/file_to_json.d.ts +1 -11
- package/dist/data_import/utils/transforms.d.ts +11 -0
- package/dist/editor.js +2 -4
- package/dist/editor.js.map +1 -1
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/field_bindings/RelationFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/VectorFieldBinding.d.ts +11 -0
- package/dist/form/index.d.ts +1 -0
- package/dist/hooks/navigation/useResolvedViews.d.ts +2 -1
- package/dist/{index-CS6uJ7oW.js → index-BAM9KCmM.js} +4 -6
- package/dist/index-BAM9KCmM.js.map +1 -0
- package/dist/{index-BuZaHcyc.js → index-CoSNm3e3.js} +3 -3
- package/dist/index-CoSNm3e3.js.map +1 -0
- package/dist/{index-eRJbMvHi.js → index-D5OQhv-T.js} +3 -3
- package/dist/index-D5OQhv-T.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +352 -148
- package/dist/index.js.map +1 -1
- package/dist/types/components/EntityFormActionsProps.d.ts +1 -1
- package/dist/types/components/EntityFormProps.d.ts +2 -1
- package/dist/types/fields.d.ts +3 -3
- package/dist/util/navigation_utils.d.ts +1 -1
- package/dist/{util-zfU1zOCX.js → util-DtbWD7LF.js} +5304 -2572
- package/dist/util-DtbWD7LF.js.map +1 -0
- package/package.json +45 -39
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
- package/src/collection_editor/ui/AddKanbanColumnAction.tsx +12 -2
- package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +1 -2
- package/src/collection_editor/ui/EditorCollectionAction.tsx +1 -2
- package/src/collection_editor/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/collection_editor/ui/EditorEntityAction.tsx +1 -2
- package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +1 -2
- package/src/collection_editor/ui/NewCollectionButton.tsx +1 -2
- package/src/collection_editor/ui/NewCollectionCard.tsx +4 -6
- package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +10 -2
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +18 -2
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +22 -7
- package/src/collection_editor/ui/collection_editor/CollectionEditorWelcomeView.tsx +16 -2
- package/src/collection_editor/ui/collection_editor/CollectionJsonImportDialog.tsx +19 -9
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +13 -2
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +24 -2
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +22 -3
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +11 -2
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +12 -2
- package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +16 -3
- package/src/collection_editor/ui/collection_editor/EnumForm.tsx +17 -2
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +18 -2
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/KanbanConfigSection.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/LayoutModeSwitch.tsx +17 -5
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +32 -6
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +7 -7
- package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +14 -2
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +16 -2
- package/src/collection_editor/ui/collection_editor/ViewModeSwitch.tsx +9 -2
- package/src/collection_editor/ui/collection_editor/properties/BlockPropertyField.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/properties/MarkdownPropertyField.tsx +9 -2
- package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +11 -2
- package/src/collection_editor/ui/collection_editor/properties/VectorPropertyField.tsx +34 -0
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +15 -7
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +1 -2
- package/src/collection_editor/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +15 -3
- package/src/collection_editor/ui/collection_editor/properties/conditions/property_paths.ts +1 -1
- package/src/collection_editor/ui/collection_editor/properties/validation/ValidationPanel.tsx +1 -2
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +0 -2
- package/src/collection_editor/validateCollectionJson.ts +9 -9
- package/src/components/AdminModeSyncer.tsx +1 -1
- package/src/components/ArrayContainer.tsx +19 -15
- package/src/components/ClearFilterSortButton.tsx +1 -2
- package/src/components/CollectionEditorDialogs.tsx +1 -1
- package/src/components/DefaultAppBar.tsx +15 -3
- package/src/components/DefaultDrawer.tsx +3 -3
- package/src/components/DrawerNavigationGroup.tsx +1 -2
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +14 -6
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +1 -1
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +1 -2
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +1 -2
- package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +1 -2
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +1 -2
- package/src/components/EntityCollectionTable/fields/VirtualTableSelect.tsx +0 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +15 -27
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +1 -2
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -5
- package/src/components/EntityCollectionTable/table_bindings.tsx +51 -45
- package/src/components/EntityCollectionView/Board.tsx +1 -2
- package/src/components/EntityCollectionView/BoardColumn.tsx +9 -2
- package/src/components/EntityCollectionView/BoardColumnTitle.tsx +5 -4
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +18 -16
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +16 -17
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +87 -18
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +20 -11
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +6 -7
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +14 -5
- package/src/components/EntityCollectionView/FilterPresetsButton.tsx +292 -0
- package/src/components/EntityCollectionView/FiltersDialog.tsx +1 -2
- package/src/components/EntityCollectionView/SplitListView.tsx +76 -25
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +20 -7
- package/src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts +1 -1
- package/src/components/EntityCollectionView/useBoardDataController.tsx +74 -6
- package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +1 -1
- package/src/components/EntityDetailView.tsx +619 -0
- package/src/components/EntityEditView.tsx +29 -10
- package/src/components/EntityEditViewFormActions.tsx +20 -7
- package/src/components/EntityPreview.tsx +14 -5
- package/src/components/EntitySidePanel.tsx +116 -62
- package/src/components/EntityView.tsx +1 -2
- package/src/components/HomePage/ContentHomePage.tsx +1 -1
- package/src/components/HomePage/FavouritesView.tsx +1 -2
- package/src/components/HomePage/NavigationCard.tsx +1 -2
- package/src/components/HomePage/NavigationCardBinding.tsx +1 -2
- package/src/components/HomePage/NavigationGroup.tsx +1 -2
- package/src/components/HomePage/SmallNavigationCard.tsx +1 -2
- package/src/components/PropertyIdCopyTooltip.tsx +1 -2
- package/src/components/RebaseAuthGate.tsx +2 -2
- package/src/components/RebaseNavigation.tsx +9 -7
- package/src/components/ReferenceTable/EntitySelectionTable.tsx +12 -8
- package/src/components/RelationSelector.tsx +34 -6
- package/src/components/SearchIconsView.tsx +10 -2
- package/src/components/SelectableTable/SelectableTable.tsx +2 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +1 -2
- package/src/components/SideDialogs.tsx +63 -38
- package/src/components/UserSelector.tsx +30 -6
- package/src/components/admin/CreationResultDialog.tsx +135 -0
- package/src/components/admin/RolesFilterSelect.tsx +45 -0
- package/src/components/admin/RolesView.tsx +53 -14
- package/src/components/admin/UserRolesSelectField.tsx +50 -0
- package/src/components/admin/UsersView.tsx +41 -124
- package/src/components/app/Scaffold.tsx +1 -2
- package/src/components/common/default_entity_actions.tsx +119 -12
- package/src/components/field_configs.tsx +39 -3
- package/src/components/history/EntityHistoryEntry.tsx +1 -2
- package/src/components/history/EntityHistoryView.tsx +1 -2
- package/src/components/index.ts +2 -0
- package/src/data_export/export/BasicExportAction.tsx +35 -38
- package/src/data_export/export/ExportCollectionAction.tsx +39 -40
- package/src/data_import/components/DataNewPropertiesMapping.tsx +15 -2
- package/src/data_import/components/ImportFileUpload.tsx +1 -2
- package/src/data_import/components/ImportNewPropertyFieldPreview.tsx +1 -2
- package/src/data_import/import/ImportCollectionAction.tsx +21 -8
- package/src/data_import/utils/data.ts +23 -5
- package/src/data_import/utils/file_headers.ts +13 -89
- package/src/data_import/utils/file_to_json.ts +43 -68
- package/src/data_import/utils/transforms.ts +47 -0
- package/src/editor/components/SlashCommandMenu.tsx +17 -2
- package/src/editor/components/editor-bubble-item.tsx +1 -1
- package/src/editor/extensions/Image/index.ts +1 -1
- package/src/editor/extensions/Image.ts +1 -1
- package/src/editor/selectors/color-selector.tsx +1 -2
- package/src/editor/selectors/link-selector.tsx +1 -2
- package/src/editor/selectors/node-selector.tsx +16 -2
- package/src/editor/selectors/text-buttons.tsx +1 -2
- package/src/editor/utils/prosemirror-utils.ts +1 -1
- package/src/form/EntityForm.tsx +16 -6
- package/src/form/EntityFormActions.tsx +11 -3
- package/src/form/PropertyFieldBinding.tsx +5 -12
- package/src/form/components/FieldHelperText.tsx +1 -2
- package/src/form/components/LocalChangesMenu.tsx +17 -2
- package/src/form/components/StorageItemPreview.tsx +1 -2
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +17 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +10 -3
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +1 -2
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +7 -7
- package/src/form/field_bindings/RelationFieldBinding.tsx +150 -147
- package/src/form/field_bindings/RepeatFieldBinding.tsx +1 -1
- package/src/form/field_bindings/SelectFieldBinding.tsx +1 -2
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -2
- package/src/form/field_bindings/VectorFieldBinding.tsx +202 -0
- package/src/form/index.tsx +1 -0
- package/src/form/validation.ts +54 -2
- package/src/hooks/navigation/useBuildNavigationStateController.tsx +2 -1
- package/src/hooks/navigation/useResolvedViews.tsx +30 -15
- package/src/hooks/navigation/useTopLevelNavigation.ts +1 -1
- package/src/index.ts +6 -0
- package/src/preview/PropertyPreview.tsx +1 -1
- package/src/preview/components/ImagePreview.tsx +1 -1
- package/src/preview/components/UrlComponentPreview.tsx +1 -2
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +2 -2
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +23 -24
- package/src/routes/RebaseRoute.tsx +64 -35
- package/src/types/components/EntityFormActionsProps.tsx +1 -1
- package/src/types/components/EntityFormProps.tsx +3 -1
- package/src/types/fields.tsx +4 -3
- package/src/util/navigation_utils.ts +4 -3
- package/src/util/previews.ts +1 -1
- package/src/util/property_utils.tsx +22 -6
- package/src/util/resolutions.ts +2 -2
- package/dist/CollectionEditorDialog-ywdxhs1L.js.map +0 -1
- package/dist/CollectionsStudioView-BDzMFzqH.js.map +0 -1
- package/dist/ContentHomePage-0tHuEIm_.js.map +0 -1
- package/dist/ExportCollectionAction-BIrq92To.js.map +0 -1
- package/dist/ImportCollectionAction-h8yg_To8.js.map +0 -1
- package/dist/PropertyEditView-BuZrNnBN.js.map +0 -1
- package/dist/RolesView-CMPsaIXo.js.map +0 -1
- package/dist/UsersView-BkeblMVT.js.map +0 -1
- package/dist/index-BuZaHcyc.js.map +0 -1
- package/dist/index-CS6uJ7oW.js.map +0 -1
- package/dist/index-eRJbMvHi.js.map +0 -1
- package/dist/util-zfU1zOCX.js.map +0 -1
|
@@ -3,11 +3,47 @@ import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
|
3
3
|
import { User } from "@rebasepro/types";
|
|
4
4
|
import { useSnackbarController, useAuthController, useTranslation } from "@rebasepro/core";
|
|
5
5
|
import { useBreadcrumbsController } from "../../index";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
Alert,
|
|
8
|
+
Button,
|
|
9
|
+
CenteredView,
|
|
10
|
+
CheckCircleIcon,
|
|
11
|
+
ChevronLeftIcon,
|
|
12
|
+
ChevronRightIcon,
|
|
13
|
+
CircularProgress,
|
|
14
|
+
Container,
|
|
15
|
+
CopyIcon,
|
|
16
|
+
Dialog,
|
|
17
|
+
DialogActions,
|
|
18
|
+
DialogContent,
|
|
19
|
+
DialogTitle,
|
|
20
|
+
IconButton,
|
|
21
|
+
iconSize,
|
|
22
|
+
KeyRoundIcon,
|
|
23
|
+
LoadingButton,
|
|
24
|
+
MailIcon,
|
|
25
|
+
MultiSelect,
|
|
26
|
+
MultiSelectItem,
|
|
27
|
+
PlusIcon,
|
|
28
|
+
SearchBar,
|
|
29
|
+
Select,
|
|
30
|
+
SelectItem,
|
|
31
|
+
Skeleton,
|
|
32
|
+
Table,
|
|
33
|
+
TableBody,
|
|
34
|
+
TableCell,
|
|
35
|
+
TableHeader,
|
|
36
|
+
TableRow,
|
|
37
|
+
TextField,
|
|
38
|
+
Tooltip,
|
|
39
|
+
Trash2Icon,
|
|
40
|
+
Typography
|
|
41
|
+
} from "@rebasepro/ui";
|
|
8
42
|
import { RoleChip } from "./RoleChip";
|
|
9
43
|
import { UserManagementDelegate, Role, UserCreationResult } from "@rebasepro/types";
|
|
10
44
|
import { ConfirmationDialog, BootstrapAdminBanner } from "@rebasepro/core";
|
|
45
|
+
import { CreationResultDialog } from "./CreationResultDialog";
|
|
46
|
+
|
|
11
47
|
|
|
12
48
|
const PAGE_SIZE = 25;
|
|
13
49
|
|
|
@@ -105,11 +141,11 @@ message: error instanceof Error ? error.message : "Failed to load users" });
|
|
|
105
141
|
|
|
106
142
|
// Load initial page when delegate finishes loading — runs exactly once.
|
|
107
143
|
useEffect(() => {
|
|
108
|
-
if (!delegateLoading && hasServerSearch && !initialFetchDone.current) {
|
|
144
|
+
if (!delegateLoading && !usersError && hasServerSearch && !initialFetchDone.current) {
|
|
109
145
|
initialFetchDone.current = true;
|
|
110
146
|
fetchPageRef.current(0, "", roleFilter, true);
|
|
111
147
|
}
|
|
112
|
-
}, [delegateLoading, hasServerSearch, roleFilter]);
|
|
148
|
+
}, [delegateLoading, usersError, hasServerSearch, roleFilter]);
|
|
113
149
|
|
|
114
150
|
// Handle search changes (debounced)
|
|
115
151
|
const handleSearch = useCallback((value: string) => {
|
|
@@ -377,7 +413,7 @@ roles: ["w-16", "w-16"] }
|
|
|
377
413
|
|
|
378
414
|
{displayUsers.length === 0 && !tableLoading && !delegateLoading && (
|
|
379
415
|
<TableRow>
|
|
380
|
-
<TableCell colspan={
|
|
416
|
+
<TableCell colspan={6}>
|
|
381
417
|
<CenteredView className="flex flex-col gap-4 my-8 items-center">
|
|
382
418
|
<Typography variant="label">
|
|
383
419
|
{usersError
|
|
@@ -469,125 +505,6 @@ roles: ["w-16", "w-16"] }
|
|
|
469
505
|
);
|
|
470
506
|
}
|
|
471
507
|
|
|
472
|
-
// ============================================
|
|
473
|
-
// CreationResultDialog Component
|
|
474
|
-
// ============================================
|
|
475
|
-
function CreationResultDialog({
|
|
476
|
-
result,
|
|
477
|
-
onClose
|
|
478
|
-
}: {
|
|
479
|
-
result: UserCreationResult;
|
|
480
|
-
onClose: () => void;
|
|
481
|
-
}) {
|
|
482
|
-
const { t } = useTranslation();
|
|
483
|
-
const snackbarController = useSnackbarController();
|
|
484
|
-
const [copied, setCopied] = useState(false);
|
|
485
|
-
|
|
486
|
-
const handleCopyPassword = async () => {
|
|
487
|
-
if (!result.temporaryPassword) return;
|
|
488
|
-
try {
|
|
489
|
-
await navigator.clipboard.writeText(result.temporaryPassword);
|
|
490
|
-
setCopied(true);
|
|
491
|
-
snackbarController.open({ type: "success",
|
|
492
|
-
message: t("password_copied") ?? "Password copied to clipboard" });
|
|
493
|
-
setTimeout(() => setCopied(false), 3000);
|
|
494
|
-
} catch {
|
|
495
|
-
// Fallback for older browsers
|
|
496
|
-
const textArea = document.createElement("textarea");
|
|
497
|
-
textArea.value = result.temporaryPassword;
|
|
498
|
-
document.body.appendChild(textArea);
|
|
499
|
-
textArea.select();
|
|
500
|
-
document.execCommand("copy");
|
|
501
|
-
document.body.removeChild(textArea);
|
|
502
|
-
setCopied(true);
|
|
503
|
-
setTimeout(() => setCopied(false), 3000);
|
|
504
|
-
}
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
if (result.invitationSent) {
|
|
508
|
-
// Invitation sent via email
|
|
509
|
-
return (
|
|
510
|
-
<Dialog open={true} onOpenChange={(open) => !open ? onClose() : undefined} maxWidth="xl">
|
|
511
|
-
<DialogTitle variant="h5" gutterBottom={false}>
|
|
512
|
-
<div className="flex items-center gap-3">
|
|
513
|
-
<MailIcon/>
|
|
514
|
-
{t("invitation_sent_title") ?? "Invitation Sent"}
|
|
515
|
-
</div>
|
|
516
|
-
</DialogTitle>
|
|
517
|
-
<DialogContent>
|
|
518
|
-
<div className="flex flex-col gap-4 py-2">
|
|
519
|
-
<div className="bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-lg p-4">
|
|
520
|
-
<Typography className="text-green-800 dark:text-green-200">
|
|
521
|
-
{(t("invitation_sent") ?? "An invitation email has been sent to {{email}}. They can use the link to set their password.")
|
|
522
|
-
.replace("{{email}}", result.user.email ?? "")}
|
|
523
|
-
</Typography>
|
|
524
|
-
</div>
|
|
525
|
-
</div>
|
|
526
|
-
</DialogContent>
|
|
527
|
-
<DialogActions>
|
|
528
|
-
<Button variant="filled" onClick={onClose}>
|
|
529
|
-
{t("ok")}
|
|
530
|
-
</Button>
|
|
531
|
-
</DialogActions>
|
|
532
|
-
</Dialog>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (result.temporaryPassword) {
|
|
537
|
-
// No email — show temporary password
|
|
538
|
-
return (
|
|
539
|
-
<Dialog open={true} onOpenChange={(open) => !open ? onClose() : undefined} maxWidth="xl">
|
|
540
|
-
<DialogTitle variant="h5" gutterBottom={false}>
|
|
541
|
-
{t("temporary_password") ?? "Temporary Password"}
|
|
542
|
-
</DialogTitle>
|
|
543
|
-
<DialogContent>
|
|
544
|
-
<div className="flex flex-col gap-4 py-2">
|
|
545
|
-
<div className="bg-amber-50 dark:bg-amber-900/30 border border-amber-200 dark:border-amber-800 rounded-lg p-4">
|
|
546
|
-
<Typography className="text-amber-800 dark:text-amber-200" variant="body2">
|
|
547
|
-
{t("temporary_password_description") ??
|
|
548
|
-
"Email is not configured. Share this temporary password with the user securely. It will not be shown again."}
|
|
549
|
-
</Typography>
|
|
550
|
-
</div>
|
|
551
|
-
|
|
552
|
-
<div>
|
|
553
|
-
<Typography variant="caption" color="secondary" className="mb-1">
|
|
554
|
-
{t("email")}
|
|
555
|
-
</Typography>
|
|
556
|
-
<Typography>
|
|
557
|
-
{result.user.email}
|
|
558
|
-
</Typography>
|
|
559
|
-
</div>
|
|
560
|
-
|
|
561
|
-
<div>
|
|
562
|
-
<Typography variant="caption" color="secondary" className="mb-1">
|
|
563
|
-
{t("temporary_password") ?? "Temporary Password"}
|
|
564
|
-
</Typography>
|
|
565
|
-
<div className="flex items-center gap-2 mt-1">
|
|
566
|
-
<code className="flex-grow bg-surface-100 dark:bg-surface-900 border border-surface-300 dark:border-surface-600 rounded px-3 py-2 font-mono text-base select-all">
|
|
567
|
-
{result.temporaryPassword}
|
|
568
|
-
</code>
|
|
569
|
-
<Tooltip title={t("copy_password") ?? "CopyIcon password"} asChild>
|
|
570
|
-
<IconButton onClick={handleCopyPassword}>
|
|
571
|
-
{copied ? <CheckCircleIcon className="text-green-600"/> : <CopyIcon/>}
|
|
572
|
-
</IconButton>
|
|
573
|
-
</Tooltip>
|
|
574
|
-
</div>
|
|
575
|
-
</div>
|
|
576
|
-
</div>
|
|
577
|
-
</DialogContent>
|
|
578
|
-
<DialogActions>
|
|
579
|
-
<Button variant="filled" onClick={onClose}>
|
|
580
|
-
{t("ok")}
|
|
581
|
-
</Button>
|
|
582
|
-
</DialogActions>
|
|
583
|
-
</Dialog>
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Shouldn't happen, but fallback
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
508
|
// ============================================
|
|
592
509
|
// UserDetailsForm Component
|
|
593
510
|
// ============================================
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useCallback, useMemo } from "react";
|
|
2
2
|
import { cls, defaultBorderMixin, IconButton, Sheet, Tooltip } from "@rebasepro/ui";
|
|
3
|
-
import { ChevronLeftIcon, MenuIcon } from "
|
|
3
|
+
import { ChevronLeftIcon, ErrorBoundary, MenuIcon } from "@rebasepro/ui";
|
|
4
4
|
import { deepEqual as equal } from "fast-equals"
|
|
5
5
|
|
|
6
6
|
import { useLargeLayout, useAdminModeController, useTranslation } from "@rebasepro/core";
|
|
7
|
-
import { ErrorBoundary } from "@rebasepro/ui";
|
|
8
7
|
import { AppContext } from "./useApp";
|
|
9
8
|
|
|
10
9
|
export const DRAWER_WIDTH = 280;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { CopyIcon, PencilIcon, Trash2Icon } from "
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
;
|
|
1
|
+
import { CopyIcon, iconSize, KeyRoundIcon, PencilIcon, Trash2Icon } from "@rebasepro/ui";
|
|
2
|
+
import type { EntityAction, User, UserCreationResult } from "@rebasepro/types";
|
|
3
|
+
import { ConfirmationDialog, useInternalUserManagementController, useSnackbarController, useTranslation } from "@rebasepro/core";
|
|
5
4
|
import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
6
5
|
import { addRecentId } from "../EntityCollectionView/utils";
|
|
7
6
|
import { navigateToEntity } from "../../util/navigation_utils";
|
|
8
7
|
import { resolveDefaultSelectedView } from "@rebasepro/common";
|
|
8
|
+
import { CreationResultDialog } from "../admin/CreationResultDialog";
|
|
9
|
+
import React, { useState } from "react";
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
export const editEntityAction: EntityAction = {
|
|
11
13
|
icon: <PencilIcon size={iconSize.smallest}/>,
|
|
@@ -44,13 +46,15 @@ export const editEntityAction: EntityAction = {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
const newFullIdPath = path ?? collection?.slug ?? entity.path;
|
|
47
|
-
const defaultSelectedView =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const defaultSelectedView = collection?.defaultEntityAction === "view"
|
|
50
|
+
? "edit"
|
|
51
|
+
: resolveDefaultSelectedView(
|
|
52
|
+
collection ? collection.defaultSelectedView : undefined,
|
|
53
|
+
{
|
|
54
|
+
status: "existing",
|
|
55
|
+
entityId: entity.id
|
|
56
|
+
}
|
|
57
|
+
);
|
|
54
58
|
navigateToEntity({
|
|
55
59
|
openEntityMode,
|
|
56
60
|
collection,
|
|
@@ -150,7 +154,110 @@ export const deleteEntityAction: EntityAction = {
|
|
|
150
154
|
}}
|
|
151
155
|
onClose={closeDialog}/>;
|
|
152
156
|
}
|
|
153
|
-
})
|
|
157
|
+
});
|
|
154
158
|
return Promise.resolve(undefined);
|
|
155
159
|
}
|
|
156
160
|
}
|
|
161
|
+
|
|
162
|
+
export function ResetPasswordActionDialog({
|
|
163
|
+
user,
|
|
164
|
+
open,
|
|
165
|
+
onClose
|
|
166
|
+
}: {
|
|
167
|
+
user: User;
|
|
168
|
+
open: boolean;
|
|
169
|
+
onClose: () => void;
|
|
170
|
+
}) {
|
|
171
|
+
const userManagement = useInternalUserManagementController();
|
|
172
|
+
const snackbarController = useSnackbarController();
|
|
173
|
+
const { t } = useTranslation();
|
|
174
|
+
const [loading, setLoading] = useState(false);
|
|
175
|
+
const [creationResult, setCreationResult] = useState<UserCreationResult | null>(null);
|
|
176
|
+
|
|
177
|
+
const handleConfirm = async () => {
|
|
178
|
+
if (!userManagement?.resetPassword) return;
|
|
179
|
+
setLoading(true);
|
|
180
|
+
try {
|
|
181
|
+
const result = await userManagement.resetPassword(user);
|
|
182
|
+
setCreationResult(result);
|
|
183
|
+
snackbarController.open({
|
|
184
|
+
type: "success",
|
|
185
|
+
message: t("reset_password_success") || "Password reset successfully"
|
|
186
|
+
});
|
|
187
|
+
} catch (error: unknown) {
|
|
188
|
+
snackbarController.open({
|
|
189
|
+
type: "error",
|
|
190
|
+
message: error instanceof Error ? error.message : (t("error_resetting_password") || "Error resetting password")
|
|
191
|
+
});
|
|
192
|
+
onClose();
|
|
193
|
+
} finally {
|
|
194
|
+
setLoading(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
if (creationResult) {
|
|
199
|
+
return (
|
|
200
|
+
<CreationResultDialog
|
|
201
|
+
result={creationResult}
|
|
202
|
+
onClose={() => {
|
|
203
|
+
setCreationResult(null);
|
|
204
|
+
onClose();
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<ConfirmationDialog
|
|
212
|
+
open={open}
|
|
213
|
+
loading={loading}
|
|
214
|
+
onAccept={handleConfirm}
|
|
215
|
+
onCancel={onClose}
|
|
216
|
+
title={<>{t("reset_password") || "Reset Password"}</>}
|
|
217
|
+
body={<>{t("reset_password_confirmation") || "Are you sure you want to reset this user's password?"}</>}
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const resetPasswordAction: EntityAction = {
|
|
223
|
+
icon: <KeyRoundIcon size={iconSize.smallest}/>,
|
|
224
|
+
name: "Reset Password",
|
|
225
|
+
key: "reset_password",
|
|
226
|
+
collapsed: false,
|
|
227
|
+
showActionsInListView: true,
|
|
228
|
+
isEnabled: ({ entity }) => Boolean(entity),
|
|
229
|
+
onClick({
|
|
230
|
+
entity,
|
|
231
|
+
context,
|
|
232
|
+
}): Promise<void> {
|
|
233
|
+
if (!entity) {
|
|
234
|
+
throw new Error("INTERNAL: resetPasswordAction: Entity is undefined");
|
|
235
|
+
}
|
|
236
|
+
if (!context.dialogsController) {
|
|
237
|
+
throw new Error("INTERNAL: resetPasswordAction: context.dialogsController is undefined");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const user: User = {
|
|
241
|
+
uid: entity.id as string,
|
|
242
|
+
email: entity.values?.email as string,
|
|
243
|
+
displayName: entity.values?.displayName as string || null,
|
|
244
|
+
photoURL: entity.values?.photoURL as string || null,
|
|
245
|
+
providerId: entity.values?.providerId as string || "custom",
|
|
246
|
+
isAnonymous: entity.values?.isAnonymous as boolean || false,
|
|
247
|
+
roles: entity.values?.roles as string[] || []
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const { closeDialog } = context.dialogsController.open({
|
|
251
|
+
key: "reset_password_dialog_" + entity.id,
|
|
252
|
+
Component: ({ open }) => (
|
|
253
|
+
<ResetPasswordActionDialog
|
|
254
|
+
user={user}
|
|
255
|
+
open={open}
|
|
256
|
+
onClose={closeDialog}
|
|
257
|
+
/>
|
|
258
|
+
)
|
|
259
|
+
});
|
|
260
|
+
return Promise.resolve(undefined);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import type { DefaultFieldConfig } from "../types/fields";
|
|
3
|
-
import type { ArrayProperty, NumberProperty, Property, PropertyConfig, StringProperty } from "@rebasepro/types";
|
|
3
|
+
import type { ArrayProperty, NumberProperty, Property, PropertyConfig, StringProperty, VectorProperty } from "@rebasepro/types";
|
|
4
4
|
import { ArrayCustomShapedFieldBinding } from "../form/field_bindings/ArrayCustomShapedFieldBinding";
|
|
5
5
|
import { ArrayOfReferencesFieldBinding } from "../form/field_bindings/ArrayOfReferencesFieldBinding";
|
|
6
6
|
import { BlockFieldBinding } from "../form/field_bindings/BlockFieldBinding";
|
|
@@ -16,9 +16,29 @@ import { SelectFieldBinding } from "../form/field_bindings/SelectFieldBinding";
|
|
|
16
16
|
import { StorageUploadFieldBinding } from "../form/field_bindings/StorageUploadFieldBinding";
|
|
17
17
|
import { SwitchFieldBinding } from "../form/field_bindings/SwitchFieldBinding";
|
|
18
18
|
import { TextFieldBinding } from "../form/field_bindings/TextFieldBinding";
|
|
19
|
+
import { VectorFieldBinding } from "../form/field_bindings/VectorFieldBinding";
|
|
19
20
|
import { isPropertyBuilder } from "@rebasepro/common";
|
|
20
21
|
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
AlignLeftIcon,
|
|
24
|
+
CalendarIcon,
|
|
25
|
+
FlagIcon,
|
|
26
|
+
FolderUpIcon,
|
|
27
|
+
GlobeIcon,
|
|
28
|
+
HashIcon,
|
|
29
|
+
LinkIcon,
|
|
30
|
+
ListIcon,
|
|
31
|
+
ListOrderedIcon,
|
|
32
|
+
MailIcon,
|
|
33
|
+
QuoteIcon,
|
|
34
|
+
RepeatIcon,
|
|
35
|
+
Rows3Icon,
|
|
36
|
+
TextIcon,
|
|
37
|
+
UploadIcon,
|
|
38
|
+
UserCheckIcon,
|
|
39
|
+
UserIcon,
|
|
40
|
+
VoteIcon
|
|
41
|
+
} from "@rebasepro/ui";
|
|
22
42
|
import { RelationFieldBinding } from "../form/field_bindings/RelationFieldBinding";
|
|
23
43
|
import { UserSelectFieldBinding } from "../form/field_bindings/UserSelectFieldBinding";
|
|
24
44
|
import { mergeDeep } from "@rebasepro/utils";
|
|
@@ -318,6 +338,18 @@ export const DEFAULT_FIELD_CONFIGS: Record<DefaultFieldConfig, PropertyConfig> =
|
|
|
318
338
|
},
|
|
319
339
|
ui: { Field: BlockFieldBinding }
|
|
320
340
|
}
|
|
341
|
+
},
|
|
342
|
+
vector_input: {
|
|
343
|
+
key: "vector_input",
|
|
344
|
+
name: "Vector input",
|
|
345
|
+
description: "Vector array for embeddings",
|
|
346
|
+
Icon: HashIcon,
|
|
347
|
+
color: "#bec920",
|
|
348
|
+
property: {
|
|
349
|
+
type: "vector",
|
|
350
|
+
dimensions: 1536,
|
|
351
|
+
ui: { Field: VectorFieldBinding }
|
|
352
|
+
}
|
|
321
353
|
}
|
|
322
354
|
};
|
|
323
355
|
|
|
@@ -358,7 +390,7 @@ export function getDefaultFieldId(property: Property) {
|
|
|
358
390
|
return "select";
|
|
359
391
|
} else if (property.userSelect) {
|
|
360
392
|
return "user_select";
|
|
361
|
-
} else if ((property as
|
|
393
|
+
} else if ((property as StringProperty).reference) {
|
|
362
394
|
return "reference_as_string";
|
|
363
395
|
} else {
|
|
364
396
|
return "text_field";
|
|
@@ -407,6 +439,10 @@ export function getDefaultFieldId(property: Property) {
|
|
|
407
439
|
return "relation";
|
|
408
440
|
} else if (property.type === "reference") {
|
|
409
441
|
return "reference";
|
|
442
|
+
} else if (property.type === "vector") {
|
|
443
|
+
return "vector_input";
|
|
444
|
+
} else if (property.type === "binary") {
|
|
445
|
+
return "text_field";
|
|
410
446
|
}
|
|
411
447
|
|
|
412
448
|
console.error("Unsupported field config mapping", property);
|
|
@@ -2,8 +2,7 @@ import type { EntityCollection } from "@rebasepro/types";
|
|
|
2
2
|
import type { Property } from "@rebasepro/types";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
import { Chip, cls, defaultBorderMixin, Tooltip, Typography
|
|
6
|
-
import { ArrowLeftIcon } from "lucide-react";
|
|
5
|
+
import { ArrowLeftIcon, Chip, cls, defaultBorderMixin, iconSize, Tooltip, Typography } from "@rebasepro/ui";
|
|
7
6
|
import { PreviewSize } from "../../types/components/PropertyPreviewProps";
|
|
8
7
|
import { getPropertyInPath } from "../../util/property_utils";
|
|
9
8
|
import { PropertyPreview } from "../../preview/PropertyPreview";
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
import type { EntityCustomViewParams } from "@rebasepro/types";
|
|
3
3
|
import { useRef, useEffect } from "react";
|
|
4
4
|
import { cls, IconButton, Label, Tooltip, Typography } from "@rebasepro/ui";
|
|
5
|
-
import { HistoryIcon } from "
|
|
5
|
+
import { ErrorBoundary, HistoryIcon } from "@rebasepro/ui";
|
|
6
6
|
import { EntityHistoryEntry } from "./EntityHistoryEntry";
|
|
7
7
|
import { useSnackbarController, useAuthController } from "@rebasepro/core";
|
|
8
8
|
import { ConfirmationDialog } from "@rebasepro/core";
|
|
9
|
-
import { ErrorBoundary } from "@rebasepro/ui";
|
|
10
9
|
import { useState } from "react";
|
|
11
10
|
import { useEntityHistory } from "../../index";
|
|
12
11
|
|
package/src/components/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type { EntityViewProps } from "./EntityView";
|
|
2
2
|
export { EntityView } from "./EntityView";
|
|
3
3
|
|
|
4
|
+
export * from "./EntityDetailView";
|
|
5
|
+
|
|
4
6
|
export type { EntitySelectionProps } from "./ReferenceTable/EntitySelectionTable";
|
|
5
7
|
export { EntitySelectionTable } from "./ReferenceTable/EntitySelectionTable";
|
|
6
8
|
|
|
@@ -2,8 +2,21 @@
|
|
|
2
2
|
import React, { useCallback } from "react";
|
|
3
3
|
|
|
4
4
|
import { Entity, Properties } from "@rebasepro/types";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
BooleanSwitchWithLabel,
|
|
7
|
+
Button,
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogActions,
|
|
10
|
+
DialogContent,
|
|
11
|
+
DialogTitle,
|
|
12
|
+
DownloadIcon,
|
|
13
|
+
IconButton,
|
|
14
|
+
Label,
|
|
15
|
+
RadioGroup,
|
|
16
|
+
RadioGroupItem,
|
|
17
|
+
Tooltip,
|
|
18
|
+
Typography
|
|
19
|
+
} from "@rebasepro/ui";
|
|
7
20
|
import { downloadEntitiesExport } from "./export";
|
|
8
21
|
|
|
9
22
|
export type BasicExportActionProps = {
|
|
@@ -73,45 +86,29 @@ export function BasicExportAction({
|
|
|
73
86
|
|
|
74
87
|
<div className={"flex flex-row gap-4"}>
|
|
75
88
|
<div className={"p-4 flex flex-col"}>
|
|
76
|
-
<
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
checked={exportType === "json"}
|
|
87
|
-
onChange={() => setExportType("json")}
|
|
88
|
-
className={cls("w-4 bg-surface-100 border-surface-300 dark:bg-surface-700 dark:border-surface-600")}/>
|
|
89
|
-
<label htmlFor="radio-json"
|
|
90
|
-
className="p-2 text-sm font-medium text-surface-900 dark:text-surface-accent-300">JSON</label>
|
|
91
|
-
</div>
|
|
89
|
+
<RadioGroup value={exportType} onValueChange={(v) => setExportType(v as "csv" | "json")}>
|
|
90
|
+
<div className="flex items-center gap-2">
|
|
91
|
+
<RadioGroupItem value="csv" id="radio-csv"/>
|
|
92
|
+
<Label htmlFor="radio-csv">CSV</Label>
|
|
93
|
+
</div>
|
|
94
|
+
<div className="flex items-center gap-2">
|
|
95
|
+
<RadioGroupItem value="json" id="radio-json"/>
|
|
96
|
+
<Label htmlFor="radio-json">JSON</Label>
|
|
97
|
+
</div>
|
|
98
|
+
</RadioGroup>
|
|
92
99
|
</div>
|
|
93
100
|
|
|
94
101
|
<div className={"p-4 flex flex-col"}>
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
as
|
|
103
|
-
|
|
104
|
-
</
|
|
105
|
-
<div className="flex items-center">
|
|
106
|
-
<input id="radio-string" type="radio" value="string" name="dateExportType"
|
|
107
|
-
checked={dateExportType === "string"}
|
|
108
|
-
onChange={() => setDateExportType("string")}
|
|
109
|
-
className={cls("w-4 bg-surface-100 border-surface-300 dark:bg-surface-700 dark:border-surface-600")}/>
|
|
110
|
-
<label htmlFor="radio-string"
|
|
111
|
-
className="p-2 text-sm font-medium text-surface-900 dark:text-surface-accent-300">Dates
|
|
112
|
-
as
|
|
113
|
-
strings ({dateRef.current.toISOString()})</label>
|
|
114
|
-
</div>
|
|
102
|
+
<RadioGroup value={dateExportType} onValueChange={(v) => setDateExportType(v as "timestamp" | "string")}>
|
|
103
|
+
<div className="flex items-center gap-2">
|
|
104
|
+
<RadioGroupItem value="timestamp" id="radio-timestamp"/>
|
|
105
|
+
<Label htmlFor="radio-timestamp">Dates as timestamps ({dateRef.current.getTime()})</Label>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="flex items-center gap-2">
|
|
108
|
+
<RadioGroupItem value="string" id="radio-string"/>
|
|
109
|
+
<Label htmlFor="radio-string">Dates as strings ({dateRef.current.toISOString()})</Label>
|
|
110
|
+
</div>
|
|
111
|
+
</RadioGroup>
|
|
115
112
|
</div>
|
|
116
113
|
</div>
|
|
117
114
|
|