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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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} </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]}
<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 |
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,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"
/>
) : (
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 |
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 |
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 |
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 |
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>
);
}