- Added standard Laravel directory structure and configuration. - Included Svelte and Tailwind configuration for the admin interface. - Added core PHPUnit and testing scripts.
151 lines
5.4 KiB
Svelte
151 lines
5.4 KiB
Svelte
<script>
|
|
let { themes, activeTheme, adminPath } = $props();
|
|
|
|
let currentActive = $state(null);
|
|
$effect(() => {
|
|
currentActive = activeTheme;
|
|
});
|
|
let processing = $state(null);
|
|
let uploading = $state(false);
|
|
let fileInput;
|
|
|
|
async function activateTheme(themeSlug) {
|
|
if (processing) return;
|
|
|
|
processing = themeSlug;
|
|
|
|
try {
|
|
const baseUrl = window.location.origin.endsWith('/') ? window.location.origin : `${window.location.origin}/`;
|
|
const path = adminPath.startsWith('/') ? adminPath.substring(1) : adminPath;
|
|
const url = `${baseUrl}${path}/themes/activate`;
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify({ theme: themeSlug })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
currentActive = themeSlug;
|
|
// Optional: show success message
|
|
} else {
|
|
alert(data.message || 'Failed to activate theme.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error activating theme:', error);
|
|
alert('An error occurred while activating the theme.');
|
|
} finally {
|
|
processing = null;
|
|
}
|
|
}
|
|
|
|
async function handleUpload(e) {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
uploading = true;
|
|
const formData = new FormData();
|
|
formData.append('theme_zip', file);
|
|
formData.append('_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
|
|
|
|
try {
|
|
const baseUrl = window.location.origin.endsWith('/') ? window.location.origin : `${window.location.origin}/`;
|
|
const path = adminPath.startsWith('/') ? adminPath.substring(1) : adminPath;
|
|
const url = `${baseUrl}${path}/themes/upload`;
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: formData
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
// Refresh themes list or add the new theme to state
|
|
// For simplicity, refresh the page since we don't have a direct themes list update function here
|
|
window.location.reload();
|
|
} else {
|
|
alert(data.message || 'Failed to upload theme.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error uploading theme:', error);
|
|
alert('An error occurred while uploading the theme.');
|
|
} finally {
|
|
uploading = false;
|
|
fileInput.value = '';
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="ui container">
|
|
<div class="ui grid">
|
|
<div class="ten wide column">
|
|
<h1 class="ui header">
|
|
<i class="paint brush icon"></i>
|
|
<div class="content">
|
|
Theme Management
|
|
<div class="sub header">Manage your site's appearance by selecting a theme.</div>
|
|
</div>
|
|
</h1>
|
|
</div>
|
|
<div class="six wide column right aligned">
|
|
<input
|
|
type="file"
|
|
bind:this={fileInput}
|
|
onchange={handleUpload}
|
|
accept=".zip"
|
|
style="display: none;"
|
|
>
|
|
<button
|
|
class="ui primary button {uploading ? 'loading' : ''}"
|
|
onclick={() => fileInput.click()}
|
|
disabled={uploading}
|
|
>
|
|
<i class="upload icon"></i> Upload Theme
|
|
</button>
|
|
</div>
|
|
<div class="sixteen wide column">
|
|
<div class="ui divider"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ui three cards">
|
|
{#each themes as theme}
|
|
<div class="card {currentActive === theme.slug ? 'blue' : ''}">
|
|
<div class="content">
|
|
<div class="header">{theme.title}</div>
|
|
<div class="meta">By {theme.author} | v{theme.version || '1.0.0'}</div>
|
|
<div class="description">
|
|
{theme.description}
|
|
</div>
|
|
</div>
|
|
<div class="extra content">
|
|
{#if currentActive === theme.slug}
|
|
<button class="ui fluid disabled button">
|
|
<i class="check icon"></i> Active
|
|
</button>
|
|
{:else}
|
|
<button
|
|
class="ui fluid primary button {processing === theme.slug ? 'loading' : ''}"
|
|
onclick={() => activateTheme(theme.slug)}
|
|
disabled={processing !== null}
|
|
>
|
|
Activate
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|