Refine ACP general settings navigation and tabbed layout
All checks were successful
CI/CD Pipeline / deploy (push) Successful in 31s
CI/CD Pipeline / promote_stable (push) Successful in 2s

This commit is contained in:
2026-02-28 19:13:33 +01:00
parent 94f665192d
commit ef84b73cb5
12 changed files with 409 additions and 211 deletions

View File

@@ -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>