Refine ACP general settings navigation and tabbed layout
This commit is contained in:
@@ -97,6 +97,7 @@ function Acp({ isAdmin }) {
|
||||
const [systemStatus, setSystemStatus] = useState(null)
|
||||
const [systemLoading, setSystemLoading] = useState(false)
|
||||
const [systemError, setSystemError] = useState('')
|
||||
const [generalSection, setGeneralSection] = useState('overview')
|
||||
const [systemSection, setSystemSection] = useState('overview')
|
||||
const [systemUpdateSection, setSystemUpdateSection] = useState('insite')
|
||||
const [usersPage, setUsersPage] = useState(1)
|
||||
@@ -1187,6 +1188,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
<i className="bi bi-arrow-clockwise me-2" aria-hidden="true" />
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1229,6 +1231,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
<i className="bi bi-arrow-repeat me-2" aria-hidden="true" />
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
@@ -2896,134 +2899,191 @@ function Acp({ isAdmin }) {
|
||||
return (
|
||||
<Container fluid className="bb-acp py-4">
|
||||
<h2 className="mb-4">{t('acp.title')}</h2>
|
||||
<Tabs defaultActiveKey="general" className="mb-3">
|
||||
<Tabs
|
||||
defaultActiveKey="general"
|
||||
className="mb-0"
|
||||
contentClassName="pt-2"
|
||||
>
|
||||
<Tab eventKey="general" title={t('acp.general')}>
|
||||
<div className="border border-1 border-dark border-top-0 rounded-bottom p-3">
|
||||
<Row className="g-4">
|
||||
<Col xs={12} lg="auto">
|
||||
<div className="bb-acp-sidebar">
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.quick_access')}</div>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.users')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.groups')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.forums')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.ranks')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.attachments')}
|
||||
</button>
|
||||
<div className="bb-acp-sidebar">
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.general')}</div>
|
||||
<div className="list-group">
|
||||
<button
|
||||
type="button"
|
||||
className={`list-group-item list-group-item-action ${
|
||||
generalSection === 'overview' ? 'is-active' : ''
|
||||
}`}
|
||||
onClick={() => setGeneralSection('overview')}
|
||||
>
|
||||
<i className="bi bi-speedometer2 me-2" aria-hidden="true" />
|
||||
Overview
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`list-group-item list-group-item-action ${
|
||||
generalSection === 'settings' ? 'is-active' : ''
|
||||
}`}
|
||||
onClick={() => setGeneralSection('settings')}
|
||||
>
|
||||
<i className="bi bi-sliders2 me-2" aria-hidden="true" />
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.board_configuration')}</div>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action is-active">
|
||||
{t('acp.general')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.forums')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.users')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.client_communication')}</div>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.authentication')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.email_settings')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.server_configuration')}</div>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.security_settings')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
{t('acp.search_settings')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={12} lg>
|
||||
<div className="bb-acp-panel mb-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<h5 className="mb-0">{t('acp.statistics')}</h5>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={refreshBoardStats}
|
||||
disabled={boardStatsLoading}
|
||||
>
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{boardStatsError && <p className="text-danger mb-2">{boardStatsError}</p>}
|
||||
{boardStatsLoading && <p className="bb-muted mb-0">{t('acp.loading')}</p>}
|
||||
{!boardStatsLoading && (
|
||||
<div className="bb-acp-stats-grid">
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('stats.statistic')}</th>
|
||||
<th>{t('stats.value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statsLeft.map((stat) => (
|
||||
<tr key={stat.label}>
|
||||
<td>{stat.label}</td>
|
||||
<td className="bb-acp-stats-value">{stat.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('stats.statistic')}</th>
|
||||
<th>{t('stats.value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statsRight.map((stat) => (
|
||||
<tr key={stat.label}>
|
||||
<td>{stat.label}</td>
|
||||
<td className="bb-acp-stats-value">{stat.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{generalSection === 'overview' && (
|
||||
<>
|
||||
<div className="bb-acp-panel mb-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<h5 className="mb-0">{t('acp.statistics')}</h5>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
className="d-inline-flex align-items-center gap-2 px-3"
|
||||
onClick={refreshBoardStats}
|
||||
disabled={boardStatsLoading}
|
||||
>
|
||||
<i className="bi bi-arrow-clockwise" aria-hidden="true" />
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{generalError && <p className="text-danger">{generalError}</p>}
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">{t('acp.general_settings')}</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<Form onSubmit={handleGeneralSave} className="bb-acp-general">
|
||||
<Row className="g-3">
|
||||
<div className="bb-acp-panel-body">
|
||||
{boardStatsError && <p className="text-danger mb-2">{boardStatsError}</p>}
|
||||
{boardStatsLoading && <p className="bb-muted mb-0">{t('acp.loading')}</p>}
|
||||
{!boardStatsLoading && (
|
||||
<div className="bb-acp-stats-grid">
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('stats.statistic')}</th>
|
||||
<th>{t('stats.value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statsLeft.map((stat) => (
|
||||
<tr key={stat.label}>
|
||||
<td>{stat.label}</td>
|
||||
<td className="bb-acp-stats-value">{stat.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('stats.statistic')}</th>
|
||||
<th>{t('stats.value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{statsRight.map((stat) => (
|
||||
<tr key={stat.label}>
|
||||
<td>{stat.label}</td>
|
||||
<td className="bb-acp-stats-value">{stat.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel mt-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h5 className="mb-1">{t('acp.admin_log_title')}</h5>
|
||||
<p className="bb-muted mb-0">{t('acp.admin_log_hint')}</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
className="d-inline-flex align-items-center gap-2 px-3"
|
||||
onClick={refreshAuditLogs}
|
||||
disabled={auditLoading}
|
||||
>
|
||||
<i className="bi bi-arrow-clockwise" aria-hidden="true" />
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{auditLoading && <p className="bb-muted mb-0">{t('acp.loading')}</p>}
|
||||
{!auditLoading && recentAdminLogs.length === 0 && (
|
||||
<p className="bb-muted mb-0">{t('admin_log.empty')}</p>
|
||||
)}
|
||||
{!auditLoading && recentAdminLogs.length > 0 && (
|
||||
<div className="bb-acp-admin-log">
|
||||
<table className="bb-acp-admin-log__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('admin_log.username')}</th>
|
||||
<th>{t('admin_log.user_ip')}</th>
|
||||
<th>{t('admin_log.time')}</th>
|
||||
<th>{t('admin_log.action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recentAdminLogs.map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td>{entry.user?.name || entry.user?.email || '—'}</td>
|
||||
<td>{entry.ip_address || '—'}</td>
|
||||
<td>{formatDateTime(entry.created_at)}</td>
|
||||
<td>{formatAuditAction(entry.action)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-link p-0"
|
||||
onClick={() => {
|
||||
const target = document.querySelector('[data-rb-event-key="audit"]')
|
||||
if (target) target.click()
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-box-arrow-up-right me-2" aria-hidden="true" />
|
||||
{t('acp.view_admin_log')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{generalSection === 'settings' && (
|
||||
<>
|
||||
{generalError && <p className="text-danger">{generalError}</p>}
|
||||
<Tabs
|
||||
defaultActiveKey="general"
|
||||
className="mb-0"
|
||||
contentClassName="pt-2"
|
||||
>
|
||||
<Tab eventKey="general" title={t('acp.general')}>
|
||||
<div className="border border-1 border-dark border-top-0 rounded-bottom p-3">
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">{t('acp.general_settings')}</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<Form onSubmit={handleGeneralSave} className="bb-acp-general">
|
||||
<Row className="g-3">
|
||||
<Col lg={6}>
|
||||
<Form.Group>
|
||||
<Form.Label>{t('acp.forum_name')}</Form.Label>
|
||||
@@ -3303,86 +3363,72 @@ function Acp({ isAdmin }) {
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
</Col>
|
||||
<Col xs={12} className="d-flex justify-content-end">
|
||||
<Button
|
||||
type="submit"
|
||||
className="bb-accent-button"
|
||||
disabled={generalSaving || generalUploading}
|
||||
>
|
||||
{generalSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel mt-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h5 className="mb-1">{t('acp.admin_log_title')}</h5>
|
||||
<p className="bb-muted mb-0">{t('acp.admin_log_hint')}</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={refreshAuditLogs}
|
||||
disabled={auditLoading}
|
||||
>
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{auditLoading && <p className="bb-muted mb-0">{t('acp.loading')}</p>}
|
||||
{!auditLoading && recentAdminLogs.length === 0 && (
|
||||
<p className="bb-muted mb-0">{t('admin_log.empty')}</p>
|
||||
)}
|
||||
{!auditLoading && recentAdminLogs.length > 0 && (
|
||||
<div className="bb-acp-admin-log">
|
||||
<table className="bb-acp-admin-log__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('admin_log.username')}</th>
|
||||
<th>{t('admin_log.user_ip')}</th>
|
||||
<th>{t('admin_log.time')}</th>
|
||||
<th>{t('admin_log.action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recentAdminLogs.map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td>{entry.user?.name || entry.user?.email || '—'}</td>
|
||||
<td>{entry.ip_address || '—'}</td>
|
||||
<td>{formatDateTime(entry.created_at)}</td>
|
||||
<td>{formatAuditAction(entry.action)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-link p-0"
|
||||
onClick={() => {
|
||||
const target = document.querySelector('[data-rb-event-key="audit"]')
|
||||
if (target) target.click()
|
||||
}}
|
||||
>
|
||||
{t('acp.view_admin_log')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Col xs={12} className="d-flex justify-content-end">
|
||||
<Button
|
||||
type="submit"
|
||||
className="bb-accent-button"
|
||||
disabled={generalSaving || generalUploading}
|
||||
>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{generalSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="client-communication" title={t('acp.client_communication')}>
|
||||
<div className="border border-1 border-dark border-top-0 rounded-bottom p-3">
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">{t('acp.client_communication')}</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-3">Placeholder</p>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
<i className="bi bi-shield-lock me-2" aria-hidden="true" />
|
||||
{t('acp.authentication')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
<i className="bi bi-envelope me-2" aria-hidden="true" />
|
||||
{t('acp.email_settings')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="server-configuration" title={t('acp.server_configuration')}>
|
||||
<div className="border border-1 border-dark border-top-0 rounded-bottom p-3">
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">{t('acp.server_configuration')}</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-3">Placeholder</p>
|
||||
<div className="list-group">
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
<i className="bi bi-shield-check me-2" aria-hidden="true" />
|
||||
{t('acp.security_settings')}
|
||||
</button>
|
||||
<button type="button" className="list-group-item list-group-item-action">
|
||||
<i className="bi bi-search me-2" aria-hidden="true" />
|
||||
{t('acp.search_settings')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="forums" title={t('acp.forums')}>
|
||||
<p className="bb-muted">{t('acp.forums_hint')}</p>
|
||||
@@ -3482,6 +3528,7 @@ function Acp({ isAdmin }) {
|
||||
className="bb-accent-button"
|
||||
onClick={() => setShowRoleCreate(true)}
|
||||
>
|
||||
<i className="bi bi-plus-circle me-2" aria-hidden="true" />
|
||||
{t('group.create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -3573,6 +3620,7 @@ function Acp({ isAdmin }) {
|
||||
className="bb-accent-button"
|
||||
onClick={() => setShowRankCreate(true)}
|
||||
>
|
||||
<i className="bi bi-plus-circle me-2" aria-hidden="true" />
|
||||
{t('rank.create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -3735,6 +3783,7 @@ function Acp({ isAdmin }) {
|
||||
className="bb-accent-button"
|
||||
disabled={attachmentSettingsSaving}
|
||||
>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{attachmentSettingsSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -3750,6 +3799,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleSeedAttachmentDefaults}
|
||||
disabled={attachmentSeedSaving}
|
||||
>
|
||||
<i className="bi bi-database-add me-2" aria-hidden="true" />
|
||||
{attachmentSeedSaving
|
||||
? t('attachment.seed_in_progress')
|
||||
: t('attachment.seed_defaults')}
|
||||
@@ -3759,6 +3809,7 @@ function Acp({ isAdmin }) {
|
||||
variant="outline-secondary"
|
||||
onClick={handleAttachmentGroupExpandAll}
|
||||
>
|
||||
<i className="bi bi-arrows-expand me-2" aria-hidden="true" />
|
||||
{t('acp.expand_all')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -3766,6 +3817,7 @@ function Acp({ isAdmin }) {
|
||||
variant="outline-secondary"
|
||||
onClick={handleAttachmentGroupCollapseAll}
|
||||
>
|
||||
<i className="bi bi-arrows-collapse me-2" aria-hidden="true" />
|
||||
{t('acp.collapse_all')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -3773,6 +3825,7 @@ function Acp({ isAdmin }) {
|
||||
className="bb-accent-button"
|
||||
onClick={() => openAttachmentGroupModal()}
|
||||
>
|
||||
<i className="bi bi-folder-plus me-2" aria-hidden="true" />
|
||||
{t('attachment.group_create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -3787,6 +3840,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleSeedAttachmentDefaults}
|
||||
disabled={attachmentSeedSaving}
|
||||
>
|
||||
<i className="bi bi-database-add me-2" aria-hidden="true" />
|
||||
{attachmentSeedSaving
|
||||
? t('attachment.seed_in_progress')
|
||||
: t('attachment.seed_defaults')}
|
||||
@@ -3805,6 +3859,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleSeedAttachmentDefaults}
|
||||
disabled={attachmentSeedSaving}
|
||||
>
|
||||
<i className="bi bi-database-add me-2" aria-hidden="true" />
|
||||
{attachmentSeedSaving
|
||||
? t('attachment.seed_in_progress')
|
||||
: t('attachment.seed_defaults')}
|
||||
@@ -3815,6 +3870,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleAttachmentGroupAutoNest}
|
||||
disabled={attachmentSeedSaving}
|
||||
>
|
||||
<i className="bi bi-diagram-2 me-2" aria-hidden="true" />
|
||||
{attachmentSeedSaving
|
||||
? t('attachment.seed_in_progress')
|
||||
: t('attachment.group_auto_nest')}
|
||||
@@ -3856,6 +3912,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={refreshAuditLogs}
|
||||
disabled={auditLoading}
|
||||
>
|
||||
<i className="bi bi-arrow-clockwise me-2" aria-hidden="true" />
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -3898,6 +3955,7 @@ function Acp({ isAdmin }) {
|
||||
}`}
|
||||
onClick={() => setSystemSection('overview')}
|
||||
>
|
||||
<i className="bi bi-heart-pulse me-2" aria-hidden="true" />
|
||||
Health
|
||||
</button>
|
||||
<button
|
||||
@@ -3907,6 +3965,7 @@ function Acp({ isAdmin }) {
|
||||
}`}
|
||||
onClick={() => setSystemSection('updates')}
|
||||
>
|
||||
<i className="bi bi-arrow-repeat me-2" aria-hidden="true" />
|
||||
Updates
|
||||
</button>
|
||||
</div>
|
||||
@@ -3930,6 +3989,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleVersionCheck}
|
||||
disabled={versionChecking}
|
||||
>
|
||||
<i className="bi bi-arrow-clockwise me-2" aria-hidden="true" />
|
||||
{t('version.recheck')}
|
||||
</Button>
|
||||
{systemUpdateAvailable && (
|
||||
@@ -3939,6 +3999,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setUpdateModalOpen(true)}
|
||||
disabled={updateRunning}
|
||||
>
|
||||
<i className="bi bi-download me-2" aria-hidden="true" />
|
||||
{t('version.update_now')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -3953,6 +4014,7 @@ function Acp({ isAdmin }) {
|
||||
variant={systemUpdateSection === 'insite' ? 'primary' : 'dark'}
|
||||
onClick={() => setSystemUpdateSection('insite')}
|
||||
>
|
||||
<i className="bi bi-activity me-2" aria-hidden="true" />
|
||||
Live Update
|
||||
</Button>
|
||||
<Button
|
||||
@@ -3960,6 +4022,7 @@ function Acp({ isAdmin }) {
|
||||
variant={systemUpdateSection === 'cli' ? 'primary' : 'dark'}
|
||||
onClick={() => setSystemUpdateSection('cli')}
|
||||
>
|
||||
<i className="bi bi-terminal me-2" aria-hidden="true" />
|
||||
CLI
|
||||
</Button>
|
||||
<Button
|
||||
@@ -3967,6 +4030,7 @@ function Acp({ isAdmin }) {
|
||||
variant={systemUpdateSection === 'ci' ? 'primary' : 'dark'}
|
||||
onClick={() => setSystemUpdateSection('ci')}
|
||||
>
|
||||
<i className="bi bi-diagram-3 me-2" aria-hidden="true" />
|
||||
CI/CD
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
@@ -4190,9 +4254,11 @@ function Acp({ isAdmin }) {
|
||||
</Form.Group>
|
||||
<div className="d-flex gap-2 justify-content-between">
|
||||
<Button type="button" variant="outline-secondary" onClick={handleReset}>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button type="submit" className="bb-accent-button">
|
||||
<i className="bi bi-check2-circle me-2" aria-hidden="true" />
|
||||
{selectedId ? t('acp.save') : t('acp.create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4405,9 +4471,11 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setShowUserModal(false)}
|
||||
disabled={userSaving}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button type="submit" className="bb-accent-button" variant="dark" disabled={userSaving}>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{userSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4495,9 +4563,11 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setShowRoleModal(false)}
|
||||
disabled={roleSaving}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button type="submit" className="bb-accent-button" variant="dark" disabled={roleSaving}>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{roleSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4548,6 +4618,7 @@ function Acp({ isAdmin }) {
|
||||
variant="dark"
|
||||
disabled={roleSaving || !roleFormName.trim()}
|
||||
>
|
||||
<i className="bi bi-plus-circle me-2" aria-hidden="true" />
|
||||
{roleSaving ? t('form.saving') : t('group.create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4717,9 +4788,11 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setShowRankModal(false)}
|
||||
disabled={rankSaving}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button type="submit" className="bb-accent-button" variant="dark" disabled={rankSaving}>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{rankSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4745,6 +4818,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setUpdateModalOpen(false)}
|
||||
disabled={updateRunning}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -4752,6 +4826,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={handleRunUpdate}
|
||||
disabled={updateRunning}
|
||||
>
|
||||
<i className="bi bi-download me-2" aria-hidden="true" />
|
||||
{updateRunning ? t('version.updating') : t('version.update_now')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
@@ -4863,6 +4938,7 @@ function Acp({ isAdmin }) {
|
||||
variant="dark"
|
||||
disabled={rankSaving || !rankFormName.trim()}
|
||||
>
|
||||
<i className="bi bi-award me-2" aria-hidden="true" />
|
||||
{rankSaving ? t('form.saving') : t('rank.create')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -4951,6 +5027,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setShowAttachmentGroupModal(false)}
|
||||
disabled={attachmentGroupSaving}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -4959,6 +5036,7 @@ function Acp({ isAdmin }) {
|
||||
variant="dark"
|
||||
disabled={attachmentGroupSaving}
|
||||
>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{attachmentGroupSaving ? t('form.saving') : t('acp.save')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -5038,6 +5116,7 @@ function Acp({ isAdmin }) {
|
||||
onClick={() => setShowAttachmentExtensionModal(false)}
|
||||
disabled={attachmentExtensionSaving}
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -5046,6 +5125,7 @@ function Acp({ isAdmin }) {
|
||||
variant="dark"
|
||||
disabled={attachmentExtensionSaving || !newAttachmentExtension.extension.trim()}
|
||||
>
|
||||
<i className="bi bi-floppy me-2" aria-hidden="true" />
|
||||
{attachmentExtensionSaving
|
||||
? t('form.saving')
|
||||
: attachmentExtensionEdit
|
||||
@@ -5074,18 +5154,20 @@ function Acp({ isAdmin }) {
|
||||
variant="outline-secondary"
|
||||
onClick={() => setShowAttachmentExtensionDelete(false)}
|
||||
disabled={attachmentExtensionSaving}
|
||||
>
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
>
|
||||
<i className="bi bi-x-circle me-2" aria-hidden="true" />
|
||||
{t('acp.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
className="bb-accent-button"
|
||||
variant="dark"
|
||||
onClick={confirmAttachmentExtensionDelete}
|
||||
disabled={attachmentExtensionSaving}
|
||||
>
|
||||
{attachmentExtensionSaving ? t('form.saving') : t('acp.delete')}
|
||||
</Button>
|
||||
>
|
||||
<i className="bi bi-trash me-2" aria-hidden="true" />
|
||||
{attachmentExtensionSaving ? t('form.saving') : t('acp.delete')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user