refactor: Invite modal component to support single & multi-select dropdown options (#17073)
* refactored code for invite modal * updated invite users modal component * minor change * optimized addition of links in invite modal dropdown * fixed a cypress test * removed msw * fixed a cypress test * fixed a cypress test * fixed a cypress test * fixed a test
This commit is contained in:
parent
e67ab8eea5
commit
a1b24ab7dc
|
|
@ -48,9 +48,8 @@ describe("Create new workspace and invite user & validate all roles", () => {
|
|||
cy.wait(2000);
|
||||
cy.xpath(HomePage.selectRole).click();
|
||||
cy.get(".t--dropdown-option")
|
||||
.should("have.length", 2)
|
||||
.should("have.length", 1)
|
||||
.and("contain.text", `App Viewer - ${workspaceId}`);
|
||||
cy.get(".t--dropdown-option").should("contain.text", `Select a role`);
|
||||
cy.get(HomePage.closeBtn).click();
|
||||
|
||||
homePage.LaunchAppFromAppHover();
|
||||
|
|
@ -88,13 +87,12 @@ describe("Create new workspace and invite user & validate all roles", () => {
|
|||
cy.wait(2000);
|
||||
cy.xpath(HomePage.selectRole).click();
|
||||
cy.get(".t--dropdown-option")
|
||||
.should("have.length", 3)
|
||||
.should("have.length", 2)
|
||||
.and(
|
||||
"contain.text",
|
||||
`App Viewer - ${workspaceId}`,
|
||||
`Developer - ${workspaceId}`,
|
||||
);
|
||||
cy.get(".t--dropdown-option").should("contain.text", `Select a role`);
|
||||
cy.get(HomePage.closeBtn).click();
|
||||
homePage.LogOutviaAPI();
|
||||
});
|
||||
|
|
@ -117,19 +115,27 @@ describe("Create new workspace and invite user & validate all roles", () => {
|
|||
Cypress.env("TESTPASSWORD1"),
|
||||
"Administrator",
|
||||
);
|
||||
homePage.FilterApplication(appid, workspaceId);
|
||||
cy.get(homePage._applicationCard)
|
||||
.first()
|
||||
.trigger("mouseover");
|
||||
homePage.InviteUserToWorkspace(
|
||||
workspaceId,
|
||||
Cypress.env("TESTUSERNAME2"),
|
||||
"App Viewer",
|
||||
);
|
||||
cy.get(HomePage.closeBtn).click();
|
||||
cy.wait(2000);
|
||||
homePage.FilterApplication(appid, workspaceId);
|
||||
cy.get(homePage._applicationCard)
|
||||
.first()
|
||||
.trigger("mouseover");
|
||||
cy.get(homePage._appHoverIcon("edit"))
|
||||
.first()
|
||||
.click({ force: true });
|
||||
// cy.xpath(homePage._editPageLanding).should("exist");
|
||||
cy.wait(4000);
|
||||
cy.xpath("//span[text()='SHARE']").click();
|
||||
cy.wait(2000);
|
||||
cy.xpath(HomePage.selectRole).click();
|
||||
cy.get(".t--dropdown-option")
|
||||
.should("have.length", 4)
|
||||
.should("have.length", 3)
|
||||
.should(
|
||||
"contain.text",
|
||||
`App Viewer - ${workspaceId}`,
|
||||
|
|
@ -139,7 +145,6 @@ describe("Create new workspace and invite user & validate all roles", () => {
|
|||
"contain.text",
|
||||
`Administrator - ${workspaceId}`,
|
||||
);
|
||||
cy.get(".t--dropdown-option").should("contain.text", `Select a role`);
|
||||
cy.get(HomePage.closeBtn).click();
|
||||
homePage.LogOutviaAPI();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@
|
|||
"test:unit": "$(npm bin)/jest -b --colors --no-cache --silent --coverage --collectCoverage=true --coverageDirectory='../../' --coverageReporters='json-summary'",
|
||||
"test:jest": "$(npm bin)/jest --watch",
|
||||
"generate:widget": "plop --plopfile generators/index.js",
|
||||
"postbuild": "rm build/mockServiceWorker.js",
|
||||
"postinstall": "patch-package && CURRENT_SCOPE=client node ../shared/install-dependencies.js",
|
||||
"preinstall": "CURRENT_SCOPE=client node ../shared/build-shared-dep.js",
|
||||
"install": "node cypress/apply-patches.js"
|
||||
|
|
@ -307,8 +306,5 @@
|
|||
"json-schema": "0.4.0",
|
||||
"node-fetch": "2.6.7",
|
||||
"babel-plugin-styled-components": "2.0.7"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
/**
|
||||
* Mock Service Worker.
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
* - Please do NOT serve this file on production.
|
||||
*/
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
const INTEGRITY_CHECKSUM = '795882c72c7304f6fa1d4a65a2418900'
|
||||
const bypassHeaderName = 'x-msw-bypass'
|
||||
const activeClientIds = new Set()
|
||||
|
||||
self.addEventListener('install', function () {
|
||||
return self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', async function (event) {
|
||||
return self.clients.claim()
|
||||
})
|
||||
|
||||
self.addEventListener('message', async function (event) {
|
||||
const clientId = event.source.id
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll()
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'INTEGRITY_CHECK_RESPONSE',
|
||||
payload: INTEGRITY_CHECKSUM,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
payload: true,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_DEACTIVATE': {
|
||||
activeClientIds.delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Resolve the "master" client for the given event.
|
||||
// Client that issues a request doesn't necessarily equal the client
|
||||
// that registered the worker. It's with the latter the worker should
|
||||
// communicate with during the response resolving phase.
|
||||
async function resolveMasterClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (client.frameType === 'top-level') {
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll()
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMasterClient(event)
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (activeClientIds.has(client.id)) {
|
||||
;(async function () {
|
||||
const clonedResponse = response.clone()
|
||||
sendToClient(client, {
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
requestId,
|
||||
type: clonedResponse.type,
|
||||
ok: clonedResponse.ok,
|
||||
status: clonedResponse.status,
|
||||
statusText: clonedResponse.statusText,
|
||||
body:
|
||||
clonedResponse.body === null ? null : await clonedResponse.text(),
|
||||
headers: serializeHeaders(clonedResponse.headers),
|
||||
redirected: clonedResponse.redirected,
|
||||
},
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
async function getResponse(event, client, requestId) {
|
||||
const { request } = event
|
||||
const requestClone = request.clone()
|
||||
const getOriginalResponse = () => fetch(requestClone)
|
||||
|
||||
// Bypass mocking when the request client is not active.
|
||||
if (!client) {
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
// The absence of the immediate/parent client in the map of the active clients
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return await getOriginalResponse()
|
||||
}
|
||||
|
||||
// Bypass requests with the explicit bypass header
|
||||
if (requestClone.headers.get(bypassHeaderName) === 'true') {
|
||||
const cleanRequestHeaders = serializeHeaders(requestClone.headers)
|
||||
|
||||
// Remove the bypass header to comply with the CORS preflight check.
|
||||
delete cleanRequestHeaders[bypassHeaderName]
|
||||
|
||||
const originalRequest = new Request(requestClone, {
|
||||
headers: new Headers(cleanRequestHeaders),
|
||||
})
|
||||
|
||||
return fetch(originalRequest)
|
||||
}
|
||||
|
||||
// Send the request to the client-side MSW.
|
||||
const reqHeaders = serializeHeaders(request.headers)
|
||||
const body = await request.text()
|
||||
|
||||
const clientMessage = await sendToClient(client, {
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers: reqHeaders,
|
||||
cache: request.cache,
|
||||
mode: request.mode,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body,
|
||||
bodyUsed: request.bodyUsed,
|
||||
keepalive: request.keepalive,
|
||||
},
|
||||
})
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_SUCCESS': {
|
||||
return delayPromise(
|
||||
() => respondWithMock(clientMessage),
|
||||
clientMessage.payload.delay,
|
||||
)
|
||||
}
|
||||
|
||||
case 'MOCK_NOT_FOUND': {
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
case 'NETWORK_ERROR': {
|
||||
const { name, message } = clientMessage.payload
|
||||
const networkError = new Error(message)
|
||||
networkError.name = name
|
||||
|
||||
// Rejecting a request Promise emulates a network error.
|
||||
throw networkError
|
||||
}
|
||||
|
||||
case 'INTERNAL_ERROR': {
|
||||
const parsedBody = JSON.parse(clientMessage.payload.body)
|
||||
|
||||
console.error(
|
||||
`\
|
||||
[MSW] Request handler function for "%s %s" has thrown the following exception:
|
||||
|
||||
${parsedBody.errorType}: ${parsedBody.message}
|
||||
(see more detailed error stack trace in the mocked response body)
|
||||
|
||||
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error.
|
||||
If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses\
|
||||
`,
|
||||
request.method,
|
||||
request.url,
|
||||
)
|
||||
|
||||
return respondWithMock(clientMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
const { request } = event
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been deleted (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = uuidv4()
|
||||
|
||||
return event.respondWith(
|
||||
handleRequest(event, requestId).catch((error) => {
|
||||
console.error(
|
||||
'[MSW] Failed to mock a "%s" request to "%s": %s',
|
||||
request.method,
|
||||
request.url,
|
||||
error,
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
function serializeHeaders(headers) {
|
||||
const reqHeaders = {}
|
||||
headers.forEach((value, name) => {
|
||||
reqHeaders[name] = reqHeaders[name]
|
||||
? [].concat(reqHeaders[name]).concat(value)
|
||||
: value
|
||||
})
|
||||
return reqHeaders
|
||||
}
|
||||
|
||||
function sendToClient(client, message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(JSON.stringify(message), [channel.port2])
|
||||
})
|
||||
}
|
||||
|
||||
function delayPromise(cb, duration) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => resolve(cb()), duration)
|
||||
})
|
||||
}
|
||||
|
||||
function respondWithMock(clientMessage) {
|
||||
return new Response(clientMessage.payload.body, {
|
||||
...clientMessage.payload,
|
||||
headers: clientMessage.payload.headers,
|
||||
})
|
||||
}
|
||||
|
||||
function uuidv4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = (Math.random() * 16) | 0
|
||||
const v = c == 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
|
@ -39,5 +39,3 @@ export const WELCOME_NON_SUPER_FORM_NAME = "WelcomeNonSuperSetupForm";
|
|||
export const SAVE_THEME_FORM_NAME = "SaveThemeForm";
|
||||
export const REDIRECT_URL_FORM = "RedirectURLForm";
|
||||
export const ENTITYID_URL_FORM = "EntityIdURLForm";
|
||||
|
||||
export const inviteModalLinks: any[] = [];
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
import { rest } from "msw";
|
||||
import testMockApi from "./mockJsons/testMockApi.json";
|
||||
/* import { rest } from "msw"; */
|
||||
|
||||
export const handlers = [
|
||||
rest.get("/api/testMockApi", (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(testMockApi));
|
||||
}),
|
||||
];
|
||||
export const handlers = [];
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"data": {
|
||||
"environment": "CE"
|
||||
}
|
||||
}
|
||||
|
|
@ -49,11 +49,7 @@ import {
|
|||
Size,
|
||||
Text,
|
||||
TextType,
|
||||
Icon,
|
||||
IconSize,
|
||||
SegmentHeader,
|
||||
TextProps,
|
||||
TooltipComponent,
|
||||
DropdownOption,
|
||||
} from "design-system";
|
||||
import { Classes, Variant } from "components/ads/common";
|
||||
|
|
@ -65,8 +61,7 @@ import { ScrollIndicator } from "design-system";
|
|||
import UserApi from "@appsmith/api/UserApi";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { fetchWorkspace } from "actions/workspaceActions";
|
||||
import { SubTextPosition } from "components/constants";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Tooltip } from "@blueprintjs/core";
|
||||
import { isEllipsisActive } from "utils/helpers";
|
||||
|
||||
|
|
@ -228,16 +223,6 @@ export const LabelText = styled(Text)`
|
|||
letter-spacing: -0.24px;
|
||||
`;
|
||||
|
||||
/*const LinksWrapper = styled.div`
|
||||
&:before {
|
||||
border-top: 1px solid var(--appsmith-color-black-200);
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
`;*/
|
||||
|
||||
export const LeftIconWrapper = styled.span`
|
||||
font-size: 20px;
|
||||
line-height: 19px;
|
||||
|
|
@ -247,145 +232,12 @@ export const LeftIconWrapper = styled.span`
|
|||
top: 1px;
|
||||
`;
|
||||
|
||||
export const SelectedIcon = styled(Icon)<{ name: string }>`
|
||||
margin-right: 6px;
|
||||
& > div:first-child {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
svg {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
rect {
|
||||
fill: ${(props) => props.theme.colors.dropdownIconBg};
|
||||
rx: 0;
|
||||
}
|
||||
path {
|
||||
fill: ${(props) => props.theme.colors.propertyPane.label};
|
||||
}
|
||||
}
|
||||
}
|
||||
svg {
|
||||
${(props) =>
|
||||
props.name === "right-arrow" ? `transform: rotate(-45deg);` : ``}
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.fillColor
|
||||
? props.fillColor
|
||||
: props.theme.colors.dropdown.selected.icon};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledSubText = styled(Text)<{
|
||||
showDropIcon?: boolean;
|
||||
subTextPosition?: SubTextPosition;
|
||||
}>`
|
||||
${(props) =>
|
||||
props.subTextPosition === SubTextPosition.BOTTOM
|
||||
? "margin-top: 3px"
|
||||
: "margin-left: auto"};
|
||||
&&& {
|
||||
color: ${(props) => props.theme.colors.dropdown.menu.subText};
|
||||
}
|
||||
&.sub-text {
|
||||
color: ${(props) => props.theme.colors.dropdown.selected.subtext};
|
||||
text-align: end;
|
||||
margin-right: ${(props) => `${props.theme.spaces[4]}px`};
|
||||
}
|
||||
`;
|
||||
|
||||
export const OptionWrapper = styled.div<{
|
||||
disabled?: boolean;
|
||||
selected: boolean;
|
||||
subTextPosition?: SubTextPosition;
|
||||
selectedHighlightBg?: string;
|
||||
}>`
|
||||
padding: ${(props) => props.theme.spaces[3] + 1}px
|
||||
${(props) => props.theme.spaces[5]}px;
|
||||
${(props) => (!props.disabled ? "cursor: pointer" : "")};
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 36px;
|
||||
flex-direction: ${(props) =>
|
||||
props.subTextPosition === SubTextPosition.BOTTOM ? "column" : "row"};
|
||||
align-items: ${(props) =>
|
||||
props.subTextPosition === SubTextPosition.BOTTOM ? "flex-start" : "center"};
|
||||
background-color: ${(props) =>
|
||||
props.selected
|
||||
? props.selectedHighlightBg || `var(--appsmith-color-black-200)`
|
||||
: `initial`};
|
||||
&&& svg {
|
||||
rect {
|
||||
fill: ${(props) => props.theme.colors.dropdownIconBg};
|
||||
}
|
||||
}
|
||||
.bp3-popover-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.${Classes.TEXT} {
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? Colors.GRAY2
|
||||
: props.selected
|
||||
? props.theme.colors.dropdown.menu.hoverText
|
||||
: props.theme.colors.dropdown.menu.text};
|
||||
}
|
||||
.${Classes.ICON} {
|
||||
margin-right: ${(props) => props.theme.spaces[5]}px;
|
||||
svg {
|
||||
path {
|
||||
${(props) =>
|
||||
props.selected
|
||||
? `fill: ${props.theme.colors.dropdown.selected.icon}`
|
||||
: `fill: ${props.theme.colors.dropdown.icon}`};
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover,
|
||||
&.highlighted {
|
||||
background-color: ${(props) =>
|
||||
props.selectedHighlightBg || `var(--appsmith-color-black-200)`};
|
||||
&&& svg {
|
||||
rect {
|
||||
fill: ${(props) => props.theme.colors.textOnDarkBG};
|
||||
}
|
||||
}
|
||||
.${Classes.TEXT} {
|
||||
color: ${(props) => props.theme.colors.dropdown.menu.hoverText};
|
||||
}
|
||||
${StyledSubText} {
|
||||
color: ${(props) => props.theme.colors.dropdown.menu.subText};
|
||||
}
|
||||
.${Classes.ICON} {
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.colors.dropdown.hovered.icon};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledText = styled(Text)`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const LabelWrapper = styled.div<{ label?: string }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
span:last-child {
|
||||
margin-top: ${(props) => props.theme.spaces[2] - 1}px;
|
||||
}
|
||||
&:hover {
|
||||
.${Classes.TEXT} {
|
||||
color: ${(props) => props.theme.colors.dropdown.selected.text};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function TooltipWrappedText(
|
||||
props: TextProps & {
|
||||
label: string;
|
||||
|
|
@ -428,19 +280,7 @@ const validateFormValues = (values: {
|
|||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof values.roles === "undefined" &&
|
||||
(typeof values.role === "undefined" || values.role?.trim().length === 0)
|
||||
) {
|
||||
throw new SubmissionError({
|
||||
_error: createMessage(INVITE_USERS_VALIDATION_ROLE_EMPTY),
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof values.role === "undefined" &&
|
||||
(typeof values.roles === "undefined" || values.roles.length === 0)
|
||||
) {
|
||||
if (typeof values.role === "undefined" || values.role.length === 0) {
|
||||
throw new SubmissionError({
|
||||
_error: createMessage(INVITE_USERS_VALIDATION_ROLE_EMPTY),
|
||||
});
|
||||
|
|
@ -453,20 +293,10 @@ const validate = (values: any) => {
|
|||
errors["users"] = createMessage(INVITE_USERS_VALIDATION_EMAILS_EMPTY);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof values.roles === "undefined" &&
|
||||
(typeof values.role === "undefined" || values.role?.trim().length === 0)
|
||||
) {
|
||||
if (typeof values.role === "undefined" || values.role.length === 0) {
|
||||
errors["role"] = createMessage(INVITE_USERS_VALIDATION_ROLE_EMPTY);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof values.role === "undefined" &&
|
||||
(typeof values.roles === "undefined" || values.roles.length === 0)
|
||||
) {
|
||||
errors["roles"] = createMessage(INVITE_USERS_VALIDATION_ROLE_EMPTY);
|
||||
}
|
||||
|
||||
if (values.users && values.users.length > 0) {
|
||||
const _users = values.users.split(",").filter(Boolean);
|
||||
|
||||
|
|
@ -485,13 +315,10 @@ export const InviteButtonWidth = "88px";
|
|||
|
||||
function WorkspaceInviteUsersForm(props: any) {
|
||||
const [emailError, setEmailError] = useState("");
|
||||
const [selectedOption, setSelectedOption] = useState<any>({});
|
||||
const [selectedOption, setSelectedOption] = useState<any[]>([]);
|
||||
const userRef = React.createRef<HTMLDivElement>();
|
||||
const history = useHistory();
|
||||
const selectedId = props?.selected?.id;
|
||||
const multiSelectDropdownOptions: Partial<DropdownOption>[] =
|
||||
props.options && props.options.length > 0 && props.isMultiSelectDropdown
|
||||
? props.options
|
||||
: [];
|
||||
|
||||
const selected = useMemo(
|
||||
() =>
|
||||
|
|
@ -515,11 +342,9 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
fetchCurrentWorkspace,
|
||||
fetchUser,
|
||||
handleSubmit,
|
||||
isAclFlow = false,
|
||||
isApplicationInvite,
|
||||
isLoading,
|
||||
isMultiSelectDropdown = false,
|
||||
links = [],
|
||||
message = "",
|
||||
placeholder = "",
|
||||
submitFailed,
|
||||
|
|
@ -528,8 +353,6 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
valid,
|
||||
} = props;
|
||||
|
||||
const [selectedItems, setSelectedItems] = useState<any[]>([]);
|
||||
|
||||
// set state for checking number of users invited
|
||||
const [numberOfUsersInvited, updateNumberOfUsersInvited] = useState(0);
|
||||
const currentWorkspace = useSelector(getCurrentAppWorkspace);
|
||||
|
|
@ -541,31 +364,30 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAclFlow) {
|
||||
fetchUser(props.workspaceId);
|
||||
fetchAllRoles(props.workspaceId);
|
||||
fetchCurrentWorkspace(props.workspaceId);
|
||||
}
|
||||
fetchUser(props.workspaceId);
|
||||
fetchAllRoles(props.workspaceId);
|
||||
fetchCurrentWorkspace(props.workspaceId);
|
||||
}, [props.workspaceId, fetchUser, fetchAllRoles, fetchCurrentWorkspace]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selected) {
|
||||
setSelectedItems([selected]);
|
||||
setSelectedOption([selected]);
|
||||
props.initialize({
|
||||
roles: [selected],
|
||||
role: [selected],
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const styledRoles = props.roles.map((role: any) => {
|
||||
return {
|
||||
id: role.id,
|
||||
value: role.name,
|
||||
label: role.description,
|
||||
};
|
||||
});
|
||||
|
||||
styledRoles.push(...links);
|
||||
const styledRoles =
|
||||
props.options && props.options.length > 0
|
||||
? props.options
|
||||
: props.roles.map((role: any) => {
|
||||
return {
|
||||
id: role.id,
|
||||
value: role.name,
|
||||
label: role.description,
|
||||
};
|
||||
});
|
||||
|
||||
const theme = useContext(ThemeContext);
|
||||
|
||||
|
|
@ -592,12 +414,15 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
[allUsers, theme],
|
||||
);
|
||||
|
||||
const onSelect = (_value?: string, options?: any) => {
|
||||
setSelectedItems(options);
|
||||
const onSelect = (_value?: string, option?: any) => {
|
||||
if (option.link) {
|
||||
history.push(option.link);
|
||||
}
|
||||
setSelectedOption(isMultiSelectDropdown ? option : [option]);
|
||||
};
|
||||
|
||||
const onRemoveOptions = (updatedItems: any) => {
|
||||
setSelectedItems(updatedItems);
|
||||
setSelectedOption(updatedItems);
|
||||
};
|
||||
|
||||
const getLabel = (selectedOption: Partial<DropdownOption>[]) => {
|
||||
|
|
@ -626,155 +451,6 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
}
|
||||
};
|
||||
|
||||
const renderOption = ({
|
||||
index,
|
||||
isHighlighted,
|
||||
option,
|
||||
optionClickHandler,
|
||||
}: {
|
||||
index?: number;
|
||||
option: DropdownOption | DropdownOption[];
|
||||
optionClickHandler?: (dropdownOption: DropdownOption) => void;
|
||||
isHighlighted?: boolean;
|
||||
}) => {
|
||||
let isSelected = false;
|
||||
if (props.isMultiSelect && Array.isArray(selected) && selected.length) {
|
||||
isSelected = !!selected.find((selectedOption: any) =>
|
||||
!Array.isArray(option) ? selectedOption.value === option.value : false,
|
||||
);
|
||||
} else {
|
||||
isSelected =
|
||||
!Array.isArray(option) && selected
|
||||
? selected.value === option.value
|
||||
: false;
|
||||
}
|
||||
return !Array.isArray(option) && !option.isSectionHeader ? (
|
||||
!option.link ? (
|
||||
<TooltipComponent
|
||||
content={
|
||||
!!option.disabledTooltipText
|
||||
? option.disabledTooltipText
|
||||
: "Action not supported"
|
||||
}
|
||||
disabled={!option.disabled}
|
||||
key={`tootltip-${index}`}
|
||||
styles={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<OptionWrapper
|
||||
aria-selected={isSelected}
|
||||
className={`t--dropdown-option ${isSelected ? "selected" : ""} ${
|
||||
isHighlighted ? "highlighted" : ""
|
||||
}`}
|
||||
data-cy={`t--dropdown-option-${option?.label}`}
|
||||
disabled={option.disabled}
|
||||
key={index}
|
||||
onClick={
|
||||
// users should be able to unselect a selected option by clicking the option again.
|
||||
isSelected && props.allowDeselection
|
||||
? () => props.removeSelectedOptionClickHandler(option)
|
||||
: () => optionClickHandler?.(option)
|
||||
}
|
||||
role="option"
|
||||
selected={
|
||||
props.isMultiSelect ? props.highlightIndex === index : isSelected
|
||||
}
|
||||
selectedHighlightBg={props.selectedHighlightBg}
|
||||
subTextPosition={option.subTextPosition ?? SubTextPosition.LEFT}
|
||||
>
|
||||
{option.leftElement && (
|
||||
<LeftIconWrapper>{option.leftElement}</LeftIconWrapper>
|
||||
)}
|
||||
{option.icon ? (
|
||||
<SelectedIcon
|
||||
fillColor={option?.iconColor}
|
||||
hoverFillColor={option?.iconColor}
|
||||
name={option.icon}
|
||||
size={option.iconSize || IconSize.XL}
|
||||
/>
|
||||
) : null}
|
||||
{props.showLabelOnly ? (
|
||||
props.truncateOption ? (
|
||||
<>
|
||||
<TooltipWrappedText
|
||||
label={option.label || ""}
|
||||
type={TextType.P1}
|
||||
/>
|
||||
{option.hasCustomBadge && props.customBadge}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text type={TextType.P1}>{option.label}</Text>
|
||||
{option.hasCustomBadge && props.customBadge}
|
||||
</>
|
||||
)
|
||||
) : option.label && option.value ? (
|
||||
<LabelWrapper className="label-container">
|
||||
<Text type={TextType.H5}>{option.value}</Text>
|
||||
<Text type={TextType.P1}>{option.label}</Text>
|
||||
</LabelWrapper>
|
||||
) : props.truncateOption ? (
|
||||
<TooltipWrappedText
|
||||
label={option.value || ""}
|
||||
type={TextType.P1}
|
||||
/>
|
||||
) : (
|
||||
<Text type={TextType.P1}>{option.value}</Text>
|
||||
)}
|
||||
{option.subText ? (
|
||||
<StyledSubText
|
||||
subTextPosition={option.subTextPosition}
|
||||
type={TextType.P3}
|
||||
>
|
||||
{option.subText}
|
||||
</StyledSubText>
|
||||
) : null}
|
||||
</OptionWrapper>
|
||||
</TooltipComponent>
|
||||
) : (
|
||||
<Link key={index} to={option.link || "/"}>
|
||||
<OptionWrapper
|
||||
className={`t--dropdown-link`}
|
||||
data-cy={`t--dropdown-option-${option?.value}`}
|
||||
disabled={option.disabled}
|
||||
role="option"
|
||||
selected={false}
|
||||
selectedHighlightBg={props.selectedHighlightBg}
|
||||
subTextPosition={option.subTextPosition ?? SubTextPosition.LEFT}
|
||||
>
|
||||
{option.leftElement && (
|
||||
<LeftIconWrapper>{option.leftElement}</LeftIconWrapper>
|
||||
)}
|
||||
{option.icon ? (
|
||||
<SelectedIcon
|
||||
fillColor={option?.iconColor}
|
||||
hoverFillColor={option?.iconColor}
|
||||
name={option.icon}
|
||||
size={option.iconSize || IconSize.XL}
|
||||
/>
|
||||
) : null}
|
||||
<Text type={TextType.P1}>{option.value}</Text>
|
||||
{option.subText ? (
|
||||
<StyledSubText
|
||||
subTextPosition={option.subTextPosition}
|
||||
type={TextType.P3}
|
||||
>
|
||||
{option.subText}
|
||||
</StyledSubText>
|
||||
) : null}
|
||||
</OptionWrapper>
|
||||
</Link>
|
||||
)
|
||||
) : (
|
||||
<SegmentHeader
|
||||
key={index}
|
||||
style={{ paddingRight: theme.spaces[5] }}
|
||||
title={!Array.isArray(option) && option.label ? option.label : ""}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<WorkspaceInviteWrapper>
|
||||
<InviteModalStyles />
|
||||
|
|
@ -797,10 +473,11 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
.join(",");
|
||||
return inviteUsersToWorkspace(
|
||||
{
|
||||
...values,
|
||||
...(props.workspaceId ? { workspaceId: props.workspaceId } : {}),
|
||||
users,
|
||||
permissionGroupId: selectedOption.id,
|
||||
workspaceId: props.workspaceId,
|
||||
permissionGroupId: isMultiSelectDropdown
|
||||
? selectedOption.map((group: any) => group.id).join(",")
|
||||
: selectedOption[0].id,
|
||||
},
|
||||
dispatch,
|
||||
);
|
||||
|
|
@ -821,36 +498,24 @@ function WorkspaceInviteUsersForm(props: any) {
|
|||
placeholder={placeholder || "Enter email address"}
|
||||
type="text"
|
||||
/>
|
||||
{isMultiSelectDropdown ? (
|
||||
<SelectField
|
||||
allowDeselection
|
||||
disabled={!!selected}
|
||||
isMultiSelect
|
||||
labelRenderer={(selected: Partial<DropdownOption>[]) =>
|
||||
getLabel(selected)
|
||||
}
|
||||
name="roles"
|
||||
onSelect={onSelect}
|
||||
options={multiSelectDropdownOptions}
|
||||
outline={false}
|
||||
placeholder="Select a role"
|
||||
removeSelectedOption={onRemoveOptions}
|
||||
selected={selectedItems}
|
||||
showLabelOnly
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<SelectField
|
||||
data-cy="t--invite-role-input"
|
||||
name="role"
|
||||
onSelect={(value, option) => setSelectedOption(option)}
|
||||
options={styledRoles}
|
||||
outline={false}
|
||||
placeholder="Select a role"
|
||||
renderOption={renderOption}
|
||||
size="small"
|
||||
/>
|
||||
)}
|
||||
<SelectField
|
||||
allowDeselection={isMultiSelectDropdown}
|
||||
data-cy="t--invite-role-input"
|
||||
disabled={props.disableDropdown}
|
||||
isMultiSelect={isMultiSelectDropdown}
|
||||
labelRenderer={(selected: Partial<DropdownOption>[]) =>
|
||||
getLabel(selected)
|
||||
}
|
||||
name={"role"}
|
||||
onSelect={(value, option) => onSelect(value, option)}
|
||||
options={styledRoles}
|
||||
outline={false}
|
||||
placeholder="Select a role"
|
||||
removeSelectedOption={onRemoveOptions}
|
||||
selected={selectedOption}
|
||||
showLabelOnly={isMultiSelectDropdown}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="t--invite-user-btn"
|
||||
|
|
@ -983,7 +648,6 @@ export default connect(
|
|||
applicationId?: string;
|
||||
workspaceId?: string;
|
||||
isApplicationInvite?: boolean;
|
||||
links?: any[];
|
||||
}
|
||||
>({
|
||||
validate,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ type FormDialogComponentProps = {
|
|||
selected?: any;
|
||||
tabs?: any[];
|
||||
options?: any[];
|
||||
links?: any[];
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
|
|
@ -137,7 +136,6 @@ export function FormDialogComponent(props: FormDialogComponentProps) {
|
|||
<Form
|
||||
{...props.customProps}
|
||||
applicationId={props.applicationId}
|
||||
links={props.links}
|
||||
message={props.message}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
placeholder={props.placeholder}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,15 @@ type DropdownWrapperProps = {
|
|||
};
|
||||
|
||||
function DropdownWrapper(props: DropdownWrapperProps) {
|
||||
const [selectedOption, setSelectedOption] = useState({
|
||||
value: props.placeholder,
|
||||
});
|
||||
const [selected, setSelected] = useState<any>([]);
|
||||
const [selectedOption, setSelectedOption] = useState<any>([
|
||||
{
|
||||
value: props.placeholder,
|
||||
},
|
||||
]);
|
||||
|
||||
const onSelectHandler = (value?: string, option?: DropdownOption) => {
|
||||
if (props?.isMultiSelect) {
|
||||
const updatedItems: DropdownOption[] = [...selected, option];
|
||||
const updatedItems: DropdownOption[] = [...selectedOption, option];
|
||||
props.input && props.input.onChange && props.input.onChange(updatedItems);
|
||||
props.onOptionSelect && props.onOptionSelect(value, updatedItems);
|
||||
} else {
|
||||
|
|
@ -42,7 +43,7 @@ function DropdownWrapper(props: DropdownWrapperProps) {
|
|||
};
|
||||
|
||||
const onRemoveOptions = (value: any) => {
|
||||
const updatedItems = selected.filter(
|
||||
const updatedItems = selectedOption.filter(
|
||||
(option: any) => option.value !== value,
|
||||
);
|
||||
props.input && props.input.onChange && props.input.onChange(updatedItems);
|
||||
|
|
@ -50,13 +51,13 @@ function DropdownWrapper(props: DropdownWrapperProps) {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props?.isMultiSelect) {
|
||||
setSelected(props.selected);
|
||||
if (props.selected) {
|
||||
setSelectedOption(props.selected);
|
||||
} else {
|
||||
if (props.input && props.input.value) {
|
||||
setSelectedOption({ value: props.input.value });
|
||||
setSelectedOption([{ value: props.input.value }]);
|
||||
} else if (props.placeholder) {
|
||||
setSelectedOption({ value: props.placeholder });
|
||||
setSelectedOption([{ value: props.placeholder }]);
|
||||
}
|
||||
}
|
||||
}, [props.input, props.placeholder, props.selected]);
|
||||
|
|
@ -73,11 +74,7 @@ function DropdownWrapper(props: DropdownWrapperProps) {
|
|||
placeholder={props.placeholder}
|
||||
removeSelectedOption={onRemoveOptions}
|
||||
renderOption={props?.renderOption}
|
||||
selected={
|
||||
props.isMultiSelect
|
||||
? (props.selected as DropdownOption[])
|
||||
: selectedOption
|
||||
}
|
||||
selected={props.isMultiSelect ? selectedOption : selectedOption[0]}
|
||||
showLabelOnly={props.showLabelOnly}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,12 +27,6 @@ import AppErrorBoundary from "./AppErrorBoundry";
|
|||
import GlobalStyles from "globalStyles";
|
||||
appInitializer();
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
import("./mocks/browser").then(({ worker }) => {
|
||||
worker.start();
|
||||
});
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={"An error has occured"}>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import BackToHomeButton from "./BackToHomeButton";
|
|||
import TourCompletionMessage from "pages/Editor/GuidedTour/TourCompletionMessage";
|
||||
import { useHref } from "pages/Editor/utils";
|
||||
import { builderURL } from "RouteBuilder";
|
||||
import { inviteModalLinks } from "@appsmith/constants/forms";
|
||||
import {
|
||||
createMessage,
|
||||
INVITE_USERS_MESSAGE,
|
||||
|
|
@ -135,7 +134,6 @@ export function AppViewerHeader(props: AppViewerHeaderProps) {
|
|||
bgColor: "transparent",
|
||||
}}
|
||||
isOpen={showAppInviteUsersDialog}
|
||||
links={inviteModalLinks}
|
||||
message={createMessage(INVITE_USERS_MESSAGE)}
|
||||
placeholder={createMessage(INVITE_USERS_PLACEHOLDER)}
|
||||
title={currentApplicationDetails.name}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import { useHref } from "pages/Editor/utils";
|
|||
import { APP_MODE } from "entities/App";
|
||||
import { builderURL, viewerURL } from "RouteBuilder";
|
||||
import { trimQueryString } from "utils/helpers";
|
||||
import { inviteModalLinks } from "@appsmith/constants/forms";
|
||||
import {
|
||||
createMessage,
|
||||
INVITE_USERS_MESSAGE,
|
||||
|
|
@ -118,7 +117,6 @@ export function PageMenu(props: AppViewerHeaderProps) {
|
|||
bgColor: "transparent",
|
||||
}}
|
||||
isOpen={showAppInviteUsersDialog}
|
||||
links={inviteModalLinks}
|
||||
message={createMessage(INVITE_USERS_MESSAGE)}
|
||||
placeholder={createMessage(INVITE_USERS_PLACEHOLDER)}
|
||||
title={application.name}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ import FormDialogComponent from "components/editorComponents/form/FormDialogComp
|
|||
import Dialog from "components/ads/DialogComponent";
|
||||
import { User } from "constants/userConstants";
|
||||
import { getCurrentUser, selectFeatureFlags } from "selectors/usersSelectors";
|
||||
import {
|
||||
CREATE_WORKSPACE_FORM_NAME,
|
||||
inviteModalLinks,
|
||||
} from "@appsmith/constants/forms";
|
||||
import { CREATE_WORKSPACE_FORM_NAME } from "@appsmith/constants/forms";
|
||||
import {
|
||||
DropdownOnSelectActions,
|
||||
getOnSelectAction,
|
||||
|
|
@ -708,7 +705,6 @@ function ApplicationsSection(props: any) {
|
|||
title={`Invite Users to ${workspace.name}`}
|
||||
>
|
||||
<Form
|
||||
links={inviteModalLinks}
|
||||
message={createMessage(INVITE_USERS_MESSAGE)}
|
||||
workspaceId={workspace.id}
|
||||
/>
|
||||
|
|
@ -734,7 +730,6 @@ function ApplicationsSection(props: any) {
|
|||
<FormDialogComponent
|
||||
Form={WorkspaceInviteUsersForm}
|
||||
canOutsideClickClose
|
||||
links={inviteModalLinks}
|
||||
message={createMessage(INVITE_USERS_MESSAGE)}
|
||||
placeholder={createMessage(INVITE_USERS_PLACEHOLDER)}
|
||||
title={`Invite Users to ${workspace.name}`}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ import EndTour from "./GuidedTour/EndTour";
|
|||
import { GUIDED_TOUR_STEPS } from "./GuidedTour/constants";
|
||||
import { viewerURL } from "RouteBuilder";
|
||||
import { useHref } from "./utils";
|
||||
import { inviteModalLinks } from "@appsmith/constants/forms";
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
|
@ -462,7 +461,6 @@ export function EditorHeader(props: EditorHeaderProps) {
|
|||
bgColor: Colors.GEYSER_LIGHT,
|
||||
}}
|
||||
isOpen={showAppInviteUsersDialog}
|
||||
links={inviteModalLinks}
|
||||
message={createMessage(INVITE_USERS_MESSAGE)}
|
||||
placeholder={createMessage(INVITE_USERS_PLACEHOLDER)}
|
||||
title={
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ function AppInviteUsersForm(props: any) {
|
|||
fetchCurrentWorkspace,
|
||||
isChangingViewAccess,
|
||||
isFetchingApplication,
|
||||
links,
|
||||
} = props;
|
||||
|
||||
const currentWorkspaceId = useSelector(getCurrentWorkspaceId);
|
||||
|
|
@ -125,7 +124,6 @@ function AppInviteUsersForm(props: any) {
|
|||
{canInviteToWorkspace && (
|
||||
<WorkspaceInviteUsersForm
|
||||
isApplicationInvite
|
||||
links={links}
|
||||
workspaceId={props.workspaceId}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import { WorkspaceRole } from "constants/workspaceConstants";
|
||||
|
||||
export const getRolesFromState = (state: AppState) => {
|
||||
return state.ui.workspaces.roles;
|
||||
};
|
||||
|
||||
export const getWorkspaceLoadingStates = (state: AppState) => {
|
||||
return {
|
||||
isFetchingWorkspace: state.ui.workspaces.loadingStates.isFetchingWorkspace,
|
||||
isFetchingAllUsers: state.ui.workspaces.loadingStates.isFetchAllUsers,
|
||||
isFetchingAllRoles: state.ui.workspaces.loadingStates.isFetchAllRoles,
|
||||
deletingUserInfo: state.ui.workspaces.workspaceUsers.filter(
|
||||
(el) => el.isDeleting,
|
||||
)[0],
|
||||
roleChangingUserInfo: state.ui.workspaces.workspaceUsers.filter(
|
||||
(el) => el.isChangingRole,
|
||||
)[0],
|
||||
};
|
||||
};
|
||||
|
||||
export const getCurrentWorkspaceId = (state: AppState) =>
|
||||
state.ui.workspaces.currentWorkspace.id;
|
||||
export const getWorkspaces = (state: AppState) => {
|
||||
return state.ui.applications.userWorkspaces;
|
||||
};
|
||||
export const getCurrentWorkspace = (state: AppState) => {
|
||||
return state.ui.applications.userWorkspaces.map((el) => el.workspace);
|
||||
};
|
||||
export const getCurrentAppWorkspace = (state: AppState) => {
|
||||
return state.ui.workspaces.currentWorkspace;
|
||||
};
|
||||
export const getAllUsers = (state: AppState) =>
|
||||
state.ui.workspaces.workspaceUsers;
|
||||
export const getAllRoles = (state: AppState) =>
|
||||
state.ui.workspaces.workspaceRoles;
|
||||
|
||||
export const getRoles = createSelector(
|
||||
getRolesFromState,
|
||||
(roles?: WorkspaceRole[]): WorkspaceRole[] | undefined => {
|
||||
return roles?.map((role) => ({
|
||||
id: role.id,
|
||||
name: role.displayName || role.name,
|
||||
isDefault: role.isDefault,
|
||||
}));
|
||||
},
|
||||
);
|
||||
|
||||
export const getRolesForField = createSelector(getAllRoles, (roles?: any) => {
|
||||
return Object.entries(roles).map((role) => {
|
||||
return {
|
||||
id: role[0],
|
||||
name: role[0],
|
||||
description: role[1],
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
export const getDefaultRole = createSelector(
|
||||
getRoles,
|
||||
(roles?: WorkspaceRole[]) => {
|
||||
return roles?.find((role) => role.isDefault);
|
||||
},
|
||||
);
|
||||
export const getCurrentError = (state: AppState) => {
|
||||
return state.ui.errors.currentError;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user