Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
c2ce945c29
|
|
@ -25,10 +25,10 @@
|
|||
"inviteUserMembersPage": "[data-cy=t--invite-users]",
|
||||
"email": "//input[@type='email']",
|
||||
"selectRole": "//span[text()='Select a role']",
|
||||
"adminRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Administrator')]",
|
||||
"viewerRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Viewer')]",
|
||||
"developerRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Developer')]",
|
||||
"inviteBtn": "//span[text()='Invite']",
|
||||
"adminRole": "//div[@class='label-title']//span[text()='Administrator']",
|
||||
"viewerRole": "//div[@class='label-title']//span[text()='App Viewer']",
|
||||
"developerRole": "//div[@class='label-title']//span[text()='Developer']",
|
||||
"inviteBtn": "//button[text()='Invite']",
|
||||
"manageUsers": ".manageUsers",
|
||||
"DeleteBtn": "[data-cy=t--deleteUser]",
|
||||
"ShareBtn": "//a[text()='Share']",
|
||||
|
|
|
|||
4
app/client/src/assets/icons/ads/manage.svg
Normal file
4
app/client/src/assets/icons/ads/manage.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="9" height="8" viewBox="0 0 9 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 4H5.4" stroke="#9F9F9F" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.40002 0L9.00002 4L4.40002 8V0Z" fill="#9F9F9F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { CommonComponentProps, hexToRgba, ThemeProp, Classes } from "./common";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import Icon, { IconName, IconSize } from "./Icon";
|
||||
import Spinner from "./Spinner";
|
||||
import { mediumButton, smallButton, largeButton } from "constants/DefaultTheme";
|
||||
|
|
@ -58,6 +58,7 @@ type ButtonProps = CommonComponentProps & {
|
|||
size?: Size;
|
||||
fill?: boolean;
|
||||
href?: string;
|
||||
tag?: "a" | "button";
|
||||
};
|
||||
|
||||
const stateStyles = (
|
||||
|
|
@ -243,7 +244,7 @@ const btnFontStyles = (props: ThemeProp & ButtonProps): BtnFontType => {
|
|||
return { buttonFont, padding, height };
|
||||
};
|
||||
|
||||
const StyledButton = styled("a")<ThemeProp & ButtonProps>`
|
||||
const ButtonStyles = css<ThemeProp & ButtonProps>`
|
||||
width: ${props => (props.fill ? "100%" : "auto")};
|
||||
height: ${props => btnFontStyles(props).height}px;
|
||||
border: none;
|
||||
|
|
@ -295,7 +296,7 @@ const StyledButton = styled("a")<ThemeProp & ButtonProps>`
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
.new-spinner {
|
||||
.${Classes.SPINNER} {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
|
@ -304,6 +305,14 @@ const StyledButton = styled("a")<ThemeProp & ButtonProps>`
|
|||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled("button")`
|
||||
${ButtonStyles}
|
||||
`;
|
||||
|
||||
const StyledLinkButton = styled("a")`
|
||||
${ButtonStyles}
|
||||
`;
|
||||
|
||||
export const VisibilityWrapper = styled.div`
|
||||
visibility: hidden;
|
||||
`;
|
||||
|
|
@ -327,6 +336,7 @@ Button.defaultProps = {
|
|||
isLoading: false,
|
||||
disabled: false,
|
||||
fill: false,
|
||||
tag: "a",
|
||||
};
|
||||
|
||||
function Button(props: ButtonProps) {
|
||||
|
|
@ -336,16 +346,8 @@ function Button(props: ButtonProps) {
|
|||
|
||||
const TextLoadingState = <VisibilityWrapper>{props.text}</VisibilityWrapper>;
|
||||
|
||||
return (
|
||||
<StyledButton
|
||||
href={props.href}
|
||||
className={props.className}
|
||||
data-cy={props.cypressSelector}
|
||||
{...props}
|
||||
onClick={(e: React.MouseEvent<HTMLElement>) =>
|
||||
props.onClick && props.onClick(e)
|
||||
}
|
||||
>
|
||||
const buttonContent = (
|
||||
<>
|
||||
{props.icon ? (
|
||||
props.isLoading ? (
|
||||
IconLoadingState
|
||||
|
|
@ -357,8 +359,37 @@ function Button(props: ButtonProps) {
|
|||
{props.text ? (props.isLoading ? TextLoadingState : props.text) : null}
|
||||
|
||||
{props.isLoading ? <Spinner size={IconSizeProp(props.size)} /> : null}
|
||||
</StyledButton>
|
||||
</>
|
||||
);
|
||||
|
||||
if (props.tag === "button") {
|
||||
return (
|
||||
<StyledButton
|
||||
className={props.className}
|
||||
data-cy={props.cypressSelector}
|
||||
{...props}
|
||||
onClick={(e: React.MouseEvent<HTMLElement>) =>
|
||||
props.onClick && props.onClick(e)
|
||||
}
|
||||
>
|
||||
{buttonContent}
|
||||
</StyledButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<StyledLinkButton
|
||||
href={props.href}
|
||||
className={props.className}
|
||||
data-cy={props.cypressSelector}
|
||||
{...props}
|
||||
onClick={(e: React.MouseEvent<HTMLElement>) =>
|
||||
props.onClick && props.onClick(e)
|
||||
}
|
||||
>
|
||||
{buttonContent}
|
||||
</StyledLinkButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Button;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@ import React from "react";
|
|||
import { CommonComponentProps, Classes } from "./common";
|
||||
import Text, { TextType } from "./Text";
|
||||
import styled from "styled-components";
|
||||
|
||||
export type Variant = "note" | "warning";
|
||||
export type Background = "dark" | "light";
|
||||
import { Variant } from "./Button";
|
||||
|
||||
type CalloutProps = CommonComponentProps & {
|
||||
variant?: Variant;
|
||||
background?: Background;
|
||||
text: string;
|
||||
fill?: boolean;
|
||||
};
|
||||
|
|
@ -16,14 +13,11 @@ type CalloutProps = CommonComponentProps & {
|
|||
const CalloutContainer = styled.div<{
|
||||
variant?: Variant;
|
||||
fill?: boolean;
|
||||
background?: Background;
|
||||
}>`
|
||||
padding: ${props => props.theme.spaces[5]}px
|
||||
${props => props.theme.spaces[11] + 1}px;
|
||||
background: ${props =>
|
||||
props.variant && props.background
|
||||
? props.theme.colors.callout[props.variant][props.background].bgColor
|
||||
: null};
|
||||
props.variant ? props.theme.colors.callout[props.variant].bgColor : null};
|
||||
height: 42px;
|
||||
|
||||
${props =>
|
||||
|
|
@ -37,25 +31,18 @@ const CalloutContainer = styled.div<{
|
|||
|
||||
.${Classes.TEXT} {
|
||||
color: ${props =>
|
||||
props.variant && props.background
|
||||
? props.theme.colors.callout[props.variant][props.background].color
|
||||
: null};
|
||||
props.variant ? props.theme.colors.callout[props.variant].color : null};
|
||||
}
|
||||
`;
|
||||
|
||||
Callout.defaultProps = {
|
||||
fill: false,
|
||||
variant: "note",
|
||||
background: "dark",
|
||||
};
|
||||
|
||||
function Callout(props: CalloutProps) {
|
||||
return (
|
||||
<CalloutContainer
|
||||
variant={props.variant}
|
||||
background={props.background}
|
||||
fill={props.fill}
|
||||
>
|
||||
<CalloutContainer variant={props.variant} fill={props.fill}>
|
||||
<Text type={TextType.P2}>{props.text}</Text>
|
||||
</CalloutContainer>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,38 +6,42 @@ import Text, { TextType } from "./Text";
|
|||
|
||||
type DropdownOption = {
|
||||
label?: string;
|
||||
value: string;
|
||||
value?: string;
|
||||
id?: string;
|
||||
icon?: IconName;
|
||||
onSelect?: (option: DropdownOption) => void;
|
||||
children?: DropdownOption[];
|
||||
onSelect?: (value?: string) => void;
|
||||
};
|
||||
|
||||
type DropdownProps = CommonComponentProps & {
|
||||
options: DropdownOption[];
|
||||
selected: DropdownOption;
|
||||
onSelect?: (value?: string) => void;
|
||||
};
|
||||
|
||||
const DropdownContainer = styled.div`
|
||||
width: 260px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const Selected = styled.div<{ isOpen: boolean; disabled?: boolean }>`
|
||||
height: 38px;
|
||||
padding: ${props => props.theme.spaces[4]}px
|
||||
${props => props.theme.spaces[6]}px;
|
||||
background: ${props =>
|
||||
props.disabled
|
||||
? props.theme.colors.dropdown.header.disabledText
|
||||
: props.theme.colors.dropdown.header.disabledBg};
|
||||
? props.theme.colors.dropdown.header.disabledBg
|
||||
: props.theme.colors.dropdown.header.bg};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
${props =>
|
||||
props.isOpen && !props.disabled
|
||||
? `border: 1.2px solid ${props.theme.colors.info.main}`
|
||||
: null};
|
||||
props.isOpen
|
||||
? `border: 1px solid ${props.theme.colors.info.main}`
|
||||
: props.disabled
|
||||
? `border: 1px solid ${props.theme.colors.dropdown.header.disabledBg}`
|
||||
: `border: 1px solid ${props.theme.colors.dropdown.header.bg}`};
|
||||
${props =>
|
||||
props.isOpen && !props.disabled ? "box-sizing: border-box" : null};
|
||||
${props =>
|
||||
|
|
@ -47,12 +51,16 @@ const Selected = styled.div<{ isOpen: boolean; disabled?: boolean }>`
|
|||
.${Classes.TEXT} {
|
||||
${props =>
|
||||
props.disabled
|
||||
? `color: ${props.theme.colors.dropdown.text}`
|
||||
: `color: ${props.theme.colors.dropdown.disabledText}`};
|
||||
? `color: ${props.theme.colors.dropdown.header.disabledText}`
|
||||
: `color: ${props.theme.colors.dropdown.header.text}`};
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownWrapper = styled.div`
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
left: 0px;
|
||||
z-index: 1;
|
||||
margin-top: ${props => props.theme.spaces[2] - 1}px;
|
||||
background: ${props => props.theme.colors.dropdown.menuBg};
|
||||
box-shadow: 0px 12px 28px ${props => props.theme.colors.dropdown.menuShadow};
|
||||
|
|
@ -117,6 +125,7 @@ const LabelWrapper = styled.div<{ label?: string }>`
|
|||
`;
|
||||
|
||||
export default function Dropdown(props: DropdownProps) {
|
||||
const { onSelect } = { ...props };
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const [selected, setSelected] = useState<DropdownOption>(props.selected);
|
||||
|
||||
|
|
@ -124,11 +133,15 @@ export default function Dropdown(props: DropdownProps) {
|
|||
setSelected(props.selected);
|
||||
}, [props.selected]);
|
||||
|
||||
const optionClickHandler = useCallback((option: DropdownOption) => {
|
||||
setSelected(option);
|
||||
setIsOpen(false);
|
||||
option.onSelect && option.onSelect(option);
|
||||
}, []);
|
||||
const optionClickHandler = useCallback(
|
||||
(option: DropdownOption) => {
|
||||
setSelected(option);
|
||||
setIsOpen(false);
|
||||
onSelect && onSelect(option.value);
|
||||
option.onSelect && option.onSelect(option.value);
|
||||
},
|
||||
[onSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownContainer
|
||||
|
|
@ -142,7 +155,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<Text type={TextType.P1}>{selected.value}</Text>
|
||||
<Icon name="downArrow" size={IconSize.SMALL} />
|
||||
<Icon name="downArrow" size={IconSize.XXS} />
|
||||
</Selected>
|
||||
|
||||
{isOpen && !props.disabled ? (
|
||||
|
|
@ -159,7 +172,9 @@ export default function Dropdown(props: DropdownProps) {
|
|||
) : null}
|
||||
<LabelWrapper label={option.label}>
|
||||
{option.label ? (
|
||||
<Text type={TextType.H5}>{option.value}</Text>
|
||||
<div className="label-title">
|
||||
<Text type={TextType.H5}>{option.value}</Text>
|
||||
</div>
|
||||
) : (
|
||||
<Text type={TextType.P1}>{option.value}</Text>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -128,15 +128,22 @@ const IconWrapper = styled.div`
|
|||
`;
|
||||
|
||||
export const EditableText = (props: EditableTextProps) => {
|
||||
const [isEditing, setIsEditing] = useState(!!props.isEditingDefault);
|
||||
const [value, setValue] = useState(props.defaultValue);
|
||||
const [lastValidValue, setLastValidValue] = useState(props.defaultValue);
|
||||
const {
|
||||
onBlur,
|
||||
onTextChanged,
|
||||
isInvalid: inputValidation,
|
||||
defaultValue,
|
||||
isEditingDefault,
|
||||
} = props;
|
||||
const [isEditing, setIsEditing] = useState(!!isEditingDefault);
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const [lastValidValue, setLastValidValue] = useState(defaultValue);
|
||||
const [isInvalid, setIsInvalid] = useState<string | boolean>(false);
|
||||
const [changeStarted, setChangeStarted] = useState<boolean>(false);
|
||||
const [savingState, setSavingState] = useState<SavingState>(
|
||||
SavingState.NOT_STARTED,
|
||||
);
|
||||
const valueRef = React.useRef(props.defaultValue);
|
||||
const valueRef = React.useRef(defaultValue);
|
||||
|
||||
useEffect(() => {
|
||||
setSavingState(props.savingState);
|
||||
|
|
@ -144,18 +151,19 @@ export const EditableText = (props: EditableTextProps) => {
|
|||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
props.onBlur(valueRef.current);
|
||||
onBlur(valueRef.current);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(props.defaultValue);
|
||||
setIsEditing(!!props.isEditingDefault);
|
||||
}, [props.defaultValue, props.isEditingDefault]);
|
||||
setValue(defaultValue);
|
||||
setIsEditing(!!isEditingDefault);
|
||||
}, [defaultValue, isEditingDefault]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.forceDefault === true) setValue(props.defaultValue);
|
||||
}, [props.forceDefault, props.defaultValue]);
|
||||
if (props.forceDefault === true) setValue(defaultValue);
|
||||
}, [props.forceDefault, defaultValue]);
|
||||
|
||||
const themeDetails = useSelector(getThemeDetails);
|
||||
const bgColor = useMemo(
|
||||
|
|
@ -167,35 +175,41 @@ export const EditableText = (props: EditableTextProps) => {
|
|||
const editMode = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
setIsEditing(true);
|
||||
const errorMessage =
|
||||
props.isInvalid && props.isInvalid(props.defaultValue);
|
||||
const errorMessage = inputValidation && inputValidation(defaultValue);
|
||||
setIsInvalid(errorMessage ? errorMessage : false);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
[props],
|
||||
[inputValidation, defaultValue],
|
||||
);
|
||||
|
||||
const onConfirm = useCallback(
|
||||
(_value: string) => {
|
||||
if (savingState === SavingState.ERROR || isInvalid) {
|
||||
setValue(lastValidValue);
|
||||
props.onBlur(lastValidValue);
|
||||
onBlur(lastValidValue);
|
||||
setSavingState(SavingState.NOT_STARTED);
|
||||
} else if (changeStarted) {
|
||||
props.onTextChanged(_value);
|
||||
props.onBlur(_value);
|
||||
onTextChanged(_value);
|
||||
onBlur(_value);
|
||||
}
|
||||
setIsEditing(false);
|
||||
setChangeStarted(false);
|
||||
},
|
||||
[changeStarted, lastValidValue, props.onBlur, props.onTextChanged],
|
||||
[
|
||||
changeStarted,
|
||||
savingState,
|
||||
isInvalid,
|
||||
lastValidValue,
|
||||
onBlur,
|
||||
onTextChanged,
|
||||
],
|
||||
);
|
||||
|
||||
const onInputchange = useCallback(
|
||||
(_value: string) => {
|
||||
const finalVal: string = _value;
|
||||
const errorMessage = props.isInvalid && props.isInvalid(finalVal);
|
||||
const errorMessage = inputValidation && inputValidation(finalVal);
|
||||
const error = errorMessage ? errorMessage : false;
|
||||
if (!error) {
|
||||
setLastValidValue(finalVal);
|
||||
|
|
@ -205,7 +219,7 @@ export const EditableText = (props: EditableTextProps) => {
|
|||
setIsInvalid(error);
|
||||
setChangeStarted(true);
|
||||
},
|
||||
[props.isInvalid],
|
||||
[inputValidation],
|
||||
);
|
||||
|
||||
const iconName =
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { ReactComponent as ViewAllIcon } from "assets/icons/ads/view-all.svg";
|
|||
import { ReactComponent as ContextMenuIcon } from "assets/icons/ads/context-menu.svg";
|
||||
import { ReactComponent as DuplicateIcon } from "assets/icons/ads/duplicate.svg";
|
||||
import { ReactComponent as LogoutIcon } from "assets/icons/ads/logout.svg";
|
||||
import { ReactComponent as ManageIcon } from "assets/icons/ads/manage.svg";
|
||||
import styled from "styled-components";
|
||||
import { CommonComponentProps, Classes } from "./common";
|
||||
import { noop } from "lodash";
|
||||
|
|
@ -88,6 +89,7 @@ export const IconCollection = [
|
|||
"context-menu",
|
||||
"duplicate",
|
||||
"logout",
|
||||
"manage",
|
||||
] as const;
|
||||
|
||||
export type IconName = typeof IconCollection[number];
|
||||
|
|
@ -97,6 +99,7 @@ const IconWrapper = styled.span<IconProps>`
|
|||
outline: none;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
svg {
|
||||
width: ${props => sizeHandler(props.size)}px;
|
||||
height: ${props => sizeHandler(props.size)}px;
|
||||
|
|
@ -189,6 +192,9 @@ const Icon = (props: IconProps & CommonComponentProps) => {
|
|||
case "logout":
|
||||
returnIcon = <LogoutIcon />;
|
||||
break;
|
||||
case "manage":
|
||||
returnIcon = <ManageIcon />;
|
||||
break;
|
||||
default:
|
||||
returnIcon = null;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,35 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { TagInput } from "@blueprintjs/core";
|
||||
import {
|
||||
Intent,
|
||||
IntentColors,
|
||||
getColorWithOpacity,
|
||||
} from "constants/DefaultTheme";
|
||||
import { Classes, TagInput } from "@blueprintjs/core";
|
||||
import { Intent } from "constants/DefaultTheme";
|
||||
const TagInputWrapper = styled.div<{ intent?: Intent }>`
|
||||
margin-right: 8px;
|
||||
|
||||
&&& {
|
||||
.bp3-tag {
|
||||
color: ${props => props.theme.colors.textDefault};
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
background: ${props =>
|
||||
props.intent
|
||||
? getColorWithOpacity(IntentColors[props.intent], 0.2)
|
||||
: getColorWithOpacity(IntentColors.none, 0.2)};
|
||||
border: 1px solid
|
||||
${props =>
|
||||
props.intent ? IntentColors[props.intent] : IntentColors.none};
|
||||
.${Classes.TAG_INPUT} {
|
||||
background-color: ${props => props.theme.colors.tagInput.bg};
|
||||
min-height: 38px;
|
||||
border: 1px solid ${props => props.theme.colors.tagInput.bg};
|
||||
border-radius: 0px;
|
||||
}
|
||||
.${Classes.TAG_INPUT}.${Classes.ACTIVE} {
|
||||
border: 1px solid ${props => props.theme.colors.info.main};
|
||||
box-shadow: ${props => props.theme.colors.tagInput.shadow};
|
||||
}
|
||||
.${Classes.INPUT_GHOST} {
|
||||
color: ${props => props.theme.colors.tagInput.text};
|
||||
&::placeholder {
|
||||
color: ${props => props.theme.colors.tagInput.placeholder};
|
||||
}
|
||||
}
|
||||
.${Classes.TAG} {
|
||||
padding: 3px 10px;
|
||||
color: ${props => props.theme.colors.tagInput.tag.text};
|
||||
background-color: ${props => props.theme.colors.info.main};
|
||||
border-radius: 0px;
|
||||
font-size: 11px;
|
||||
line-height: 13px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
@ -39,6 +39,7 @@ export type TextInputProps = CommonComponentProps & {
|
|||
defaultValue?: string;
|
||||
validator?: (value: string) => { isValid: boolean; message: string };
|
||||
onChange?: (value: string) => void;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
|
||||
type boxReturnType = {
|
||||
|
|
@ -61,6 +62,11 @@ const boxStyles = (
|
|||
color = theme.colors.textInput.disable.text;
|
||||
borderColor = theme.colors.textInput.disable.border;
|
||||
}
|
||||
if (props.readOnly) {
|
||||
bgColor = theme.colors.textInput.readOnly.bg;
|
||||
color = theme.colors.textInput.readOnly.text;
|
||||
borderColor = theme.colors.textInput.readOnly.border;
|
||||
}
|
||||
if (!isValid) {
|
||||
bgColor = hexToRgba(theme.colors.danger.main, 0.1);
|
||||
color = theme.colors.danger.main;
|
||||
|
|
@ -77,28 +83,35 @@ const StyledInput = styled.input<
|
|||
outline: 0;
|
||||
box-shadow: none;
|
||||
border: 1px solid ${props => props.inputStyle.borderColor};
|
||||
padding: ${props => props.theme.spaces[4]}px
|
||||
${props => props.theme.spaces[6]}px;
|
||||
padding: 0px ${props => props.theme.spaces[6]}px;
|
||||
height: 38px;
|
||||
background-color: ${props => props.inputStyle.bgColor};
|
||||
color: ${props => props.inputStyle.color};
|
||||
|
||||
&::placeholder {
|
||||
color: ${props => props.theme.colors.textInput.placeholder};
|
||||
}
|
||||
&:focus {
|
||||
border: 1px solid
|
||||
${props =>
|
||||
props.isValid
|
||||
? props.theme.colors.info.main
|
||||
: props.theme.colors.danger.main};
|
||||
box-shadow: ${props =>
|
||||
props.isValid
|
||||
? "0px 0px 4px 4px rgba(203, 72, 16, 0.18)"
|
||||
: "0px 0px 4px 4px rgba(226, 44, 44, 0.18)"};
|
||||
}
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
${props =>
|
||||
!props.readOnly
|
||||
? `
|
||||
&:focus {
|
||||
border: 1px solid
|
||||
${
|
||||
props.isValid
|
||||
? props.theme.colors.info.main
|
||||
: props.theme.colors.danger.main
|
||||
};
|
||||
box-shadow: ${
|
||||
props.isValid
|
||||
? "0px 0px 4px 4px rgba(203, 72, 16, 0.18)"
|
||||
: "0px 0px 4px 4px rgba(226, 44, 44, 0.18)"
|
||||
};
|
||||
}
|
||||
`
|
||||
: null};
|
||||
`;
|
||||
|
||||
const InputWrapper = styled.div`
|
||||
|
|
@ -172,6 +185,7 @@ const TextInput = forwardRef(
|
|||
{...props}
|
||||
placeholder={props.placeholder}
|
||||
onChange={memoizedChangeHandler}
|
||||
readOnly={props.readOnly}
|
||||
/>
|
||||
{ErrorMessage}
|
||||
</InputWrapper>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CommonComponentProps, Classes, lighten, darken } from "./common";
|
||||
import { CommonComponentProps, Classes } from "./common";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import Spinner from "./Spinner";
|
||||
|
|
|
|||
|
|
@ -1,36 +1,17 @@
|
|||
import React, { createRef, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||
import TextInput from "components/ads/TextInput";
|
||||
import Button, { Category, Size } from "components/ads/Button";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
const StyledInput = styled.input`
|
||||
flex: 1;
|
||||
border: 1px solid #d3dee3;
|
||||
border-right: none;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
color: #768896;
|
||||
border-radius: 4px 0 0 4px;
|
||||
width: 90%;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const SelectButton = styled(BaseButton)`
|
||||
&&&& {
|
||||
max-width: 70px;
|
||||
margin: 0 0px;
|
||||
min-height: 32px;
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
font-weight: bold;
|
||||
background-color: #f6f7f8;
|
||||
font-size: 14px;
|
||||
&.bp3-button {
|
||||
padding: 0px 0px;
|
||||
}
|
||||
div {
|
||||
flex-basis: calc(100% - 110px);
|
||||
}
|
||||
a {
|
||||
flex-basis: 110px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -54,21 +35,23 @@ const CopyToClipboard = (props: any) => {
|
|||
};
|
||||
return (
|
||||
<Wrapper>
|
||||
<StyledInput
|
||||
type="text"
|
||||
<TextInput
|
||||
fill
|
||||
ref={copyURLInput}
|
||||
readOnly
|
||||
onClick={() => {
|
||||
onChange={() => {
|
||||
selectText();
|
||||
}}
|
||||
value={copyText}
|
||||
defaultValue={copyText}
|
||||
/>
|
||||
<SelectButton
|
||||
|
||||
<Button
|
||||
text={isCopied ? "Copied" : "Copy"}
|
||||
accent="secondary"
|
||||
category={Category.tertiary}
|
||||
onClick={() => {
|
||||
copyToClipboard(copyText);
|
||||
}}
|
||||
size={Size.large}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,36 @@ import { isPermitted } from "pages/Applications/permissionHelpers";
|
|||
|
||||
const StyledDialog = styled(Dialog)<{ setMaxWidth?: boolean }>`
|
||||
&& {
|
||||
background: white;
|
||||
& .bp3-dialog-header {
|
||||
padding: ${props => props.theme.spaces[4]}px
|
||||
${props => props.theme.spaces[4]}px;
|
||||
border-radius: 0px;
|
||||
padding-bottom: 5px;
|
||||
background: ${props => props.theme.colors.modal.bg};
|
||||
width: 640px;
|
||||
|
||||
& .${Classes.DIALOG_HEADER} {
|
||||
padding: ${props => props.theme.spaces[4]}px;
|
||||
background: ${props => props.theme.colors.modal.bg};
|
||||
box-shadow: none;
|
||||
.${Classes.ICON} {
|
||||
color: ${props => props.theme.colors.modal.iconColor};
|
||||
}
|
||||
.${Classes.HEADING} {
|
||||
color: ${props => props.theme.colors.modal.headerText};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.${Classes.BUTTON}.${Classes.MINIMAL}:hover {
|
||||
background-color: ${props => props.theme.colors.modal.bg};
|
||||
}
|
||||
}
|
||||
& .bp3-dialog-footer-actions {
|
||||
& .${Classes.DIALOG_BODY} {
|
||||
margin: ${props => props.theme.spaces[9]}px;
|
||||
}
|
||||
& .${Classes.DIALOG_FOOTER_ACTIONS} {
|
||||
display: block;
|
||||
}
|
||||
${props => props.setMaxWidth && `width: 100vh;`}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import Dropdown from "components/ads/Dropdown";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
type DropdownWrapperProps = {
|
||||
placeholder: string;
|
||||
input?: {
|
||||
value?: string;
|
||||
onChange?: (value?: string) => void;
|
||||
};
|
||||
options: Array<{ id: string; value: string; label: string }>;
|
||||
};
|
||||
|
||||
const DropdownWrapper = (props: DropdownWrapperProps) => {
|
||||
const [selectedOption, setSelectedOption] = useState({
|
||||
value: props.placeholder,
|
||||
});
|
||||
const onSelectHandler = (value?: string) => {
|
||||
props.input && props.input.onChange && props.input.onChange(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props.placeholder) {
|
||||
setSelectedOption({ value: props.placeholder });
|
||||
} else if (props.input && props.input.value) {
|
||||
setSelectedOption({ value: props.input.value });
|
||||
}
|
||||
}, [props.input, props.placeholder]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
options={props.options}
|
||||
selected={selectedOption}
|
||||
onSelect={onSelectHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownWrapper;
|
||||
|
|
@ -4,7 +4,7 @@ import {
|
|||
WrappedFieldMetaProps,
|
||||
WrappedFieldInputProps,
|
||||
} from "redux-form";
|
||||
import SelectComponent from "components/editorComponents/SelectComponent";
|
||||
import DropdownWrapper from "./DropdownWrapper";
|
||||
|
||||
const renderComponent = (
|
||||
componentProps: SelectFieldProps & {
|
||||
|
|
@ -14,15 +14,15 @@ const renderComponent = (
|
|||
) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SelectComponent {...componentProps} />
|
||||
<DropdownWrapper {...componentProps} />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
type SelectFieldProps = {
|
||||
name: string;
|
||||
placeholder?: string;
|
||||
options?: Array<{ id: string; name: string; value?: string }>;
|
||||
placeholder: string;
|
||||
options: Array<{ id: string; value: string; label: string }>;
|
||||
size?: "large" | "small";
|
||||
outline?: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
WrappedFieldMetaProps,
|
||||
WrappedFieldInputProps,
|
||||
} from "redux-form";
|
||||
import TagInputComponent from "components/editorComponents/TagInputComponent";
|
||||
import TagInputComponent from "components/ads/TagInputComponent";
|
||||
import { Intent } from "constants/DefaultTheme";
|
||||
|
||||
const renderComponent = (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import { withKnobs, select, text, boolean } from "@storybook/addon-knobs";
|
||||
import Callout from "components/ads/Callout";
|
||||
import { StoryWrapper } from "components/ads/common";
|
||||
import { Variant } from "components/ads/Button";
|
||||
|
||||
export default {
|
||||
title: "Callout",
|
||||
|
|
@ -13,9 +14,8 @@ export const CalloutStory = () => (
|
|||
<StoryWrapper>
|
||||
<Callout
|
||||
text={text("text", "Lorem ipsum dolar sit adicipling dolare")}
|
||||
variant={select("variant", ["note", "warning"], "note")}
|
||||
background={select("background", ["light", "dark"], "dark")}
|
||||
fill={boolean("fill", false)}
|
||||
variant={select("variant", Object.values(Variant), Variant.info)}
|
||||
/>
|
||||
</StoryWrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const Text = () => (
|
|||
onSelect: action("selected-option"),
|
||||
},
|
||||
]}
|
||||
onSelect={action("selected-option")}
|
||||
selected={{
|
||||
id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"),
|
||||
value: text("Selected value", "First option"),
|
||||
|
|
@ -64,6 +65,7 @@ export const IconAndText = () => (
|
|||
onSelect: action("selected-option"),
|
||||
},
|
||||
]}
|
||||
onSelect={action("selected-option")}
|
||||
selected={{
|
||||
id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"),
|
||||
value: text("Selected value", "Delete"),
|
||||
|
|
@ -102,6 +104,7 @@ export const LabelAndText = () => (
|
|||
onSelect: action("selected-option"),
|
||||
},
|
||||
]}
|
||||
onSelect={action("selected-option")}
|
||||
selected={{
|
||||
id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"),
|
||||
value: text("Selected value", "Developer"),
|
||||
|
|
|
|||
|
|
@ -479,7 +479,6 @@ type ColorType = {
|
|||
hoverBG: Color;
|
||||
hoverBGOpacity: number;
|
||||
hoverBorder: ShadeColor;
|
||||
targetBg: string;
|
||||
iconColor: ShadeColor;
|
||||
};
|
||||
appCardColors: string[];
|
||||
|
|
@ -522,7 +521,7 @@ type ColorType = {
|
|||
dropdown: {
|
||||
header: {
|
||||
text: ShadeColor;
|
||||
disabled: ShadeColor;
|
||||
disabledText: ShadeColor;
|
||||
bg: ShadeColor;
|
||||
disabledBg: ShadeColor;
|
||||
};
|
||||
|
|
@ -563,6 +562,11 @@ type ColorType = {
|
|||
border: ShadeColor;
|
||||
};
|
||||
placeholder: ShadeColor;
|
||||
readOnly: {
|
||||
bg: ShadeColor;
|
||||
border: ShadeColor;
|
||||
text: ShadeColor;
|
||||
};
|
||||
};
|
||||
menuBorder: ShadeColor;
|
||||
editableText: {
|
||||
|
|
@ -637,6 +641,46 @@ type ColorType = {
|
|||
profileDropdown: {
|
||||
userName: ShadeColor;
|
||||
};
|
||||
modal: {
|
||||
bg: ShadeColor;
|
||||
headerText: ShadeColor;
|
||||
iconColor: string;
|
||||
user: {
|
||||
textColor: ShadeColor;
|
||||
};
|
||||
email: {
|
||||
message: ShadeColor;
|
||||
desc: ShadeColor;
|
||||
};
|
||||
manageUser: ShadeColor;
|
||||
};
|
||||
tagInput: {
|
||||
bg: ShadeColor;
|
||||
tag: {
|
||||
text: ShadeColor;
|
||||
};
|
||||
text: ShadeColor;
|
||||
placeholder: ShadeColor;
|
||||
shadow: string;
|
||||
};
|
||||
callout: {
|
||||
info: {
|
||||
color: string;
|
||||
bgColor: string;
|
||||
};
|
||||
success: {
|
||||
color: string;
|
||||
bgColor: string;
|
||||
};
|
||||
danger: {
|
||||
color: string;
|
||||
bgColor: string;
|
||||
};
|
||||
warning: {
|
||||
color: string;
|
||||
bgColor: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export const dark: ColorType = {
|
||||
|
|
@ -683,7 +727,6 @@ export const dark: ColorType = {
|
|||
hoverBG: Colors.BLACK,
|
||||
hoverBGOpacity: 0.5,
|
||||
hoverBorder: darkShades[4],
|
||||
targetBg: "rgba(0, 0, 0, 0.1)",
|
||||
iconColor: darkShades[9],
|
||||
},
|
||||
appCardColors: [
|
||||
|
|
@ -736,9 +779,9 @@ export const dark: ColorType = {
|
|||
dropdown: {
|
||||
header: {
|
||||
text: darkShades[7],
|
||||
disabled: darkShades[6],
|
||||
bg: darkShades[2],
|
||||
disabledBg: darkShades[0],
|
||||
disabledText: darkShades[6],
|
||||
bg: darkShades[0],
|
||||
disabledBg: darkShades[2],
|
||||
},
|
||||
menuBg: darkShades[3],
|
||||
menuShadow: "rgba(0, 0, 0, 0.6)",
|
||||
|
|
@ -777,6 +820,11 @@ export const dark: ColorType = {
|
|||
border: darkShades[0],
|
||||
},
|
||||
placeholder: darkShades[5],
|
||||
readOnly: {
|
||||
bg: darkShades[0],
|
||||
border: darkShades[0],
|
||||
text: darkShades[7],
|
||||
},
|
||||
},
|
||||
menuBorder: darkShades[4],
|
||||
editableText: {
|
||||
|
|
@ -851,6 +899,46 @@ export const dark: ColorType = {
|
|||
profileDropdown: {
|
||||
userName: darkShades[9],
|
||||
},
|
||||
modal: {
|
||||
bg: darkShades[1],
|
||||
headerText: darkShades[9],
|
||||
iconColor: "#6D6D6D",
|
||||
user: {
|
||||
textColor: darkShades[7],
|
||||
},
|
||||
email: {
|
||||
message: darkShades[8],
|
||||
desc: darkShades[6],
|
||||
},
|
||||
manageUser: darkShades[6],
|
||||
},
|
||||
tagInput: {
|
||||
bg: darkShades[0],
|
||||
tag: {
|
||||
text: darkShades[9],
|
||||
},
|
||||
text: darkShades[9],
|
||||
placeholder: darkShades[5],
|
||||
shadow: "0px 0px 4px 4px rgba(203, 72, 16, 0.18)",
|
||||
},
|
||||
callout: {
|
||||
info: {
|
||||
color: "#EE5A1A",
|
||||
bgColor: "#241C1B",
|
||||
},
|
||||
success: {
|
||||
color: "#30CF89",
|
||||
bgColor: "#17211E",
|
||||
},
|
||||
danger: {
|
||||
color: "#FF4D4D",
|
||||
bgColor: "#2B1A1D",
|
||||
},
|
||||
warning: {
|
||||
color: "#E0B30E",
|
||||
bgColor: "#29251A",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const light: ColorType = {
|
||||
|
|
@ -897,7 +985,6 @@ export const light: ColorType = {
|
|||
hoverBG: Colors.WHITE,
|
||||
hoverBGOpacity: 0.7,
|
||||
hoverBorder: lightShades[2],
|
||||
targetBg: "rgba(0, 0, 0, 0.1)",
|
||||
iconColor: lightShades[11],
|
||||
},
|
||||
appCardColors: [
|
||||
|
|
@ -950,7 +1037,7 @@ export const light: ColorType = {
|
|||
dropdown: {
|
||||
header: {
|
||||
text: lightShades[9],
|
||||
disabled: darkShades[6],
|
||||
disabledText: darkShades[6],
|
||||
bg: lightShades[2],
|
||||
disabledBg: lightShades[1],
|
||||
},
|
||||
|
|
@ -990,7 +1077,12 @@ export const light: ColorType = {
|
|||
text: lightShades[9],
|
||||
border: lightShades[2],
|
||||
},
|
||||
placeholder: lightShades[6],
|
||||
placeholder: lightShades[7],
|
||||
readOnly: {
|
||||
bg: lightShades[2],
|
||||
border: lightShades[2],
|
||||
text: lightShades[7],
|
||||
},
|
||||
},
|
||||
menuBorder: lightShades[3],
|
||||
editableText: {
|
||||
|
|
@ -1065,6 +1157,46 @@ export const light: ColorType = {
|
|||
profileDropdown: {
|
||||
userName: lightShades[9],
|
||||
},
|
||||
modal: {
|
||||
bg: lightShades[11],
|
||||
headerText: lightShades[10],
|
||||
iconColor: "#A9A7A7",
|
||||
user: {
|
||||
textColor: lightShades[9],
|
||||
},
|
||||
email: {
|
||||
message: lightShades[9],
|
||||
desc: lightShades[7],
|
||||
},
|
||||
manageUser: lightShades[6],
|
||||
},
|
||||
tagInput: {
|
||||
bg: lightShades[2],
|
||||
tag: {
|
||||
text: lightShades[11],
|
||||
},
|
||||
text: lightShades[9],
|
||||
placeholder: darkShades[7],
|
||||
shadow: "0px 0px 4px 4px rgba(203, 72, 16, 0.18)",
|
||||
},
|
||||
callout: {
|
||||
info: {
|
||||
color: "#D44100",
|
||||
bgColor: "#F8F3F0",
|
||||
},
|
||||
success: {
|
||||
color: "#007340",
|
||||
bgColor: "#D9FDED",
|
||||
},
|
||||
danger: {
|
||||
color: "#C60707",
|
||||
bgColor: "#FFE9E9",
|
||||
},
|
||||
warning: {
|
||||
color: "#DCAD00",
|
||||
bgColor: "#FAF6E6",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const theme: Theme = {
|
||||
|
|
@ -1171,28 +1303,6 @@ export const theme: Theme = {
|
|||
lightBg: lightShades[0],
|
||||
darkBg: lightShades[10],
|
||||
},
|
||||
callout: {
|
||||
note: {
|
||||
dark: {
|
||||
color: "#EE5A1A",
|
||||
bgColor: "#241C1B",
|
||||
},
|
||||
light: {
|
||||
color: "#D44100",
|
||||
bgColor: "#F8F3F0",
|
||||
},
|
||||
},
|
||||
warning: {
|
||||
light: {
|
||||
color: "#DCAD00",
|
||||
bgColor: "#FAF6E6",
|
||||
},
|
||||
dark: {
|
||||
color: "#E0B30E",
|
||||
bgColor: "#29251A",
|
||||
},
|
||||
},
|
||||
},
|
||||
appBackground: "#EFEFEF",
|
||||
primaryOld: Colors.GREEN,
|
||||
primaryDarker: Colors.JUNGLE_GREEN,
|
||||
|
|
|
|||
|
|
@ -45,11 +45,10 @@ import { Classes as CsClasses } from "components/ads/common";
|
|||
type NameWrapperProps = {
|
||||
hasReadPermission: boolean;
|
||||
showOverlay: boolean;
|
||||
isMenuOpen: boolean;
|
||||
};
|
||||
|
||||
const NameWrapper = styled((props: HTMLDivProps & NameWrapperProps) => (
|
||||
<div {...omit(props, ["hasReadPermission", "showOverlay", "isMenuOpen"])} />
|
||||
<div {...omit(props, ["hasReadPermission", "showOverlay"])} />
|
||||
))`
|
||||
.bp3-card {
|
||||
border-radius: 0;
|
||||
|
|
@ -81,7 +80,7 @@ const NameWrapper = styled((props: HTMLDivProps & NameWrapperProps) => (
|
|||
|
||||
& div.image-container {
|
||||
background: ${
|
||||
props.hasReadPermission && !props.isMenuOpen
|
||||
props.hasReadPermission
|
||||
? getColorWithOpacity(
|
||||
props.theme.colors.card.hoverBG,
|
||||
props.theme.colors.card.hoverBGOpacity,
|
||||
|
|
@ -206,8 +205,6 @@ const ContextDropdownWrapper = styled.div`
|
|||
|
||||
.${Classes.POPOVER_TARGET} {
|
||||
span {
|
||||
background: ${props => props.theme.colors.card.targetBg};
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.colors.card.iconColor};
|
||||
|
|
@ -385,7 +382,6 @@ export const ApplicationCard = (props: ApplicationCardProps) => {
|
|||
!isMenuOpen && setShowOverlay(false);
|
||||
}}
|
||||
hasReadPermission={hasReadPermission}
|
||||
isMenuOpen={isMenuOpen}
|
||||
className="t--application-card"
|
||||
>
|
||||
<Wrapper
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ const ApplicationsSection = () => {
|
|||
{applications.map((application: any) => {
|
||||
return (
|
||||
application.pages?.length > 0 && (
|
||||
<PaddingWrapper>
|
||||
<PaddingWrapper key={application.id}>
|
||||
<ApplicationCard
|
||||
key={application.id}
|
||||
application={application}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ type TagProps = CommonComponentProps & {
|
|||
userName?: string;
|
||||
};
|
||||
|
||||
const ProfileImage = styled.div<{ backgroundColor?: string }>`
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
export const ProfileImage = styled.div<{ backgroundColor?: string }>`
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
|
|
|
|||
|
|
@ -12,33 +12,24 @@ import {
|
|||
import { getDefaultPageId } from "sagas/SagaUtils";
|
||||
import { getApplicationViewerPageURL } from "constants/routes";
|
||||
import OrgInviteUsersForm from "./OrgInviteUsersForm";
|
||||
import { StyledSwitch } from "components/propertyControls/StyledControls";
|
||||
import Spinner from "components/editorComponents/Spinner";
|
||||
import { getCurrentUser } from "selectors/usersSelectors";
|
||||
import Text, { TextType } from "components/ads/Text";
|
||||
import Toggle from "components/ads/Toggle";
|
||||
|
||||
const Title = styled.div`
|
||||
font-weight: bold;
|
||||
padding: 10px 0px;
|
||||
`;
|
||||
|
||||
const ShareWithPublicOption = styled.div`
|
||||
{
|
||||
display: flex;
|
||||
padding: 10px 0px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const ShareToggle = styled.div`
|
||||
{
|
||||
&&& label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
&&& div {
|
||||
margin-right: 5px;
|
||||
}
|
||||
display: flex;
|
||||
}
|
||||
flex-basis: 48px;
|
||||
height: 23px;
|
||||
`;
|
||||
|
||||
const AppInviteUsersForm = (props: any) => {
|
||||
|
|
@ -84,29 +75,30 @@ const AppInviteUsersForm = (props: any) => {
|
|||
{canShareWithPublic && (
|
||||
<>
|
||||
<ShareWithPublicOption>
|
||||
Make the application public
|
||||
<Text type={TextType.H5}>Make the application public</Text>
|
||||
<ShareToggle>
|
||||
{(isChangingViewAccess || isFetchingApplication) && (
|
||||
<Spinner size={20} />
|
||||
)}
|
||||
{currentApplicationDetails && (
|
||||
<StyledSwitch
|
||||
onChange={() => {
|
||||
<Toggle
|
||||
isLoading={isChangingViewAccess || isFetchingApplication}
|
||||
value={currentApplicationDetails.isPublic}
|
||||
disabled={isChangingViewAccess || isFetchingApplication}
|
||||
onToggle={() => {
|
||||
changeAppViewAccess(
|
||||
applicationId,
|
||||
!currentApplicationDetails.isPublic,
|
||||
);
|
||||
}}
|
||||
disabled={isChangingViewAccess || isFetchingApplication}
|
||||
checked={currentApplicationDetails.isPublic}
|
||||
large
|
||||
/>
|
||||
)}
|
||||
</ShareToggle>
|
||||
</ShareWithPublicOption>
|
||||
</>
|
||||
)}
|
||||
<Title>Get Shareable link for this for this application </Title>
|
||||
<Title>
|
||||
<Text type={TextType.H5}>
|
||||
Get Shareable link for this for this application
|
||||
</Text>
|
||||
</Title>
|
||||
<CopyToClipBoard copyText={getViewApplicationURL()} />
|
||||
|
||||
{canInviteToOrg && (
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useEffect } from "react";
|
||||
import React, { Fragment, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import TagListField from "components/editorComponents/form/fields/TagListField";
|
||||
import { reduxForm, SubmissionError } from "redux-form";
|
||||
import SelectField from "components/editorComponents/form/fields/SelectField";
|
||||
import Divider from "components/editorComponents/Divider";
|
||||
import Button from "components/editorComponents/Button";
|
||||
import { connect } from "react-redux";
|
||||
import { connect, useSelector } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
import {
|
||||
getRolesForField,
|
||||
|
|
@ -17,8 +16,6 @@ import Spinner from "components/editorComponents/Spinner";
|
|||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { InviteUsersToOrgFormValues, inviteUsersToOrg } from "./helpers";
|
||||
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
import FormMessage from "components/editorComponents/form/FormMessage";
|
||||
import {
|
||||
INVITE_USERS_SUBMIT_SUCCESS,
|
||||
INVITE_USERS_VALIDATION_EMAILS_EMPTY,
|
||||
|
|
@ -26,7 +23,6 @@ import {
|
|||
INVITE_USERS_VALIDATION_ROLE_EMPTY,
|
||||
} from "constants/messages";
|
||||
import history from "utils/history";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { isEmail } from "utils/formhelpers";
|
||||
import {
|
||||
isPermitted,
|
||||
|
|
@ -35,32 +31,28 @@ import {
|
|||
import { getAppsmithConfigs } from "configs";
|
||||
import { ReactComponent as NoEmailConfigImage } from "assets/images/email-not-configured.svg";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import Button, { Variant, Size } from "components/ads/Button";
|
||||
import Text, { TextType } from "components/ads/Text";
|
||||
import Icon, { IconSize } from "components/ads/Icon";
|
||||
import { Classes } from "components/ads/common";
|
||||
import Callout from "components/ads/Callout";
|
||||
import { getInitialsAndColorCode } from "utils/AppsmithUtils";
|
||||
import { getThemeDetails } from "selectors/themeSelectors";
|
||||
import { ProfileImage } from "pages/common/ProfileDropdown";
|
||||
|
||||
const OrgInviteTitle = styled.div`
|
||||
font-weight: bold;
|
||||
padding: 10px 0px;
|
||||
`;
|
||||
|
||||
const DropDownOption = styled.div`
|
||||
padding: 10px 0;
|
||||
`;
|
||||
|
||||
const OptionTitle = styled.div`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const OptionDescription = styled.div`
|
||||
padding: 5px 0px;
|
||||
max-width: 250px;
|
||||
`;
|
||||
|
||||
const StyledForm = styled.form`
|
||||
width: 100%;
|
||||
background: white;
|
||||
padding: ${props => props.theme.spaces[5]}px;
|
||||
background: ${props => props.theme.colors.modal.bg};
|
||||
&&& {
|
||||
.wrapper > div {
|
||||
width: 70%;
|
||||
.wrapper > div:nth-child(1) {
|
||||
width: 60%;
|
||||
}
|
||||
.wrapper > div:nth-child(2) {
|
||||
width: 40%;
|
||||
}
|
||||
.bp3-input {
|
||||
box-shadow: none;
|
||||
|
|
@ -69,10 +61,39 @@ const StyledForm = styled.form`
|
|||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
.manageUsers {
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
const ManageUsers = styled("a")`
|
||||
margin-top: 20px;
|
||||
display: inline-flex;
|
||||
&&&& {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.${Classes.TEXT} {
|
||||
color: ${props => props.theme.colors.modal.manageUser};
|
||||
margin-right: ${props => props.theme.spaces[1]}px;
|
||||
}
|
||||
.${Classes.ICON} {
|
||||
svg path {
|
||||
fill: ${props => props.theme.colors.modal.manageUser};
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.${Classes.TEXT} {
|
||||
color: ${props => props.theme.colors.modal.headerText};
|
||||
}
|
||||
.${Classes.ICON} {
|
||||
svg path {
|
||||
fill: ${props => props.theme.colors.modal.headerText};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ErrorBox = styled.div<{ message?: boolean }>`
|
||||
${props => (props.message ? `margin: ${props.theme.spaces[9]}px 0px` : null)};
|
||||
`;
|
||||
|
||||
const StyledInviteFieldGroup = styled.div`
|
||||
|
|
@ -84,36 +105,51 @@ const StyledInviteFieldGroup = styled.div`
|
|||
display: flex;
|
||||
width: 85%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: 5px;
|
||||
border-width: 1px;
|
||||
margin-right: 5px;
|
||||
border-right: 0px;
|
||||
border-style: solid;
|
||||
border-color: ${Colors.ATHENS_GRAY};
|
||||
}
|
||||
`;
|
||||
|
||||
const UserList = styled.div`
|
||||
max-height: 200px;
|
||||
margin-top: 20px;
|
||||
overflow-y: scroll;
|
||||
.user {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const User = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 54px;
|
||||
padding-left: 15px;
|
||||
justify-content: space-between;
|
||||
color: ${props => props.theme.colors.modal.user.textColor};
|
||||
`;
|
||||
|
||||
const UserInfo = styled.div`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const UserRole = styled.div`
|
||||
flex-basis: 25%;
|
||||
.${Classes.TEXT} {
|
||||
color: ${props => props.theme.colors.modal.headerText};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
&&&.${Classes.BUTTON} {
|
||||
width: 83px;
|
||||
height: 31px;
|
||||
border-radius: 0px;
|
||||
const UserName = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
span:nth-child(1) {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
`;
|
||||
|
||||
const RoleDivider = styled.div`
|
||||
border-top: 1px solid ${props => props.theme.colors.menuBorder};
|
||||
`;
|
||||
|
||||
const Loading = styled(Spinner)`
|
||||
padding-top: 10px;
|
||||
margin: auto;
|
||||
|
|
@ -123,15 +159,16 @@ const Loading = styled(Spinner)`
|
|||
const MailConfigContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 5px;
|
||||
padding: ${props => props.theme.spaces[9]}px
|
||||
${props => props.theme.spaces[2]}px;
|
||||
align-items: center;
|
||||
&& > span {
|
||||
color: #2e3d49;
|
||||
color: ${props => props.theme.colors.modal.email.message};
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
&& > a {
|
||||
color: rgba(46, 61, 73, 0.5);
|
||||
color: ${props => props.theme.colors.modal.email.desc};
|
||||
font-size: 12px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -208,22 +245,39 @@ const OrgInviteUsersForm = (props: any) => {
|
|||
const styledRoles = props.roles.map((role: any) => {
|
||||
return {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
content: (
|
||||
<DropDownOption>
|
||||
<OptionTitle>{role.name}</OptionTitle>
|
||||
<OptionDescription>{role.description}</OptionDescription>
|
||||
</DropDownOption>
|
||||
),
|
||||
value: role.name,
|
||||
label: role.description,
|
||||
};
|
||||
});
|
||||
|
||||
const themeDetails = useSelector(getThemeDetails);
|
||||
|
||||
const allUsersProfiles = React.useMemo(
|
||||
() =>
|
||||
allUsers.map(
|
||||
(user: { username: string; roleName: string; name: string }) => {
|
||||
const details = getInitialsAndColorCode(
|
||||
user.name || user.username,
|
||||
themeDetails.theme.colors.appCardColors,
|
||||
);
|
||||
return {
|
||||
...user,
|
||||
imageBackground: details[1],
|
||||
initials: details[0],
|
||||
};
|
||||
},
|
||||
),
|
||||
[allUsers, themeDetails],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isApplicationInvite && (
|
||||
<>
|
||||
<Divider />
|
||||
<OrgInviteTitle>Invite Users to {currentOrg?.name} </OrgInviteTitle>
|
||||
<OrgInviteTitle>
|
||||
<Text type={TextType.H5}>Invite Users to {currentOrg?.name} </Text>
|
||||
</OrgInviteTitle>
|
||||
</>
|
||||
)}
|
||||
<StyledForm
|
||||
|
|
@ -233,12 +287,6 @@ const OrgInviteUsersForm = (props: any) => {
|
|||
return inviteUsersToOrg({ ...values, orgId: props.orgId }, dispatch);
|
||||
})}
|
||||
>
|
||||
{submitSucceeded && (
|
||||
<FormMessage intent="primary" message={INVITE_USERS_SUBMIT_SUCCESS} />
|
||||
)}
|
||||
{submitFailed && error && (
|
||||
<FormMessage intent="danger" message={error} />
|
||||
)}
|
||||
<StyledInviteFieldGroup>
|
||||
<div className="wrapper">
|
||||
<TagListField
|
||||
|
|
@ -258,14 +306,14 @@ const OrgInviteUsersForm = (props: any) => {
|
|||
data-cy="t--invite-role-input"
|
||||
/>
|
||||
</div>
|
||||
<StyledButton
|
||||
<Button
|
||||
tag="button"
|
||||
className="t--invite-user-btn"
|
||||
disabled={!valid}
|
||||
text="Invite"
|
||||
filled
|
||||
intent="primary"
|
||||
loading={submitting && !(submitFailed && !anyTouched)}
|
||||
type="submit"
|
||||
size={Size.large}
|
||||
variant={Variant.info}
|
||||
isLoading={submitting && !(submitFailed && !anyTouched)}
|
||||
/>
|
||||
</StyledInviteFieldGroup>
|
||||
{isLoading ? (
|
||||
|
|
@ -286,27 +334,63 @@ const OrgInviteUsersForm = (props: any) => {
|
|||
</MailConfigContainer>
|
||||
)}
|
||||
<UserList style={{ justifyContent: "space-between" }}>
|
||||
{allUsers.map((user: { username: string; roleName: string }) => {
|
||||
return (
|
||||
<div className="user" key={user.username}>
|
||||
<div>{user.username}</div>
|
||||
<div>{user.roleName}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{allUsersProfiles.map(
|
||||
(user: {
|
||||
username: string;
|
||||
name: string;
|
||||
roleName: string;
|
||||
imageBackground: string;
|
||||
initials: string;
|
||||
}) => {
|
||||
return (
|
||||
<Fragment key={user.username}>
|
||||
<User>
|
||||
<UserInfo>
|
||||
<ProfileImage backgroundColor={user.imageBackground}>
|
||||
<Text type={TextType.H6} highlight>
|
||||
{user.initials}
|
||||
</Text>
|
||||
</ProfileImage>
|
||||
<UserName>
|
||||
<Text type={TextType.H5}>{user.name}</Text>
|
||||
<Text type={TextType.P2}>{user.username}</Text>
|
||||
</UserName>
|
||||
</UserInfo>
|
||||
<UserRole>
|
||||
<Text type={TextType.P1}>{user.roleName}</Text>
|
||||
</UserRole>
|
||||
</User>
|
||||
|
||||
<RoleDivider />
|
||||
</Fragment>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</UserList>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<ErrorBox message={submitSucceeded || submitFailed}>
|
||||
{submitSucceeded && (
|
||||
<Callout
|
||||
text={INVITE_USERS_SUBMIT_SUCCESS}
|
||||
variant={Variant.success}
|
||||
fill
|
||||
/>
|
||||
)}
|
||||
{submitFailed && error && (
|
||||
<Callout text={error} variant={Variant.danger} fill />
|
||||
)}
|
||||
</ErrorBox>
|
||||
{!pathRegex.test(currentPath) && canManage && (
|
||||
<Button
|
||||
<ManageUsers
|
||||
className="manageUsers"
|
||||
text="Manage Users"
|
||||
filled
|
||||
intent="primary"
|
||||
onClick={() => {
|
||||
history.push(`/org/${props.orgId}/settings/members`);
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Text type={TextType.H6}>MANAGE USERS</Text>
|
||||
<Icon name="manage" size={IconSize.XXS} />
|
||||
</ManageUsers>
|
||||
)}
|
||||
</StyledForm>
|
||||
</>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user