diff --git a/admin/src/App/AuroraAdmin.php b/admin/src/App/AuroraAdmin.php index 2e5bcf3..e0f2516 100644 --- a/admin/src/App/AuroraAdmin.php +++ b/admin/src/App/AuroraAdmin.php @@ -13,6 +13,7 @@ use AuroraAdmin\Options\Settings; use AuroraAdmin\Shell\Shell; use AuroraAdmin\Pages\DashboardTakeover; use AuroraAdmin\Pages\SettingsPage; +use AuroraAdmin\Pages\SetupWizard; use AuroraAdmin\Rest\DashboardData; use AuroraAdmin\Analytics\Analytics; use AuroraAdmin\Rest\AnalyticsData; @@ -63,6 +64,7 @@ class AuroraAdmin new Shell(); new DashboardTakeover(); new SettingsPage(); + new SetupWizard(); new DashboardData(); new Analytics(); new AnalyticsData(); diff --git a/admin/src/Pages/SetupWizard.php b/admin/src/Pages/SetupWizard.php new file mode 100644 index 0000000..950313d --- /dev/null +++ b/admin/src/Pages/SetupWizard.php @@ -0,0 +1,151 @@ + + + "POST", + "callback" => [self::class, "rest_complete"], + "permission_callback" => function () { + return current_user_can("manage_options"); + }, + ]); + } + + public static function rest_complete() + { + update_option(self::OPT_COMPLETE, 1); + delete_option(self::OPT_REDIRECT); + return ["success" => true]; + } + + public static function render() + { + $data = [ + "restUrl" => esc_url_raw(get_rest_url()), + "restNonce" => wp_create_nonce("wp_rest"), + "userName" => wp_get_current_user()->display_name, + "settings" => Settings::get(), + "dashboardUrl" => esc_url_raw(admin_url("index.php")), + "settingsUrl" => esc_url_raw(admin_url("admin.php?page=aurora-admin")), + ]; + ?> +
+ +import { ref, reactive, computed, watch, onMounted } from 'vue'; +import SettingsField from '@/pages/settings/components/SettingsField.vue'; +import { applyThemePalette, applyFontFamily } from '@/pages/settings/applyLiveTheme.js'; + +const props = defineProps({ + serverData: { type: Object, required: true }, +}); + +// Step definitions. Welcome and Done are custom content; the middle three +// render their `fields` through the shared SettingsField component (same +// controls as the settings page — theme picker, font picker, media-library +// image fields, toggles). +const steps = [ + { id: 'welcome', label: 'Welcome' }, + { + id: 'appearance', + label: 'Appearance', + title: 'Choose your look', + subtitle: + 'Pick a color theme and font for the whole admin. You can change these any time in Settings.', + fields: [ + { + key: 'theme_preset', + type: 'theme-picker', + label: 'Theme', + description: 'The accent and surface colors used across the admin.', + default: 'indigo', + }, + { + key: 'font_family', + type: 'font-picker', + label: 'Font family', + description: 'A Google Font used across the Aurora interface.', + }, + ], + }, + { + id: 'branding', + label: 'Branding', + title: 'Add your branding', + subtitle: + 'All optional — drop in a logo and favicon, or rename Aurora for a fully white-labeled admin.', + fields: [ + { key: 'logo', type: 'image', label: 'Logo', description: 'Shown at the top of the sidebar menu.' }, + { + key: 'dark_logo', + type: 'image', + label: 'Dark mode logo', + description: 'Optional alternative logo; falls back to the standard one.', + }, + { + key: 'admin_favicon', + type: 'image', + label: 'Admin favicon', + description: 'Replaces the WordPress favicon in the admin.', + }, + { + key: 'plugin_name', + type: 'text', + label: 'Rename Aurora Admin', + description: 'White-labels the plugin name in menus and the plugins list.', + placeholder: 'Aurora Admin', + }, + ], + }, + { + id: 'features', + label: 'Features', + title: 'Choose your features', + subtitle: + 'Aurora replaces these WordPress admin screens with faster versions of its own. Turn off any you’d rather keep native.', + fields: [ + { key: 'use_custom_dashboard', type: 'toggle', label: 'Dashboard', description: 'Replace the WordPress dashboard with Aurora’s.', default: true }, + { key: 'modern_posts', type: 'toggle', label: 'Posts', description: 'Aurora posts management screen.', default: true }, + { key: 'modern_pages', type: 'toggle', label: 'Pages', description: 'Aurora pages management screen.', default: true }, + { key: 'modern_media', type: 'toggle', label: 'Media library', description: 'Aurora media library screen.', default: true }, + { key: 'modern_users', type: 'toggle', label: 'Users', description: 'Aurora users management screen.', default: true }, + { key: 'modern_comments', type: 'toggle', label: 'Comments', description: 'Aurora comments management screen.', default: true }, + { key: 'modern_plugins', type: 'toggle', label: 'Plugins', description: 'Aurora plugins management screen.', default: true }, + { + key: 'full_admin_takeover', + type: 'toggle', + label: 'Full admin takeover', + description: 'Also replace Appearance, Tools, and the native Settings screens. Off by default.', + default: false, + }, + ], + }, + { id: 'done', label: 'Done', title: 'You’re all set' }, +]; + +// Working settings, seeded from the server + each field's default. +const settings = reactive({ ...(props.serverData.settings || {}) }); +steps.forEach((s) => + (s.fields || []).forEach((f) => { + if (settings[f.key] === undefined && f.default !== undefined) settings[f.key] = f.default; + }) +); + +const stepIndex = ref(0); +const current = computed(() => steps[stepIndex.value]); +const isFirst = computed(() => stepIndex.value === 0); +const isDone = computed(() => current.value.id === 'done'); + +const goNext = () => { + if (stepIndex.value < steps.length - 1) stepIndex.value++; +}; +const goBack = () => { + if (stepIndex.value > 0) stepIndex.value--; +}; + +// Live preview of theme + font as they're picked (nothing is persisted until +// the wizard is finished, but the whole admin previews immediately). +watch(() => settings.theme_preset, (p) => applyThemePalette(p || 'indigo'), { immediate: true }); +watch(() => settings.font_family, (f) => applyFontFamily(f), { immediate: true }); + +onMounted(() => { + // The shell (which normally does this) is disabled on this page. + document.documentElement.classList.add('aurora-dark'); + applyThemePalette(settings.theme_preset || 'indigo'); + applyFontFamily(settings.font_family); +}); + +const saving = ref(false); +const finish = async (target) => { + if (saving.value) return; + saving.value = true; + try { + await fetch(`${props.serverData.restUrl}wp/v2/settings`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': props.serverData.restNonce }, + body: JSON.stringify({ aurora_admin_settings: { ...settings } }), + }); + await fetch(`${props.serverData.restUrl}aurora-admin/v1/setup/complete`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': props.serverData.restNonce }, + }); + } catch { + /* Even if persistence fails, don't trap the user in the wizard. */ + } + window.location.href = target || props.serverData.dashboardUrl || '/wp-admin/'; +}; + + + + + diff --git a/app/vite.config.js b/app/vite.config.js index a90463b..243f4d1 100644 --- a/app/vite.config.js +++ b/app/vite.config.js @@ -34,6 +34,7 @@ export default defineConfig({ shell: fileURLToPath(new URL('./src/entries/shell.js', import.meta.url)), dashboard: fileURLToPath(new URL('./src/entries/dashboard.js', import.meta.url)), settings: fileURLToPath(new URL('./src/entries/settings.js', import.meta.url)), + 'setup-wizard': fileURLToPath(new URL('./src/entries/setup-wizard.js', import.meta.url)), 'menu-creator': fileURLToPath(new URL('./src/entries/menu-creator.js', import.meta.url)), 'admin-notices': fileURLToPath(new URL('./src/entries/admin-notices.js', import.meta.url)), 'role-editor': fileURLToPath(new URL('./src/entries/role-editor.js', import.meta.url)), diff --git a/aurora-admin.php b/aurora-admin.php index 96d3ec5..391ed95 100644 --- a/aurora-admin.php +++ b/aurora-admin.php @@ -26,6 +26,7 @@ require AURORA_ADMIN_PATH . "admin/src/Shell/MenuSerializer.php"; require AURORA_ADMIN_PATH . "admin/src/Shell/Shell.php"; require AURORA_ADMIN_PATH . "admin/src/Pages/DashboardTakeover.php"; require AURORA_ADMIN_PATH . "admin/src/Pages/SettingsPage.php"; +require AURORA_ADMIN_PATH . "admin/src/Pages/SetupWizard.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/DashboardData.php"; require AURORA_ADMIN_PATH . "admin/src/Analytics/Analytics.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/AnalyticsData.php";