antd#Breadcrumb TypeScript Examples

The following examples show how to use antd#Breadcrumb. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Breadcrumbs.tsx    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
Breadcrumbs = (props: Props) => {
  const {screens, onBreadcrumbClickFactory, activeScreenId} = props;
 
  if(screens.length > 1) {
    return (
      <div className={styles['breadcrumbs-container']}>
        <Breadcrumb separator={">"}>
          {screens.map(({caption, id}) => {
            return (
              <Breadcrumb.Item
                key={id}
                className={`
                  ${styles['breadcrumb']}
                  ${id === activeScreenId ? styles['breadcrumb-active'] : ``}`
                }
                onClick={onBreadcrumbClickFactory(id)}
              >
                {caption}
              </Breadcrumb.Item>
            )
          })}
        </Breadcrumb>
      </div>
    )
  }
  return null;
}
Example #2
Source File: Bread.tsx    From react_admin with MIT License 6 votes vote down vote up
Breads: React.FC<{}> = () => {
  return (
    <div className="bread">
      <Breadcrumb>
        <Breadcrumb.Item href="">
          <HomeOutlined className="bread-icon" />
        </Breadcrumb.Item>
        <Breadcrumb.Item href="">
          <UserOutlined />
          <span>Application List</span>
        </Breadcrumb.Item>
        <Breadcrumb.Item>Application</Breadcrumb.Item>
      </Breadcrumb>
    </div>
  );
}
Example #3
Source File: AddressInformation.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
AddressInformation: React.FC = () => {
	const { addressId } = useValues(addressLogic);

	const { id }: any = useParams();

	const { data, isLoading, isError } = useAddressInformation(id, addressId);
	const { setAddressInformationToggle } = useActions(addressLogic);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setAddressInformationToggle(false);
					}}
				>
					Address Info
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Edit Address</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb} loading={isLoading} error={isError}>
			<AddressInformationForm data={data} />
		</Page>
	);
}
Example #4
Source File: CreateNewAddress.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
CreateNewAddress: React.FC = () => {
	const { setCreatNewAddressToggle } = useActions(addressLogic);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setCreatNewAddressToggle(false);
					}}
				>
					Address Info
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Create New Address</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb}>
			<CreateNewAddressForm />
		</Page>
	);
}
Example #5
Source File: ChartDrillPaths.tsx    From datart with Apache License 2.0 6 votes vote down vote up
ChartDrillPaths: FC<{}> = memo(() => {
  const { drillOption, onDrillOptionChange } = useContext(ChartDrillContext);

  if (!drillOption || drillOption.mode === DrillMode.Normal) {
    return <div></div>;
  }

  const drilledFields = drillOption.getDrilledFields();
  return (
    <StyledChartDrillPaths>
      <Breadcrumb>
        {drilledFields.map(f => {
          return (
            <StyledDrillNode
              key={f.uid}
              isActive={Boolean(
                drillOption?.getCurrentFields()?.some(df => df.uid === f.uid),
              )}
              onClick={() => {
                if (drillOption.mode === DrillMode.Drill) {
                  drillOption.drillUp(f);
                } else if (drillOption.mode === DrillMode.Expand) {
                  drillOption.expandUp(f);
                }
                onDrillOptionChange?.(drillOption);
              }}
            >
              {getColumnRenderName(f)}
            </StyledDrillNode>
          );
        })}
      </Breadcrumb>
    </StyledChartDrillPaths>
  );
})
Example #6
Source File: CreateNewDomainAlias.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
CreateNewDomainAlias: React.FC = () => {
	const breadcrum = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/domain-aliases'>Domain Aliases</Link>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Create New Address</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={breadcrum}>
			<CreateNewDomainAliasForm />
		</Page>
	);
}
Example #7
Source File: CreateNewForwardedAddress.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
CreateNewForwardedAddress: React.FC = () => {
	const breadcrum = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/forwarded-addresses'>Forwarded Addresses</Link>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Create new Forwarded Address</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={breadcrum}>
			<CreateNewForwardedAddressForm />
		</Page>
	);
}
Example #8
Source File: CreateNewUser.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
CreateNewUser: React.FC = () => {
	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/users'>Users</Link>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Add User</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb}>
			<CreateNewUserForm />
		</Page>
	);
}
Example #9
Source File: ForwardedAddressInformation.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
ForwardedAddressInformation: React.FC = () => {
	const { id }: any = useParams();

	const { data, isLoading, isError } = useForwardedAddressInformation(id);

	const formattedData = ForwardedAddressInfoFomatter(data);

	const breadcrum = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/forwarded-addresses'>Forwarded Addresses</Link>
			</Breadcrumb.Item>
			<Breadcrumb.Item>{_.get(data, 'address')}</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={breadcrum} loading={isLoading} error={isError}>
			<Tabs defaultActiveKey='update-forwarded-address-information'>
				<TabPane tab='Details' key='update-forwarded-address-information'>
					<ForwardedAddressInformationForm data={formattedData} />
				</TabPane>
				<TabPane tab='List Users' key='target-users-list'>
					<ListTarget data={formattedData} />
				</TabPane>
			</Tabs>
		</Page>
	);
}
Example #10
Source File: Messages.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
Messages: React.FC = () => {
	const { setUpdateMailboxToggle, setShowMailboxMessagesTable } = useActions(mailboxesLogic);
	const { mailboxName } = useValues(mailboxesLogic);
	const { messageSourceToggle, messageDetailsToggle } = useValues(messagesLogic);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setUpdateMailboxToggle(false);
						setShowMailboxMessagesTable(false);
					}}
				>
					Mailboxes
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>{mailboxName}</Breadcrumb.Item>
		</Breadcrumb>
	);

	if (messageDetailsToggle) {
		return <MessageDetails />;
	} else if (messageSourceToggle) {
		return <MessageSource />;
	} else {
		return (
			<Page title={pageBreadcrumb}>
				<MailboxMessages />
			</Page>
		);
	}
	// }
}
Example #11
Source File: ResolveId.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
ResolveId: React.FC = () => {
	const { Search } = Input;
	const { resolveId } = useActions(dkimLogic);
	const { domainId } = useValues(dkimLogic);
	const onSearch = (value: any) => {
		if (value.length > 0) {
			resolveId(value);
		}
	};
	const descriptionBox = (
		<Card>
			<Descriptions bordered>
				<Descriptions.Item label='ID'>{domainId}</Descriptions.Item>
			</Descriptions>
		</Card>
	);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/dkim'>DKIM</Link>
			</Breadcrumb.Item>

			<Breadcrumb.Item>Resolve DKIM ID</Breadcrumb.Item>
		</Breadcrumb>
	);
	return (
		<Page title={pageBreadcrumb}>
			<Search placeholder='Enter Alias' allowClear enterButton='Search' size='large' onSearch={onSearch} />
			{domainId.length > 0 && descriptionBox}
		</Page>
	);
}
Example #12
Source File: DendronBreadCrumb.tsx    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
export function DendronBreadCrumb(props: DendronCommonProps) {
  const { dendronRouter } = props;
  const { noteActive } = useNoteActive(dendronRouter.getActiveNoteId());
  // no breadcrumb for home page
  if (!verifyNoteData(props)) {
    return null;
  }
  if (
    !noteActive ||
    !verifyNoteData(props) ||
    noteActive.id === props.noteIndex.id
  ) {
    return null;
  }
  const noteParents = NoteUtils.getNoteWithParents({
    note: noteActive,
    notes: props.notes,
  });
  return (
    <Breadcrumb style={{ margin: "16px 0" }}>
      {_.map(noteParents, (note) => {
        const dest = getNoteUrl({note, noteIndex: props.noteIndex})
        return (
          <Breadcrumb.Item key={note.id}>
            <Link href={dest}>
              {note.title}
            </Link>
          </Breadcrumb.Item>
        )
      })}
    </Breadcrumb>
  );
}
Example #13
Source File: index.tsx    From nebula-studio with Apache License 2.0 6 votes vote down vote up
NebulaBreadcrumb: React.FC<IProps> = (props: IProps) => {
  const { routes, extraNode } = props;
  return (
    <PageHeader
      title={null}
      className={styles.studioBreadcrumb}
      breadcrumbRender={() => {
        return <div className={cls(styles.breadcrumbContainer, 'studioCenterLayout')}>
          <Breadcrumb
            className={styles.breadcrumb}
            routes={routes} 
            itemRender={itemRender} 
          />
          {extraNode}
        </div>;
      }}
    />
  );
}
Example #14
Source File: RenameDomain.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
RenameDomain: React.FC = () => {
	const { setRenameDomainToggle } = useActions(addressLogic);
	const { setForwardedAddressInfo, loading, error } = useActions(addressLogic);
	const [form] = Form.useForm();

	const { mutate } = useRenameDomain();

	const onFinishDomain = (values: { oldDomain: string; newDomain: string }) => {
		mutate(values);
		setRenameDomainToggle(true);
		setForwardedAddressInfo({});
	};

	const onResetDomain = () => {
		form.resetFields();
	};

	const breadcrum = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/forwarded-addresses' onClick={() => setRenameDomainToggle(true)}>
					Forwarded Addresses
				</Link>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Rename domain</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={breadcrum} loading={loading} error={error}>
			<Form
				layout={'inline'}
				name='basic'
				form={form}
				initialValues={{ remember: true }}
				onFinish={onFinishDomain}
			>
				<Form.Item
					label='Old Domain'
					name='oldDomain'
					tooltip={_.get(SAddress, 'RenameDomain.oldDomain')}
					rules={[{ required: true, message: 'Please input your old domain!' }]}
				>
					<Input placeholder='example.com' />
				</Form.Item>
				<Form.Item
					label='New Domain'
					name='newDomain'
					tooltip={_.get(SAddress, 'RenameDomain.newDomain')}
					rules={[{ required: true, message: 'Please input your new domain!' }]}
				>
					<Input placeholder='domain.in' />
				</Form.Item>
				<Form.Item>
					<Button htmlType='button' onClick={onResetDomain}>
						Reset
					</Button>
					<Button type='primary' htmlType='submit'>
						Submit
					</Button>
				</Form.Item>
			</Form>
		</Page>
	);
}
Example #15
Source File: DkimDetails.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
DkimDetails: React.SFC = () => {
	const { TabPane } = Tabs;
	const params: { id: string } = useParams();
	const { data, isLoading } = useDkimDetails(params.id);

	const DescriptionBox = () => {
		return (
			!isLoading && (
				<Descriptions size='small' bordered column={1}>
					<Descriptions.Item label='id'>{data.id}</Descriptions.Item>
					<Descriptions.Item label='domain'>{data.domain}</Descriptions.Item>
					<Descriptions.Item label='selector'>{data.selector}</Descriptions.Item>
					<Descriptions.Item label='description'>{data.description}</Descriptions.Item>
					<Descriptions.Item label='fingerprint'>{data.fingerprint}</Descriptions.Item>
					<Descriptions.Item label='DNS name'>{data.dnsTxt.name}</Descriptions.Item>
					<Descriptions.Item label=' DNS value'>{data.dnsTxt.value}</Descriptions.Item>
					<Descriptions.Item label=' created'>{data.created}</Descriptions.Item>
				</Descriptions>
			)
		);
	};

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/dkim'>DKIM</Link>
			</Breadcrumb.Item>

			<Breadcrumb.Item>{!isLoading && data.domain}</Breadcrumb.Item>
		</Breadcrumb>
	);
	return (
		<Page title={pageBreadcrumb} loading={isLoading}>
			<Tabs defaultActiveKey='descriptionBox'>
				<TabPane tab='Details' key='descriptionBox'>
					<DescriptionBox />
				</TabPane>
			</Tabs>
		</Page>
	);
}
Example #16
Source File: MessageSource.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
MessageSource: React.FC = () => {
	const { mailboxId, mailboxName } = useValues(mailboxesLogic);
	const { setUpdateMailboxToggle, setShowMailboxMessagesTable } = useActions(mailboxesLogic);

	const { setMessageSourceToggle } = useActions(messagesLogic);
	const { messageId } = useValues(messagesLogic);

	const { id }: any = useParams();

	const { data } = useMessageSource({ userId: id, mailboxId: mailboxId, messageNumber: messageId });

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setUpdateMailboxToggle(false);
						setShowMailboxMessagesTable(false);
					}}
				>
					Mailboxes
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setMessageSourceToggle(false);
					}}
				>
					{mailboxName}
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Message Source</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb}>
			<Card>
				<BraftEditor language='en' controls={[]} value={BraftEditor.createEditorState(data)} readOnly />
			</Card>
		</Page>
	);
}
Example #17
Source File: CreateDkim.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
CreateDkim: React.FC = () => {
	const [form] = Form.useForm();
	const { error } = useValues(dkimLogic);
	const { mutate, data, isSuccess } = useCreateDkim();

	const history = useHistory();

	if (isSuccess && !_.get(data, 'data.error')) {
		history.push(`${getBasePath()}/dkim`);
	}

	const onFinish = (values: UpdateDkimKeyRequest) => {
		mutate(values);
	};

	const reset = () => {
		form.resetFields();
	};

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<Link to='/dkim'>DKIM</Link>
			</Breadcrumb.Item>

			<Breadcrumb.Item>Create DKIM</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb} error={error}>
			<Form {...layout} form={form} onFinish={onFinish}>
				<Form.Item
					label='Domain'
					name='domain'
					rules={[
						{
							required: true,
							message: 'Please input Domain',
						},
					]}
					tooltip={dkimTooltip.domain}
				>
					<Input />
				</Form.Item>
				<Form.Item
					label='Selector'
					name='selector'
					rules={[
						{
							required: true,
							message: 'Please input Selector',
						},
					]}
				>
					<Input placeholder={dkimTooltip.selector} />
				</Form.Item>
				<Form.Item label='Description' name='description'>
					<Input placeholder={dkimTooltip.description} />
				</Form.Item>
				<Form.Item label='PrivateKey' name='privateKey' tooltip={dkimTooltip.privateKey}>
					<Input />
				</Form.Item>
				<Form.Item {...tailLayout}>
					<Space size='middle'>
						<Button type='primary' htmlType='submit'>
							Create
						</Button>
						<Button type='default' htmlType='button' onClick={reset}>
							Reset
						</Button>
					</Space>
				</Form.Item>
			</Form>
		</Page>
	);
}
Example #18
Source File: ChartDrillPaths.tsx    From datart with Apache License 2.0 5 votes vote down vote up
StyledDrillNode = styled(Breadcrumb.Item)<{ isActive: boolean }>`
  color: ${p => (p.isActive ? p.theme.primary : p.theme.normal)} !important;
  cursor: pointer;
  user-select: none;
`
Example #19
Source File: Layout.tsx    From nextjs-ant-design-typescript with MIT License 5 votes vote down vote up
AppLayout = (props: React.PropsWithChildren<Props>) => {
  const [isCollapsed, setIsCollapsed] = useState(false);

  const onChangeIsCollapsed = (isCollapsed: boolean) => {
    setIsCollapsed(isCollapsed);
  };

  const pathname = props.router.pathname;
  const pathsplit: string[] = pathname.split('/');
  const routes = routesMaker(pathsplit);

  return (
    <Layout style={{ minHeight: '100vh' }}>
      <Sider
        collapsible
        collapsed={isCollapsed}
        onCollapse={onChangeIsCollapsed}
      >
        <Link href="/menu1">
          <a>
            <div className="App-logo" />
          </a>
        </Link>
        <Menu
          theme="dark"
          defaultSelectedKeys={['/menu1']}
          selectedKeys={[pathsplit.pop()]}
          defaultOpenKeys={[pathsplit[1]]}
          mode="inline"
        >
          <Item key="menu1" icon={<DesktopOutlined />}>
            <Link href="/menu1">
              <a>Menu 1</a>
            </Link>
          </Item>
          <Item key="menu2" icon={<DashboardOutlined />}>
            <Link href="/menu2">
              <a>Menu 2</a>
            </Link>
          </Item>
          <SubMenu key="menu3" icon={<SettingOutlined />} title="Menu 3">
            <Item key="submenu1">
              <Link href="/menu3/submenu1">
                <a>Submenu 1</a>
              </Link>
            </Item>
            <Item key="submenu2">
              <Link href="/menu3/submenu2">
                <a>Submenu 2</a>
              </Link>
            </Item>
          </SubMenu>
        </Menu>
      </Sider>
      <Layout style={{ padding: '0 16px 16px' }}>
        <Breadcrumb
          style={{ margin: '16px 0' }}
          itemRender={itemRender}
          routes={routes}
        />
        <Content
          className="site-layout-background"
          style={{
            padding: 16,
            minHeight: 280,
            backgroundColor: '#ffffff',
          }}
        >
          {props.children}
        </Content>
      </Layout>
    </Layout>
  );
}
Example #20
Source File: breadcrumb.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
CP_Breadcrumb = (props: CP_BREADCRUMB.Props) => {
  const { data, operations, execOperation, props: configProps } = props;
  const { list = [] } = data || {};
  const { visible = true } = configProps || {};

  const onClickItem = (key: string) => {
    operations?.click && execOperation(operations.click, key);
  };

  if (!visible) return null;
  return (
    <Breadcrumb separator={<ErdaIcon className="align-middle text-xs" type="right" size="14px" />}>
      {map(list, (item, idx) => {
        if (item.menus) {
          const menu = (
            <Menu>
              {item.menus.map((menuItem) => (
                <Menu.Item>
                  <span className={cls} onClick={() => onClickItem(menuItem.key)}>
                    <Ellipsis title={menuItem.item}>{menuItem.item}</Ellipsis>
                  </span>
                </Menu.Item>
              ))}
            </Menu>
          );
          return (
            <Breadcrumb.Item key={item.activeKey} overlay={menu}>
              <span className="inline-block align-bottom" style={{ maxWidth: 140 }}>
                <Ellipsis title={item.item}>{item.item}</Ellipsis>
              </span>
            </Breadcrumb.Item>
          );
        }
        const [cls, onClick] = idx !== list.length - 1 ? ['cursor-pointer', () => onClickItem(item.key)] : ['', noop];
        return (
          <Breadcrumb.Item key={item.key}>
            <span className={`${cls} inline-block align-bottom`} onClick={onClick} style={{ maxWidth: 140 }}>
              <Ellipsis title={item.item}>{item.item}</Ellipsis>
            </span>
          </Breadcrumb.Item>
        );
      })}
    </Breadcrumb>
  );
}
Example #21
Source File: AppBarBreadcrumb.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function AppBarBreadcrumb(
  props: BasicBreadcrumbProps
): React.ReactElement {
  const { currentApp, previousWorkspace } = useRecentApps();
  const { items: breadcrumbItems } = currentApp?.breadcrumb || {};

  const handleGoBackPreviousWorkspace = (): void => {
    getRuntime().popWorkspaceStack();
    getHistory().push(previousWorkspace.url);
  };

  return (
    <div className={classnames(styles.breadcrumbContainer, props.className)}>
      {previousWorkspace && (
        <a
          role="button"
          className={styles.workspaceButton}
          onClick={handleGoBackPreviousWorkspace}
        >
          <GeneralIcon icon={{ lib: "fa", icon: "reply" }} />
          <span style={{ marginLeft: 7 }}>
            {previousWorkspace.appLocaleName}
          </span>
        </a>
      )}
      <Breadcrumb separator={props.separator || ">"}>
        {breadcrumbItems &&
          breadcrumbItems.map((item: BreadcrumbItemConf, index: number) => {
            return (
              <Breadcrumb.Item key={index}>
                {index === 0 && <HomeOutlined />}
                {item.to ? <Link to={item.to}>{item.text}</Link> : item.text}
              </Breadcrumb.Item>
            );
          })}
        {currentApp && !props.noCurrentApp ? (
          <Breadcrumb.Item>
            {!breadcrumbItems?.length &&
              (props.showCurrentAppIcon ? (
                <GeneralIcon icon={currentApp.menuIcon} />
              ) : (
                <HomeOutlined />
              ))}
            <span>
              {props.breadcrumb &&
              props.breadcrumb.length > 0 &&
              currentApp.homepage &&
              !currentApp.internal ? (
                <Link to={currentApp.homepage}>{currentApp.localeName}</Link>
              ) : (
                currentApp.localeName
              )}
            </span>
          </Breadcrumb.Item>
        ) : null}
        {props.breadcrumb &&
          props.breadcrumb.map((item, index) => (
            <Breadcrumb.Item key={String(index)}>
              {item.to ? <Link to={item.to}>{item.text}</Link> : item.text}
            </Breadcrumb.Item>
          ))}
      </Breadcrumb>
    </div>
  );
}
Example #22
Source File: index.tsx    From spotify-recently-played-readme with MIT License 5 votes vote down vote up
export default function Home(): JSX.Element {
    const router = useRouter();
    const [currentUser, setCurrentUser] = useState<string | undefined>(undefined);
    const error = router.query['error'];

    useEffect(() => {
        // Read 'spotifyuser' cookie
        const user = Cookie.get('spotifyuser');
        if (user) {
            setCurrentUser(user);
        }
    });

    const handleClearCreds = () => {
        Cookie.remove('spotifyuser');
        window.location.reload();
    };

    return (
        <div className="container">
            <Head>
                <title>Spotify Recently Played README Generator</title>
                <link rel="icon" href="/favicon.ico" />
            </Head>

            <Breadcrumb separator=">" style={{ marginBottom: 25 }}>
                <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
            </Breadcrumb>

            <div>
                <Title level={2}>Spotify Recently Played README Generator</Title>
                {error && <Alert message="Error" description={error} type="error" style={{ marginBottom: 18 }} />}

                {!currentUser ? (
                    <Space className="vert-space" direction="vertical" size="middle">
                        <Text>Get started by authorizing the app below.</Text>
                        <SpotifyAuthButton clientId={ClientId} redirectUri={RedirectUri} />
                    </Space>
                ) : (
                    <Space className="vert-space" direction="vertical" size="middle">
                        <MarkdownSnippet username={currentUser} />
                        <SpotifyAuthButton clientId={ClientId} redirectUri={RedirectUri} label="Re-authorize" />
                        <Button type="link" danger onClick={handleClearCreds}>
                            Clear local credentials
                        </Button>
                    </Space>
                )}
            </div>
        </div>
    );
}
Example #23
Source File: pdf.tsx    From condo with MIT License 4 votes vote down vote up
PdfView = () => {
    const intl = useIntl()
    const ServerErrorMessage = intl.formatMessage({ id: 'ServerError' })
    const TicketInfoMessage = intl.formatMessage({ id: 'Problem' })
    const TicketAuthorMessage = intl.formatMessage({ id: 'Author' })
    const ClientInfoMessage = intl.formatMessage({ id: 'ClientInfo' })
    const AddressMessage = intl.formatMessage({ id: 'field.Address' })
    const FullNameMessage = intl.formatMessage({ id: 'field.FullName' })
    const SourceMessage = intl.formatMessage({ id: 'pages.condo.ticket.field.Source' })
    const ExecutorMessage = intl.formatMessage({ id: 'field.Executor' })
    const ClassifierMessage = intl.formatMessage({ id: 'Classifier' })
    const AssigneeMessage = intl.formatMessage({ id: 'field.Responsible' })
    const NotesMessage = intl.formatMessage({ id: 'pages.condo.ticket.id.Notes' })
    const EmergencyMessage = intl.formatMessage({ id: 'Emergency' })
    const WarrantyMessage = intl.formatMessage({ id: 'Warranty' })
    const PaidMessage = intl.formatMessage({ id: 'Paid' }).toLowerCase()
    const ShortFlatNumber = intl.formatMessage({ id: 'field.ShortFlatNumber' })
    const SectionName = intl.formatMessage({ id: 'pages.condo.property.section.Name' })
    const FloorName = intl.formatMessage({ id: 'pages.condo.property.floor.Name' })

    const containerRef = useRef(null)

    const router = useRouter()
    // NOTE: cast `string | string[]` to `string`
    const { query: { id } } = router as { query: { [key: string]: string } }

    const { loading: ticketIsLoading, obj: ticket, error } = Ticket.useObject({ where: { id:id } })

    const ticketOrganizationId = get(ticket, ['organization', 'id'], null)
    const ticketExecutorUserId = get(ticket, ['executor', 'id'], null)
    const ticketAssigneeUserId = get(ticket, ['assignee', 'id'], null)

    const { loading: executorIsLoading, obj: executor } = OrganizationEmployee.useObject({
        where: {
            organization: {
                id: ticketOrganizationId,
            },
            user: {
                id: ticketExecutorUserId,
            },
        },
    })

    const { loading: assigneeIsLoading, obj: assignee } = OrganizationEmployee.useObject({
        where: {
            organization: {
                id: ticketOrganizationId,
            },
            user: {
                id: ticketAssigneeUserId,
            },
        },
    })
    const loading = ticketIsLoading || assigneeIsLoading || executorIsLoading

    useEffect(() => {
        if (ticket && !loading) {
            // TODO: (savelevmatthew) let user choose format?
            createPdf({ element: containerRef.current, fileName: getTicketPdfName(intl, ticket), format: 'a5' }).catch((e) => {
                notification.error({
                    message: intl.formatMessage(({ id: 'errors.PdfGenerationError' })),
                    description: e.message,
                })
            })
        }
    }, [loading])

    const TicketTitleMessage = getTicketTitleMessage(intl, ticket)

    if (error || loading || !ticket) {
        return (
            <LoadingOrErrorPage title={TicketTitleMessage} loading={loading} error={error ? ServerErrorMessage : null}/>
        )
    }

    const TicketCreationDate = getTicketCreateMessage(intl, ticket)
    const ticketAddress = get(ticket, ['property', 'address'], ticket.propertyAddress)
        + (ticket.sectionName && ticket.floorName ? `, ${SectionName} ${ticket.sectionName}, ${FloorName} ${ticket.floorName}` : '')
        + (ticket.unitName ? `, ${ShortFlatNumber} ${ticket.unitName}` : '')
    const isEmergency = get(ticket, 'isEmergency')
    const isWarranty = get(ticket, 'isWarranty')
    const isPaid = get(ticket, 'isPaid')
    return (
        <Row gutter={[12, 40]} style={{ filter: 'grayscale(1)', maxWidth: '800px', padding: '40px' }} ref={containerRef}>
            <Col span={24}>
                <Row align={'top'}>
                    <Col span={16}>
                        <Typography.Title level={1} style={{ margin: '0 0 16px', whiteSpace: 'pre-line' }} >
                            {`${TicketTitleMessage}
                                ${String(getTicketLabel(intl, ticket)).toLowerCase()}`}
                        </Typography.Title>
                        <Typography.Text>
                            <Typography.Text>{TicketCreationDate}, {TicketAuthorMessage} </Typography.Text>
                            <Typography.Text ellipsis>{get(ticket, ['createdBy', 'name'])}</Typography.Text>
                        </Typography.Text>
                    </Col>
                    <Col span={8}>
                        {isEmergency && (<Typography.Title level={2}>{EmergencyMessage.toLowerCase()}</Typography.Title>)}
                        {isWarranty && (<Typography.Title style={{ marginTop: '0' }} level={2}>{WarrantyMessage.toLowerCase()}</Typography.Title>)}
                        {isPaid && (<Typography.Title style={{ marginTop: '0' }} level={2}>{PaidMessage.toLowerCase()}</Typography.Title>)}
                    </Col>
                </Row>
            </Col>
            <Col span={24}>
                <Row>
                    <Col span={6}>
                        <TicketDescriptionField
                            title={SourceMessage}
                            value={get(ticket, ['source', 'name'])}
                        />
                    </Col>
                    <Col span={6}>
                        <TicketDescriptionField
                            title={ClassifierMessage}
                            value={
                                <Breadcrumb separator={<>≫<br/></>}>
                                    {
                                        compact([
                                            get(ticket, ['placeClassifier', 'name']),
                                            get(ticket, ['categoryClassifier', 'name']),
                                            get(ticket, ['problemClassifier', 'name']),
                                        ]).map(name => {
                                            return (
                                                <Breadcrumb.Item key={name}>{name}</Breadcrumb.Item>
                                            )
                                        })
                                    }
                                </Breadcrumb>
                            }
                        />
                    </Col>
                    <Col span={6}>
                        <TicketUserInfoField
                            title={ExecutorMessage}
                            user={executor}
                        />
                    </Col>
                    <Col span={6}>
                        <TicketUserInfoField
                            title={AssigneeMessage}
                            user={assignee}
                        />
                    </Col>
                </Row>
            </Col>
            <FocusContainer color={colors.black}>
                <Col span={24}>
                    <Row gutter={[0, 24]}>
                        <Col span={24}>
                            <Typography.Title level={5}>{ClientInfoMessage}</Typography.Title>
                        </Col>
                        <Col span={24}>
                            <Row gutter={[12, 12]}>
                                <Col span={12}>
                                    <TicketDescriptionField
                                        title={AddressMessage}
                                        value={ticketAddress}
                                    />
                                </Col>
                                <Col span={12}>
                                    <TicketUserInfoField
                                        title={FullNameMessage}
                                        user={{
                                            name: ticket.clientName,
                                            phone: ticket.clientPhone,
                                        }}
                                    />
                                </Col>
                            </Row>
                        </Col>
                        <Col span={24}>
                            <Typography.Title level={5}>{TicketInfoMessage}</Typography.Title>
                        </Col>
                        <Col span={24}>
                            <Typography.Text style={{ fontSize: '24px' }}>{ticket.details}</Typography.Text>
                        </Col>
                    </Row>
                </Col>
            </FocusContainer>
            <Col span={24}>
                <Typography.Title level={5}>
                    {NotesMessage}
                </Typography.Title>
            </Col>
            <Col span={24} id={'pdfLineInput'}/>
        </Row>
    )
}
Example #24
Source File: index.tsx    From condo with MIT License 4 votes vote down vote up
TicketContent = ({ ticket }) => {
    const intl = useIntl()
    const TicketInfoMessage = intl.formatMessage({ id: 'Problem' })
    const AddressMessage = intl.formatMessage({ id: 'field.Address' })
    const ResidentClientMessage = intl.formatMessage({ id: 'pages.condo.ticket.field.ResidentClient' })
    const NotResidentClientMessage = intl.formatMessage({ id: 'pages.condo.ticket.field.NotResidentClient' })
    const FilesFieldLabel = intl.formatMessage({ id: 'pages.condo.ticket.field.Files' })
    const ExecutorMessage = intl.formatMessage({ id: 'field.Executor' })
    const ClassifierMessage = intl.formatMessage({ id: 'Classifier' })
    const AssigneeMessage = intl.formatMessage({ id: 'field.Responsible' })
    const DeletedMessage = intl.formatMessage({ id: 'Deleted' })
    const SectionName = intl.formatMessage({ id: 'pages.condo.property.section.Name' })
    const FloorName = intl.formatMessage({ id: 'pages.condo.property.floor.Name' })
    const Deadline = intl.formatMessage({ id: 'ticket.deadline.CompleteBefore' })
    const ToCompleteMessage = intl.formatMessage({ id: 'ticket.deadline.ToComplete' }).toLowerCase()
    const LessThenDayMessage = intl.formatMessage({ id: 'ticket.deadline.LessThenDay' }).toLowerCase()
    const OverdueMessage = intl.formatMessage({ id: 'ticket.deadline.Overdue' }).toLowerCase()
    const UnitTypePrefix = intl.formatMessage({ id: `pages.condo.ticket.field.unitType.${ticket.unitType}` })
    const ReviewValueMessage = intl.formatMessage({ id: 'ticket.reviewValue' })
    const ReviewWithoutCommentMessage = intl.formatMessage({ id: 'ticket.reviewComment.withoutComment' })
    const NoReviewMessage = intl.formatMessage({ id: 'ticket.reviewValue.noReview' })

    const propertyWasDeleted = !(ticket.property)
    const ticketDeadline = ticket.deadline ? dayjs(ticket.deadline) : null
    const ticketUnit = ticket.unitName ? `${UnitTypePrefix.toLowerCase()} ${ticket.unitName}` : ''
    const ticketSectionAndFloor = ticket.sectionName && ticket.floorName
        ? `(${SectionName.toLowerCase()} ${ticket.sectionName}, ${FloorName.toLowerCase()} ${ticket.floorName})`
        : ''

    const ticketReviewValue = ticket.reviewValue
    const ticketReviewComment = ticket.reviewComment
    const reviewValueToText = useMemo(() => ({
        [REVIEW_VALUES.BAD]: `${getReviewMessageByValue(REVIEW_VALUES.BAD, intl)} ?`,
        [REVIEW_VALUES.GOOD]: `${getReviewMessageByValue(REVIEW_VALUES.GOOD, intl)} ?`,
    }), [intl])

    const { objs: files } = TicketFile.useObjects({
        where: { ticket: { id: ticket ? ticket.id : null } },
    }, {
        fetchPolicy: 'network-only',
    })

    const ticketOrganizationId = get(ticket, ['organization', 'id'], null)
    const ticketExecutorUserId = get(ticket, ['executor', 'id'], null)
    const ticketAssigneeUserId = get(ticket, ['assignee', 'id'], null)

    const { obj: executor } = OrganizationEmployee.useObject({
        where: {
            organization: {
                id: ticketOrganizationId,
            },
            user: {
                id: ticketExecutorUserId,
            },
        },
    })

    const { obj: assignee } = OrganizationEmployee.useObject({
        where: {
            organization: {
                id: ticketOrganizationId,
            },
            user: {
                id: ticketAssigneeUserId,
            },
        },
    })

    const getTicketDeadlineMessage = useCallback(() => {
        if (!ticketDeadline) return

        const deadlineType = getDeadlineType(ticket)
        const { moreThanDayDiff, overdueDiff } = getHumanizeDeadlineDateDifference(ticket)

        switch (deadlineType) {
            case TicketDeadlineType.MORE_THAN_DAY: {
                return (
                    <Typography.Text type={'warning'} strong>
                        ({ToCompleteMessage.replace('{days}', moreThanDayDiff)})
                    </Typography.Text>
                )
            }
            case TicketDeadlineType.LESS_THAN_DAY: {
                return (
                    <Typography.Text type={'warning'} strong>
                        ({LessThenDayMessage})
                    </Typography.Text>
                )
            }
            case TicketDeadlineType.OVERDUE: {
                return (
                    <Typography.Text type={'danger'} strong>
                        ({OverdueMessage.replace('{days}', overdueDiff)})
                    </Typography.Text>
                )
            }
        }

    }, [LessThenDayMessage, OverdueMessage, ToCompleteMessage, ticket, ticketDeadline])

    const ticketClassifierNames = useMemo(() => compact([
        get(ticket, ['placeClassifier', 'name']),
        get(ticket, ['categoryClassifier', 'name']),
        get(ticket, ['problemClassifier', 'name']),
    ]), [ticket])

    const overdueMessage = useMemo(() => getTicketDeadlineMessage(),
        [getTicketDeadlineMessage])

    const getClassifierTextType = useCallback(
        (index: number): BaseType => index !== ticketClassifierNames.length - 1 ? null : 'secondary',
        [ticketClassifierNames.length])

    const address = get(ticket, ['property', 'address'], ticket.propertyAddress)
    const addressMeta = get(ticket, ['property', 'addressMeta'], ticket.propertyAddressMeta)
    const { streetPart, renderPostfix } = getAddressDetails({ address, addressMeta })

    const TicketUnitMessage = useCallback(() => (
        <Typography.Paragraph style={{ margin: 0 }}>
            <Typography.Text strong>{ticketUnit}&nbsp;</Typography.Text>
            <Typography.Text>{ticketSectionAndFloor}</Typography.Text>
        </Typography.Paragraph>
    ), [ticketSectionAndFloor, ticketUnit])

    const DeletedPropertyAddressMessage = useCallback(() => (
        <>
            <Typography.Paragraph style={{ margin: 0 }} type={'secondary'}>
                {renderPostfix}
            </Typography.Paragraph>
            <Typography.Paragraph style={{ margin: 0 }} type={'secondary'}>
                {streetPart}
            </Typography.Paragraph>
            {
                ticketUnit && (
                    <Typography.Text type={'secondary'}>
                        <TicketUnitMessage />
                    </Typography.Text>

                )
            }
            <Typography.Text type={'secondary'}>
                ({ DeletedMessage })
            </Typography.Text>
        </>
    ), [DeletedMessage, TicketUnitMessage, renderPostfix, streetPart, ticketUnit])

    const PropertyAddressMessage = useCallback(() => (
        <>
            <Typography.Paragraph style={{ margin: 0 }} type={'secondary'}>
                {renderPostfix}
            </Typography.Paragraph>
            <Link href={`/property/${get(ticket, ['property', 'id'])}`}>
                <Typography.Link style={TICKET_CARD_LINK_STYLE}>
                    {streetPart}
                </Typography.Link>
            </Link>
            {ticketUnit && <TicketUnitMessage />}
        </>
    ), [TicketUnitMessage, renderPostfix, streetPart, ticket, ticketUnit])

    const contactId = get(ticket, ['contact', 'id'])
    const ClientMessage = useMemo(() => contactId ? ResidentClientMessage : NotResidentClientMessage,
        [NotResidentClientMessage, ResidentClientMessage, contactId])

    return (
        <Col span={24}>
            <Row gutter={[0, 40]}>
                <Col span={24}>
                    <Row gutter={[0, 24]}>
                        {
                            ticket.status.type === CLOSED_STATUS_TYPE ? (
                                <PageFieldRow title={ReviewValueMessage}>
                                    <Typography.Text>
                                        {
                                            ticketReviewValue ? (
                                                <>
                                                    {reviewValueToText[ticketReviewValue]}&nbsp;
                                                    <Typography.Text type={'secondary'}>
                                                        ({ticketReviewComment ? ticketReviewComment.replace(';', ',') : ReviewWithoutCommentMessage})
                                                    </Typography.Text>
                                                </>
                                            ) : (
                                                <Typography.Text type={'secondary'}>
                                                    {NoReviewMessage}
                                                </Typography.Text>
                                            )
                                        }
                                    </Typography.Text>
                                </PageFieldRow>
                            ) : null
                        }
                        {
                            ticketDeadline ? (
                                <PageFieldRow title={Deadline}>
                                    <Typography.Text strong> {dayjs(ticketDeadline).format('DD MMMM YYYY')} </Typography.Text>
                                    {overdueMessage}
                                </PageFieldRow>
                            ) : null
                        }
                        <PageFieldRow title={AddressMessage} highlight>
                            {
                                propertyWasDeleted ? (
                                    <DeletedPropertyAddressMessage />
                                ) : <PropertyAddressMessage />
                            }
                        </PageFieldRow>
                        <PageFieldRow title={ClientMessage} highlight>
                            {
                                contactId
                                    ? <Link href={`/contact/${contactId}`}>
                                        <Typography.Link style={TICKET_CARD_LINK_STYLE}>
                                            <TicketUserInfoField
                                                user={{
                                                    name: get(ticket, ['contact', 'name']),
                                                    phone: get(ticket, ['contact', 'phone']),
                                                }}
                                            />
                                        </Typography.Link>
                                    </Link>
                                    : <Typography.Text>
                                        <TicketUserInfoField
                                            user={{
                                                name: get(ticket, 'clientName'),
                                                phone: get(ticket, 'clientPhone'),
                                            }}
                                        />
                                    </Typography.Text>
                            }
                        </PageFieldRow>
                        <PageFieldRow title={TicketInfoMessage}>
                            {ticket.details}
                        </PageFieldRow>
                        {!isEmpty(files) && (
                            <PageFieldRow title={FilesFieldLabel}>
                                <TicketFileList files={files} />
                            </PageFieldRow>
                        )}
                    </Row>
                </Col>
                <Col span={24}>
                    <Row gutter={[0, 24]}>
                        <PageFieldRow title={ClassifierMessage}>
                            <Breadcrumb separator="»">
                                {
                                    ticketClassifierNames.map((name, index) => {
                                        return (
                                            <Breadcrumb.Item key={name}>
                                                <Typography.Text
                                                    style={CLASSIFIER_VALUE_STYLE}
                                                    strong
                                                    type={getClassifierTextType(index)}
                                                >
                                                    {name}
                                                </Typography.Text>
                                            </Breadcrumb.Item>
                                        )
                                    })
                                }
                            </Breadcrumb>
                        </PageFieldRow>
                        <PageFieldRow title={ExecutorMessage}>
                            <Link href={`/employee/${get(executor, 'id')}`}>
                                <Typography.Link style={TICKET_CARD_LINK_STYLE}>
                                    <Typography.Text strong>
                                        <TicketUserInfoField user={{
                                            name: get(executor, 'name'),
                                            phone: get(executor, 'phone'),
                                            email: get(executor, 'email'),
                                        }}/>
                                    </Typography.Text>
                                </Typography.Link>
                            </Link>
                        </PageFieldRow>
                        <PageFieldRow title={AssigneeMessage}>
                            <Link href={`/employee/${get(assignee, 'id')}`}>
                                <Typography.Link style={TICKET_CARD_LINK_STYLE}>
                                    <Typography.Text strong>
                                        <TicketUserInfoField user={{
                                            name: get(assignee, 'name'),
                                            phone: get(assignee, 'phone'),
                                            email: get(assignee, 'email'),
                                        }}/>
                                    </Typography.Text>
                                </Typography.Link>
                            </Link>
                        </PageFieldRow>
                    </Row>
                </Col>
            </Row>
        </Col>
    )
}
Example #25
Source File: index.tsx    From Aragorn with MIT License 4 votes vote down vote up
FileManage = () => {
  const {
    state: {
      uploaderProfiles,
      configuration: { defaultUploaderProfileId }
    }
  } = useAppContext();

  const [windowHeight, setWindowHeight] = useState(window.innerHeight);

  useEffect(() => {
    function handleResize() {
      setWindowHeight(window.innerHeight);
    }
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  const { id } = useParams<{ id: string }>();

  const [hasFileManageFeature, setHasFileManageFeature] = useState(false);

  const [uploaderProfile, setUploaderProfile] = useState({} as UploaderProfile);

  useEffect(() => {
    const currentId = id || defaultUploaderProfileId;
    setCurrentProfile(currentId as string);
  }, []);

  useEffect(() => {
    if (uploaderProfile?.id) {
      getList();
    }
  }, [uploaderProfile]);

  const [list, setList] = useState([] as ListFile[]);
  const [listLoading, setListLoading] = useState(false);

  const getList = (directoryPath?: string) => {
    setListLoading(true);
    ipcRenderer.send('file-list-get', uploaderProfile.id, directoryPath);
  };

  const [dirPath, setDirPath] = useState([] as string[]);

  useEffect(() => {
    function handleListGetReply(_, res?: FileListResponse) {
      setListLoading(false);
      if (res === undefined) {
        setHasFileManageFeature(false);
        setList([]);
        message.info(`${uploaderProfile.uploaderName}暂不支持文件管理功能`);
        return;
      }
      setHasFileManageFeature(true);
      if (res.success) {
        setList(res.data);
      } else {
        message.error(`文件列表获取失败 ${res.desc || ''}`);
      }
    }

    function handleFileDeleteReply(_, res?: DeleteFileResponse) {
      if (res === undefined) {
        return;
      }
      if (res.success) {
        message.success({ content: '文件删除成功', key: 'file-manage-delete' });
        getList(dirPath.join('/'));
      } else {
        message.error({ content: `文件删除失败 ${res.desc || ''}`, key: 'file-manage-delete' });
      }
    }

    function handleFileUploadReply() {
      getList(dirPath.join('/'));
    }

    function handleDirectoryCreateReply(_, res?: CreateDirectoryResponse) {
      if (res === undefined) {
        return;
      }
      if (res.success) {
        message.success('目录创建成功');
        setModalVisible(false);
        getList(dirPath.join('/'));
      } else {
        message.error(`目录创建失败 ${res.desc || ''}`);
      }
    }

    function handleExportReplay(_, res) {
      setExportLoading(false);
      if (res) {
        shell.showItemInFolder(res);
        setRowKeys([]);
        setSelectRows([]);
      }
    }

    ipcRenderer.on('file-list-get-reply', handleListGetReply);
    ipcRenderer.on('file-delete-reply', handleFileDeleteReply);
    ipcRenderer.on('file-upload-reply', handleFileUploadReply);
    ipcRenderer.on('directory-create-reply', handleDirectoryCreateReply);
    ipcRenderer.on('export-reply', handleExportReplay);

    return () => {
      ipcRenderer.removeListener('file-list-get-reply', handleListGetReply);
      ipcRenderer.removeListener('file-delete-reply', handleFileDeleteReply);
      ipcRenderer.removeListener('file-upload-reply', handleFileUploadReply);
      ipcRenderer.removeListener('directory-create-reply', handleDirectoryCreateReply);
      ipcRenderer.removeListener('export-reply', handleExportReplay);
    };
  }, [uploaderProfile, dirPath]);

  const handleNameClick = (record: ListFile) => {
    if (record.type === 'directory') {
      const newPath = [...dirPath, formatFileName(record.name)];
      setDirPath(newPath);
      getList(newPath.join('/'));
    } else {
      clipboard.writeText(record.url as string);
      message.success('链接已复制到粘贴板');
    }
  };

  const handlePathClick = (index: number) => {
    if (index === -1) {
      setDirPath([]);
      getList();
    } else {
      const newPath = dirPath.slice(0, index + 1);
      setDirPath(newPath);
      getList(newPath.join('/'));
    }
  };

  const setCurrentProfile = (uploaderProfileId: string) => {
    setDirPath([]);
    const uploaderProfile = uploaderProfiles.find(item => item.id === uploaderProfileId);
    setUploaderProfile(uploaderProfile as UploaderProfile);
  };

  const formatFileName = (name: string) => {
    if (dirPath.length > 0) {
      const pathPrefix = dirPath.join('/') + '/';
      return name.split(pathPrefix).pop() || '';
    } else {
      return name;
    }
  };

  const [selectRowKeys, setRowKeys] = useState([] as string[]);
  const [selectRows, setSelectRows] = useState([] as ListFile[]);

  const handleTableRowChange = (selectedRowKeys, selectedRows: ListFile[]) => {
    setRowKeys(selectedRowKeys);
    setSelectRows(selectedRows);
  };

  const handleRefresh = () => {
    getList(dirPath.join('/'));
  };

  const handleBatchDelete = () => {
    Modal.confirm({
      title: '确认删除',
      onOk: () => {
        const names = selectRows.map(item => [...dirPath, formatFileName(item.name)].join('/'));
        message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
        ipcRenderer.send('file-delete', uploaderProfile.id, names);
      }
    });
  };

  const handleDelete = (record: ListFile) => {
    let name = record.name;
    Modal.confirm({
      title: '确认删除',
      content: name,
      onOk: () => {
        let name = record.name;
        if (record.type === 'directory') {
          name = `${[...dirPath, record.name].join('/')}/`;
        } else {
          name = [...dirPath, formatFileName(record.name)].join('/');
        }
        message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
        ipcRenderer.send('file-delete', uploaderProfile.id, [name]);
      }
    });
  };

  const uploadRef = useRef<HTMLInputElement>(null);

  const handleFileUpload = (event: React.FormEvent<HTMLInputElement>) => {
    const fileList = event.currentTarget.files || [];
    const filesPath = Array.from(fileList).map(file => file.path);
    const pathPrefix = dirPath.join('/');
    ipcRenderer.send('file-upload', uploaderProfile.id, filesPath, pathPrefix);
    event.currentTarget.value = '';
  };

  const [modalVisible, setModalVisible] = useState(false);

  const [form] = Form.useForm();

  const handleCreateDirectory = () => {
    form.validateFields().then(values => {
      ipcRenderer.send('directory-create', uploaderProfile.id, values?.directoryPath || '');
    });
  };

  const handleDownload = (record: ListFile) => {
    ipcRenderer.send('file-download', record.name, record.url);
  };

  const [exportLoading, setExportLoading] = useState(false);

  const handleExport = () => {
    const data = selectRows.map(item => {
      const fileNameArr = item.name.split('.');
      fileNameArr.pop();
      return {
        name: fileNameArr.join('.'),
        url: item.url
      };
    });
    setExportLoading(true);
    ipcRenderer.send('export', data);
  };

  const columns: ColumnsType<ListFile> = [
    {
      title: '文件名',
      dataIndex: 'name',
      ellipsis: true,
      render: (val: string, record: ListFile) => (
        <div style={{ display: 'flex', alignItems: 'center' }}>
          {record.type === 'directory' ? (
            <FolderFilled style={{ fontSize: 16 }} />
          ) : (
            <FileOutlined style={{ fontSize: 16 }} />
          )}
          {record.type === 'directory' ? (
            <a
              title={val}
              onClick={() => handleNameClick(record)}
              className="table-filename"
              style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
            >
              {formatFileName(val)}
            </a>
          ) : (
            <Popover
              placement="topLeft"
              content={() =>
                /(jpg|png|gif|jpeg)$/.test(val) ? (
                  <Image
                    style={{ maxWidth: 500 }}
                    src={record.url}
                    fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
                  />
                ) : (
                  val
                )
              }
              trigger="hover"
            >
              <a
                title={val}
                onClick={() => handleNameClick(record)}
                className="table-filename"
                style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
              >
                {formatFileName(val)}
              </a>
            </Popover>
          )}
        </div>
      )
    },
    {
      title: '文件大小',
      dataIndex: 'size',
      ellipsis: true,
      width: 120,
      render: val => (val ? filesize(val) : '-')
    },
    {
      title: '更新时间',
      dataIndex: 'lastModified',
      ellipsis: true,
      width: 200,
      render: val => (val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '-')
    },
    {
      title: '操作',
      width: 120,
      render: (_, record) => (
        <Space>
          {record.type !== 'directory' && (
            <>
              <DownloadOutlined onClick={() => handleDownload(record)} />
              <CopyOutlined onClick={() => handleNameClick(record)} />
            </>
          )}
          <DeleteOutlined onClick={() => handleDelete(record)} />
        </Space>
      )
    }
  ];

  return (
    <div className="storage-page">
      <header>
        <span>文件管理</span>
        <Divider />
      </header>
      <Space style={{ marginBottom: 10 }}>
        <Select style={{ minWidth: 120 }} value={uploaderProfile?.id} onChange={setCurrentProfile}>
          {uploaderProfiles.map(item => (
            <Select.Option key={item.name} value={item.id}>
              {item.name}
            </Select.Option>
          ))}
        </Select>
        <Button
          title="上传"
          icon={<UploadOutlined />}
          disabled={!hasFileManageFeature}
          type="primary"
          onClick={() => {
            uploadRef.current?.click();
          }}
        />
        <Button title="刷新" icon={<ReloadOutlined />} disabled={!hasFileManageFeature} onClick={handleRefresh} />
        <Button
          title="创建文件夹"
          icon={<FolderAddOutlined />}
          disabled={!hasFileManageFeature}
          onClick={() => {
            setModalVisible(true);
          }}
        />
        <Button
          title="导出"
          icon={<ExportOutlined />}
          disabled={selectRows.length === 0}
          onClick={handleExport}
          loading={exportLoading}
        />
        <Button title="删除" icon={<DeleteOutlined />} disabled={selectRows.length === 0} onClick={handleBatchDelete} />
      </Space>
      <Breadcrumb style={{ marginBottom: 10 }}>
        <Breadcrumb.Item>
          <a onClick={() => handlePathClick(-1)}>全部文件</a>
        </Breadcrumb.Item>
        {dirPath.map((item, index) => (
          <Breadcrumb.Item key={item}>
            <a onClick={() => handlePathClick(index)}>{item}</a>
          </Breadcrumb.Item>
        ))}
      </Breadcrumb>
      <div className="table-wrapper">
        <Table
          size="small"
          rowKey="name"
          scroll={{ y: windowHeight - 270 }}
          dataSource={list}
          columns={columns}
          pagination={{
            size: 'small',
            defaultPageSize: 100,
            pageSizeOptions: ['50', '100', '200'],
            hideOnSinglePage: true
          }}
          loading={listLoading}
          rowSelection={{
            onChange: handleTableRowChange,
            selectedRowKeys: selectRowKeys,
            getCheckboxProps: record => ({ disabled: record?.type === 'directory' })
          }}
        />
      </div>
      <input ref={uploadRef} type="file" multiple hidden onChange={handleFileUpload} />
      <Modal
        title="创建目录"
        visible={modalVisible}
        onCancel={() => setModalVisible(false)}
        onOk={handleCreateDirectory}
        destroyOnClose={true}
      >
        <Form form={form} preserve={false}>
          <Form.Item
            label="目录名称"
            name="directoryPath"
            rules={[{ required: true }, { pattern: domainPathRegExp, message: '目录名不能以 / 开头或结尾' }]}
          >
            <Input autoFocus />
          </Form.Item>
        </Form>
      </Modal>
    </div>
  );
}
Example #26
Source File: AddFiltersForm.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
AddFiltersForm: React.FC<Props> = (props: Props) => {
	const { setShowAddFilterForm, setFilterId } = useActions(filtersLogic);
	const { filterId } = useValues(filtersLogic);
	const [form] = Form.useForm();
	const { confirm } = Modal;

	const params: { id: string } = useParams();
	const { data, isLoading } = useFilterDetails(params.id, filterId);
	const { data: mailboxesList } = useMailboxes({ userId: params.id });
	const { mutate: createFilter } = useCreateFilter();
	const { mutate: updateFilter } = useUpdateFilter();

	const onFinish = (values: any) => {
		const query = _.pick(values, ['from', 'to', 'subject', 'listId', 'text', 'ha', 'size']);
		const action = _.pick(values, ['seen', 'flag', 'delete', 'spam', 'mailbox', 'targets']);
		const filter: any = _.pick(values, ['name', 'disabled']);

		_.set(filter, 'query', query);
		_.set(filter, 'action', action);
		showConfirm(filter);
	};

	function showConfirm(filter: CreateFilterRequest) {
		confirm({
			title: 'Are you sure you want to save this Filter ?',
			icon: <ExclamationCircleOutlined />,

			onOk() {
				props.action === 'create'
					? createFilter({ userId: params.id, filterDetails: filter })
					: updateFilter({ userId: params.id, filterId: filterId, filterDetails: filter });
			},
		});
	}

	const reset = () => {
		form.resetFields();
	};

	useEffect(() => {
		form.setFieldsValue({ ...data });
	}, [data]);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setShowAddFilterForm(false);
						setFilterId('');
					}}
				>
					Filters
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>{props.action === 'create' ? 'Add Filter' : 'Update Filter'}</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb} loading={isLoading}>
			<Form wrapperCol={{ span: 6 }} labelCol={{ span: 3 }} form={form} onFinish={onFinish}>
				<Form.Item label='Filter name' name='name'>
					<Input placeholder='Name of the Filter' />
				</Form.Item>
				<Form.Item label='Query' tooltip={filtersTooltip.query}>
					{_.map(['from', 'to', 'subject', 'listId', 'text'], (query) => {
						return (
							<Form.Item key={query} name={query} label={query} tooltip={_.get(filtersTooltip, query)}>
								<Input />
							</Form.Item>
						);
					})}
					<Form.Item name='ha' label='has attachment' tooltip={filtersTooltip.ha} valuePropName='checked'>
						<Switch />
					</Form.Item>
					<Form.Item name='size' label='size' tooltip={filtersTooltip.size}>
						<Input />
					</Form.Item>
				</Form.Item>
				<Form.Item label='Action' tooltip={filtersTooltip.action}>
					{_.map(['seen', 'flag', 'delete', 'spam'], (query) => {
						return (
							<Form.Item
								name={query}
								key={query}
								label={query}
								tooltip={_.get(filtersTooltip, query)}
								valuePropName='checked'
							>
								<Switch />
							</Form.Item>
						);
					})}
					<Form.Item name='mailbox' label='Mailbox' tooltip={filtersTooltip.mailbox}>
						<Select showSearch>
							{_.map(mailboxesList, (mailbox: any) => {
								return (
									<Select.Option value={mailbox?.id} key={mailbox?.name + mailbox?.id}>
										{mailbox?.name}
									</Select.Option>
								);
							})}
						</Select>
					</Form.Item>
					<Form.Item name='targets' label='Targets' tooltip={filtersTooltip.targets}>
						<Select mode='tags' placeholder='Enter Targets' />
					</Form.Item>
				</Form.Item>
				<Form.Item
					name='disabled'
					label='Disable Filter'
					tooltip={filtersTooltip.disabled}
					valuePropName='checked'
				>
					<Switch />
				</Form.Item>
				<Form.Item {...tailLayout}>
					<Space size='middle'>
						<Button type='default' htmlType='button' onClick={reset}>
							Reset
						</Button>
						<Button type='primary' htmlType='submit'>
							Save
						</Button>
					</Space>
				</Form.Item>
			</Form>
		</Page>
	);
}
Example #27
Source File: machine-manage.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
MachineManage = () => {
  const [{ drawerVisible, activeMachine }, updater, update] = useUpdate({
    drawerVisible: false,
    activeMachine: {} as ORG_MACHINE.IMachine,
  });

  const [{ siteName, clusterName }] = routeInfoStore.useStore((s) => [s.query]);
  const { id } = routeInfoStore.useStore((s) => s.params);

  const [isFetching] = useLoading(machineManageStore, ['getGroupInfos']);
  const { getGroupInfos, clearGroupInfos, offlineMachine } = machineManageStore;

  const [groupInfos] = machineManageStore.useStore((s) => [s.groupInfos]);

  useUnmount(() => {
    clearGroupInfos();
  });

  const getMachineList = React.useCallback(() => {
    getGroupInfos({
      groups: ['cluster'],
      clusters: [{ clusterName }],
      filters: [
        {
          key: 'edge_site',
          values: [siteName],
        },
      ],
    });
  }, [clusterName, getGroupInfos, siteName]);

  React.useEffect(() => {
    getMachineList();
  }, [getMachineList]);

  const tableList = React.useMemo(() => {
    const { machines } = groupInfos[0] || {};
    return map(machines, (m) => {
      return m;
    });
  }, [groupInfos]);

  const showMonitor = (record: ORG_MACHINE.IMachine) => {
    update({
      drawerVisible: true,
      activeMachine: record,
    });
  };

  const offlineHandle = (record: ORG_MACHINE.IMachine) => {
    offlineMachine({
      siteIP: record.ip,
      id: +id,
    }).then(() => {
      getMachineList();
    });
  };

  const columns: Array<ColumnProps<ORG_MACHINE.IMachine>> = [
    {
      title: 'IP',
      width: 160,
      dataIndex: 'ip',
    },
    {
      title: i18n.t('Number of instances'),
      dataIndex: 'tasks',
      width: 176,
      sorter: (a: ORG_MACHINE.IMachine, b: ORG_MACHINE.IMachine) => Number(a.tasks) - Number(b.tasks),
    },
    {
      title: 'CPU',
      width: 120,
      dataIndex: 'cpuAllocatable',
      render: (_, data: ORG_MACHINE.IMachine) => {
        const { cpuAllocatable, cpuUsage, cpuRequest, cpuUsagePercent, cpuDispPercent } = data;
        return (
          <div className="percent-row">
            {DoubleProgressItem({
              usedPercent: Math.ceil(cpuUsagePercent),
              requestPercent: Math.ceil(cpuDispPercent),
              usage: cpuUsage,
              request: cpuRequest,
              total: cpuAllocatable,
              unit: i18n.t('core'),
            })}
          </div>
        );
      },
    },
    {
      title: i18n.t('memory'),
      width: 120,
      dataIndex: 'memProportion',
      render: (_, data: ORG_MACHINE.IMachine) => {
        const { memAllocatable, memUsage, memRequest, memUsagePercent, memDispPercent } = data;
        return (
          <div className="percent-row">
            {DoubleProgressItem({
              usedPercent: Math.ceil(memUsagePercent),
              requestPercent: Math.ceil(memDispPercent),
              usage: memUsage,
              request: memRequest,
              total: memAllocatable,
              unitType: 'STORAGE',
            })}
          </div>
        );
      },
    },
    {
      title: <span className="main-title">{i18n.t('Label')} </span>,
      dataIndex: 'labels',
      className: 'machine-labels',
      render: (value: string) => {
        const keyArray = value?.split(',') || [];
        return (
          <TagsRow
            labels={keyArray.map((label) => {
              return { label };
            })}
          />
        );
      },
    },
    {
      title: i18n.t('operations'),
      dataIndex: 'id',
      key: 'operation',
      width: 180,
      fixed: 'right',
      render: (_id: string, record: ORG_MACHINE.IMachine) => {
        return (
          <TableActions>
            <span className="table-operations-btn" onClick={() => showMonitor(record)}>
              {i18n.t('Machine Overview')}
            </span>
            <PopConfirm title={`${i18n.t('confirm to go offline')}?`} onConfirm={() => offlineHandle(record)}>
              <span className="table-operations-btn">{i18n.t('msp:Offline')}</span>
            </PopConfirm>
          </TableActions>
        );
      },
    },
  ];

  const onCloseDrawer = React.useCallback(() => {
    updater.drawerVisible(false);
  }, [updater]);
  return (
    <div className="machine-table">
      <Breadcrumb
        separator={<ErdaIcon className="align-middle" type="right" size="14px" />}
        className="path-breadcrumb mb-2"
      >
        <Breadcrumb.Item className="hover-active" onClick={() => goTo(goTo.pages.ecpResource)}>
          {siteName}
        </Breadcrumb.Item>
        <Breadcrumb.Item>{i18n.t('cmp:node list')}</Breadcrumb.Item>
      </Breadcrumb>

      <Table
        className="machine-list-table"
        loading={isFetching}
        rowKey="ip"
        pagination={false}
        bordered
        columns={columns}
        dataSource={tableList}
        scroll={{ x: 1300 }}
      />
      <Drawer
        width="80%"
        visible={drawerVisible}
        title={i18n.t('Machine Overview')}
        destroyOnClose
        onClose={onCloseDrawer}
      >
        <MachineDetail type="insight" machineDetail={activeMachine} />
      </Drawer>
    </div>
  );
}
Example #28
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ServiceManager = () => {
  const [propsContainerList, propsServiceList, runtimeJson, runtimeStatus, serviceReqStatus, metrics] =
    dcosServiceStore.useStore((s) => [
      s.containerList,
      s.serviceList,
      s.runtimeJson,
      s.runtimeStatus,
      s.serviceReqStatus,
      s.metrics,
    ]);
  const list = clusterStore.useStore((s) => s.list);
  const { getClusterList } = clusterStore.effects;
  const { getContainerList, getServiceList, getRuntimeJson, getRuntimeStatus } = dcosServiceStore.effects;
  const { clearRuntimeJson, clearRuntimeStatus } = dcosServiceStore.reducers;
  const [isFetchingClusters] = useLoading(clusterStore, ['getClusterList']);
  const [isFetchingServices, isFetchingContainers] = useLoading(dcosServiceStore, [
    'getServiceList',
    'getContainerList',
  ]);

  const [{ path, cluster, environment, ip, serviceList, containerList }, updater, update] = useUpdate<IState>({
    path: [{ q: '', name: '' }],
    cluster: '',
    environment: 'dev',
    ip: undefined,
    serviceList: [],
    containerList: [],
  });

  React.useEffect(() => {
    update({
      serviceList: propsServiceList,
      containerList: propsContainerList,
    });
  }, [update, propsContainerList, propsServiceList]);

  useEffectOnce(() => {
    getClusterList().then((_list: ORG_CLUSTER.ICluster[]) => {
      !isEmpty(_list) &&
        update({
          cluster: _list[0].name,
          path: [{ q: _list[0].name, name: _list[0].name }],
        });
    });
    return () => {
      clearInterval(reqSt);
    };
  });

  const fetchServiceList = React.useCallback(
    (q: { paths: DCOS_SERVICES.path[]; environment: string; ip?: string }) => {
      const depth = q.paths.length;
      if (depth < 5 && depth > 0) {
        getServiceList(q);
      }
    },
    [getServiceList],
  );

  useDebounce(
    () => {
      fetchServiceList({ paths: path, environment, ip });
    },
    300,
    [ip, path, environment],
  );

  useUpdateEffect(() => {
    clearInterval(reqSt);
    if (['runtime', 'service'].includes(curLevel() as string)) {
      reqRuntimeStatus();
      reqSt = setInterval(() => reqRuntimeStatus(), 5000);
    }
  }, [serviceList]);

  useUpdateEffect(() => {
    combineStatuToService(runtimeStatus);
  }, [runtimeStatus]);

  useUpdateEffect(() => {
    const { cpu, mem } = metrics;
    cpu?.loading === false && mem?.loading === false && combineMetricsToList(formatMetricsToObj(metrics)); // 两部分数据都返回后才开始combine数据
  }, [metrics]);

  useUpdateEffect(() => {
    if (!serviceReqStatus) {
      clearInterval(reqSt);
    }
  }, [serviceReqStatus]);

  const formatMetricsToObj = (_metrics: {
    cpu: { data: Array<{ tag: string; data: number }> };
    mem: { data: Array<{ tag: string; data: number }> };
  }) => {
    const metricsObj = {};
    const { cpu, mem } = _metrics;
    (cpu.data || []).forEach((cItem) => {
      cItem.tag && (metricsObj[cItem.tag] = { cpuUsagePercent: cItem.data / 100 || 0, diskUsage: 0 });
    });
    (mem.data || []).forEach((mItem) => {
      if (mItem.tag) {
        !metricsObj[mItem.tag] && (metricsObj[mItem.tag] = {});
        metricsObj[mItem.tag].memUsage = mItem.data || 0;
      }
    });
    return metricsObj;
  };

  const combineMetricsToList = (metricsObj: Obj) => {
    let newContainerList = [...containerList];
    let newServiceList = [...serviceList];
    if (curLevel('container')) {
      // 当前层级在container上,
      newContainerList = newContainerList.map((item) => {
        let _metrics = null;
        try {
          _metrics = metricsObj[item.containerId] || null;
        } catch (e) {
          _metrics = null;
        }
        return { ...item, metrics: _metrics };
      });
      updater.containerList(newContainerList);
    } else {
      const combineKye = curLevel('service') ? 'name' : 'id';
      newServiceList = newServiceList.map((item) => {
        let _metrics = null;
        try {
          _metrics = metricsObj[item[combineKye]] || null;
        } catch (e) {
          _metrics = null;
        }
        return { ...item, metrics: _metrics };
      });
      updater.serviceList(newServiceList);
    }
  };

  const onJsonShow = (_visible: boolean) => {
    const runtimeId = getLevel('runtime').id;
    _visible && runtimeJson === null && runtimeId !== 'unknown' && getRuntimeJson({ runtimeId });
  };

  const combineStatuToService = (_runtimeStatus: Obj) => {
    let newServiceList = [...serviceList];
    if (curLevel('runtime')) {
      // runtime的status在runtimeStatus中runtimeId为key对象中
      newServiceList = newServiceList.map((item) => {
        let status = '';
        try {
          status = runtimeStatus[item.id].status || '';
        } catch (e) {
          status = '';
        }
        return { ...item, status };
      });
    } else if (curLevel('service')) {
      // service的status在runtimeStatus对应runtimeId为key的对象中的more字段中
      const runtimeId = getLevel('runtime').id;
      newServiceList = newServiceList.map((item) => {
        let status = '';
        try {
          status = runtimeStatus[runtimeId].more[item.name] || '';
        } catch (e) {
          status = '';
        }
        return { ...item, status };
      });
    }
    updater.serviceList(newServiceList);
  };

  const into = (p: { q: string; name: string }) => {
    if (curLevel('runtime')) clearRuntimeJson();
    const newPath = path.concat(p);
    update({
      path: newPath,
    });
    const depth = newPath.length;
    if (depth >= 5) {
      getContainerList(newPath);
    }
  };

  const backTo = (depth: number) => {
    if (curLevel('runtime')) clearRuntimeJson();
    update({
      path: path.slice(0, depth + 1),
    });
  };

  const curLevel = (lev = '') => {
    const levArr = ['project', 'application', 'runtime', 'service', 'container'];
    const curLev = levArr[path.length - 1];
    return lev ? lev === curLev : curLev;
  };

  const getLevel = (lev = '') => {
    const levs = {
      project: path[1] ? { id: path[1].q, name: path[1].name } : null,
      application: path[2] ? { id: path[2].q, name: path[2].name } : null,
      runtime: path[3] ? { id: path[3].q, name: path[3].name } : null,
      service: path[4] ? { id: path[4].q, name: path[4].name } : null,
    };
    return levs[lev] || null;
  };

  const handleEnvChange = (_environment: string) => {
    update({ environment: _environment });
  };

  const handleClusterChange = (_cluster: string) => {
    update({
      cluster: _cluster,
      path: [{ q: _cluster, name: cluster }],
    });
  };

  const reqRuntimeStatus = () => {
    let runtimeIds = '';
    if (curLevel('runtime')) {
      // runtime,批量查询runtime的状态
      runtimeIds = map(serviceList, 'id').join(',');
    } else if (curLevel('service')) {
      // service,查询单个runtime状态
      runtimeIds = getLevel('runtime').id;
    }
    if (runtimeIds && runtimeIds !== 'unknown') {
      getRuntimeStatus({ runtimeIds });
    } else {
      clearInterval(reqSt);
    }
  };
  const jsonString = runtimeJson === null ? '' : JSON.stringify(runtimeJson, null, 2);

  const slot = (
    <IF check={path.length === 1}>
      <div className="filter-group mb-4 ml-3-group">
        <Select
          value={cluster}
          className="w-[150px] bg-black-06 rounded"
          bordered={false}
          onChange={handleClusterChange}
        >
          {map(list, (v) => (
            <Option key={v.name} value={v.name}>
              {v.displayName || v.name}
            </Option>
          ))}
        </Select>
        <Input
          allowClear
          value={ip}
          className="w-[150px] bg-black-06 rounded"
          placeholder={i18n.t('cmp:Search by IP')}
          onChange={(e) => update({ ip: e.target.value })}
        />
        <Select
          value={environment}
          className="w-[150px] bg-black-06 rounded"
          bordered={false}
          onChange={handleEnvChange}
        >
          {map(ENV_MAP, (v, k) => (
            <Option key={k} value={k}>
              {v.cnName}
            </Option>
          ))}
        </Select>
      </div>
    </IF>
  );

  return (
    <Spin spinning={isFetchingClusters}>
      <Holder when={isEmpty(list)}>
        <Breadcrumb
          separator={<ErdaIcon className="text-xs align-middle" type="right" size="14px" />}
          className="path-breadcrumb"
        >
          {path.map((p, i) => {
            const isLast = i === path.length - 1;
            return (
              <Breadcrumb.Item
                key={i}
                className={isLast ? '' : 'hover-active'}
                onClick={() => {
                  if (!isLast) backTo(i);
                }}
              >
                {p.name}
              </Breadcrumb.Item>
            );
          })}
        </Breadcrumb>
        <div className="to-json">
          {path.length === 4 ? (
            <JsonChecker
              buttonText={i18n.t('runtime configs')}
              jsonString={jsonString}
              onToggle={onJsonShow}
              modalConfigs={{ title: i18n.t('runtime configs') }}
            />
          ) : null}
        </div>
      </Holder>
      <Spin spinning={isFetchingServices || isFetchingContainers}>
        <PureServiceList
          into={into}
          slot={slot}
          depth={path.length}
          serviceList={serviceList}
          onReload={
            path.length < 5 ? () => fetchServiceList({ paths: path, environment, ip }) : () => getContainerList(path)
          }
          containerList={containerList}
          haveMetrics={false}
          extraQuery={{ filter_cluster_name: cluster }}
        />
        {path.length === 2 ? (
          <AssociatedAddons projectId={path[1].q} environment={ENV_MAP[environment].enName} />
        ) : null}
      </Spin>
    </Spin>
  );
}
Example #29
Source File: MessageDetails.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
MessageDetails: React.FC = () => {
	const { id }: any = useParams();

	const { mailboxId, mailboxName } = useValues(mailboxesLogic);
	const { messageId, attachmentId } = useValues(messagesLogic);

	const attachment = useDownloadAttachment({
		userId: id,
		attachment: attachmentId,
		mailboxId: mailboxId,
		messageId: messageId,
	});

	const { data, isLoading, isError } = useMessageDetails({ userId: id, mailboxId: mailboxId, messageId: messageId });

	const { setMessageDetailsToggle, setAttachmentId } = useActions(messagesLogic);
	const { setUpdateMailboxToggle, setShowMailboxMessagesTable } = useActions(mailboxesLogic);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={() => {
						setUpdateMailboxToggle(false);
						setShowMailboxMessagesTable(false);
					}}
				>
					Mailboxes
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setMessageDetailsToggle(false);
					}}
				>
					{mailboxName}
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Message</Breadcrumb.Item>
		</Breadcrumb>
	);

	const MessageTitle = (
		<div>
			<p>From : {`${_.get(data, 'from.name', '')}<${_.get(data, 'from.address', '')}>`}</p>
			<p>To : {`${_.get(data, 'to.0.name', '')}<${_.get(data, 'to.0.address', '')}>`}</p>
			<p>Subject : {_.get(data, 'subject', '')}</p>
		</div>
	);
	return (
		<Page title={pageBreadcrumb} loading={isLoading} error={isError}>
			{_.isUndefined(data) ? null : (
				<Card title={MessageTitle} extra={<>{moment(data.date).format(DATE_TIME_FORMAT_AP)}</>}>
					<BraftEditor language='en' controls={[]} value={BraftEditor.createEditorState(data.html[0])} />
					{_.isEmpty(data.attachments) ? null : (
						<Descriptions size='small' bordered column={1}>
							<Descriptions.Item label='Attachments'>
								{_.map(data.attachments, (attachment, index) => {
									return (
										<Row key={index}>
											<Col>
												<p>
													Filename: {_.get(attachment, 'filename', '')}
													<br />
													Content Type: {_.get(attachment, 'contentType', '')}
													<br />
													Size: {_.get(attachment, 'sizeKb', '')} KB
												</p>
											</Col>
											<Col offset={1}>
												<Tooltip title={'Download file'}>
													<Button
														className='ant-btn-icon'
														shape='circle'
														onClick={() => {
															setAttachmentId(attachment);
														}}
													>
														<DownloadOutlined className={'blue-color'} />
													</Button>
												</Tooltip>
											</Col>
										</Row>
									);
								})}
							</Descriptions.Item>
						</Descriptions>
					)}
				</Card>
			)}
		</Page>
	);
}