diff --git a/packages/client/src/components/app/ChangeAccessModal.tsx b/packages/client/src/components/app/ChangeAccessModal.tsx
index 1798173b77..973ef2e8ed 100644
--- a/packages/client/src/components/app/ChangeAccessModal.tsx
+++ b/packages/client/src/components/app/ChangeAccessModal.tsx
@@ -11,7 +11,8 @@ import {
import { Control, Controller } from 'react-hook-form';
import { HdrAuto, Edit, Visibility } from '@mui/icons-material';
import { AppDetailsFormTypes } from './app-details.utility';
-import { PERMISSION_DESCRIPTION_MAP } from '../settings/member-permissions.constants';
+
+import { PERMISSION_DESCRIPTION_MAP } from '@/constants';
import { useRootStore } from '@/hooks';
const StyledContentBox = styled(Stack)(({ theme }) => ({
@@ -56,7 +57,7 @@ interface ChangeAccessModalProps {
export const ChangeAccessModal = (props: ChangeAccessModalProps) => {
const { open, onClose, control, getValues } = props;
- const permissionDescriptions = PERMISSION_DESCRIPTION_MAP['app'];
+ const permissionDescriptions = PERMISSION_DESCRIPTION_MAP['APP'];
const { monolithStore } = useRootStore();
const notification = useNotification();
diff --git a/packages/client/src/components/engine/EditEngineDetails.tsx b/packages/client/src/components/engine/EditEngineDetails.tsx
index 5920533c6d..35bce2a0f2 100644
--- a/packages/client/src/components/engine/EditEngineDetails.tsx
+++ b/packages/client/src/components/engine/EditEngineDetails.tsx
@@ -20,9 +20,6 @@ const StyledEditorContainer = styled('div')(({ theme }) => ({
}));
interface EditEngineDetailsProps {
- /** Type of Engine */
- type: string;
-
/** Track if the edit is open */
open: boolean;
@@ -37,7 +34,7 @@ interface EditEngineDetailsProps {
* Wrap the Engine routes and provide styling/functionality
*/
export const EditEngineDetails = observer((props: EditEngineDetailsProps) => {
- const { open = false, type, onClose = () => null, values = {} } = props;
+ const { open = false, onClose = () => null, values = {} } = props;
// get the notification
const notification = useNotification();
@@ -54,7 +51,7 @@ export const EditEngineDetails = observer((props: EditEngineDetailsProps) => {
);
// get the engine information
- const { id } = useEngine();
+ const { id, type } = useEngine();
// track the options
const [filterOptions, setFilterOptions] = useState<
diff --git a/packages/client/src/components/engine/EngineAccessButton.tsx b/packages/client/src/components/engine/EngineAccessButton.tsx
index b36f984a8e..9ce8fbd426 100644
--- a/packages/client/src/components/engine/EngineAccessButton.tsx
+++ b/packages/client/src/components/engine/EngineAccessButton.tsx
@@ -13,25 +13,17 @@ import {
TextArea,
} from '@semoss/ui';
import { EditRounded, RemoveRedEyeRounded, Add } from '@mui/icons-material';
-import { PERMISSION_DESCRIPTION_MAP } from '@/components/settings/member-permissions.constants';
import { Role } from '@/types';
import { useRootStore, useEngine } from '@/hooks';
+import { PERMISSION_DESCRIPTION_MAP } from '@/constants';
const StyledCard = styled(Card)({
borderRadius: '12px',
});
-interface EngineAccessButtonProps {
- /**
- * Model, Vector, Storage, Database, Function
- */
- name: string;
-}
-
-export const EngineAccessButton = (props: EngineAccessButtonProps) => {
- const { name } = props;
- const { id, role } = useEngine();
+export const EngineAccessButton = () => {
+ const { id, type, role } = useEngine();
const { monolithStore } = useRootStore();
const notification = useNotification();
@@ -155,9 +147,8 @@ export const EngineAccessButton = (props: EngineAccessButtonProps) => {
subheader={
{
- PERMISSION_DESCRIPTION_MAP[
- name.toLowerCase()
- ]?.author
+ PERMISSION_DESCRIPTION_MAP[type]
+ .author
}
}
@@ -198,9 +189,8 @@ export const EngineAccessButton = (props: EngineAccessButtonProps) => {
subheader={
{
- PERMISSION_DESCRIPTION_MAP[
- name.toLowerCase()
- ]?.editor
+ PERMISSION_DESCRIPTION_MAP[type]
+ .editor
}
}
@@ -241,9 +231,8 @@ export const EngineAccessButton = (props: EngineAccessButtonProps) => {
subheader={
{
- PERMISSION_DESCRIPTION_MAP[
- name.toLowerCase()
- ]?.readonly
+ PERMISSION_DESCRIPTION_MAP[type]
+ .readonly
}
}
diff --git a/packages/client/src/components/engine/EngineShell.tsx b/packages/client/src/components/engine/EngineShell.tsx
index 2df94c636b..3d28bc36f5 100644
--- a/packages/client/src/components/engine/EngineShell.tsx
+++ b/packages/client/src/components/engine/EngineShell.tsx
@@ -156,7 +156,7 @@ export const EngineShell = (props: EngineShellProps) => {
-
+
{role === 'OWNER' && (
-
-
-
-
-
- setAddMembersModal(false)}
- >
- Cancel
-
- {
- submitNonCredUsers();
- }}
- >
- Save
-
-
-
+
+ return null;
+ })}
+
+
+
+ {
+ setPage(v);
+ setSelectedMembers([]);
+ }}
+ page={page}
+ rowsPerPage={rowsPerPage}
+ rowsPerPageOptions={[5, 10, 20]}
+ onRowsPerPageChange={(e) => {
+ // set the new limit
+ setRowsPerPage(
+ parseInt(
+ e.target.value,
+ 10,
+ ),
+ );
+ }}
+ count={totalMembers}
+ />
+
+
+
+ ) : (
+
+
+ No members
+
+ {
+ openAddMembersModal();
+ }}
+ >
+ Add Members
+
+
+ )}
+ >
+ )}
+
+
+ {
+ // clear out the deleted members
+ setPendingDeletedMembers([]);
+
+ // close the model
+ setDeleteMembersModal(false);
+
+ // refresh if successful
+ if (success) {
+ // trigger the update
+ onChange();
+
+ // refresh
+ getMembers.refresh();
+ }
+ }}
+ />
+ {
+ // clear out the deleted members
+ setAddMembersModal(false);
+
+ // refresh if successful
+ if (success) {
+ // trigger the update
+ onChange();
+
+ getMembers.refresh();
+ }
+ }}
+ />
);
};
diff --git a/packages/client/src/components/settings/PendingMembersTable.tsx b/packages/client/src/components/settings/PendingMembersTable.tsx
index 0a86248aa4..9f8679b205 100644
--- a/packages/client/src/components/settings/PendingMembersTable.tsx
+++ b/packages/client/src/components/settings/PendingMembersTable.tsx
@@ -1,5 +1,4 @@
import { useEffect, useState } from 'react';
-import { useForm, useFieldArray } from 'react-hook-form';
import {
styled,
Button,
@@ -17,8 +16,18 @@ import { Add, Check, Close, ExpandLess, ExpandMore } from '@mui/icons-material';
import { AxiosResponse } from 'axios';
import { useRootStore, usePixel, useSettings } from '@/hooks';
+import { ALL_TYPES } from '@/types';
+import { LoadingScreen } from '@/components/ui';
-import { SETTINGS_ROLE, SETTINGS_MODE } from './settings.types';
+import { SETTINGS_ROLE, SETTINGS_PENDING_USER } from './settings.types';
+
+const StyledMemberLoading = styled('div')(({ theme }) => ({
+ position: 'relative',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '160px',
+}));
const StyledMemberContent = styled('div')({
display: 'flex',
@@ -129,124 +138,81 @@ const permissionMapper = {
'Read-Only': 'READ_ONLY', // DISPLAY: BE
};
-// Pending Members Table
-interface PendingMember {
- ID: string;
- NAME: string;
- EMAIL: string;
- PERMISSION: SETTINGS_ROLE;
- // Requester Info
- REQUEST_TIMESTAMP: string;
- REQUEST_TYPE: string;
- REQUEST_USERID: string;
-}
-
const StyledNoPendingReqs = styled('div')(({ theme }) => ({
width: '100%',
- height: theme.spacing(6),
+ height: '503px',
display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(1),
justifyContent: 'center',
alignItems: 'center',
- backgroundColor: 'white',
}));
interface PendingMemberTableProps {
/**
- * Mode of setting
+ * Id of the engine
*/
- mode: SETTINGS_MODE;
+ id: string;
/**
- * Id of the setting
+ * Type of the engine
*/
- id: string;
+ type: ALL_TYPES;
+
+ /**
+ * Called when permissions are changed
+ */
+ onChange?: () => void;
}
export const PendingMembersTable = (props: PendingMemberTableProps) => {
- const { mode, id } = props;
+ const { id, type, onChange = () => null } = props;
const { monolithStore } = useRootStore();
const notification = useNotification();
const { adminMode } = useSettings();
- const [selectedPending, setSelectedPending] = useState([]);
+ const [renderedMembers, setRenderedMembers] = useState<
+ SETTINGS_PENDING_USER[]
+ >([]);
+ const [selectedMembers, setSelectedMembers] = useState<
+ Record
+ >({});
const [openTable, setOpenTable] = useState(false);
- const { control, watch, setValue } = useForm<{
- PENDING_MEMBERS: PendingMember[];
- }>({
- defaultValues: {
- // Members Table
- PENDING_MEMBERS: [],
- },
- });
-
- const { remove: pendingMemberRemove } = useFieldArray({
- control,
- name: 'PENDING_MEMBERS',
- });
- const pendingMembers = watch('PENDING_MEMBERS');
-
- useEffect(() => {
- if (pendingMembers.length) {
- setOpenTable(true);
- }
- }, [pendingMembers]);
-
const pendingUserAccessPixel =
- mode === 'engine'
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
? `GetEngineUserAccessRequest(engine='${id}');`
- : mode === 'app'
+ : type === 'APP'
? `GetProjectUserAccessRequest(project='${id}')`
: '';
// Pending Member Requests Pixel call
- const pendingUserAccess = usePixel<
- {
- ENGINEID: string;
- ID: string;
- PERMISSION: number;
- REQUEST_TIMESTAMP: string;
- REQUEST_TYPE: string;
- REQUEST_USERID: string;
- }[]
- >(pendingUserAccessPixel);
+ const pendingUserAccess = usePixel(
+ pendingUserAccessPixel,
+ );
- /**
- * @name useEffect
- * @desc - sets pending members in react hook form
- */
+ // track if the page is loading
+ const isLoading =
+ pendingUserAccess.status === 'INITIAL' ||
+ pendingUserAccess.status === 'LOADING';
+
+ // set the rendered users
useEffect(() => {
- // pixel call to get pending members
- if (pendingUserAccess.status !== 'SUCCESS' || !pendingUserAccess.data) {
+ if (pendingUserAccess.status !== 'SUCCESS') {
return;
}
- const newPendingMembers = [];
+ const updatedMembers = pendingUserAccess.data.map((m) => ({
+ ...m,
+ PERMISSION: permissionMapper[m.PERMISSION], // comes in as 1,2,3 -> map to Author, Edit, Read-only
+ }));
- pendingUserAccess.data.forEach((mem) => {
- newPendingMembers.push({
- ...mem,
- PERMISSION: permissionMapper[mem.PERMISSION], // comes in as 1,2,3 -> map to Author, Edit, Read-only
- });
- });
-
- // set new members with the Pending Members in react hook form
- setValue('PENDING_MEMBERS', newPendingMembers);
-
- // notify user for pending members
- if (newPendingMembers.length) {
- let message =
- newPendingMembers.length === 1
- ? `1 member has `
- : `${newPendingMembers.length} members have `;
-
- message += `requested access`;
- }
-
- return () => {
- // TODO
- };
+ setRenderedMembers(updatedMembers);
}, [pendingUserAccess.status, pendingUserAccess.data]);
/** API Functions */
@@ -255,10 +221,7 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
* @param members - members to pass to approve api call
* @description Approve list of Pending Members
*/
- const approvePendingMembers = async (
- members: PendingMember[],
- quickActionFlag?: boolean, // quick approve button
- ) => {
+ const approvePendingMembers = async (members: SETTINGS_PENDING_USER[]) => {
try {
// construct requests for post data
const requests = members.map((mem, i) => {
@@ -279,13 +242,19 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
}
let response: AxiosResponse<{ success: boolean }> | null = null;
- if (mode === 'engine') {
+ if (
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ) {
response = await monolithStore.approveEngineUserAccessRequest(
adminMode,
id,
requests,
);
- } else if (mode === 'app') {
+ } else if (type === 'APP') {
response = await monolithStore.approveProjectUserAccessRequest(
adminMode,
id,
@@ -298,40 +267,24 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
return;
}
- // if (response.success) {
- // get index of pending members in order to remove
- const indexesToRemove = [];
- requests.forEach((mem) => {
- pendingMembers.find((m, i) => {
- if (mem.userid === m.REQUEST_USERID)
- indexesToRemove.push(i);
- });
- });
-
- // remove indexes
- pendingMemberRemove(indexesToRemove);
+ if (response.data.success) {
+ const updatedMembers = {
+ ...selectedMembers,
+ } as Record;
- if (!quickActionFlag) {
- // remove from selected pending members
- setSelectedPending([]);
- } else {
- let indexToRemoveFromSelected;
- // remove from selected
- selectedPending.find((m, i) => {
- if (m.ID !== requests[0].requestid) {
- indexToRemoveFromSelected = i;
+ for (const m of members) {
+ if (updatedMembers[m.ID]) {
+ delete updatedMembers[m.ID];
}
- });
+ }
+ setSelectedMembers(updatedMembers);
- const filteredArr = selectedPending.splice(
- indexToRemoveFromSelected,
- 1,
- );
+ // refresh the data
+ pendingUserAccess.refresh();
- setSelectedPending(filteredArr);
- }
+ // trigger onChange
+ onChange();
- if (response.data.success) {
notification.add({
color: 'success',
message: 'Succesfully approved user permissions',
@@ -356,10 +309,7 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
* @param quickActionFlag - quick deny button on table
* @description Deny Selected Pending Members
*/
- const denyPendingMembers = async (
- members: PendingMember[],
- quickActionFlag?: boolean,
- ) => {
+ const denyPendingMembers = async (members: SETTINGS_PENDING_USER[]) => {
try {
// construct requests for post data
const requests = members.map((m) => {
@@ -376,13 +326,19 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
}
let response: AxiosResponse<{ success: boolean }> | null = null;
- if (mode === 'engine') {
+ if (
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ) {
response = await monolithStore.denyEngineUserAccessRequest(
adminMode,
id,
requests,
);
- } else if (mode === 'app') {
+ } else if (type === 'APP') {
response = await monolithStore.denyProjectUserAccessRequest(
adminMode,
id,
@@ -395,42 +351,24 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
return;
}
- // get index of pending members in order to remove
- const indexesToRemove = [];
- requests.forEach((mem) => {
- pendingMembers.find((m, i) => {
- if (mem === m.ID) indexesToRemove.push(i);
- });
- });
-
- // remove indexes from react hook form
- pendingMemberRemove(indexesToRemove);
+ if (response.data.success) {
+ const updatedMembers = {
+ ...selectedMembers,
+ } as Record;
- if (!quickActionFlag) {
- setSelectedPending([]);
- // close modal
- // setDenySelectedModal(false);
- } else {
- // remove from selected pending members
- let indexToRemoveFromSelected = 0;
- // remove from selected
- selectedPending.find((m, i) => {
- if (m.ID !== requests[0]) {
- indexToRemoveFromSelected = i;
+ for (const m of members) {
+ if (updatedMembers[m.ID]) {
+ delete updatedMembers[m.ID];
}
- });
+ }
+ setSelectedMembers(updatedMembers);
- const filteredArr = selectedPending.splice(
- indexToRemoveFromSelected,
- 1,
- );
+ // refresh the data
+ pendingUserAccess.refresh();
- setSelectedPending(filteredArr);
- // close modal
- // setDenySelectedModal(false);
- }
+ // trigger onChange
+ onChange();
- if (response.data.success) {
notification.add({
color: 'success',
message: 'Succesfully denied user permissions',
@@ -452,38 +390,26 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
/** HELPERS */
/**
* @name updatePendingMemberPermission
- * @param mem
+ * @param member
* @param value
* @desc Updates pending member permission in radiogroup
*/
const updatePendingMemberPermission = (
- mem: PendingMember,
+ member: SETTINGS_PENDING_USER,
role: SETTINGS_ROLE,
) => {
- const updatedPendingMems = pendingMembers.map((user) => {
- if (user.REQUEST_USERID === mem.REQUEST_USERID) {
+ const updatedRenderedMembers = renderedMembers.map((m) => {
+ if (member.ID === m.ID) {
return {
- ...user,
+ ...m,
PERMISSION: role,
};
- } else {
- return user;
}
- });
- const updateSelectedPendingMems = selectedPending.map((user) => {
- if (user.REQUEST_USERID === mem.REQUEST_USERID) {
- return {
- ...user,
- PERMISSION: role,
- };
- } else {
- return user;
- }
+ return m;
});
- setSelectedPending(updateSelectedPendingMems);
- setValue('PENDING_MEMBERS', updatedPendingMems);
+ setRenderedMembers(updatedRenderedMembers);
};
return (
@@ -501,9 +427,9 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
- {pendingMembers.length < 2
- ? `${pendingMembers.length} pending request`
- : `${pendingMembers.length} pending requests`}
+ {renderedMembers.length < 2
+ ? `${renderedMembers.length} pending request`
+ : `${renderedMembers.length} pending requests`}
@@ -521,17 +447,20 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
- {selectedPending.length > 0 && (
+ {Object.keys(selectedMembers).length > 0 && (
<>
{
- denyPendingMembers(
- selectedPending,
- false,
- );
+ const members =
+ renderedMembers.filter(
+ (m) =>
+ selectedMembers[m.ID],
+ );
+
+ denyPendingMembers(members);
}}
>
Deny Selected
@@ -541,9 +470,14 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
{
+ const members =
+ renderedMembers.filter(
+ (m) =>
+ selectedMembers[m.ID],
+ );
+
approvePendingMembers(
- selectedPending,
- false,
+ Object.values(members),
);
}}
>
@@ -561,236 +495,244 @@ export const PendingMembersTable = (props: PendingMemberTableProps) => {
- {pendingMembers.length ? (
-
-
-
-
- 0
- }
- onChange={() => {
- if (
- selectedPending.length !==
- pendingMembers.length
- ) {
- setSelectedPending(
- pendingMembers,
- );
- } else {
- setSelectedPending([]);
- }
- }}
- />
-
- ID
-
-
-
-
-
- Request Date
-
-
+
+
+
+
+ ) : (
+ <>
+ {renderedMembers.length ? (
+
+
+
+
-
-
-
-
-
- Permission
-
-
- Actions
-
-
-
-
- {pendingMembers.map((x, i) => {
- const user = pendingMembers[i];
-
- let isSelected = false;
-
- if (user) {
- isSelected = selectedPending.some(
- (value: PendingMember) => {
- return (
- value.REQUEST_USERID ===
- user.REQUEST_USERID
- );
- },
- );
- }
- if (user) {
- return (
-
-
- {
- if (
- isSelected
- ) {
- const selPending =
- [];
- selectedPending.forEach(
+
+ 0
+ }
+ onChange={() => {
+ if (
+ Object.keys(
+ selectedMembers,
+ ).length !==
+ renderedMembers.length
+ ) {
+ const updatedMembers =
+ renderedMembers.reduce(
(
- u: PendingMember,
+ acc,
+ val,
) => {
- if (
- u.REQUEST_USERID !==
- user.REQUEST_USERID
- )
- selPending.push(
- u,
- );
+ acc[
+ val.ID
+ ] =
+ val;
+
+ return acc;
},
+ {},
);
- setSelectedPending(
- selPending,
- );
- } else {
- setSelectedPending(
- [
- ...selectedPending,
- user,
- ],
- );
- }
- }}
- />
-
-
- {user.REQUEST_USERID}
-
-
- {user.NAME}
-
-
- {user.REQUEST_TIMESTAMP}
-
-
- {
- const val =
- e.target
- .value;
- if (val) {
- updatePendingMemberPermission(
- user,
- val as SETTINGS_ROLE,
- );
- }
- }}
- >
-
-
-
-
-
-
-
- {
- approvePendingMembers(
- [user],
- true,
+ setSelectedMembers(
+ updatedMembers,
);
- }}
- >
-
-
- {
- denyPendingMembers(
- [user],
- true,
+ } else {
+ setSelectedMembers(
+ {},
);
+ }
+ }}
+ />
+
+
+ ID
+
+
+ Name
+
+
+
+ Request Date
+
+
-
-
-
-
- );
- } else {
- return (
-
-
-
-
-
-
-
- );
- }
- })}
-
-
- ) : (
-
-
- 0 requests currently pending
-
-
+
+
+
+
+
+ Permission
+
+
+ Actions
+
+
+
+
+ {renderedMembers.map(
+ (member, i) => {
+ const isSelected =
+ !!selectedMembers[
+ member.ID
+ ];
+
+ return (
+
+
+ {
+ // update selected members
+ const updatedMembers =
+ {
+ ...selectedMembers,
+ } as Record<
+ string,
+ true
+ >;
+
+ if (
+ isSelected
+ ) {
+ delete updatedMembers[
+ member
+ .ID
+ ];
+ } else {
+ updatedMembers[
+ member.ID
+ ] =
+ true;
+ }
+
+ setSelectedMembers(
+ updatedMembers,
+ );
+ }}
+ />
+
+
+ {
+ member.REQUEST_USERID
+ }
+
+
+ {member.NAME}
+
+
+ {
+ member.REQUEST_TIMESTAMP
+ }
+
+
+ {
+ const val =
+ e
+ .target
+ .value;
+ if (
+ val
+ ) {
+ updatePendingMemberPermission(
+ member,
+ val as SETTINGS_ROLE,
+ );
+ }
+ }}
+ >
+
+
+
+
+
+
+
+ {
+ approvePendingMembers(
+ [
+ member,
+ ],
+ );
+ }}
+ >
+
+
+ {
+ denyPendingMembers(
+ [
+ member,
+ ],
+ );
+ }}
+ >
+
+
+
+
+ );
+ },
+ )}
+
+
+ ) : (
+
+
+ No requests pending
+
+
+ )}
+ >
)}
diff --git a/packages/client/src/components/settings/SettingsTiles.tsx b/packages/client/src/components/settings/SettingsTiles.tsx
index 0d8c36c50a..da836e7694 100644
--- a/packages/client/src/components/settings/SettingsTiles.tsx
+++ b/packages/client/src/components/settings/SettingsTiles.tsx
@@ -15,11 +15,10 @@ import {
import { AxiosResponse } from 'axios';
+import { ALL_TYPES } from '@/types';
import { useRootStore, usePixel, useSettings } from '@/hooks';
import { LoadingScreen } from '@/components/ui';
-import { SETTINGS_MODE } from './settings.types';
-
const StyledAlert = styled(Alert, {
shouldForwardProp: (prop) => prop !== 'setBounds',
})<{ setBounds?: boolean }>(({ theme, setBounds }) => ({
@@ -49,7 +48,9 @@ const StyledGrid = styled(Grid)(() => ({
flex: '1',
}));
-const StyledTypography = styled(Typography)<{
+const StyledTypography = styled(Typography, {
+ shouldForwardProp: (prop) => prop !== 'isDisabled',
+})<{
// Track if discoverable will be disabled or not
isDisabled: boolean;
}>(({ isDisabled, theme }) => ({
@@ -58,9 +59,9 @@ const StyledTypography = styled(Typography)<{
interface SettingsTilesProps {
/**
- * Mode of setting
+ * Type of setting
*/
- mode: SETTINGS_MODE;
+ type: ALL_TYPES;
/**
* Id of the setting
@@ -68,7 +69,7 @@ interface SettingsTilesProps {
id: string;
/**
- * Name of setting
+ * Name of the setting
*/
name: string;
@@ -90,7 +91,7 @@ interface SettingsTilesProps {
}
export const SettingsTiles = (props: SettingsTilesProps) => {
- const { id, mode, name, condensed, onDelete, direction = 'column' } = props;
+ const { id, type, name, condensed, onDelete, direction = 'column' } = props;
const { monolithStore, configStore } = useRootStore();
const notification = useNotification();
@@ -103,11 +104,15 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
const [loading, setLoading] = useState(false);
const engineInfo = usePixel(
- mode === 'engine'
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
? adminMode
? `AdminEngineInfo(engine='${id}');`
: `EngineInfo(engine='${id}');`
- : mode === 'app'
+ : type === 'APP'
? adminMode
? `AdminProjectInfo(project='${id}')`
: `ProjectInfo(project='${id}')`
@@ -120,7 +125,13 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
return;
}
- if (mode === 'engine') {
+ if (
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ) {
const data = engineInfo.data as {
database_global: boolean;
database_discoverable: boolean;
@@ -128,7 +139,7 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
setDiscoverable(data.database_discoverable);
setGlobal(data.database_global);
- } else if (mode === 'app') {
+ } else if (type === 'APP') {
const data = engineInfo.data as {
project_global: boolean;
project_discoverable: boolean;
@@ -149,9 +160,13 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
// run the pixel
const response = await monolithStore.runQuery(
- mode === 'engine'
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
? `DeleteEngine(engine=['${id}']);`
- : mode === 'app'
+ : type === 'APP'
? `DeleteProject(project=['${id}']);`
: '',
);
@@ -194,7 +209,13 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
// run the pixel
const response = await monolithStore.runQuery(
- mode === 'engine' ? `CloseEngine(engine=['${id}']);` : '',
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ? `CloseEngine(engine=['${id}']);`
+ : '',
);
const operationType = response.pixelReturn[0].operationType;
@@ -232,13 +253,19 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
setLoading(true);
let response: AxiosResponse<{ success: boolean }> | null = null;
- if (mode === 'engine') {
+ if (
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ) {
response = await monolithStore.setEngineVisiblity(
adminMode,
id,
!discoverable,
);
- } else if (mode === 'app') {
+ } else if (type === 'APP') {
response = await monolithStore.setProjectVisiblity(
adminMode,
id,
@@ -284,13 +311,19 @@ export const SettingsTiles = (props: SettingsTilesProps) => {
setLoading(true);
let response: AxiosResponse<{ success: boolean }> | null = null;
- if (mode === 'engine') {
+ if (
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
+ ) {
response = await monolithStore.setEngineGlobal(
adminMode,
id,
!global,
);
- } else if (mode === 'app') {
+ } else if (type === 'APP') {
response = await monolithStore.setProjectGlobal(
adminMode,
id,
diff --git a/packages/client/src/components/settings/UpdateSMSS.tsx b/packages/client/src/components/settings/UpdateSMSS.tsx
index c303eb687c..60b02754b5 100644
--- a/packages/client/src/components/settings/UpdateSMSS.tsx
+++ b/packages/client/src/components/settings/UpdateSMSS.tsx
@@ -2,14 +2,14 @@ import { useState, useEffect } from 'react';
import { styled, useNotification, Button, Paper, Typography } from '@semoss/ui';
import Editor from '@monaco-editor/react';
+import { ALL_TYPES } from '@/types';
import { useRootStore, usePixel, useSettings } from '@/hooks';
-import { SETTINGS_MODE } from './settings.types';
interface UpdateSMSSProps {
/**
- * Mode of setting
+ * Type of setting
*/
- mode: SETTINGS_MODE;
+ type: ALL_TYPES;
/**
* Id of the setting
@@ -36,7 +36,7 @@ const StyledPaper = styled(Paper)(() => ({
}));
export const UpdateSMSS = (props: UpdateSMSSProps) => {
- const { mode, id } = props;
+ const { type, id } = props;
const { monolithStore } = useRootStore();
const notification = useNotification();
@@ -47,11 +47,15 @@ export const UpdateSMSS = (props: UpdateSMSSProps) => {
const [readOnly, setReadOnly] = useState(true);
const smssDetails = usePixel(
- mode === 'engine'
+ type === 'DATABASE' ||
+ type === 'STORAGE' ||
+ type === 'MODEL' ||
+ type === 'VECTOR' ||
+ type === 'FUNCTION'
? adminMode
? `AdminGetEngineSMSS(engine=['${id}'])`
: `GetEngineSMSS(engine=['${id}'])`
- : mode === 'app'
+ : type === 'APP'
? adminMode
? `AdminGetProjectSMSS(project=['${id}'])`
: `GetProjectSMSS(project=['${id}'])`
diff --git a/packages/client/src/components/settings/UserAddOverlay.tsx b/packages/client/src/components/settings/UserAddOverlay.tsx
new file mode 100644
index 0000000000..700432a33d
--- /dev/null
+++ b/packages/client/src/components/settings/UserAddOverlay.tsx
@@ -0,0 +1,586 @@
+import { useEffect } from 'react';
+import {
+ LocalPoliceRounded,
+ CloudUploadRounded,
+ DownloadForOfflineRounded,
+} from '@mui/icons-material';
+import {
+ styled,
+ useNotification,
+ List,
+ Modal,
+ Button,
+ Typography,
+ Stack,
+ TextField,
+ Select,
+ Switch,
+} from '@semoss/ui';
+import { useForm, useFormState, Controller } from 'react-hook-form';
+import { useRootStore, useSettings } from '@/hooks';
+import { AxiosResponse } from 'axios';
+
+const StyledModalContent = styled(Modal.Content)(({ theme }) => ({
+ maxWidth: '50rem',
+}));
+
+const StyledForm = styled('div')(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(1),
+}));
+
+const StyledListItem = styled(List.Item)({
+ padding: '4px 0',
+});
+
+const StyledList = styled(List)({
+ padding: 0,
+});
+
+const StyledCountryCodeExt = styled(TextField)({
+ width: '168px',
+});
+
+const StyledPhoneNumber = styled(TextField)({
+ width: '550px',
+});
+
+const StyledPermissions = styled(Typography)({
+ padding: '25px 0',
+});
+
+interface User {
+ id: string;
+ type: string;
+ name?: string;
+ admin?: boolean;
+ publisher?: boolean;
+ exporter?: boolean;
+ email?: string;
+ phone?: string;
+ phoneextension?: string;
+ countrycode?: string;
+ username?: string;
+}
+
+interface EditUserForm {
+ id: string;
+ username: string;
+ name: string;
+ password: string;
+ email: string;
+ phone: string;
+ phoneextension: string;
+ countrycode: string;
+ admin: boolean;
+ exporter: boolean;
+ publisher: boolean;
+ type: string;
+}
+
+const capitalize = (input: string) => {
+ return input.charAt(0).toUpperCase() + input.slice(1);
+};
+
+const passwordValidate = (password: string) => {
+ if (!password) {
+ return true;
+ }
+ if (!password.match(/[a-z]/g)) {
+ return false;
+ }
+
+ if (!password.match(/[A-Z]/g)) {
+ return false;
+ }
+
+ if (!password.match(/[0-9]/g)) {
+ return false;
+ }
+
+ if (!password.match(/[!@#$%^&*]/g)) {
+ return false;
+ }
+
+ return true;
+};
+
+const numberValidate = (number: string) => {
+ if (!number) {
+ return false;
+ }
+ if (!number.match(/^[()-.\s0-9]{8,}$/)) {
+ return false;
+ }
+
+ return true;
+};
+
+interface UserAddOverlayProps {
+ /**
+ * Track if the model is open or close
+ */
+ open: boolean;
+
+ /**
+ * User that is being edited
+ */
+ user: User | null;
+
+ /**
+ * Called on close
+ *
+ * @returns - method that is called onClose
+ */
+ onClose: (success: boolean) => void;
+}
+
+export const UserAddOverlay = (props: UserAddOverlayProps) => {
+ const { open = false, user: user = null, onClose = () => null } = props;
+
+ const { configStore, monolithStore } = useRootStore();
+ const { adminMode } = useSettings();
+ const notification = useNotification();
+
+ const isNewUser = user === null;
+
+ const {
+ control,
+ reset,
+ handleSubmit,
+ watch,
+ formState: { errors },
+ } = useForm({
+ defaultValues: {
+ id: user?.id,
+ username: user?.username,
+ name: user?.name,
+ email: user?.email,
+ phone: user?.phone,
+ phoneextension: user?.phoneextension,
+ countrycode: user?.countrycode,
+ admin: user?.admin,
+ exporter: user?.exporter,
+ publisher: user?.exporter,
+ type: user?.type,
+ },
+ });
+
+ useEffect(() => {
+ // reset on open or close
+ reset({
+ ...(user || {}),
+ });
+ }, [user, open]);
+
+ const type = watch('type', '');
+
+ // TODO: Standardize
+ const providers = configStore.store.config.providers.map((val) => ({
+ name: capitalize(val),
+ provider: capitalize(val),
+ }));
+
+ /**
+ * Create / edit the user
+ */
+ const editUser = handleSubmit(
+ async (data: EditUserForm) => {
+ let success = false;
+
+ try {
+ let response: AxiosResponse | null = null;
+ if (isNewUser) {
+ response = await monolithStore.createUser(adminMode, data);
+ } else {
+ response = await monolithStore.editMemberInfo(
+ adminMode,
+ data,
+ );
+ }
+
+ console.log(response);
+
+ if (!response) {
+ return;
+ }
+
+ // ignore if there is no response
+ if (response.data) {
+ notification.add({
+ color: 'success',
+ message: isNewUser
+ ? 'Successfully added user'
+ : 'Successfully editted user',
+ });
+
+ success = true;
+ } else {
+ notification.add({
+ color: 'error',
+ message: isNewUser
+ ? 'Error adding user'
+ : 'Error editting user',
+ });
+ }
+ } catch (e) {
+ notification.add({
+ color: 'error',
+ message: String(e),
+ });
+ } finally {
+ // close the overlay
+ onClose(success);
+ }
+ },
+ (e) => {
+ console.warn(e);
+
+ notification.add({
+ color: 'error',
+ message: 'Form is Invalid',
+ });
+ },
+ );
+
+ return (
+
+ Add Users
+
+
+ );
+};
diff --git a/packages/client/src/components/settings/UserTable.tsx b/packages/client/src/components/settings/UserTable.tsx
new file mode 100644
index 0000000000..fb058bf9af
--- /dev/null
+++ b/packages/client/src/components/settings/UserTable.tsx
@@ -0,0 +1,520 @@
+import { useMemo, useRef, useState } from 'react';
+import { Delete, Edit } from '@mui/icons-material';
+import {
+ styled,
+ useNotification,
+ Button,
+ Checkbox,
+ Typography,
+ AvatarGroup,
+ Avatar,
+ Table,
+ IconButton,
+ Search,
+} from '@semoss/ui';
+import { useRootStore, useAPI, useSettings, useDebounceValue } from '@/hooks';
+import { LoadingScreen } from '@/components/ui';
+import { UserAddOverlay } from './UserAddOverlay';
+import { UserTableUser } from './UserTableUser';
+
+const StyledMemberContent = styled('div')({
+ display: 'flex',
+ width: '100%',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '25px',
+ flexShrink: '0',
+});
+
+const StyledMemberInnerContent = styled('div')({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '20px',
+ alignSelf: 'stretch',
+});
+
+const StyledTableContainer = styled(Table.Container)(({ theme }) => ({
+ borderRadius: '12px',
+ border: `1px solid ${theme.palette.secondary.border}`,
+}));
+
+const StyledMemberLoading = styled('div')(({ theme }) => ({
+ position: 'relative',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '160px',
+}));
+
+const StyledMemberTable = styled(Table)({
+ backgroundColor: 'white',
+});
+
+const StyledTableTitleContainer = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+ alignSelf: 'stretch',
+ boxShadow: '0px -1px 0px 0px rgba(0, 0, 0, 0.12) inset',
+ backgroundColor: 'white',
+});
+
+const StyledTableTitleDiv = styled('div')({
+ display: 'flex',
+ padding: '12px 24px 12px 16px',
+ alignItems: 'center',
+ gap: '10px',
+});
+
+const StyledTableTitleMemberContainer = styled('div')({
+ display: 'flex',
+ alignItems: 'flex-start',
+ flex: '1 0 0',
+});
+
+const StyledAvatarGroupContainer = styled('div')({
+ display: 'flex',
+ width: '130px',
+ height: '56px',
+ padding: '10px 16px',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ gap: '10px',
+});
+
+const StyledTableTitleMemberCountContainer = styled('div')({
+ display: 'flex',
+ height: '56px',
+ padding: '6px 16px 6px 8px',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ gap: '10px',
+});
+
+const StyledTableTitleMemberCount = styled('div')({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+});
+
+const StyledSearchButtonContainer = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+ // gap: '10px',
+});
+
+const StyledAddMemberContainer = styled('div')({
+ display: 'flex',
+ padding: '10px 24px 10px 8px',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ gap: '10px',
+});
+
+const StyledNoUsersDiv = styled('div')(({ theme }) => ({
+ width: '100%',
+ height: '503px',
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(1),
+ justifyContent: 'center',
+ alignItems: 'center',
+}));
+
+interface User {
+ id: string;
+ type: string;
+ name?: string;
+ admin?: boolean;
+ publisher?: boolean;
+ exporter?: boolean;
+ email?: string;
+ phone?: string;
+ phoneextension?: string;
+ countrycode?: string;
+ username?: string;
+}
+
+interface UserTableProps {
+ /**
+ * Called users are changed
+ */
+ onChange?: () => void;
+}
+
+export const UserTable = (props: UserTableProps) => {
+ const { onChange = () => null } = props;
+
+ const { adminMode } = useSettings();
+ const { monolithStore } = useRootStore();
+ const notification = useNotification();
+
+ const [page, setPage] = useState(0);
+ const [rowsPerPage, setRowsPerPage] = useState(5);
+ const [search, setSearch] = useState('');
+
+ // debounce the input
+ const debouncedSearch = useDebounceValue(search);
+
+ /** Add User State */
+ const [addModalOpen, setAddModalOpen] = useState(false);
+ const [addModalUser, setAddModalUser] = useState(null);
+
+ const userSearchRef = useRef(undefined);
+
+ const getUsers = useAPI([
+ 'getAllUsers',
+ adminMode,
+ debouncedSearch ? debouncedSearch : '',
+ (page + 1) * rowsPerPage - rowsPerPage, // offset
+ rowsPerPage, // limit
+ ]);
+
+ // track if the page is loading
+ const isLoading =
+ getUsers.status === 'INITIAL' || getUsers.status === 'LOADING';
+ const renderedUsers = getUsers.status === 'SUCCESS' ? getUsers.data : [];
+ const totalUsers = getUsers.status === 'SUCCESS' ? 0 : 0;
+ const hasUsers = getUsers.status === 'SUCCESS' && getUsers.data.length > 0;
+ /**
+ * Update a user
+ * @param user - user to update
+ */
+ const updateUser = async (user: User) => {
+ try {
+ const response = await monolithStore.editMemberInfo(
+ adminMode,
+ user,
+ );
+
+ if (!response) {
+ return;
+ }
+
+ // ignore if there is no response
+ if (response.data) {
+ notification.add({
+ color: 'success',
+ message: 'Succesfully updated user',
+ });
+
+ onChange();
+
+ // refresh the users
+ getUsers.refresh();
+ } else {
+ notification.add({
+ color: 'error',
+ message: `Error changing user`,
+ });
+ }
+ } catch (e) {
+ notification.add({
+ color: 'error',
+ message: String(e),
+ });
+ }
+ };
+
+ /**
+ * Delate a user info
+ * @param user - user to update
+ */
+ const deleteUser = async (user: User) => {
+ try {
+ const response = await monolithStore.deleteMember(
+ adminMode,
+ user.id,
+ user.type,
+ );
+
+ if (!response) {
+ return;
+ }
+
+ // ignore if there is no response
+ if (response.data) {
+ notification.add({
+ color: 'success',
+ message: 'Succesfully deleting user',
+ });
+
+ onChange();
+
+ // refresh the users
+ getUsers.refresh();
+ } else {
+ notification.add({
+ color: 'error',
+ message: `Error deleting user`,
+ });
+ }
+ } catch (e) {
+ notification.add({
+ color: 'error',
+ message: String(e),
+ });
+ }
+ };
+
+ // Avatars rendered
+ const Avatars = useMemo(() => {
+ if (!renderedUsers.length) {
+ return [];
+ }
+
+ let i = 0;
+ const avatarList = [];
+ while (i < 5 && i < renderedUsers.length) {
+ avatarList.push(
+
+ {(renderedUsers[i].name || ' ').charAt(0).toUpperCase()}
+ ,
+ );
+
+ i++;
+ }
+
+ return avatarList;
+ }, [renderedUsers.length]);
+
+ return (
+
+
+
+
+
+ Users
+
+
+ {Avatars.length > 0 ? (
+
+
+ {Avatars.map((el) => {
+ return el;
+ })}
+
+
+ ) : null}
+
+
+
+ {totalUsers} Users
+
+
+
+
+
+
+ {
+ setSearch(e.target.value);
+ }}
+ />
+
+
+
+ {
+ // open the modal to a new user
+ setAddModalOpen(true);
+ setAddModalUser(null);
+ }}
+ >
+ Add User
+
+
+
+
+ {isLoading ? (
+
+
+
+
+
+ ) : (
+ <>
+ {hasUsers ? (
+
+
+
+
+ User
+
+
+ Role
+
+
+
+
+
+
+
+ {renderedUsers.map((user) => {
+ if (user) {
+ return (
+
+
+
+
+
+ {
+ updateUser({
+ ...user,
+ publisher:
+ !user.publisher,
+ });
+ }}
+ />
+ {
+ updateUser({
+ ...user,
+ exporter:
+ !user.exporter,
+ });
+ }}
+ />
+ {
+ updateUser({
+ ...user,
+ admin: !user.admin,
+ });
+ }}
+ />
+
+
+ {
+ setAddModalOpen(
+ true,
+ );
+
+ setAddModalUser(
+ user,
+ );
+ }}
+ >
+
+
+ {
+ deleteUser(
+ user,
+ );
+ }}
+ >
+
+
+
+
+ );
+ }
+
+ return null;
+ })}
+
+
+
+ {
+ setPage(v);
+ }}
+ page={page}
+ rowsPerPage={rowsPerPage}
+ rowsPerPageOptions={[5, 10, 20]}
+ onRowsPerPageChange={(e) => {
+ // set the new limit
+ setRowsPerPage(
+ parseInt(
+ e.target.value,
+ 10,
+ ),
+ );
+ }}
+ count={totalUsers}
+ />
+
+
+
+ ) : (
+
+
+ No users
+
+ {
+ // open the modal to a new user
+ setAddModalOpen(true);
+ setAddModalUser(null);
+ }}
+ >
+ Add User
+
+
+ )}
+ >
+ )}
+
+
+
+ {
+ // close it
+ setAddModalOpen(false);
+
+ // de-select the user
+ setAddModalUser(null);
+
+ // refresh if successful
+ if (success) {
+ // trigger the update
+ onChange();
+
+ getUsers.refresh();
+ }
+ }}
+ />
+
+ );
+};
diff --git a/packages/client/src/components/settings/UserTableUser.tsx b/packages/client/src/components/settings/UserTableUser.tsx
new file mode 100644
index 0000000000..725c2a3ff2
--- /dev/null
+++ b/packages/client/src/components/settings/UserTableUser.tsx
@@ -0,0 +1,137 @@
+import { styled, Avatar, Typography, Stack } from '@semoss/ui';
+
+const StyledUser = styled(Stack)(({ theme }) => ({
+ paddingTop: theme.spacing(1),
+ paddingRight: theme.spacing(2),
+ paddingBottom: theme.spacing(1),
+ paddingLeft: theme.spacing(2),
+}));
+
+const StyledAvatar = styled(Avatar)({
+ height: '32px',
+ width: '32px',
+});
+
+// TODO:Refactor when Typography coloris updated
+const StyledPrimaryText = styled(Typography)(({ theme }) => ({
+ color: theme.palette.text.primary,
+}));
+
+const StyledSecondaryText = styled(Typography)(({ theme }) => ({
+ color: theme.palette.text.secondary,
+}));
+
+interface UserTableUserProps {
+ /**
+ * Name of the user
+ */
+ name: string;
+
+ /**
+ * Name of the user
+ */
+ id: string;
+
+ /**
+ * Email of the user
+ */
+ email: string;
+
+ /**
+ * Type of the user
+ */
+ type: string;
+
+ /**
+ * Optional action to render
+ */
+ action?: React.ReactNode;
+}
+
+/**
+ * @name extractInitials
+ *
+ * Extract a initials for a string
+ *
+ * @param str
+ */
+const extractInitials = (str: string): string => {
+ if (str.length < 1) {
+ return '';
+ }
+
+ return str.split(' ').reduce((prev, curr) => {
+ return prev + (curr[0] || '');
+ }, '');
+};
+
+export const UserTableUser = (props: UserTableUserProps) => {
+ const { name, id, email, type, action } = props;
+
+ const initials = extractInitials(name);
+
+ return (
+
+ {initials}
+
+
+ {name || <> >}
+
+
+
+
+ User ID:
+
+
+ {id || <> >}
+
+
+
+
+ Email:
+
+
+ {email || <> >}
+
+
+
+
+ Type:
+
+
+ {type || <> >}
+
+
+
+
+ {action}
+
+ );
+};
diff --git a/packages/client/src/components/settings/index.ts b/packages/client/src/components/settings/index.ts
index 51561ab63b..3ddd316f81 100644
--- a/packages/client/src/components/settings/index.ts
+++ b/packages/client/src/components/settings/index.ts
@@ -1,5 +1,5 @@
-export * from './PendingMembersTable';
-export * from './MembersTable';
+export * from './settings.types';
+
export * from './TeamMembersTable';
export * from './TeamProjectsTable';
export * from './TeamEnginesTable';
@@ -7,3 +7,6 @@ export * from './SettingsTiles';
export * from './UpdateSMSS';
export * from './FileTable';
export * from './EngineQASidebar';
+export * from './MembersTable';
+export * from './PendingMembersTable';
+export * from './UserTable';
diff --git a/packages/client/src/components/settings/member-permissions.constants.ts b/packages/client/src/components/settings/member-permissions.constants.ts
deleted file mode 100644
index d3fab06e2e..0000000000
--- a/packages/client/src/components/settings/member-permissions.constants.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-export const PERMISSION_DESCRIPTION_MAP: Record<
- string,
- Record
-> = {
- function: {
- author: 'Ability to hide or delete the function, provision other authors, and all editor permissions',
- editor: 'Ability to edit the function code, provision other users as editors and read-only users, and all read-only permissions',
- readonly: 'Ability to execute the function',
- },
- app: {
- author: 'Ability to hide or delete the data app, provision other authors and all editor permissions',
- editor: 'Ability to edit the data app code, provision other users as editors and read only users, and all read-only permissions',
- readonly:
- 'Ability to view the data app. User still requires permission to all dependent databases, models, remote storage, vector databases, etc',
- },
- model: {
- author: 'Ability to edit the model connection details, set the model as discoverable, delete the model, provision other authors, and all editor permissions',
- editor: 'Ability to edit the model details, provision other users as editors and read-only users, and all read-only permissions',
- readonly: 'Ability to run the model',
- },
- storage: {
- author: 'Ability to hide or delete the remote storage, provision other authors, and all editor permissions',
- editor: 'Ability to push and delete files from the remote storage, and all read-only permissions',
- readonly: 'Ability to view and pull files from the remote storage',
- },
- database: {
- author: 'Ability to edit the database connection details, set the database as discoverable, delete the database, provision other authors, and all editor permissions',
- editor: 'Ability to edit the database structure, provision other users as editors and read-only users, and all read-only permissions',
- readonly: 'Ability to query and read data from the database',
- },
- vector: {
- author: 'Ability to hide or delete the vector database, provision other authors, and all editor permissions',
- editor: 'Ability to add and remove files from the vector database, and all read-only permissions',
- readonly: 'Ability to query against the vector database',
- },
-};
diff --git a/packages/client/src/components/settings/settings.types.ts b/packages/client/src/components/settings/settings.types.ts
index 1860628180..61161363c4 100644
--- a/packages/client/src/components/settings/settings.types.ts
+++ b/packages/client/src/components/settings/settings.types.ts
@@ -1,3 +1,23 @@
export type SETTINGS_ROLE = 'Author' | 'Editor' | 'Read-Only';
-export type SETTINGS_MODE = 'engine' | 'app';
+export type SETTINGS_PROVISIONED_USER = {
+ id: string;
+ name: string;
+ type: string;
+ email: string;
+ permission: string;
+ permission_granted_by: string;
+ permission_granted_by_type: string;
+ date_added: string;
+};
+
+export type SETTINGS_PENDING_USER = {
+ ID: string;
+ NAME: string;
+ EMAIL: string;
+ PERMISSION: string;
+ // Requester Info
+ REQUEST_TIMESTAMP: string;
+ REQUEST_TYPE: string;
+ REQUEST_USERID: string;
+};
diff --git a/packages/client/src/components/ui/LoadingScreen/LoadingScreen.tsx b/packages/client/src/components/ui/LoadingScreen/LoadingScreen.tsx
index 6f8a4278da..625b8f43a3 100644
--- a/packages/client/src/components/ui/LoadingScreen/LoadingScreen.tsx
+++ b/packages/client/src/components/ui/LoadingScreen/LoadingScreen.tsx
@@ -4,12 +4,15 @@ import { Backdrop, CircularProgress, Typography, Stack } from '@semoss/ui';
import { LoadingScreenContext } from './LoadingScreenContext';
export interface LoadingScreenProps {
+ /** Relative loading screen */
+ relative?: boolean;
+
/** Content to overlay the Loading Screen on */
children: React.ReactNode;
}
export const LoadingScreen = (props: LoadingScreenProps): JSX.Element => {
- const { children } = props;
+ const { relative = false, children } = props;
// when the count is > 0 it is loading
const [count, setCount] = useState(0);
@@ -73,6 +76,7 @@ export const LoadingScreen = (props: LoadingScreenProps): JSX.Element => {
// zIndex: (theme) =>
// Math.max.apply(Math, Object.values(theme.zIndex)) + 1,
zIndex: 1501,
+ position: relative ? 'relative' : undefined,
}}
>
{
{workspace.role === 'OWNER' ? (
{
navigate('/settings/app');
@@ -89,15 +89,14 @@ export const SettingsView = () => {
{view === 'CURRENT' && (
console.log('TODO')}
+ onChange={() => console.log('TODO')}
/>
)}
{view === 'PENDING' && (
)}
diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts
index 7d85ba4a56..eac5454ad6 100644
--- a/packages/client/src/constants.ts
+++ b/packages/client/src/constants.ts
@@ -1,3 +1,4 @@
+import { ALL_TYPES } from './types';
import Logo from '@/assets/logo.svg';
export const THEME_TITLE = process.env.THEME_TITLE;
@@ -8,3 +9,40 @@ export const THEME = {
name: THEME_TITLE || 'SEMOSS',
logo: Logo,
};
+
+export const PERMISSION_DESCRIPTION_MAP: Record<
+ ALL_TYPES,
+ Record
+> = {
+ APP: {
+ author: 'Ability to hide or delete the data app, provision other authors and all editor permissions',
+ editor: 'Ability to edit the data app code, provision other users as editors and read only users, and all read-only permissions',
+ readonly:
+ 'Ability to view the data app. User still requires permission to all dependent databases, models, remote storage, vector databases, etc',
+ },
+ FUNCTION: {
+ author: 'Ability to hide or delete the function, provision other authors, and all editor permissions',
+ editor: 'Ability to edit the function code, provision other users as editors and read-only users, and all read-only permissions',
+ readonly: 'Ability to execute the function',
+ },
+ MODEL: {
+ author: 'Ability to edit the model connection details, set the model as discoverable, delete the model, provision other authors, and all editor permissions',
+ editor: 'Ability to edit the model details, provision other users as editors and read-only users, and all read-only permissions',
+ readonly: 'Ability to run the model',
+ },
+ STORAGE: {
+ author: 'Ability to hide or delete the remote storage, provision other authors, and all editor permissions',
+ editor: 'Ability to push and delete files from the remote storage, and all read-only permissions',
+ readonly: 'Ability to view and pull files from the remote storage',
+ },
+ DATABASE: {
+ author: 'Ability to edit the database connection details, set the database as discoverable, delete the database, provision other authors, and all editor permissions',
+ editor: 'Ability to edit the database structure, provision other users as editors and read-only users, and all read-only permissions',
+ readonly: 'Ability to query and read data from the database',
+ },
+ VECTOR: {
+ author: 'Ability to hide or delete the vector database, provision other authors, and all editor permissions',
+ editor: 'Ability to add and remove files from the vector database, and all read-only permissions',
+ readonly: 'Ability to query against the vector database',
+ },
+};
diff --git a/packages/client/src/hooks/index.ts b/packages/client/src/hooks/index.ts
index e2091af033..ed6ccd2bce 100644
--- a/packages/client/src/hooks/index.ts
+++ b/packages/client/src/hooks/index.ts
@@ -13,6 +13,7 @@ import { useStepper } from './useStepper';
import { useWorkspace } from './useWorkspace';
import { useLLMComparison } from './useLLMCompare';
import { useDebounce } from './useDebounce';
+import { useDebounceValue } from './useDebounceValue';
export {
useAPI,
@@ -30,4 +31,5 @@ export {
useWorkspace,
useLLMComparison,
useDebounce,
+ useDebounceValue,
};
diff --git a/packages/client/src/hooks/useDebounceValue.ts b/packages/client/src/hooks/useDebounceValue.ts
new file mode 100644
index 0000000000..b0c11b06bd
--- /dev/null
+++ b/packages/client/src/hooks/useDebounceValue.ts
@@ -0,0 +1,25 @@
+import { useEffect, useRef, useState } from 'react';
+
+/**
+ * Debouncea
+ * @param value - new value
+ * @param delay - delay timer
+ * @returns debounced value
+ */
+export const useDebounceValue = (value: T, delay = 500) => {
+ const [debouncedValue, setDebouncedValue] = useState(undefined);
+ const timerRef = useRef>();
+
+ useEffect(() => {
+ // waittill it is set
+ timerRef.current = setTimeout(() => {
+ setDebouncedValue(value);
+ }, delay);
+
+ return () => {
+ clearTimeout(timerRef.current);
+ };
+ }, [value, delay]);
+
+ return debouncedValue;
+};
diff --git a/packages/client/src/pages/app/AppDetailPage.tsx b/packages/client/src/pages/app/AppDetailPage.tsx
index c3731dfb03..2ca0b78a1f 100644
--- a/packages/client/src/pages/app/AppDetailPage.tsx
+++ b/packages/client/src/pages/app/AppDetailPage.tsx
@@ -717,9 +717,9 @@ export const AppDetailPage = () => {
}}
>
{
navigate('/settings/app');
@@ -743,14 +743,13 @@ export const AppDetailPage = () => {
>
{
+ onChange={() => {
fetchUserSpecificData();
}}
/>
diff --git a/packages/client/src/pages/engine/EngineSettingsPage.tsx b/packages/client/src/pages/engine/EngineSettingsPage.tsx
index 0e5b39329e..791f7ab4c1 100644
--- a/packages/client/src/pages/engine/EngineSettingsPage.tsx
+++ b/packages/client/src/pages/engine/EngineSettingsPage.tsx
@@ -30,16 +30,16 @@ export const EngineSettingsPage = () => {
>
{
navigate(`/engine/${type.toLowerCase()}`);
}}
/>
-
-
+
+
);
diff --git a/packages/client/src/pages/engine/EngineSmssPage.tsx b/packages/client/src/pages/engine/EngineSmssPage.tsx
index 20ca92fe21..7b6c2c23ce 100644
--- a/packages/client/src/pages/engine/EngineSmssPage.tsx
+++ b/packages/client/src/pages/engine/EngineSmssPage.tsx
@@ -13,7 +13,7 @@ const StyledContainer = styled('div')(({ theme }) => ({
}));
export const EngineSmssPage = () => {
- const { id } = useEngine();
+ const { id, type } = useEngine();
return (
{
}}
>
-
+
);
diff --git a/packages/client/src/pages/settings/AppSettingsDetailPage.tsx b/packages/client/src/pages/settings/AppSettingsDetailPage.tsx
index 2f96bb303c..2e3153abeb 100644
--- a/packages/client/src/pages/settings/AppSettingsDetailPage.tsx
+++ b/packages/client/src/pages/settings/AppSettingsDetailPage.tsx
@@ -81,7 +81,7 @@ export const AppSettingsDetailPage = () => {
{permission === 'OWNER' ? (
{
{view === 'CURRENT' && (
- getUserEnginePermission.refresh()
- }
+ type={'APP'}
+ onChange={() => getUserEnginePermission.refresh()}
/>
)}
{view === 'PENDING' && (
-
+
)}
{view === 'APP' && }
diff --git a/packages/client/src/pages/settings/EngineSettingsDetailPage.tsx b/packages/client/src/pages/settings/EngineSettingsDetailPage.tsx
index 3db3da0db4..2553296d73 100644
--- a/packages/client/src/pages/settings/EngineSettingsDetailPage.tsx
+++ b/packages/client/src/pages/settings/EngineSettingsDetailPage.tsx
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { styled, ToggleTabsGroup } from '@semoss/ui';
-import { ENGINE_TYPES, Role } from '@/types';
+import { Role, ALL_TYPES } from '@/types';
import { useSettings, useAPI } from '@/hooks';
import {
SettingsTiles,
@@ -36,7 +36,7 @@ type VIEW = 'CURRENT' | 'PENDING';
*/
interface EngineSettingsDetailPageProps {
/** Type of the page to render */
- type: ENGINE_TYPES;
+ type: ALL_TYPES;
}
export const EngineSettingsDetailPage = (
@@ -44,11 +44,6 @@ export const EngineSettingsDetailPage = (
) => {
const { type } = props;
- // get a pretty name
- const name = type
- .replace(/^[-_]*(.)/, (_, c) => c.toUpperCase())
- .replace(/[-_]+(.)/g, (_, c) => ' ' + c.toUpperCase());
-
const { id } = useParams();
const navigate = useNavigate();
const { adminMode } = useSettings();
@@ -98,8 +93,8 @@ export const EngineSettingsDetailPage = (
{permission === 'OWNER' ? (
{
navigate('..', { relative: 'path' });
@@ -120,21 +115,16 @@ export const EngineSettingsDetailPage = (
{view === 'CURRENT' && (
- getUserEnginePermission.refresh()
- }
+ onChange={() => getUserEnginePermission.refresh()}
/>
)}
{view === 'PENDING' && (
-
+
)}
- {permission === 'OWNER' ? (
-
- ) : null}
+ {permission === 'OWNER' ? : null}
);
};
diff --git a/packages/client/src/pages/settings/EngineSettingsIndexPage.tsx b/packages/client/src/pages/settings/EngineSettingsIndexPage.tsx
index b81cb82a8a..90da322fe6 100644
--- a/packages/client/src/pages/settings/EngineSettingsIndexPage.tsx
+++ b/packages/client/src/pages/settings/EngineSettingsIndexPage.tsx
@@ -1,9 +1,6 @@
import { useNavigate } from 'react-router-dom';
import { useEffect, useState, useRef, useReducer } from 'react';
-import { ENGINE_TYPES } from '@/types';
-import { useRootStore, usePixel, useAPI, useSettings } from '@/hooks';
-
import {
Grid,
Search,
@@ -23,6 +20,8 @@ import {
FormatListBulletedOutlined,
} from '@mui/icons-material';
+import { ALL_TYPES } from '@/types';
+import { useRootStore, usePixel, useAPI, useSettings } from '@/hooks';
import { EngineLandscapeCard, EngineTileCard } from '@/components/engine';
import { removeUnderscores } from '@/utility';
@@ -96,7 +95,7 @@ const reducer = (state, action) => {
*/
interface EngineSettingsIndexPageProps {
/** Type of the page to render */
- type: ENGINE_TYPES;
+ type: ALL_TYPES;
}
export const EngineSettingsIndexPage = (
diff --git a/packages/client/src/pages/settings/MemberSettingsPage.tsx b/packages/client/src/pages/settings/MemberSettingsPage.tsx
index cf5ca6c572..14431bb048 100644
--- a/packages/client/src/pages/settings/MemberSettingsPage.tsx
+++ b/packages/client/src/pages/settings/MemberSettingsPage.tsx
@@ -1,1362 +1,13 @@
-import { useEffect, useState } from 'react';
-import {
- Delete,
- Add,
- Edit,
- Close,
- LocalPoliceRounded,
- CloudUploadRounded,
- DownloadForOfflineRounded,
-} from '@mui/icons-material';
-import {
- styled,
- useNotification,
- List,
- Modal,
- Button,
- Checkbox,
- Typography,
- AvatarGroup,
- Avatar,
- Table,
- IconButton,
- Stack,
- TextField,
- Select,
- Switch,
-} from '@semoss/ui';
-import { useForm, useFormState, Controller } from 'react-hook-form';
-import { useNavigate } from 'react-router-dom';
-import { useRootStore, useAPI, useSettings } from '@/hooks';
-import { LoadingScreen } from '@/components/ui';
-
-const StyledContainer = styled('div')({
- width: '100%',
-});
-
-const StyledEnd = styled('div')(({ theme }) => ({
- display: 'flex',
- justifyContent: 'space-between',
- paddingBottom: theme.spacing(1),
-}));
-
-const StyledMemberTypography = styled(Typography)(({ theme }) => ({
- paddingTop: theme.spacing(1),
- color: theme.palette.grey['500'],
-}));
-
-const StyledTitle = styled('div')({
- display: 'flex',
-});
-
-const StyledModal = styled('div')({
- width: '1000px',
- height: '854px',
- padding: '16px',
-});
-
-const StyledListItem = styled(List.Item)({
- padding: '4px 0',
-});
-
-const StyledList = styled(List)({
- padding: 0,
-});
-
-const StyledModalTitle = styled(Modal.Title)({
- padding: '24px',
-});
-
-const StyledModalContent = styled(Modal.Content)({
- paddingTop: '4px',
-});
-
-const StyledStack = styled(Stack)({
- marginTop: '4px',
-});
-
-const StyledAddMemberButton = styled(Button)({
- textAlign: 'right',
-});
-
-const StyledCountryCodeExt = styled(TextField)({
- width: '168px',
-});
-
-const StyledPhoneNumber = styled(TextField)({
- width: '550px',
-});
-
-const StyledCredentials = styled(Typography)({
- padding: '8px 0px',
-});
-
-const StyledPermissions = styled(Typography)({
- padding: '25px 0',
-});
-
-const StyledPaddingTopStack = styled(Stack)({
- paddingTop: '4px',
-});
-
-const StyledMembers = styled(Typography)({
- marginRight: '16px',
-});
-
-const StyledAvatarGroup = styled(AvatarGroup)({
- marginRight: '8px',
-});
-
-const StyledAvatar = styled(Avatar)({
- height: 32,
- width: 32,
-});
-
-interface Member {
- admin: boolean;
- email: string;
- id: string;
- name: string;
- type: string;
- username: string;
- password: string;
- phone: string;
- publisher: boolean;
- exporter: boolean;
- phoneextension: string;
- countrycode: string;
-}
-
-interface PendingMember {
- admin: boolean;
- username: string;
- email: string;
- countrycode: string;
- phone: string;
- phoneextension: string;
- id: string;
- name: string;
- type: string;
- publisher: boolean;
- exporter: boolean;
-}
-
-interface EditUserForm {
- id: string;
- username: string;
- name: string;
- password: string;
- email: string;
- phone: string;
- phoneextension: string;
- countrycode: string;
- admin: boolean;
- exporter: boolean;
- publisher: boolean;
- type: string;
-}
-
-const capitalize = (input: string) => {
- return input.charAt(0).toUpperCase() + input.slice(1);
-};
-
-const passwordValidate = (password: string) => {
- if (!password) {
- return false;
- }
- if (!password.match(/[a-z]/g)) {
- return false;
- }
-
- if (!password.match(/[A-Z]/g)) {
- return false;
- }
-
- if (!password.match(/[0-9]/g)) {
- return false;
- }
-
- if (!password.match(/[!@#$%^&*]/g)) {
- return false;
- }
-
- return true;
-};
-
-const numberValidate = (number: string) => {
- if (!number) {
- return false;
- }
- if (!number.match(/^[()-.\s0-9]{8,}$/)) {
- return false;
- }
-
- return true;
-};
+import { Navigate } from 'react-router-dom';
+import { useSettings } from '@/hooks';
+import { UserTable } from '@/components/settings';
export const MemberSettingsPage = () => {
const { adminMode } = useSettings();
- const { configStore, monolithStore } = useRootStore();
- const notification = useNotification();
- const navigate = useNavigate();
if (!adminMode) {
- navigate('/settings');
+ return ;
}
- const [members, setMembers] = useState([]);
- const [addMemberModal, setAddMemberModal] = useState(false);
- const [memberInfoModal, setMemberInfoModal] = useState(false);
- const [activeMember, setActiveMember] = useState(null);
- const [page, setPage] = useState(0);
-
- const {
- control,
- reset,
- handleSubmit,
- getValues,
- watch,
- formState: { errors },
- } = useForm<{
- // edit existing member fields
- id: string;
- username: string;
- name: string;
- password: string;
- email: string;
- phone: string;
- phoneextension: string;
- countrycode: string;
- admin: boolean;
- exporter: boolean;
- publisher: boolean;
- type: string;
- // add pending member fields
- }>({
- defaultValues: {
- id: activeMember?.id,
- username: activeMember?.username,
- name: activeMember?.name,
- email: activeMember?.email,
- phone: activeMember?.phone,
- phoneextension: activeMember?.phoneextension,
- countrycode: activeMember?.countrycode,
- admin: activeMember?.admin,
- exporter: activeMember?.exporter,
- publisher: activeMember?.exporter,
- type: activeMember?.type,
- },
- });
-
- const type = watch('type', '');
-
- const { dirtyFields } = useFormState({
- control,
- });
-
- const providers = configStore.store.config.providers.map((val) =>
- capitalize(val),
- );
-
- const updateMemberInfo = (member: Member) => {
- monolithStore['editMemberInfo'](adminMode, member)
- .then(() => {
- const message =
- 'You have successfully updated user information';
- notification.add({
- color: 'success',
- message: message,
- });
- getMembers.refresh();
- })
- .catch((error) => {
- notification.add({
- color: 'error',
- message: error,
- });
- });
- };
-
- const updateActiveMember = handleSubmit((data) => {
- setMemberInfoModal(false);
- monolithStore['editMemberInfo'](adminMode, data)
- .then(() => {
- const message = `You have successfully updated user information`;
- notification.add({
- color: 'success',
- message: message,
- });
- getMembers.refresh();
- })
- .catch((error) => {
- notification.add({
- color: 'error',
- message: error,
- });
- });
- });
-
- const deleteActiveMember = (member: Member) => {
- monolithStore['deleteMember'](adminMode, member.id, member.type).then(
- () => {
- setActiveMember(null);
- getMembers.refresh();
- },
- );
- };
-
- const createUser = handleSubmit((data: EditUserForm) => {
- monolithStore['createUser'](adminMode, data).then((resp) => {
- if (resp.data) {
- const message = `You have successfully added new user(s)`;
- notification.add({
- color: 'success',
- message: message,
- });
- getMembers.refresh();
- const newMember = members.find((m) => {
- m.id == data.id;
- });
- setActiveMember(newMember);
- setAddMemberModal(false);
- }
- });
- });
-
- const getMembers = useAPI(['getAllUsers', adminMode]);
-
- // TODO: Remove this useEffect. It is unnecessary.
- useEffect(() => {
- // REST call to get all apps
- if (getMembers.status !== 'SUCCESS' || !getMembers.data) {
- return;
- }
-
- // flush into a non optional object
- const updated: Member[] = getMembers.data.map((m) => ({
- admin: false,
- email: '',
- name: '',
- username: '',
- password: '',
- phone: '',
- publisher: false,
- exporter: false,
- phoneextension: '',
- countrycode: '',
- ...m,
- }));
-
- setMembers(updated);
-
- () => {
- console.warn('Cleaning up getMembers');
- setMembers([]);
- };
- }, [getMembers.status, getMembers.data]);
- // show a loading screen when getProjects is pending
- if (getMembers.status !== 'SUCCESS') {
- return (
-
- );
- }
-
- const buildMemberModal = () => {
- return (
-
-
-
- Edit Member
-
- {
- setActiveMember(null);
- setMemberInfoModal(false);
- }}
- >
-
-
-
- {activeMember && (
-
-
-
- Details
-
-
-
-
-
-
- {
- setActiveMember(null);
- setMemberInfoModal(false);
- }}
- >
- Cancel
-
- {
- updateActiveMember();
- }}
- >
- Save
-
-
-
- )}
-
- );
- };
-
- const buildNewMemberModal = () => {
- return (
-
-
-
- Add Member
-
- {
- reset();
- setAddMemberModal(false);
- }}
- >
-
-
-
-
-
-
- Details
-
-
-
-
-
-
- {
- reset();
- setAddMemberModal(false);
- }}
- >
- Cancel
-
- {
- createUser();
- }}
- color="primary"
- >
- Save
-
-
-
-
- );
- };
-
- return (
- <>
-
-
-
-
- Members
-
- {members && (
- <>
-
- {members.map((mem, i) => {
- return (
-
- {mem.name[0]}
-
- );
- })}
-
- {members.length > 1 ? (
-
- {members.length} members
-
- ) : (
-
- {members.length} member
-
- )}
- >
- )}
-
-
- }
- onClick={() => {
- setAddMemberModal(true);
- }}
- >
- Add New
-
-
-
- {members && (
-
-
-
-
-
- Name
-
-
- Email
-
-
- Type
-
-
- Role
-
-
- Action
-
-
-
-
- {members.map((mem, i) => (
-
- {mem.name}
- {mem.email}
- {mem.type}
-
- {
- updateMemberInfo({
- ...mem,
- publisher:
- !mem.publisher,
- });
- }}
- />
- {
- updateMemberInfo({
- ...mem,
- exporter: !mem.exporter,
- });
- }}
- />
- {
- updateMemberInfo({
- ...mem,
- admin: !mem.admin,
- });
- }}
- />
-
-
- {
- setActiveMember(mem);
- reset(mem);
- setMemberInfoModal(true);
- }}
- >
-
-
- {
- deleteActiveMember(mem);
- }}
- >
-
-
-
-
- ))}
-
-
-
- {
- setPage(v);
- }}
- page={page}
- count={Math.ceil(members.length / 5)}
- />
-
-
-
-
- )}
-
-
- {/* MemberInfoModal */}
-
- {buildMemberModal()}
-
- {/* Add New User Modal */}
-
- {buildNewMemberModal()}
-
- >
- );
+ return ;
};
diff --git a/packages/client/src/pages/settings/MyProfilePage.tsx b/packages/client/src/pages/settings/MyProfilePage.tsx
index e7eafcaf3f..18077abb98 100644
--- a/packages/client/src/pages/settings/MyProfilePage.tsx
+++ b/packages/client/src/pages/settings/MyProfilePage.tsx
@@ -238,17 +238,22 @@ export const MyProfilePage = () => {
const response = await monolithStore.editMemberInfo(true, userObj);
- notification.add({
- color: 'success',
- message: 'Successfully edited profile information',
- });
- } catch (e) {
- if (e instanceof Error) {
+ if (response.data) {
+ notification.add({
+ color: 'success',
+ message: 'Successfully edited profile information',
+ });
+ } else {
notification.add({
color: 'error',
- message: e.message,
+ message: 'Error editing profile information',
});
}
+ } catch (e) {
+ notification.add({
+ color: 'error',
+ message: String(e),
+ });
}
};
diff --git a/packages/client/src/stores/monolith/monolith.store.ts b/packages/client/src/stores/monolith/monolith.store.ts
index 60b4391c02..e7c619e8a5 100644
--- a/packages/client/src/stores/monolith/monolith.store.ts
+++ b/packages/client/src/stores/monolith/monolith.store.ts
@@ -607,10 +607,19 @@ export class MonolithStore {
/**
* @name getEngineUsersNoCredentials
* @param admin
- * @param appId
+ * @param engineId
+ * @param limit
+ * @param offSet
+ * @param searchTerm
* @returns
*/
- async getEngineUsersNoCredentials(admin: boolean, appId: string) {
+ async getEngineUsersNoCredentials(
+ admin: boolean,
+ engineId: string,
+ limit: number,
+ offset: number,
+ searchTerm: string,
+ ) {
let url = `${Env.MODULE}/api/auth/`;
// Currently no admin ENDPOINT;
@@ -622,8 +631,21 @@ export class MonolithStore {
// get the response
const response = await axios
- .get[]>(url, {
- params: { engineId: appId },
+ .get<
+ {
+ id: string;
+ email: string;
+ name: string;
+ type: string;
+ username: string;
+ }[]
+ >(url, {
+ params: {
+ engineId: engineId,
+ limit: limit,
+ offset: offset,
+ searchTerm: searchTerm,
+ },
})
.catch((error) => {
throw Error(error);
@@ -1814,10 +1836,19 @@ export class MonolithStore {
/**
* @name getProjectUsersNoCredentials
* @param admin if admin initiated the call
- * @param projectId the id of app
+ * @param appId the id of app
+ * @param limit
+ * @param offSet
+ * @param searchTerm
* @desc get the existing users and their permissions for this app
*/
- async getProjectUsersNoCredentials(admin: boolean, projectId: string) {
+ async getProjectUsersNoCredentials(
+ admin: boolean,
+ appId: string,
+ limit: number,
+ offset: number,
+ searchTerm: string,
+ ) {
let url = `${Env.MODULE}/api/auth/`;
if (admin) {
@@ -1828,8 +1859,21 @@ export class MonolithStore {
// get the response
const response = await axios
- .get[]>(url, {
- params: { projectId: projectId },
+ .get<
+ {
+ id: string;
+ email: string;
+ name: string;
+ type: string;
+ username: string;
+ }[]
+ >(url, {
+ params: {
+ projectId: appId,
+ limit: limit,
+ offset: offset,
+ searchTerm: searchTerm,
+ },
})
.catch((error) => {
throw Error(error);
@@ -2535,7 +2579,12 @@ export class MonolithStore {
* @param admin - is admin user
* @returns MemberInterface[]
*/
- async getAllUsers(admin: boolean) {
+ async getAllUsers(
+ admin: boolean,
+ searchTerm?: string,
+ offset?: number,
+ limit?: number,
+ ) {
let url = `${Env.MODULE}/api/auth/`;
if (admin) {
@@ -2556,8 +2605,18 @@ export class MonolithStore {
publisher?: boolean;
exporter?: boolean;
email?: string;
+ phone?: string;
+ phoneextension?: string;
+ countrycode?: string;
+ username?: string;
}[]
- >(url)
+ >(url, {
+ params: {
+ filterWord: searchTerm,
+ offset: offset,
+ limit: limit,
+ },
+ })
.catch((error) => {
throw Error(error);
});
@@ -2588,7 +2647,7 @@ export class MonolithStore {
postData += 'user=' + encodeURIComponent(JSON.stringify(user));
const response = await axios
- .post<{ success: boolean }>(url, postData, {
+ .post(url, postData, {
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
@@ -2619,7 +2678,7 @@ export class MonolithStore {
postData += 'userId=' + encodeURIComponent(userId);
postData += '&type=' + encodeURIComponent(userType);
- const response = await axios.post<{ success: boolean }>(url, postData, {
+ const response = await axios.post(url, postData, {
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
@@ -2664,15 +2723,11 @@ export class MonolithStore {
newUserInfo += '&password=' + encodeURIComponent(user.password);
}
- const response = await axios.post<{ success: boolean }>(
- url,
- newUserInfo,
- {
- headers: {
- 'content-type': 'application/x-www-form-urlencoded',
- },
+ const response = await axios.post(url, newUserInfo, {
+ headers: {
+ 'content-type': 'application/x-www-form-urlencoded',
},
- );
+ });
return response;
}
diff --git a/packages/ui/src/components/Autocomplete/Autocomplete.tsx b/packages/ui/src/components/Autocomplete/Autocomplete.tsx
index 6ad3ff0492..66702bb33c 100644
--- a/packages/ui/src/components/Autocomplete/Autocomplete.tsx
+++ b/packages/ui/src/components/Autocomplete/Autocomplete.tsx
@@ -34,14 +34,12 @@ export interface AutocompleteProps<
| "PaperComponent"
| "PopperComponent"
| "renderGroup"
- | "renderOption"
| "renderTags"
| "slotProps"
| "unstable_classNamePrefix"
| "unstable_isActiveElementInListbox"
| "autoComplete"
| "autoHighlight"
- | "loading"
| "autoSelect"
| "blurOnSelect"
| "clearOnBlur"
@@ -53,7 +51,6 @@ export interface AutocompleteProps<
| "disableListWrap"
| "getOptionDisabled"
| "handleHomeEndKeys"
- | "includeInputInList"
| "openOnFocus"
| "selectOnFocus"
| "selectOnFocus"
diff --git a/packages/ui/src/components/Table/TablePagination.tsx b/packages/ui/src/components/Table/TablePagination.tsx
index fa8438eb12..8447036c43 100644
--- a/packages/ui/src/components/Table/TablePagination.tsx
+++ b/packages/ui/src/components/Table/TablePagination.tsx
@@ -43,6 +43,12 @@ export interface TablePaginationProps {
HTMLTextAreaElement | HTMLInputElement
>;
+ /**
+ * Is the pagination enabled?
+ *
+ */
+ disabled?: boolean;
+
/** custom style object */
sx?: SxProps;
}