+ return resource || { username: '', uuid: props.uuid };
+ })(renderUsername);
+
+// Virtual machine resource
+
+const renderHostname = (item: { hostname: string }) =>
+ <Typography noWrap>{item.hostname}</Typography>;
+
+export const VirtualMachineHostname = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<VirtualMachinesResource>(props.uuid)(state.resources);
+ return resource || { hostname: '' };
+ })(renderHostname);
+
+const renderVirtualMachineLogin = (login: {user: string}) =>
+ <Typography noWrap>{login.user}</Typography>
+
+export const VirtualMachineLogin = connect(
+ (state: RootState, props: { linkUuid: string }) => {
+ const permission = getResource<LinkResource>(props.linkUuid)(state.resources);
+ const user = getResource<UserResource>(permission?.tailUuid || '')(state.resources);
+
+ return {user: user?.username || permission?.tailUuid || ''};
+ })(renderVirtualMachineLogin);
+
+// Common methods
+const renderCommonData = (data: string) =>
+ <Typography noWrap>{data}</Typography>;
+
+const renderCommonDate = (date: string) =>
+ <Typography noWrap>{formatDate(date)}</Typography>;
+
+export const CommonUuid = withResourceData('uuid', renderCommonData);
+
+// Api Client Authorizations
+export const TokenApiClientId = withResourceData('apiClientId', renderCommonData);
+
+export const TokenApiToken = withResourceData('apiToken', renderCommonData);
+
+export const TokenCreatedByIpAddress = withResourceData('createdByIpAddress', renderCommonDate);
+
+export const TokenDefaultOwnerUuid = withResourceData('defaultOwnerUuid', renderCommonData);
+
+export const TokenExpiresAt = withResourceData('expiresAt', renderCommonDate);
+
+export const TokenLastUsedAt = withResourceData('lastUsedAt', renderCommonDate);
+
+export const TokenLastUsedByIpAddress = withResourceData('lastUsedByIpAddress', renderCommonData);
+
+export const TokenScopes = withResourceData('scopes', renderCommonData);
+
+export const TokenUserId = withResourceData('userId', renderCommonData);
+
+const clusterColors = [
+ ['#f44336', '#fff'],
+ ['#2196f3', '#fff'],
+ ['#009688', '#fff'],
+ ['#cddc39', '#fff'],
+ ['#ff9800', '#fff']
+];
+
+export const ResourceCluster = (props: { uuid: string }) => {
+ const CLUSTER_ID_LENGTH = 5;
+ const pos = props.uuid.length > CLUSTER_ID_LENGTH ? props.uuid.indexOf('-') : 5;
+ const clusterId = pos >= CLUSTER_ID_LENGTH ? props.uuid.substring(0, pos) : '';
+ const ci = pos >= CLUSTER_ID_LENGTH ? (((((
+ (props.uuid.charCodeAt(0) * props.uuid.charCodeAt(1))
+ + props.uuid.charCodeAt(2))
+ * props.uuid.charCodeAt(3))
+ + props.uuid.charCodeAt(4))) % clusterColors.length) : 0;
+ return <span style={{
+ backgroundColor: clusterColors[ci][0],
+ color: clusterColors[ci][1],
+ padding: "2px 7px",
+ borderRadius: 3
+ }}>{clusterId}</span>;
+};
+
+// Links Resources
+const renderLinkName = (item: { name: string }) =>
+ <Typography noWrap>{item.name || '(none)'}</Typography>;
+
+export const ResourceLinkName = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ return resource || { name: '' };
+ })(renderLinkName);
+
+const renderLinkClass = (item: { linkClass: string }) =>
+ <Typography noWrap>{item.linkClass}</Typography>;
+
+export const ResourceLinkClass = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ return resource || { linkClass: '' };
+ })(renderLinkClass);
+
+const getResourceDisplayName = (resource: Resource): string => {
+ if ((resource as UserResource).kind === ResourceKind.USER
+ && typeof (resource as UserResource).firstName !== 'undefined') {
+ // We can be sure the resource is UserResource
+ return getUserDisplayName(resource as UserResource);
+ } else {
+ return (resource as GroupContentsResource).name;
+ }
+}
+
+const renderResourceLink = (dispatch: Dispatch, item: Resource) => {
+ var displayName = getResourceDisplayName(item);
+
+ return <Typography noWrap color="primary" style={{ 'cursor': 'pointer' }} onClick={() => dispatch<any>(navigateTo(item.uuid))}>
+ {resourceLabel(item.kind, item && item.kind === ResourceKind.GROUP ? (item as GroupResource).groupClass || '' : '')}: {displayName || item.uuid}
+ </Typography>;
+};
+
+export const ResourceLinkTail = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ const tailResource = getResource<Resource>(resource?.tailUuid || '')(state.resources);
+
+ return {
+ item: tailResource || { uuid: resource?.tailUuid || '', kind: resource?.tailKind || ResourceKind.NONE }
+ };
+ })((props: { item: Resource } & DispatchProp<any>) =>
+ renderResourceLink(props.dispatch, props.item));
+
+export const ResourceLinkHead = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ const headResource = getResource<Resource>(resource?.headUuid || '')(state.resources);
+
+ return {
+ item: headResource || { uuid: resource?.headUuid || '', kind: resource?.headKind || ResourceKind.NONE }
+ };
+ })((props: { item: Resource } & DispatchProp<any>) =>
+ renderResourceLink(props.dispatch, props.item));
+
+export const ResourceLinkUuid = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ return resource || { uuid: '' };
+ })(renderUuid);
+
+export const ResourceLinkHeadUuid = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const headResource = getResource<Resource>(link?.headUuid || '')(state.resources);
+
+ return headResource || { uuid: '' };
+ })(renderUuid);
+
+export const ResourceLinkTailUuid = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const tailResource = getResource<Resource>(link?.tailUuid || '')(state.resources);
+
+ return tailResource || { uuid: '' };
+ })(renderUuid);
+
+const renderLinkDelete = (dispatch: Dispatch, item: LinkResource, canManage: boolean) => {
+ if (item.uuid) {
+ return canManage ?
+ <Typography noWrap>
+ <IconButton data-cy="resource-delete-button" onClick={() => dispatch<any>(openRemoveGroupMemberDialog(item.uuid))}>
+ <RemoveIcon />
+ </IconButton>
+ </Typography> :
+ <Typography noWrap>
+ <IconButton disabled data-cy="resource-delete-button">
+ <RemoveIcon />
+ </IconButton>
+ </Typography>;
+ } else {
+ return <Typography noWrap></Typography>;
+ }
+}
+
+export const ResourceLinkDelete = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const isBuiltin = isBuiltinGroup(link?.headUuid || '') || isBuiltinGroup(link?.tailUuid || '');
+
+ return {
+ item: link || { uuid: '', kind: ResourceKind.NONE },
+ canManage: link && getResourceLinkCanManage(state, link) && !isBuiltin,
+ };
+ })((props: { item: LinkResource, canManage: boolean } & DispatchProp<any>) =>
+ renderLinkDelete(props.dispatch, props.item, props.canManage));
+
+export const ResourceLinkTailEmail = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<UserResource>(link?.tailUuid || '')(state.resources);
+
+ return resource || { email: '' };
+ })(renderEmail);
+
+export const ResourceLinkTailUsername = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<UserResource>(link?.tailUuid || '')(state.resources);
+