2020-08-18 06:40:11 +00:00
|
|
|
import React, { useState } from "react";
|
2019-11-21 10:52:49 +00:00
|
|
|
import styled from "styled-components";
|
2019-11-22 14:02:55 +00:00
|
|
|
import {
|
|
|
|
|
getApplicationViewerPageURL,
|
|
|
|
|
BUILDER_PAGE_URL,
|
|
|
|
|
} from "constants/routes";
|
2020-08-18 06:40:11 +00:00
|
|
|
import { Card, Classes } from "@blueprintjs/core";
|
2019-11-21 10:52:49 +00:00
|
|
|
import { ApplicationPayload } from "constants/ReduxActionConstants";
|
2020-01-20 08:07:00 +00:00
|
|
|
import Button from "components/editorComponents/Button";
|
2020-08-18 06:40:11 +00:00
|
|
|
import { theme, getColorWithOpacity } from "constants/DefaultTheme";
|
2019-11-21 10:52:49 +00:00
|
|
|
import ContextDropdown, {
|
|
|
|
|
ContextDropdownOption,
|
|
|
|
|
} from "components/editorComponents/ContextDropdown";
|
2020-04-03 11:08:38 +00:00
|
|
|
import { Colors } from "constants/Colors";
|
2020-05-27 13:36:06 +00:00
|
|
|
import {
|
|
|
|
|
isPermitted,
|
|
|
|
|
PERMISSION_TYPE,
|
|
|
|
|
} from "pages/Applications/permissionHelpers";
|
2020-08-18 06:40:11 +00:00
|
|
|
import { getInitialsAndColorCode, getColorCode } from "utils/AppsmithUtils";
|
|
|
|
|
import { ControlIcons } from "icons/ControlIcons";
|
|
|
|
|
import history from "utils/history";
|
|
|
|
|
|
|
|
|
|
const NameWrapper = styled.div<{
|
|
|
|
|
hasReadPermission: boolean;
|
|
|
|
|
showOverlay: boolean;
|
|
|
|
|
}>`
|
|
|
|
|
${props =>
|
|
|
|
|
props.showOverlay &&
|
|
|
|
|
`
|
|
|
|
|
{
|
|
|
|
|
background-color: white;
|
|
|
|
|
|
|
|
|
|
.overlay {
|
|
|
|
|
${props.hasReadPermission &&
|
|
|
|
|
`text-decoration: none;
|
|
|
|
|
&:after {
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
& .control {
|
|
|
|
|
display: block;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}`}
|
|
|
|
|
|
|
|
|
|
& div.image-container {
|
|
|
|
|
background: ${
|
|
|
|
|
props.hasReadPermission
|
|
|
|
|
? getColorWithOpacity(
|
|
|
|
|
props.theme.card.hoverBG,
|
|
|
|
|
props.theme.card.hoverBGOpacity,
|
|
|
|
|
)
|
|
|
|
|
: null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`}
|
|
|
|
|
border-radius: ${props => props.theme.radii[1]}px;
|
|
|
|
|
width: ${props => props.theme.card.minWidth + props.theme.spaces[5] * 2}px;
|
|
|
|
|
margin: ${props => props.theme.spaces[5]}px
|
|
|
|
|
${props => props.theme.spaces[5]}px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const Name = styled.div`
|
|
|
|
|
padding-left: ${props => props.theme.spaces[5]}px;
|
|
|
|
|
padding-right: ${props => props.theme.spaces[5]}px;
|
|
|
|
|
padding-bottom: ${props => props.theme.spaces[5]}px;
|
|
|
|
|
height: 45px;
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: ${Colors.OXFORD_BLUE};
|
|
|
|
|
line-height: 21px;
|
|
|
|
|
letter-spacing: 0.1px;
|
|
|
|
|
`;
|
2019-11-21 10:52:49 +00:00
|
|
|
|
2020-08-18 06:40:11 +00:00
|
|
|
const Wrapper = styled(Card)<{
|
|
|
|
|
hasReadPermission?: boolean;
|
|
|
|
|
backgroundColor: string;
|
|
|
|
|
}>`
|
2019-11-21 10:52:49 +00:00
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: ${props => props.theme.card.minWidth}px;
|
|
|
|
|
height: ${props => props.theme.card.minHeight}px;
|
|
|
|
|
position: relative;
|
|
|
|
|
border-radius: ${props => props.theme.radii[1]}px;
|
2020-08-18 06:40:11 +00:00
|
|
|
background-color: ${props => props.backgroundColor};
|
2019-11-21 10:52:49 +00:00
|
|
|
margin: ${props => props.theme.spaces[5]}px
|
|
|
|
|
${props => props.theme.spaces[5]}px;
|
2020-08-18 06:40:11 +00:00
|
|
|
.overlay {
|
2020-01-20 08:07:00 +00:00
|
|
|
display: block;
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 0;
|
2020-08-18 06:40:11 +00:00
|
|
|
height: 100%;
|
2020-01-20 08:07:00 +00:00
|
|
|
width: 100%;
|
2020-05-14 10:47:13 +00:00
|
|
|
${props => !props.hasReadPermission && `pointer-events: none;`}
|
2020-01-20 08:07:00 +00:00
|
|
|
}
|
2019-11-21 10:52:49 +00:00
|
|
|
`;
|
2020-08-18 06:40:11 +00:00
|
|
|
|
2019-11-21 10:52:49 +00:00
|
|
|
const ApplicationImage = styled.div`
|
2020-01-28 08:21:22 +00:00
|
|
|
&& {
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
& {
|
|
|
|
|
.control {
|
|
|
|
|
button {
|
|
|
|
|
span {
|
|
|
|
|
font-weight: ${props => props.theme.fontWeights[3]};
|
2020-08-18 06:40:11 +00:00
|
|
|
color: white;
|
2020-01-28 08:21:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-21 10:52:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2020-05-05 12:16:51 +00:00
|
|
|
const Control = styled.div<{ fixed?: boolean }>`
|
2019-11-21 10:52:49 +00:00
|
|
|
outline: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
2020-08-18 06:40:11 +00:00
|
|
|
|
|
|
|
|
.${Classes.BUTTON} {
|
|
|
|
|
margin-top: 7px;
|
|
|
|
|
|
|
|
|
|
div {
|
|
|
|
|
width: auto;
|
|
|
|
|
height: auto;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.${Classes.BUTTON_TEXT} {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.more {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: ${props => props.theme.spaces[6]}px;
|
|
|
|
|
top: ${props => props.theme.spaces[4]}px;
|
|
|
|
|
}
|
2019-11-21 10:52:49 +00:00
|
|
|
`;
|
|
|
|
|
|
2020-08-18 06:40:11 +00:00
|
|
|
const Initials = styled.span`
|
|
|
|
|
font-size: 40px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
margin: auto;
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const APPLICATION_CONTROL_FONTSIZE_INDEX = 5;
|
2019-11-21 10:52:49 +00:00
|
|
|
|
|
|
|
|
type ApplicationCardProps = {
|
|
|
|
|
application: ApplicationPayload;
|
2020-01-27 08:24:58 +00:00
|
|
|
duplicate?: (applicationId: string) => void;
|
|
|
|
|
share?: (applicationId: string) => void;
|
|
|
|
|
delete?: (applicationId: string) => void;
|
2019-11-21 10:52:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const ApplicationCard = (props: ApplicationCardProps) => {
|
2020-08-18 06:40:11 +00:00
|
|
|
const [showOverlay, setShowOverlay] = useState(false);
|
|
|
|
|
|
2020-05-27 13:36:06 +00:00
|
|
|
const hasEditPermission = isPermitted(
|
|
|
|
|
props.application?.userPermissions ?? [],
|
|
|
|
|
PERMISSION_TYPE.MANAGE_APPLICATION,
|
2020-05-14 10:47:13 +00:00
|
|
|
);
|
2020-05-27 13:36:06 +00:00
|
|
|
const hasReadPermission = isPermitted(
|
|
|
|
|
props.application?.userPermissions ?? [],
|
|
|
|
|
PERMISSION_TYPE.READ_APPLICATION,
|
2020-05-14 10:47:13 +00:00
|
|
|
);
|
2019-11-21 10:52:49 +00:00
|
|
|
const duplicateApp = () => {
|
2020-01-27 08:24:58 +00:00
|
|
|
props.duplicate && props.duplicate(props.application.id);
|
2019-11-21 10:52:49 +00:00
|
|
|
};
|
|
|
|
|
const shareApp = () => {
|
2020-01-27 08:24:58 +00:00
|
|
|
props.share && props.share(props.application.id);
|
2019-11-21 10:52:49 +00:00
|
|
|
};
|
|
|
|
|
const deleteApp = () => {
|
2020-01-27 08:24:58 +00:00
|
|
|
props.delete && props.delete(props.application.id);
|
2019-11-21 10:52:49 +00:00
|
|
|
};
|
2020-01-27 08:24:58 +00:00
|
|
|
const moreActionItems: ContextDropdownOption[] = [];
|
|
|
|
|
if (props.share) {
|
|
|
|
|
moreActionItems.push({
|
2019-11-21 10:52:49 +00:00
|
|
|
value: "share",
|
|
|
|
|
onSelect: shareApp,
|
|
|
|
|
label: "Share",
|
2020-01-27 08:24:58 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (props.duplicate) {
|
|
|
|
|
moreActionItems.push({
|
|
|
|
|
value: "duplicate",
|
|
|
|
|
onSelect: duplicateApp,
|
|
|
|
|
label: "Duplicate",
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-06-23 06:29:22 +00:00
|
|
|
if (props.delete && hasEditPermission) {
|
2020-01-27 08:24:58 +00:00
|
|
|
moreActionItems.push({
|
2019-11-21 10:52:49 +00:00
|
|
|
value: "delete",
|
|
|
|
|
onSelect: deleteApp,
|
|
|
|
|
label: "Delete",
|
|
|
|
|
intent: "danger",
|
2020-01-27 08:24:58 +00:00
|
|
|
});
|
|
|
|
|
}
|
2020-08-18 06:40:11 +00:00
|
|
|
let initials = getInitialsAndColorCode(props.application.name)[0];
|
|
|
|
|
|
|
|
|
|
if (initials.length < 2 && props.application.name.length > 1) {
|
|
|
|
|
initials += props.application.name[1].toUpperCase() || "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const colorCode = getColorCode(props.application.id);
|
2020-01-27 08:24:58 +00:00
|
|
|
|
2019-11-22 14:02:55 +00:00
|
|
|
const viewApplicationURL = getApplicationViewerPageURL(
|
|
|
|
|
props.application.id,
|
|
|
|
|
props.application.defaultPageId,
|
|
|
|
|
);
|
|
|
|
|
const editApplicationURL = BUILDER_PAGE_URL(
|
|
|
|
|
props.application.id,
|
|
|
|
|
props.application.defaultPageId,
|
|
|
|
|
);
|
2020-05-14 10:47:13 +00:00
|
|
|
|
2019-11-21 10:52:49 +00:00
|
|
|
return (
|
2020-08-18 06:40:11 +00:00
|
|
|
<NameWrapper
|
|
|
|
|
showOverlay={showOverlay}
|
|
|
|
|
onMouseEnter={() => setShowOverlay(true)}
|
|
|
|
|
onMouseLeave={() => setShowOverlay(false)}
|
|
|
|
|
hasReadPermission={hasReadPermission}
|
|
|
|
|
className="t--application-card"
|
|
|
|
|
>
|
|
|
|
|
<Wrapper
|
|
|
|
|
key={props.application.id}
|
|
|
|
|
hasReadPermission={hasReadPermission}
|
|
|
|
|
backgroundColor={colorCode}
|
|
|
|
|
>
|
|
|
|
|
<Initials>{initials}</Initials>
|
|
|
|
|
{showOverlay && (
|
|
|
|
|
<div className="overlay">
|
|
|
|
|
<ApplicationImage className="image-container">
|
|
|
|
|
<Control className="control">
|
|
|
|
|
{!!moreActionItems.length && (
|
|
|
|
|
<ContextDropdown
|
|
|
|
|
options={moreActionItems}
|
|
|
|
|
toggle={{
|
|
|
|
|
type: "icon",
|
|
|
|
|
icon: "MORE_HORIZONTAL_CONTROL",
|
|
|
|
|
iconSize:
|
|
|
|
|
theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
|
|
|
|
|
}}
|
|
|
|
|
className="more"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{hasEditPermission && (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => history.push(editApplicationURL)}
|
|
|
|
|
filled
|
|
|
|
|
text="EDIT"
|
|
|
|
|
intent="primary"
|
|
|
|
|
icon={
|
|
|
|
|
<ControlIcons.EDIT_WHITE
|
|
|
|
|
color={Colors.WHITE}
|
|
|
|
|
width={9}
|
|
|
|
|
height={9}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
className="t--application-edit-link"
|
|
|
|
|
fluid
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => history.push(viewApplicationURL)}
|
|
|
|
|
intent="none"
|
|
|
|
|
outline
|
|
|
|
|
fluid
|
|
|
|
|
text="LAUNCH"
|
|
|
|
|
icon={<ControlIcons.LAUNCH_CONTROL width={9} height={9} />}
|
|
|
|
|
size="small"
|
|
|
|
|
className="t--application-view-link"
|
|
|
|
|
/>
|
|
|
|
|
</Control>
|
|
|
|
|
</ApplicationImage>
|
|
|
|
|
</div>
|
2020-06-23 06:29:22 +00:00
|
|
|
)}
|
2020-08-18 06:40:11 +00:00
|
|
|
</Wrapper>
|
|
|
|
|
<Name>{props.application.name}</Name>
|
|
|
|
|
</NameWrapper>
|
2019-11-21 10:52:49 +00:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default ApplicationCard;
|