@ant-design/icons#FileOutlined JavaScript Examples
The following examples show how to use
@ant-design/icons#FileOutlined.
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: index.jsx From juno with Apache License 2.0 | 6 votes |
MenuItems = [
{
icon: <FileOutlined />,
key: 'config-edit',
label: '配置编辑',
},
{
icon: <CloudServerOutlined />,
key: 'publish',
label: '版本发布',
},
]
Example #2
Source File: FileView.js From react-chat-app with MIT License | 6 votes |
FileView = props => {
const [hovered, setHovered] = useState(false)
const { attachment } = props
const style={
...styles.fileView,
...{
color: hovered ? '#1890ff' : '#434343',
border: hovered ? '1px solid #1890ff' : '1px solid #434343',
}
}
if (!attachment) {
return (
<div style={styles.loadingContainer}>
<LoadingOutlined style={{ color: 'white', padding: '4px', fontSize: '24px' }} />
</div>
)
}
return (
<div
style={style}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={() => window.open(attachment.file)}
>
<FileOutlined />{' '}{ getFileName(attachment.file) }
</div>
)
}
Example #3
Source File: custom-trigger-debug.jsx From virtuoso-design-system with MIT License | 5 votes |
render() {
return (
<Layout>
<Sider trigger={null} collapsible collapsed={this.state.collapsed}>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['3']} defaultOpenKeys={['sub1']}>
<Menu.Item key="1" icon={<PieChartOutlined />}>
Option 1
</Menu.Item>
<Menu.Item key="2" icon={<DesktopOutlined />}>
Option 2
</Menu.Item>
<SubMenu key="sub1" icon={<UserOutlined />} title="User">
<Menu.Item key="3">Tom</Menu.Item>
<Menu.Item key="4">Bill</Menu.Item>
<Menu.Item key="5">Alex</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<TeamOutlined />} title="Team">
<Menu.Item key="6">Team 1</Menu.Item>
<Menu.Item key="8">Team 2</Menu.Item>
</SubMenu>
<Menu.Item key="9" icon={<FileOutlined />} />
</Menu>
</Sider>
<Layout>
<Header className="site-layout-background" style={{ padding: 0 }}>
{React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick: this.toggle,
})}
</Header>
<Content
className="site-layout-background"
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
}}
>
Content
</Content>
</Layout>
</Layout>
);
}
Example #4
Source File: side.jsx From virtuoso-design-system with MIT License | 5 votes |
render() {
const { collapsed } = this.state;
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider collapsible collapsed={collapsed} onCollapse={this.onCollapse}>
<div className="logo" />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
<Menu.Item key="1" icon={<PieChartOutlined />}>
Option 1
</Menu.Item>
<Menu.Item key="2" icon={<DesktopOutlined />}>
Option 2
</Menu.Item>
<SubMenu key="sub1" icon={<UserOutlined />} title="User">
<Menu.Item key="3">Tom</Menu.Item>
<Menu.Item key="4">Bill</Menu.Item>
<Menu.Item key="5">Alex</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<TeamOutlined />} title="Team">
<Menu.Item key="6">Team 1</Menu.Item>
<Menu.Item key="8">Team 2</Menu.Item>
</SubMenu>
<Menu.Item key="9" icon={<FileOutlined />}>
Files
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }} />
<Content style={{ margin: '0 16px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>
<Breadcrumb.Item>User</Breadcrumb.Item>
<Breadcrumb.Item>Bill</Breadcrumb.Item>
</Breadcrumb>
<div className="site-layout-background" style={{ padding: 24, minHeight: 360 }}>
Bill is a cat.
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
);
}
Example #5
Source File: adminSubmissions.js From ctf_platform with MIT License | 4 votes |
render() {
return (
<Layout style={{ height: "100%", width: "100%", backgroundColor: "rgba(0, 0, 0, 0)" }}>
<Modal
title="Create New Transaction"
visible={this.state.createTModal}
footer={null}
onCancel={() => { this.setState({ createTModal: false }) }}
>
<CreateT refresh={this.refresh.bind(this)} challengeList={this.state.challengeList} setState={this.setState.bind(this)}></CreateT>
</Modal>
<Modal
title="Edit Transaction"
visible={this.state.editTModal}
footer={null}
destroyOnClose
onCancel={() => { this.setState({ editTModal: false }) }}
>
<EditT initialData={this.state.initialData} refresh={this.refresh.bind(this)} challengeList={this.state.challengeList} setState={this.setState.bind(this)}></EditT>
</Modal>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div style={{ display: "flex", alignItems: "center", height: "2ch" }}>
<Button type="primary" style={{ marginBottom: "2vh", marginRight: "1ch" }} icon={<FileOutlined />} onClick={() => { this.setState({ createTModal: true }) }}>Create New Transaction</Button>
{this.state.loading && (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
<Ellipsis color="#177ddc" size={60} ></Ellipsis>
<h1>Loading Transactions</h1>
</div>
)}
</div>
<Button loading={this.state.loading} type="primary" shape="circle" size="large" style={{ marginBottom: "2vh", maxWidth: "25ch" }} icon={<RedoOutlined />} onClick={async () => { await this.fillTableData(); message.success("Submissions list refreshed.") }} />
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<Button disabled={this.state.disableEditButtons} style={{ marginBottom: "2vh", marginRight: "1ch", backgroundColor: "#a61d24" }} icon={<DeleteOutlined />} onClick={() => {
confirm({
confirmLoading: this.state.disableEditButtons,
title: 'Are you sure you want to delete the transactions(s) [' + this.state.selectedTableKeys.join(", ") + ']? This action is irreversible.',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.deleteTransactions(close.bind(this), this.state.selectedTableKeys) },
onCancel: () => { },
});
}}>Delete Transactions</Button>
</div>
<Table rowSelection={{ selectedRowKeys: this.state.selectedTableKeys, onChange: this.handleTableSelect.bind(this) }} style={{ overflow: "auto" }} dataSource={this.state.dataSource} locale={{
emptyText: (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", marginTop: "10vh" }}>
<FileUnknownTwoTone style={{ color: "#177ddc", fontSize: "400%", zIndex: 1 }} />
<h1 style={{ fontSize: "200%" }}>No Submissions Found/Created.</h1>
</div>
)
}}>
<Column title="Time" dataIndex="timestamp" key="timestamp" />
<Column title="Submittor" dataIndex="author" key="author" render={(text, row, index) => {
return <Link to={"/Profile/" + text}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Submittor"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.author.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
/>
<Column title="Team" dataIndex="team" key="team" render={(text, row, index) => {
return <Link to={"/Team/" + text}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}}
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Team"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.team.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />}
/>
<Column title="Category" dataIndex="category" key="category" filters={
this.state.categoryList.map((category) => {
return { text: category, value: category }
})} onFilter={(value, record) => { return value === record.category }} />
<Column render={(text, row, index) => {
return <Link to={"/Challenges/" + row.challengeID}><a style={{ fontWeight: 700 }}>{text}</a></Link>;
}} title="Challenge" dataIndex="challenge" key="challenge"
filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Challenge Name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.challenge.toLowerCase().trim().includes(value.toLowerCase())}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />} />
<Column title="Hint ID" dataIndex="hint_id" key="hint_id" filterDropdown={({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder="Search Hint ID"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => confirm()}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => { confirm() }}
icon={<SearchOutlined />}
>
Search
</Button>
<Button onClick={() => clearFilters()}>
Reset
</Button>
</Space>
</div>
)}
onFilter={(value, record) => record.hint_id == parseInt(value)}
filterIcon={filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />} />
<Column title="Type" dataIndex="type" key="type" filters={[{ text: "Submission", value: "submission" }, { text: "Hint", value: "hint" }, { text: "Blocked Submission", value: "blocked_submission" }, { text: "Initial Register", value: "initial_register" }]} onFilter={(value, record) => { return value === record.type }} />
<Column title="Points Awarded" dataIndex="points" key="points" sorter={(a, b) => a.points - b.points} />
<Column title="Flag Submitted" dataIndex="submission" key="submission" />
<Column title="Correct" dataIndex="correct" key="correct" filters={[{ text: "✅", value: "✅" }, { text: "❌", value: "❌" }]} onFilter={(value, record) => { return value === record.correct }} />
<Column
title=""
key="edit"
render={(text, record) => (
<Button icon={<EditOutlined />} onClick={() => { this.setState({ editTModal: true, initialData: record }) }}> Edit</Button>
)}
/>
</Table>
</Layout >
);
}
Example #6
Source File: AppRouter.jsx From React-Nest-Admin with MIT License | 4 votes |
sidebar = () => {
return (
<Menu theme="dark" mode="inline" defaultSelectedKeys={["1"]}>
<Menu.Item key="1">
<Link to={{ pathname: "/" }}>
<HomeOutlined />
<span>首页</span>
</Link>
</Menu.Item>
<Menu.Item key="5">
<Link to={{ pathname: "/user-management" }}>
<UserOutlined />
<span>用户管理</span>
</Link>
</Menu.Item>
<SubMenu
key="sub1"
title={
<span>
<FileOutlined />
<span>文件管理</span>
</span>
}
>
<Menu.Item key="sub1-1">
<Link to={{ pathname: "/file/list" }}>
<OrderedListOutlined />
<span>文件列表</span>
</Link>
</Menu.Item>
<Menu.Item key="sub1-2">
<Link to={{ pathname: "/file/add" }}>
<UploadOutlined />
<span>文件上传</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub2"
title={
<span>
<DashboardOutlined />
<span>可视化图表</span>
</span>
}
>
<Menu.Item key="sub2-1">
<Link to={{ pathname: "/charts/line-chart" }}>
<LineChartOutlined />
<span>折线图</span>
</Link>
</Menu.Item>
<Menu.Item key="sub2-2">
<Link to={{ pathname: "/charts/bar-chart" }}>
<BarChartOutlined />
<span>柱状图</span>
</Link>
</Menu.Item>
<Menu.Item key="sub2-3">
<Link to={{ pathname: "/charts/pie-chart" }}>
<PieChartOutlined />
<span>饼状图</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub5"
title={
<span>
<FileExcelOutlined />
<span>Excel表格</span>
</span>
}
>
<Menu.Item key="sub5-1">
<Link to={{ pathname: "/excel/export-csv" }}>
<ExportOutlined />
<span>Export csv</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub4"
title={
<span>
<EditOutlined />
<span>文本编辑器</span>
</span>
}
>
<Menu.Item key="sub4-1">
<Link to={{ pathname: "/rich-text-editor" }}>
<Html5Outlined />
<span>富文本编辑器</span>
</Link>
</Menu.Item>
<Menu.Item key="sub4-2">
<Link to={{ pathname: "/md-editor" }}>
<FileMarkdownOutlined />
<span>Markdown编辑器</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub3"
title={
<span>
<BugOutlined />
<span>错误页面</span>
</span>
}
>
<Menu.Item key="sub3-1">
<Link to={{ pathname: "/not-found" }}>
<InboxOutlined />
<span>404</span>
</Link>
</Menu.Item>
<Menu.Item key="sub3-2">
<Link to={{ pathname: "/not-allow" }}>
<StopOutlined />
<span>403</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="6">
<a
href="https://github.com/cnscorpions/React-Nest-Admin"
target="_blank"
rel="noopener noreferrer"
>
<GithubOutlined />
<span>Github</span>
</a>
</Menu.Item>
</Menu>
);
}
Example #7
Source File: index.jsx From juno with Apache License 2.0 | 4 votes |
function Files(props) {
const {
currentConfig,
configList,
configListLoading,
currentContent,
deleteConfig,
aid,
env,
loadConfigList,
appName,
k8sClusters,
} = props;
let { zoneList } = props;
const fileChanged = currentConfig && currentConfig.content !== currentContent;
useEffect(() => {
if (props.appName && props.env) {
loadConfigList(props.appName, props.env);
}
}, []);
k8sClusters.forEach((cluster) => {
if (
cluster.env.indexOf(env) > -1 &&
zoneList.findIndex((zone) => zone.zone_code === cluster.zone_code) < 0
) {
zoneList.push({
zone_code: cluster.zone_code,
zone_name: cluster.zone_name,
});
}
});
const renderConfigListByZone = (zone) => {
let configs = (configList || []).filter((item) => item.zone === zone);
if (configs.length === 0) {
return (
<div className={styles.noConfigTip}>
<StopOutlined />
该Zone暂无配置
</div>
);
}
return configs.map((cfg, index) => {
const active = cfg.id === currentConfig?.id;
return (
<li
key={index}
className={[styles.configItem, active && styles.configItemActive].join(' ')}
onClick={() => {
if (fileChanged) {
confirm({
title: '当前配置未保存',
content: '当前文件修改未保存,切换配置后当前的修改将丢失。是否切换文件?',
cancelText: '我点错了',
okText: '确认',
onOk: () => {
props.loadConfigDetail(cfg.id);
},
});
} else {
props.loadConfigDetail(cfg.id);
}
}}
>
<div className={styles.configListTextItem}>
<div>
{cfg.config_status === 1 ? (
<Tag color="green">已发布</Tag>
) : cfg.config_status === 2 ? (
<Tag color="yellow">未发布</Tag>
) : (
''
)}
{cfg.name}.{cfg.format}
</div>
<div>
{currentConfig &&
currentConfig.content !== currentContent &&
cfg.id === currentConfig.id && <span className={styles.notSavedTip}>未保存</span>}
</div>
</div>
<div>
<div onClick={(ev) => ev.stopPropagation()}>
<Popconfirm
title={'谨慎操作,删除后无法找回.确定删除?'}
onConfirm={(ev) => {
deleteConfig(cfg.id).then((r) => {
loadConfigList(props.appName, props.env);
});
}}
>
<OptionButton type={'text'}>
<DeleteOutlined />
</OptionButton>
</Popconfirm>
</div>
</div>
</li>
);
});
};
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div className={styles.options}>
<OptionButton onClick={() => props.showCreateModal(true)} title={'新增配置'}>
<FileOutlined />
</OptionButton>
{currentConfig && (
<OptionButton title={'历史变更'} onClick={() => props.showHistoryModal(true)}>
<HistoryOutlined />
</OptionButton>
)}
</div>
<ul className={styles.zoneList}>
<ScrollArea style={{ height: '100%' }}>
{configListLoading && (
<div style={{ textAlign: 'center', paddingTop: '30px' }}>
<Spin />
<div>加载中</div>
</div>
)}
{!configListLoading &&
zoneList &&
zoneList.map((zone, index) => {
return (
<li key={index}>
<div className={styles.zoneTitle}>{zone.zone_name}</div>
<ul className={styles.configList}>{renderConfigListByZone(zone.zone_code)}</ul>
</li>
);
})}
{!configListLoading && (!zoneList || !zoneList.length) && (
<div className={styles.noConfigTip}>
<StopOutlined />
当前应用环境无机房
</div>
)}
</ScrollArea>
</ul>
</div>
);
}
Example #8
Source File: confgo.jsx From juno with Apache License 2.0 | 4 votes |
render() {
const that = this;
const {
appId = 0,
app = {},
apps = [],
configList = [],
configText,
commonText,
appConfigList = [],
configChangeList = [],
statusList = [],
configHistoryList = [],
resourceData = {},
appInfo: { aid, appName },
msg,
idcList,
file_path = '',
zoneCode,
} = this.props;
console.log('render -> configText', configText);
const { users = [] } = app;
const appInfo = appConfigList[0] || {};
const {
caid,
file_name,
format,
env,
filterKey,
selectItemID,
selectKey,
selectValue,
selectComment,
selectIsResource,
selectResourceID,
selectIsPublic,
fileDelModalVisible: showFileManage,
fileDelConfirmLoading,
result_list = [],
} = this.state;
const changeNum = configList.filter((v) => v.status * 1 !== 1).length;
const leftCardStyle = {
borderRadius: '4px',
marginTop: '4px',
marginLeft: '4px',
};
const statusMap = (t, status) => {
const map = {
1: <span></span>,
2: <span>{getOpSapn('new')}</span>,
3: <span>{getOpSapn('update')}</span>,
4: <span>{getOpSapn('del')}</span>,
};
return map[status];
};
const changListCols = [
{
key: 'key',
dataIndex: 'key',
title: 'Block',
width: 120,
render(t, r) {
return <span style={{ wordBreak: 'break-word' }}>{t}</span>;
},
},
{
key: 'op_type',
dataIndex: 'op_type',
title: '操作',
width: 80,
render(t) {
const opMap = {
1: getOpSapn('new', '新增'),
2: getOpSapn('update', '更新'),
3: getOpSapn('del', '删除'),
};
return <span>{opMap[t]}</span>;
},
},
{
key: 'old_value',
dataIndex: 'old_value',
title: '旧值',
render(t) {
let tmp = t;
if (t.length > 200) {
tmp = tmp.substring(0, 200) + '...';
}
return <span style={{ wordBreak: 'break-word' }}>{tmp}</span>;
},
},
{
key: 'new_value',
dataIndex: 'new_value',
title: '新值',
render(t) {
let tmp = t;
if (t.length > 200) {
tmp = tmp.substring(0, 200) + '...';
}
return <span style={{ wordBreak: 'break-word' }}>{tmp}</span>;
},
},
{
key: 'update_time',
dataIndex: 'update_time',
title: '时间',
width: 180,
render(t) {
return moment(t * 1000).format('YYYY-MM-DD HH:mm:ss');
},
},
{
key: 'op_name',
dataIndex: 'op_name',
width: 180,
title: '操作人',
},
];
const statusCol = [
{
key: 'is_use',
dataIndex: 'is_use',
title: (
<span>
配置状态
<Tooltip
title={`当前实例上的systemd配置文件项是否接入配置中心\n。若显示未接入,则需要修改systemd下发配置,路径改为配置中心提供的路径\n。若已经修改下发过,点击【同步实例状态】刷新。`}
>
<QuestionCircleOutlined style={{ marginLeft: '6px' }} />
</Tooltip>
</span>
),
render(t) {
return t ? getOpSapn('new', '已接入') : <div>{getOpSapn('del', '未接入')}</div>;
},
},
{
key: 'is_latest',
dataIndex: 'is_latest',
title: (
<span>
文件状态
<Tooltip
title={`当前实例上的配置文件是否为最新发布\n。若显示未同步,点击【刷新文件状态】。若刷新无效则需要【重新发布】一次。`}
>
<QuestionCircleOutlined style={{ marginLeft: '6px' }} />
</Tooltip>
</span>
),
render(t) {
return t ? getOpSapn('new', '已同步') : getOpSapn('del', '未同步');
},
},
{
key: 'is_effect',
dataIndex: 'is_effect',
title: (
<span>
配置状态
<Tooltip title={`重启之后应用配置是否生效`}>
<QuestionCircleOutlined style={{ marginLeft: '6px' }} />
</Tooltip>
</span>
),
render(t) {
return t ? getOpSapn('new', '已生效') : <div>{getOpSapn('del', '未生效')}</div>;
},
},
{
key: 'hostname',
dataIndex: 'hostname',
title: '实例名称',
},
{
key: 'message',
dataIndex: 'message',
title: '提交日志',
},
{
key: 'timestamp',
dataIndex: 'timestamp',
title: '文件同步时间/进程启动时间',
render(t, r) {
const { process_start_time = 0, is_latest, is_use } = r;
if (process_start_time !== 0) {
const syncTime = t * 1000;
const startTime = process_start_time * 1000;
//进程生效状态
let process_start_status = null;
if (syncTime > startTime) {
//配置未完全生效
process_start_status = (
<Tooltip title={'配置文件已经同步完成,进程尚未重启生效'}>
<Icon type="clock-circle" style={{ color: 'orange' }} />
</Tooltip>
);
} else if (is_latest && is_use) {
// 配置文件已同步,进程已重启,生效
process_start_status = (
<Tooltip title={'最新配置已经生效'}>
<Icon type="check-circle" style={{ color: 'green' }} />
</Tooltip>
);
}
return (
<div>
<p>{moment(syncTime).format('YYYY-MM-DD HH:mm:ss')}</p>
<p>
{moment(startTime).format('YYYY-MM-DD HH:mm:ss')} {process_start_status}
</p>
</div>
);
}
return (
<div>
<p>{moment(t * 1000).format('YYYY-MM-DD HH:mm:ss')}</p>
</div>
);
},
},
{
key: 'params',
dataIndex: 'params',
title: (
<span>
启动参数
<Tooltip
title={
'当前实例上的systemd配置中的启动参数, ${ConfigDir}变量为配置中心默认下发路径。启动时间为进程启动时间。'
}
>
<Icon style={{ marginLeft: '6px' }} type="question-circle" />
</Tooltip>
</span>
),
render(t, r) {
const paramsArr = t.split('/bin/%(program_name)s');
if (paramsArr.length !== 2) {
return t;
}
const params = paramsArr[1];
return (
<div>
<p style={{ margin: 0 }}>{params}</p>
</div>
);
},
},
{
key: 'op',
dataIndex: 'op',
title: '操作',
render(t, r) {
const { pub_id, is_latest } = r;
if (is_latest) {
return (
<div>
<Button
style={{ color: 'black' }}
onClick={(e) => {
that.showConfirm('restart', this.state.zone_code, r.hostname);
}}
>
重启
</Button>
</div>
);
}
},
},
];
const customPanelStyle = {
background: '#f7f7f7',
borderRadius: 4,
marginBottom: 8,
border: 0,
overflow: 'hidden',
};
//文本编辑器options
const options = {
lineNumbers: true, //显示行号
mode: { name: 'text/html' }, //定义mode
theme: 'ambiance', //选中的theme
lineWrapping: true,
};
if (msg === '权限错误') {
return (
<div style={{ marginTop: 10 }}>
<Alert
message="权限不足"
description="对线上配置操作需要管理员权限"
type="error"
showIcon
/>
</div>
);
}
let idcArr = [];
let zone_codeMap = [];
idcList.forEach((element) => {
idcArr.push(element.zone_code);
zone_codeMap[element.zone_code] = element.zone_name;
});
const genHeader = (record) => (
<div>
<div className={'cube'}>
{record.is_public == 0 && <Tag color="#2db7f5">私有</Tag>}
{record.is_public == 1 && <Tag color="#f50">公有</Tag>}
{record.is_public == 2 && <Tag color="#87d068">关联</Tag>}
</div>
<div className={'cube-title'}>
<h3>{record.key}</h3>
</div>
</div>
);
const genExtra = (record) => (
<div>
{statusMap(record.key, record.status)}
<Divider type="vertical" />
{record.status != 4 && (
<Tag
color={'blue'}
onClick={(event) => {
event.stopPropagation();
that.setState({
selectItemID: record.id,
selectKey: record.key,
selectValue: record.value,
selectComment: record.comment,
selectIsResource: record.is_resource,
selectResourceID: record.resource_id,
selectIsPublic: record.is_public,
showUpdateItem: true,
});
that.getEnvResource();
}}
>
编辑
</Tag>
)}
{record.status != 4 && (
<Popconfirm
title="确定删除吗?"
onConfirm={() => {
that.delItem({ id: record.id });
}}
>
<Tag color={'red'}>删除</Tag>
</Popconfirm>
)}
</div>
);
let configItemList = [];
configList.forEach((element) => {
configItemList.push(
<Panel
header={genHeader(element)}
key={element.key}
className="site-collapse-custom-panel"
extra={genExtra(element)}
>
<ReactCodeMirror
ref="editor"
value={element.value}
options={{
mode: 'text/x-toml',
lineNumbers: true,
autoMatchParens: true,
lineWrapping: false,
readOnly: this.state.readOnly,
scrollbarStyle: null,
}}
/>
</Panel>,
);
});
return (
<Layout>
<Sider width={250} style={{ backgroundColor: 'transparent' }}>
<Card style={leftCardStyle}>
<p style={{ textAlign: 'left' }}>
配置文件列表
<Button
style={{ marginLeft: '8px' }}
size={'small'}
type={'primary'}
icon={<PlusCircleOutlined />}
onClick={() => {
this.setState({ showConfigFile: true });
}}
>
添加配置
</Button>
</p>
{appInfo && appInfo.configs ? (
<Menu
selectedKeys={[caid + '_' + env + '_' + format]}
mode="inline"
style={{ height: '50%', borderRight: 0 }}
defaultOpenKeys={idcArr}
onClick={this.changeAppConfig}
>
{Object.keys(this.sortObj(appInfo.files, idcArr)).map((zone_code) => {
if (zoneCode == 'all' || zoneCode == zone_code) {
return (
<Menu.SubMenu key={zone_code} title={<h4>{`${zone_codeMap[zone_code]}`}</h4>}>
{appInfo.files[zone_code].map((v) => {
console.log('this.state.zone_code', this.state.zone_code);
return (
<Menu.Item
style={{ paddingLeft: '20px' }}
key={v.id + '_' + v.env + '_' + v.format}
>
{v.file_name}
</Menu.Item>
);
})}
</Menu.SubMenu>
);
}
})}
</Menu>
) : (
<div style={{ textAlign: 'left' }}>
<Button type={'primary'} onClick={() => this.setState({ showConfigFile: true })}>
添加配置文件
</Button>
</div>
)}
</Card>
<Card style={leftCardStyle}>
<p style={{ textAlign: 'left' }}>文件管理</p>
<Button type="primary" onClick={this.handleFileManage} style={{ marginLeft: '4px' }}>
文件管理
</Button>
<Button type="primary" onClick={this.handleFileDiff} style={{ marginLeft: '4px' }}>
文件对比
</Button>
</Card>
</Sider>
<Content>
<div>
{caid !== 0 && (
<Row style={{ marginTop: '4px', marginLeft: '4px', marginRight: '4px' }}>
<Col span={4} style={{ textAlign: 'left' }}>
<Button
style={{ float: 'left', marginRight: '6px' }}
onClick={(e) => {
//加载节点数据
this.getAppList().then((_) => {
//获取配置列表
this.autoChangeConfig(); //自动选择第一个配置文件
message.success('数据已更新');
});
}}
>
<Icon type="sync" />
刷新数据
</Button>
<Tooltip
title={`当前页面为静态页面,修改配置前需要刷新获取最新配置数据,以免覆盖其他人的配置数据`}
>
<QuestionCircleOutlined />
</Tooltip>
</Col>
<Col span={20} style={{ textAlign: 'right' }}>
<Button.Group>
<Button
type={'primary'}
onClick={() => {
that.setState({ showPublish: true });
}}
>
发布配置
</Button>
<Button
type={'danger'}
onClick={(e) => {
that.changeTab('status');
}}
>
重启列表
</Button>
<Button
type={'primary'}
onClick={(e) => {
that.setState({ showRollback: true });
}}
>
配置回滚
</Button>
<Button
onClick={() => {
that.getHistoryList();
that.setState({ showHistory: true });
}}
>
发布历史
</Button>
</Button.Group>
</Col>
</Row>
)}
{caid !== 0 && (
<Tabs
style={{
backgroundColor: '#fff',
marginTop: '5px',
marginLeft: '5px',
marginRight: '5px',
}}
activeKey={this.state.tab}
onChange={this.changeTab}
>
<TabPane
tab={
<span>
<div style={{ marginLeft: 10 }}>
<TableOutlined />
配置编辑
</div>
</span>
}
key="table"
>
<Row>
<Col style={{ marginTop: '-5px' }}>
<Button.Group>
<Button
style={{ marginLeft: '10px' }}
type="primary"
onClick={() => {
that.setState({ showAddItem: true });
that.getEnvResource();
}}
>
添加 Block
</Button>
</Button.Group>
</Col>
</Row>
<Row gutter={24}>
<Col span={24} style={{ marginTop: '10px' }}>
<Collapse
bordered={false}
defaultActiveKey={['application']}
expandIcon={({ isActive }) => (
<CaretRightOutlined rotate={isActive ? 90 : 0} />
)}
className="site-collapse-custom-collapse"
expandIconPosition="right"
>
{configItemList}
</Collapse>
</Col>
</Row>
</TabPane>
<TabPane
tab={
<span>
<FileOutlined />
发布预览
</span>
}
key="text"
>
<Spin spinning={this.state.loading} />
<Row>
<Col
style={{
textAlign: 'left',
marginLeft: '8px',
marginBottom: '8px',
fontSize: '18px',
}}
span={12}
>
{file_name}
</Col>
<Col
style={{
textAlign: 'right',
marginRight: '8px',
marginBottom: '8px',
}}
span={11}
>
<Button.Group>
<span>
<Button
onClick={(e) => {
copy(configText);
message.success('已复制到剪切板');
}}
>
复制
</Button>
</span>
</Button.Group>
</Col>
</Row>
<div className={'configEditor'}>
<ReactCodeMirror
ref="editor"
value={configText}
options={{
mode: 'text/x-toml',
lineNumbers: true,
autoMatchParens: true,
lineWrapping: true,
readOnly: this.state.readOnly,
}}
onChange={(editor, data, value) => {
this.configInputText = editor.getValue();
}}
/>
</div>
</TabPane>
<TabPane
tab={
<span>
<FileDoneOutlined />
<Badge count={changeNum} overflowCount={9999} offset={[0, 18]}>
变更历史
</Badge>
</span>
}
key="history"
>
<Table
columns={changListCols}
dataSource={configChangeList}
size={'small'}
pagination={false}
rowKey={'id'}
/>
</TabPane>
<TabPane
tab={
<span>
<HddOutlined />
实例列表
</span>
}
key="status"
>
<Spin spinning={this.state.showStatusSync} />
<div style={{ marginLeft: '10px' }}>
配置文件路径:
<span
style={{
marginLeft: '8px',
marginRight: '8px',
fontSize: '16px',
}}
>{`${file_path}`}</span>
<a
onClick={(e) => {
copy(`${file_path}`);
message.success('已复制,请重新下发配置文件生效');
}}
>
点击复制
</a>
<Tooltip
title={`修改systemd下发的配置文件路径,由原来的项目相对路径改为配置中心的路径`}
>
<Icon style={{ marginLeft: '6px' }} type="question-circle" />
</Tooltip>
<span
style={{
float: 'right',
marginRight: '8px',
marginBottom: '8px',
}}
>
<Button
type={'primary'}
onClick={(e) => {
that.setState({
showStatusSync: true,
});
SyncConfigNodes({ caid: caid })
.then((rs) => {
that.getConfigStatusList();
message.success('同步成功');
that.setState({
showStatusSync: false,
});
})
.catch((err) => {
that.setState({
showStatusSync: false,
});
});
}}
>
刷新实例状态
</Button>
</span>
<Table
size="small"
columns={statusCol}
dataSource={statusList}
pagination={false}
/>
</div>
</TabPane>
</Tabs>
)}
<NewItemForm
show={this.state.showAddItem}
cancel={() => {
this.setState({ showAddItem: false });
}}
item={{
resourceData: resourceData,
}}
env={this.state.env}
zone_code={this.state.zone_code}
submit={this.addItem}
caid={this.state.caid}
zone_codeMap={zone_codeMap}
/>
<UpdateItemForm
show={this.state.showUpdateItem}
env={this.state.env}
zone_code={this.state.zone_code}
cancel={() => {
this.setState({ showUpdateItem: false });
}}
changeResource={(e) => {
that.setState({
selectIsResource: e,
});
}}
changeResourceID={(e) => {
that.setState({
selectResourceID: e * 1,
});
}}
caid={this.state.caid}
submit={this.updateItem}
item={{
id: selectItemID,
key: selectKey,
value: selectValue,
comment: selectComment,
is_resource: selectIsResource,
resource_id: selectResourceID,
resourceData: resourceData,
is_public: selectIsPublic,
}}
zone_codeMap={zone_codeMap}
/>
<PublishForm
show={this.state.showPublish}
publish_loading={this.state.publish_loading}
file_name={file_name}
item={{ caid }}
cancel={() => {
this.setState({ showPublish: false });
}}
submit={this.publishItem}
/>
<NewConfigFile
show={this.state.showConfigFile}
cancel={() => {
this.setState({ showConfigFile: false });
}}
submit={this.AddConfigFile}
zoneList={this.props.zoneList}
/>
<HistoryList
show={this.state.showHistory}
cancel={() => {
this.setState({ showHistory: false });
this.props.dispatch({
type: 'confuNew/setPublishChangeData',
payload: {},
});
}}
list={configHistoryList}
/>
<Preview
oldCode={configText}
newCode={this.configInputText}
show={this.state.showPreview}
cancel={() => {
this.setState({ showPreview: false });
}}
/>
{this.state.showRollback && (
<RollbackView
caid={caid}
show={this.state.showRollback}
rollback={() => {
that.getConfigList();
}}
cancel={() => {
this.setState({ showRollback: false });
}}
/>
)}
<FileManageView
show={this.state.showFileManage}
app_name={appName}
app_id={aid}
env={env}
zone_code={this.state.zone_code}
cancel={() => {
this.setState({ showFileManage: false }, () => {
this.autoChangeConfig;
});
}}
/>
<FileDiffView
show={this.state.showFileDiff}
originCid={this.state.caid}
rafeCid={0}
appConfigList={this.props.appConfigList}
cancel={() => {
this.setState({ showFileDiff: false });
}}
/>
</div>
</Content>
<Modal
title="操作面板"
visible={this.state.visible}
onOk={(e) => {
this.refreshState();
this.setState({ visible: false, result_list: [] });
}}
okText={'确定'}
onCancel={(e) => {
this.refreshState();
this.setState({ visible: false, result_list: [] });
}}
cancelText={'关闭'}
>
<div>
<Spin spinning={this.state.loading} />
</div>
<div style={{ backgroundColor: 'black', borderRadius: '5px' }}>
{result_list.map((v, i) => {
const { name, content } = v;
return (
<p key={i} style={{ color: 'green' }}>
{content}
</p>
);
})}
</div>
</Modal>
</Layout>
);
}
Example #9
Source File: FileSystem.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
render: (value, item) => {
let icon;
if (item['isDir']) {
icon = <FolderTwoTone/>;
} else {
if (item['isLink']) {
icon = <LinkOutlined/>;
} else {
const fileExtension = item['name'].split('.').pop().toLowerCase();
switch (fileExtension) {
case "doc":
case "docx":
icon = <FileWordOutlined/>;
break;
case "xls":
case "xlsx":
icon = <FileExcelOutlined/>;
break;
case "bmp":
case "jpg":
case "jpeg":
case "png":
case "tif":
case "gif":
case "pcx":
case "tga":
case "exif":
case "svg":
case "psd":
case "ai":
case "webp":
icon = <FileImageOutlined/>;
break;
case "md":
icon = <FileMarkdownOutlined/>;
break;
case "pdf":
icon = <FilePdfOutlined/>;
break;
case "txt":
icon = <FileTextOutlined/>;
break;
case "zip":
case "gz":
case "tar":
case "tgz":
icon = <FileZipOutlined/>;
break;
default:
icon = <FileOutlined/>;
break;
}
}
}
return <span className={'dode'}>{icon} {item['name']}</span>;
},
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.name.localeCompare(b.name);
},
sortDirections: ['descend', 'ascend'],
},
{
title: '大小',
dataIndex: 'size',
key: 'size',
render: (value, item) => {
if (!item['isDir'] && !item['isLink']) {
return <span className={'dode'}>{renderSize(value)}</span>;
}
return <span className={'dode'}/>;
},
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.size - b.size;
},
}, {
title: '修改日期',
dataIndex: 'modTime',
key: 'modTime',
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.modTime.localeCompare(b.modTime);
},
sortDirections: ['descend', 'ascend'],
render: (value, item) => {
return <span className={'dode'}>{value}</span>;
},
}, {
title: '属性',
dataIndex: 'mode',
key: 'mode',
render: (value, item) => {
return <span className={'dode'}>{value}</span>;
},
}, {
title: '操作',
dataIndex: 'action',
key: 'action',
width: 210,
render: (value, item) => {
if (item['key'] === '..') {
return undefined;
}
let disableDownload = !this.state.download;
let disableEdit = !this.state.edit;
if (item['isDir'] || item['isLink']) {
disableDownload = true;
disableEdit = true
}
return (
<>
<Button type="link" size='small' disabled={disableEdit}
onClick={() => this.showEditor(item['name'], item['key'])}>
编辑
</Button>
<Button type="link" size='small' disabled={disableDownload} onClick={async () => {
download(`${server}/${this.state.storageType}/${this.state.storageId}/download?file=${window.encodeURIComponent(item['key'])}&X-Auth-Token=${getToken()}&t=${new Date().getTime()}`);
}}>
下载
</Button>
<Button type={'link'} size={'small'} disabled={!this.state.rename} onClick={() => {
this.setState({
renameVisible: true,
currentFileKey: item['key']
})
}}>重命名</Button>
<Popconfirm
title="您确认要删除此文件吗?"
onConfirm={async () => {
await this.delete(item['key']);
await this.refresh();
}}
okText="是"
cancelText="否"
>
<Button type={'link'} size={'small'} disabled={!this.state.delete} danger>删除</Button>
</Popconfirm>
</>
);
},
}
];
const {selectedRowKeys} = this.state;
const rowSelection = {
selectedRowKeys,
onChange: (selectedRowKeys) => {
this.setState({selectedRowKeys});
},
getCheckboxProps: (record) => ({
disabled: record['disabled'],
}),
};
let hasSelected = selectedRowKeys.length > 0;
if (hasSelected) {
if (!this.state.delete) {
hasSelected = false;
}
}
const title = (
<div className='fs-header'>
<div className='fs-header-left'>
<Input value={this.state.currentDirectoryInput} onChange={this.handleCurrentDirectoryInputChange}
onPressEnter={this.handleCurrentDirectoryInputPressEnter}/>
</div>
<div className='fs-header-right'>
<Space>
<div className='fs-header-right-item'>
<Tooltip title="创建文件夹">
<Button type="primary" size="small"
disabled={!this.state.upload}
icon={<FolderAddOutlined/>}
onClick={() => {
this.setState({
mkdirVisible: true
})
}} ghost/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="上传文件">
<Button type="primary" size="small"
icon={<CloudUploadOutlined/>}
disabled={!this.state.upload}
onClick={() => {
window.document.getElementById('file-upload').click();
}} ghost/>
<input type="file" id="file-upload" style={{display: 'none'}}
onChange={this.handleUploadFile} multiple/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="上传文件夹">
<Button type="primary" size="small"
icon={<UploadOutlined/>}
disabled={!this.state.upload}
onClick={() => {
window.document.getElementById('dir-upload').click();
}} ghost/>
<input type="file" id="dir-upload" style={{display: 'none'}}
onChange={this.handleUploadDir} webkitdirectory='' multiple/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="刷新">
<Button type="primary" size="small"
icon={<ReloadOutlined/>}
onClick={this.refresh}
ghost/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="批量删除">
<Button type="primary" size="small" ghost danger disabled={!hasSelected}
icon={<DeleteOutlined/>}
loading={this.state.delBtnLoading}
onClick={() => {
let rowKeys = this.state.selectedRowKeys;
const content = <div>
您确定要删除选中的<Text style={{color: '#1890FF'}}
strong>{rowKeys.length}</Text>条记录吗?
</div>;
confirm({
icon: <ExclamationCircleOutlined/>,
content: content,
onOk: async () => {
for (let i = 0; i < rowKeys.length; i++) {
if (rowKeys[i] === '..') {
continue;
}
await this.delete(rowKeys[i]);
}
this.refresh();
},
onCancel() {
},
});
}}>
</Button>
</Tooltip>
</div>
</Space>
</div>
</div>
);
return (
<div>
<Card title={title} bordered={true} size="small" style={{minHeight: this.state.minHeight}}>
<Table columns={columns}
rowSelection={rowSelection}
dataSource={this.state.files}
size={'small'}
pagination={false}
loading={this.state.loading}
onRow={record => {
return {
onDoubleClick: event => {
if (record['isDir'] || record['isLink']) {
if (record['path'] === '..') {
// 获取当前目录的上级目录
let currentDirectory = this.state.currentDirectory;
let parentDirectory = currentDirectory.substring(0, currentDirectory.lastIndexOf('/'));
this.loadFiles(parentDirectory);
} else {
this.loadFiles(record['path']);
}
} else {
}
},
};
}}
/>
</Card>
{
this.state.mkdirVisible ?
<Modal
title="创建文件夹"
visible={this.state.mkdirVisible}
okButtonProps={{form: 'mkdir-form', key: 'submit', htmlType: 'submit'}}
onOk={() => {
this.mkdirFormRef.current
.validateFields()
.then(async values => {
this.mkdirFormRef.current.resetFields();
let params = {
'dir': this.state.currentDirectory + '/' + values['dir']
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/${this.state.storageType}/${this.state.storageId}/mkdir?${paramStr}`);
if (result.code === 1) {
message.success('创建成功');
this.loadFiles(this.state.currentDirectory);
} else {
message.error(result.message);
}
this.setState({
confirmLoading: false,
mkdirVisible: false
})
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
mkdirVisible: false
})
}}
>
<Form ref={this.mkdirFormRef} id={'mkdir-form'}>
<Form.Item name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
{
this.state.renameVisible ?
<Modal
title="重命名"
visible={this.state.renameVisible}
okButtonProps={{form: 'rename-form', key: 'submit', htmlType: 'submit'}}
onOk={() => {
this.renameFormRef.current
.validateFields()
.then(async values => {
this.renameFormRef.current.resetFields();
try {
let currentDirectory = this.state.currentDirectory;
if (!currentDirectory.endsWith("/")) {
currentDirectory += '/';
}
let params = {
'oldName': this.state.currentFileKey,
'newName': currentDirectory + values['newName'],
}
if (params['oldName'] === params['newName']) {
message.success('重命名成功');
return;
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/${this.state.storageType}/${this.state.storageId}/rename?${paramStr}`);
if (result['code'] === 1) {
message.success('重命名成功');
this.refresh();
} else {
message.error(result.message);
}
} finally {
this.setState({
confirmLoading: false,
renameVisible: false
})
}
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
renameVisible: false
})
}}
>
<Form id={'rename-form'}
ref={this.renameFormRef}
initialValues={{newName: getFileName(this.state.currentFileKey)}}>
<Form.Item name='newName' rules={[{required: true, message: '请输入新的名称'}]}>
<Input autoComplete="off" placeholder="新的名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
<Modal
title={"编辑 " + this.state.fileName}
className='modal-no-padding'
visible={this.state.editorVisible}
destroyOnClose={true}
width={window.innerWidth * 0.8}
centered={true}
okButtonProps={{form: 'rename-form', key: 'submit', htmlType: 'submit'}}
onOk={this.edit}
confirmLoading={this.state.confirmLoading}
onCancel={this.hideEditor}
>
<MonacoEditor
language="javascript"
height={window.innerHeight * 0.8}
theme="vs-dark"
value={this.state.fileContent}
options={{
selectOnLineNumbers: true
}}
editorDidMount={(editor, monaco) => {
console.log('editorDidMount', editor);
editor.focus();
}}
onChange={(newValue, e) => {
console.log('onChange', newValue, e);
this.setState(
{
fileContent: newValue
}
)
}}
/>
</Modal>
</div>
);
}
Example #10
Source File: index.js From getlink-next with MIT License | 4 votes |
App = ({ user, isAdmin, isDev }) => {
const [type, setType] = useState('image');
const handleTypeChange = useCallback((e) => {
setType(e.key);
}, []);
return (
<Context.Provider value={{ user, isAdmin, isDev }}>
<Layout style={{ height: '100%', flexDirection: 'row' }}>
<HTMLHead />
<Sider
breakpoint="lg"
collapsedWidth={0}
>
<a className="logo" href="https://github.com/int64ago/getlink-next" target="_blank">
Get Link!
</a>
<Menu
theme="dark"
mode="inline"
selectedKeys={[type]}
onClick={handleTypeChange}
>
<Menu.Item key="image">
<FileImageOutlined />IMAGE
</Menu.Item>
<Menu.Item key="video">
<VideoCameraOutlined />VIDEO
</Menu.Item>
<Menu.Item key="file">
<FileOutlined />FILE
</Menu.Item>
<Menu.Item key="placeholder">
<ContainerOutlined />Placeholder
</Menu.Item>
<Menu.Item key="qrcode">
<QrcodeOutlined />QR Code
</Menu.Item>
<Menu.Item key="urlshorten">
<LinkOutlined />URL Shortener
</Menu.Item>
</Menu>
</Sider>
<Layout style={{ background: '#fff', overflow: 'hidden' }}>
<Header>
{user ? (
<Dropdown overlay={(
<Menu>
<Menu.Item>
<a href="/api/logout">Logout</a>
</Menu.Item>
</Menu>
)}>
<Avatar src={user.picture} />
</Dropdown>
) : (
<div>
<Button type="link" href="/api/login">Login</Button>
</div>
)}
</Header>
<Content
style={{
padding: 24,
height: '100%',
background: '#fff',
overflow: 'auto',
}}
>
{type === 'image' && <Uploader type="image" />}
{type === 'video' && <Uploader type="video" />}
{type === 'file' && <Uploader type="file" />}
{type === 'qrcode' && <QRCode />}
{type === 'urlshorten' && <ShortUrl />}
{type === 'placeholder' && <Placeholder />}
</Content>
</Layout>
</Layout>
</Context.Provider>
);
}