Add asset import/export and local dev server setup
- Configure Vite dev server with localhost binding and public asset proxy - Add npm scripts for concurrent Laravel/Vite development (dev:local, dev:test) - Implement asset import/export in ACP via ZIP file upload/download - Create AssetController for asset management endpoints - Add asset management UI tab in admin panel
This commit is contained in:
@@ -43,6 +43,8 @@ import {
|
||||
createAttachmentExtension,
|
||||
updateAttachmentExtension,
|
||||
deleteAttachmentExtension,
|
||||
exportAssets,
|
||||
importAssets,
|
||||
} from '../api/client'
|
||||
|
||||
const StatusIcon = ({ status = 'bad', tooltip }) => {
|
||||
@@ -222,6 +224,10 @@ function Acp({ isAdmin }) {
|
||||
const [systemCliChecking, setSystemCliChecking] = useState(false)
|
||||
const [systemCliError, setSystemCliError] = useState('')
|
||||
const [systemCliToast, setSystemCliToast] = useState({ show: false, variant: 'success', message: '' })
|
||||
const [assetExporting, setAssetExporting] = useState(false)
|
||||
const [assetImporting, setAssetImporting] = useState(false)
|
||||
const [assetError, setAssetError] = useState('')
|
||||
const [assetToast, setAssetToast] = useState({ show: false, variant: 'success', message: '' })
|
||||
const settingsDetailMap = {
|
||||
forum_name: 'forumName',
|
||||
default_theme: 'defaultTheme',
|
||||
@@ -787,6 +793,44 @@ function Acp({ isAdmin }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleAssetExport = async () => {
|
||||
setAssetExporting(true)
|
||||
setAssetError('')
|
||||
try {
|
||||
await exportAssets()
|
||||
setAssetToast({ show: true, variant: 'success', message: 'Assets exported successfully' })
|
||||
} catch (err) {
|
||||
setAssetError(err.message)
|
||||
setAssetToast({ show: true, variant: 'danger', message: 'Failed to export assets' })
|
||||
} finally {
|
||||
setAssetExporting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAssetImport = async (file) => {
|
||||
if (!file) return
|
||||
setAssetImporting(true)
|
||||
setAssetError('')
|
||||
try {
|
||||
await importAssets(file)
|
||||
setAssetToast({ show: true, variant: 'success', message: 'Assets imported successfully' })
|
||||
window.location.reload()
|
||||
} catch (err) {
|
||||
setAssetError(err.message)
|
||||
setAssetToast({ show: true, variant: 'danger', message: 'Failed to import assets' })
|
||||
} finally {
|
||||
setAssetImporting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const assetImportDropzone = useDropzone({
|
||||
accept: {
|
||||
'application/zip': ['.zip'],
|
||||
},
|
||||
maxFiles: 1,
|
||||
onDrop: (files) => handleAssetImport(files[0]),
|
||||
})
|
||||
|
||||
const faviconIcoDropzone = useDropzone({
|
||||
accept: {
|
||||
'image/png': ['.png'],
|
||||
@@ -4337,6 +4381,78 @@ function Acp({ isAdmin }) {
|
||||
</Col>
|
||||
</Row>
|
||||
</Tab>
|
||||
<Tab eventKey="assets" title="Assets">
|
||||
<Row className="g-4">
|
||||
<Col xs={12}>
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">Asset Management</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{assetToast.show && (
|
||||
<div
|
||||
className={`alert alert-${assetToast.variant} mb-3`}
|
||||
role="alert"
|
||||
>
|
||||
{assetToast.message}
|
||||
</div>
|
||||
)}
|
||||
{assetError && (
|
||||
<div className="alert alert-danger mb-3" role="alert">
|
||||
{assetError}
|
||||
</div>
|
||||
)}
|
||||
<div className="row mb-4">
|
||||
<div className="col-md-6">
|
||||
<h6 className="mb-2">Export Assets</h6>
|
||||
<p className="text-muted mb-3">
|
||||
Download all logos, favicons, and other assets as a ZIP file.
|
||||
</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleAssetExport}
|
||||
disabled={assetExporting}
|
||||
>
|
||||
{assetExporting ? 'Exporting...' : 'Export Assets'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<h6 className="mb-2">Import Assets</h6>
|
||||
<p className="text-muted mb-3">
|
||||
Upload a ZIP file containing assets to import them.
|
||||
</p>
|
||||
<div
|
||||
{...assetImportDropzone.getRootProps()}
|
||||
className="border border-dashed rounded p-4 text-center"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
backgroundColor: assetImportDropzone.isDragActive
|
||||
? 'rgba(0, 0, 0, 0.05)'
|
||||
: 'transparent',
|
||||
}}
|
||||
>
|
||||
<input {...assetImportDropzone.getInputProps()} />
|
||||
{assetImporting ? (
|
||||
<p className="mb-0">Importing assets...</p>
|
||||
) : (
|
||||
<>
|
||||
<i
|
||||
className="bi bi-cloud-upload"
|
||||
style={{ fontSize: '2rem', marginBottom: '0.5rem' }}
|
||||
></i>
|
||||
<p className="mb-0">
|
||||
Drag and drop a ZIP file here, or click to select
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<Modal show={showModal} onHide={handleReset} centered size="lg">
|
||||
<Modal.Header closeButton closeVariant="white">
|
||||
|
||||
Reference in New Issue
Block a user