diff --git a/src/systems/dashboard/confirmDialog.js b/src/systems/dashboard/confirmDialog.js new file mode 100644 index 0000000..2fe2c59 --- /dev/null +++ b/src/systems/dashboard/confirmDialog.js @@ -0,0 +1,217 @@ +/** + * Confirmation Dialog System + * + * Provides styled confirmation and alert dialogs to replace native browser popups. + * Supports three variants: danger (red), warning (yellow), and info (blue). + */ + +/** + * Show a confirmation dialog + * @param {Object} options - Dialog options + * @param {string} options.title - Dialog title + * @param {string} options.message - Dialog message + * @param {string} [options.variant='danger'] - Dialog variant: 'danger', 'warning', or 'info' + * @param {string} [options.confirmText='Confirm'] - Confirm button text + * @param {string} [options.cancelText='Cancel'] - Cancel button text + * @param {Function} [options.onConfirm] - Callback when confirmed + * @param {Function} [options.onCancel] - Callback when cancelled + * @returns {Promise} Resolves to true if confirmed, false if cancelled + */ +export function showConfirmDialog(options) { + return new Promise((resolve) => { + const { + title = 'Confirm Action', + message = 'Are you sure?', + variant = 'danger', + confirmText = 'Confirm', + cancelText = 'Cancel', + onConfirm = null, + onCancel = null + } = options; + + // Get modal elements + const modal = document.getElementById('rpg-confirm-dialog'); + const modalContent = modal.querySelector('.rpg-confirm-content'); + const icon = document.getElementById('rpg-confirm-icon'); + const titleEl = document.getElementById('rpg-confirm-title'); + const messageEl = document.getElementById('rpg-confirm-message'); + const confirmBtn = document.getElementById('rpg-confirm-confirm'); + const cancelBtn = document.getElementById('rpg-confirm-cancel'); + const closeBtn = modal.querySelector('.rpg-confirm-close'); + + if (!modal) { + console.error('[ConfirmDialog] Modal not found'); + return resolve(false); + } + + // Set icon based on variant + const iconMap = { + danger: 'fa-solid fa-triangle-exclamation', + warning: 'fa-solid fa-circle-exclamation', + info: 'fa-solid fa-circle-info' + }; + icon.className = `rpg-confirm-icon ${iconMap[variant] || iconMap.danger}`; + + // Set variant class on modal content + modalContent.className = `rpg-modal-content rpg-confirm-content rpg-confirm-${variant}`; + + // Set content + titleEl.textContent = title; + messageEl.textContent = message; + confirmBtn.textContent = confirmText; + cancelBtn.textContent = cancelText; + + // Show modal + modal.style.display = 'flex'; + + // Handle confirm + const handleConfirm = () => { + modal.style.display = 'none'; + cleanup(); + if (onConfirm) onConfirm(); + resolve(true); + }; + + // Handle cancel + const handleCancel = () => { + modal.style.display = 'none'; + cleanup(); + if (onCancel) onCancel(); + resolve(false); + }; + + // Handle keyboard + const handleKeyDown = (e) => { + if (e.key === 'Escape') { + handleCancel(); + } else if (e.key === 'Enter') { + handleConfirm(); + } + }; + + // Handle backdrop click + const handleBackdropClick = (e) => { + if (e.target === modal) { + handleCancel(); + } + }; + + // Clean up event listeners + const cleanup = () => { + confirmBtn.removeEventListener('click', handleConfirm); + cancelBtn.removeEventListener('click', handleCancel); + closeBtn.removeEventListener('click', handleCancel); + document.removeEventListener('keydown', handleKeyDown); + modal.removeEventListener('click', handleBackdropClick); + }; + + // Attach event listeners + confirmBtn.addEventListener('click', handleConfirm); + cancelBtn.addEventListener('click', handleCancel); + closeBtn.addEventListener('click', handleCancel); + document.addEventListener('keydown', handleKeyDown); + modal.addEventListener('click', handleBackdropClick); + + // Focus confirm button + setTimeout(() => confirmBtn.focus(), 100); + }); +} + +/** + * Show an alert dialog (info only, single OK button) + * @param {Object} options - Dialog options + * @param {string} options.title - Dialog title + * @param {string} options.message - Dialog message + * @param {string} [options.variant='info'] - Dialog variant: 'danger', 'warning', or 'info' + * @param {string} [options.okText='OK'] - OK button text + * @param {Function} [options.onOk] - Callback when OK clicked + * @returns {Promise} Resolves when OK clicked + */ +export function showAlertDialog(options) { + return new Promise((resolve) => { + const { + title = 'Alert', + message = '', + variant = 'info', + okText = 'OK', + onOk = null + } = options; + + // Get modal elements + const modal = document.getElementById('rpg-confirm-dialog'); + const modalContent = modal.querySelector('.rpg-confirm-content'); + const icon = document.getElementById('rpg-confirm-icon'); + const titleEl = document.getElementById('rpg-confirm-title'); + const messageEl = document.getElementById('rpg-confirm-message'); + const confirmBtn = document.getElementById('rpg-confirm-confirm'); + const cancelBtn = document.getElementById('rpg-confirm-cancel'); + const closeBtn = modal.querySelector('.rpg-confirm-close'); + + if (!modal) { + console.error('[ConfirmDialog] Modal not found'); + return resolve(); + } + + // Set icon based on variant + const iconMap = { + danger: 'fa-solid fa-triangle-exclamation', + warning: 'fa-solid fa-circle-exclamation', + info: 'fa-solid fa-circle-info' + }; + icon.className = `rpg-confirm-icon ${iconMap[variant] || iconMap.info}`; + + // Set variant class on modal content + modalContent.className = `rpg-modal-content rpg-confirm-content rpg-confirm-${variant}`; + + // Set content + titleEl.textContent = title; + messageEl.textContent = message; + confirmBtn.textContent = okText; + + // Hide cancel button for alerts + cancelBtn.style.display = 'none'; + + // Show modal + modal.style.display = 'flex'; + + // Handle OK + const handleOk = () => { + modal.style.display = 'none'; + cancelBtn.style.display = ''; // Restore for future confirms + cleanup(); + if (onOk) onOk(); + resolve(); + }; + + // Handle keyboard + const handleKeyDown = (e) => { + if (e.key === 'Escape' || e.key === 'Enter') { + handleOk(); + } + }; + + // Handle backdrop click + const handleBackdropClick = (e) => { + if (e.target === modal) { + handleOk(); + } + }; + + // Clean up event listeners + const cleanup = () => { + confirmBtn.removeEventListener('click', handleOk); + closeBtn.removeEventListener('click', handleOk); + document.removeEventListener('keydown', handleKeyDown); + modal.removeEventListener('click', handleBackdropClick); + }; + + // Attach event listeners + confirmBtn.addEventListener('click', handleOk); + closeBtn.addEventListener('click', handleOk); + document.addEventListener('keydown', handleKeyDown); + modal.addEventListener('click', handleBackdropClick); + + // Focus OK button + setTimeout(() => confirmBtn.focus(), 100); + }); +} diff --git a/src/systems/dashboard/dashboardIntegration.js b/src/systems/dashboard/dashboardIntegration.js index 6c87690..d066d06 100644 --- a/src/systems/dashboard/dashboardIntegration.js +++ b/src/systems/dashboard/dashboardIntegration.js @@ -14,6 +14,7 @@ import { WidgetRegistry } from './widgetRegistry.js'; import { generateDefaultDashboard } from './defaultLayout.js'; import { TabScrollManager } from './tabScrollManager.js'; import { HeaderOverflowManager } from './headerOverflowManager.js'; +import { showConfirmDialog } from './confirmDialog.js'; // Widget imports import { registerUserInfoWidget } from './widgets/userInfoWidget.js'; @@ -224,9 +225,17 @@ function setupDashboardEventListeners(dependencies) { // Reset layout button const resetLayoutBtn = document.querySelector('#rpg-dashboard-reset-layout'); if (resetLayoutBtn) { - resetLayoutBtn.addEventListener('click', () => { + resetLayoutBtn.addEventListener('click', async () => { if (dashboardManager) { - if (confirm('Reset dashboard to default layout? This will remove all widgets and reload the defaults.')) { + const confirmed = await showConfirmDialog({ + title: 'Reset Layout?', + message: 'This will remove all widgets and reload the default layout. This action cannot be undone.', + variant: 'danger', + confirmText: 'Reset', + cancelText: 'Cancel' + }); + + if (confirmed) { console.log('[RPG Companion] Reset layout button clicked'); dashboardManager.resetLayout(); } diff --git a/src/systems/dashboard/dashboardManager.js b/src/systems/dashboard/dashboardManager.js index e37addd..9ebe72d 100644 --- a/src/systems/dashboard/dashboardManager.js +++ b/src/systems/dashboard/dashboardManager.js @@ -137,7 +137,7 @@ export class DashboardManager { this.dashboard.tabs.push({ id: 'main', name: 'Main', - icon: '🏠', + icon: 'fa-solid fa-house', order: 0, widgets: [] }); @@ -361,7 +361,7 @@ export class DashboardManager { button.className = 'rpg-dashboard-tab'; button.dataset.tabId = tab.id; button.innerHTML = ` - ${tab.icon} + ${tab.name} `; @@ -1189,6 +1189,47 @@ export class DashboardManager { return config; } + /** + * Migrate emoji icons to Font Awesome + * @param {Object} config - Dashboard configuration + * @returns {Object} Migrated configuration + */ + migrateEmojiIcons(config) { + // Map of common emojis to Font Awesome classes + const emojiToFontAwesome = { + '📊': 'fa-solid fa-chart-line', + '🌍': 'fa-solid fa-map', + '🎒': 'fa-solid fa-bag-shopping', + '🏠': 'fa-solid fa-house', + '📄': 'fa-solid fa-file', + '⚙️': 'fa-solid fa-gear', + '👤': 'fa-solid fa-user', + '📝': 'fa-solid fa-note-sticky', + '🗂️': 'fa-solid fa-folder', + '📁': 'fa-solid fa-folder-open' + }; + + if (config && config.tabs) { + config.tabs.forEach(tab => { + // Check if icon is an emoji (contains emoji characters) + if (tab.icon && /[\u{1F300}-\u{1F9FF}]/u.test(tab.icon)) { + // Convert to Font Awesome if we have a mapping + const faIcon = emojiToFontAwesome[tab.icon]; + if (faIcon) { + console.log(`[DashboardManager] Migrating emoji icon "${tab.icon}" → "${faIcon}" for tab "${tab.name}"`); + tab.icon = faIcon; + } else { + // Fallback to generic file icon + console.warn(`[DashboardManager] Unknown emoji icon "${tab.icon}", using fa-solid fa-file for tab "${tab.name}"`); + tab.icon = 'fa-solid fa-file'; + } + } + }); + } + + return config; + } + /** * Apply dashboard configuration * @param {Object} config - Dashboard configuration @@ -1196,6 +1237,9 @@ export class DashboardManager { applyDashboardConfig(config) { console.log('[DashboardManager] Applying dashboard config'); + // Migrate emoji icons to Font Awesome + config = this.migrateEmojiIcons(config); + // Update grid config from dashboard config if (config.gridConfig) { this.config.rowHeight = config.gridConfig.rowHeight || this.config.rowHeight; diff --git a/src/systems/dashboard/dashboardTemplate.html b/src/systems/dashboard/dashboardTemplate.html index 906778d..9361cb5 100644 --- a/src/systems/dashboard/dashboardTemplate.html +++ b/src/systems/dashboard/dashboardTemplate.html @@ -132,4 +132,28 @@ + + + diff --git a/src/systems/dashboard/defaultLayout.js b/src/systems/dashboard/defaultLayout.js index a6766cd..ffc66ac 100644 --- a/src/systems/dashboard/defaultLayout.js +++ b/src/systems/dashboard/defaultLayout.js @@ -36,7 +36,7 @@ export function generateDefaultDashboard() { { id: 'tab-status', name: 'Status', - icon: '📊', + icon: 'fa-solid fa-user', order: 0, widgets: [ // Row 0: User Info (left) + User Mood (top right in 3-col) @@ -86,7 +86,7 @@ export function generateDefaultDashboard() { { id: 'tab-scene', name: 'Scene', - icon: '🌍', + icon: 'fa-solid fa-map', order: 1, widgets: [ // Row 0: Calendar (left) + Weather (right) @@ -162,7 +162,7 @@ export function generateDefaultDashboard() { { id: 'tab-inventory', name: 'Inventory', - icon: '🎒', + icon: 'fa-solid fa-bag-shopping', order: 2, widgets: [ { diff --git a/src/systems/dashboard/editModeManager.js b/src/systems/dashboard/editModeManager.js index e4cf99e..7206650 100644 --- a/src/systems/dashboard/editModeManager.js +++ b/src/systems/dashboard/editModeManager.js @@ -5,6 +5,8 @@ * Handles edit controls, widget library, and layout modifications. */ +import { showConfirmDialog } from './confirmDialog.js'; + /** * @typedef {Object} EditModeConfig * @property {HTMLElement} container - Dashboard container element @@ -453,9 +455,16 @@ export class EditModeManager { * Show confirmation dialog before canceling * @param {Function} onConfirm - Callback if confirmed */ - confirmCancel(onConfirm) { - const message = 'You have unsaved changes. Are you sure you want to cancel?'; - if (confirm(message)) { + async confirmCancel(onConfirm) { + const confirmed = await showConfirmDialog({ + title: 'Discard Changes?', + message: 'You have unsaved changes. Are you sure you want to discard them?', + variant: 'warning', + confirmText: 'Discard', + cancelText: 'Keep Editing' + }); + + if (confirmed) { onConfirm(); } } @@ -464,9 +473,16 @@ export class EditModeManager { * Show confirmation dialog before deleting widget * @param {string} widgetId - Widget ID to delete */ - confirmDeleteWidget(widgetId) { - const message = 'Are you sure you want to delete this widget?'; - if (confirm(message)) { + async confirmDeleteWidget(widgetId) { + const confirmed = await showConfirmDialog({ + title: 'Delete Widget?', + message: 'Are you sure you want to delete this widget? This action cannot be undone.', + variant: 'danger', + confirmText: 'Delete', + cancelText: 'Cancel' + }); + + if (confirmed) { if (this.onWidgetDelete) { this.onWidgetDelete(widgetId); } @@ -477,9 +493,16 @@ export class EditModeManager { * Show confirmation dialog before resetting layout * @param {Function} onConfirm - Callback if confirmed */ - confirmReset(onConfirm) { - const message = 'This will reset the layout to default. Are you sure?'; - if (confirm(message)) { + async confirmReset(onConfirm) { + const confirmed = await showConfirmDialog({ + title: 'Reset Layout?', + message: 'This will reset the layout to default. All widgets will be removed and the default layout will be restored.', + variant: 'danger', + confirmText: 'Reset', + cancelText: 'Cancel' + }); + + if (confirmed) { onConfirm(); } } diff --git a/src/systems/dashboard/tabManager.js b/src/systems/dashboard/tabManager.js index 64bbf33..f34ddee 100644 --- a/src/systems/dashboard/tabManager.js +++ b/src/systems/dashboard/tabManager.js @@ -97,7 +97,7 @@ export class TabManager { const tab = { id, name: config.name, - icon: config.icon || '📄', + icon: config.icon || 'fa-solid fa-file', order, widgets: [] }; diff --git a/src/systems/dashboard/widgets/inventoryWidget.js b/src/systems/dashboard/widgets/inventoryWidget.js index 9c6ee14..98b96ef 100644 --- a/src/systems/dashboard/widgets/inventoryWidget.js +++ b/src/systems/dashboard/widgets/inventoryWidget.js @@ -16,6 +16,7 @@ import { parseItems, serializeItems } from '../../../utils/itemParser.js'; import { sanitizeItemName, sanitizeLocationName } from '../../../utils/security.js'; +import { showAlertDialog } from '../confirmDialog.js'; /** * Convert location name to safe HTML ID @@ -663,7 +664,11 @@ export function registerInventoryWidget(registry, dependencies) { const itemName = sanitizeItemName(rawItemName); if (!itemName) { - alert('Invalid item name.'); + showAlertDialog({ + title: 'Invalid Item', + message: 'Please enter a valid item name.', + variant: 'warning' + }); hideAddItemForm(widget, field, location); return; } @@ -819,7 +824,11 @@ export function registerInventoryWidget(registry, dependencies) { const locationName = sanitizeLocationName(rawLocationName); if (!locationName) { - alert('Invalid location name.'); + showAlertDialog({ + title: 'Invalid Location', + message: 'Please enter a valid location name.', + variant: 'warning' + }); hideAddLocationForm(widget); return; } @@ -829,7 +838,11 @@ export function registerInventoryWidget(registry, dependencies) { // Check if location already exists if (inventory.stored[locationName]) { - alert('A location with this name already exists.'); + showAlertDialog({ + title: 'Duplicate Location', + message: 'A location with this name already exists.', + variant: 'warning' + }); hideAddLocationForm(widget); return; } diff --git a/style.css b/style.css index 530b0d4..8a6baaf 100644 --- a/style.css +++ b/style.css @@ -1133,7 +1133,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { font-size: 0.75rem; border: 1px solid transparent; background: transparent; - color: var(--SmartThemeBodyColor); + color: var(--rpg-text); border-radius: 6px; cursor: pointer; transition: all 0.2s; @@ -1143,13 +1143,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } .rpg-dashboard-tab:hover { - background: var(--SmartThemeBlurTintColor); + background: var(--rpg-accent); opacity: 0.9; } .rpg-dashboard-tab.active { - background: var(--SmartThemeBlurTintColor); - border-color: var(--SmartThemeBorderColor); + background: var(--rpg-accent); + border-color: var(--rpg-border); opacity: 1; font-weight: 600; } @@ -1182,9 +1182,9 @@ body:has(.rpg-panel.rpg-position-left) #sheld { width: 2rem; height: 2rem; padding: 0; - border: 1px solid var(--SmartThemeBorderColor); - background: var(--SmartThemeBlurTintColor); - color: var(--SmartThemeBodyColor); + border: 1px solid var(--rpg-border); + background: var(--rpg-bg); + color: var(--rpg-text); border-radius: 6px; cursor: pointer; transition: all 0.2s; @@ -1200,7 +1200,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-tab-nav-arrow:hover { opacity: 1; - background: var(--SmartThemeQuoteColor); + background: var(--rpg-highlight); transform: translateY(-50%) scale(1.05); } @@ -1232,7 +1232,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { left: 0; background: linear-gradient( to right, - var(--SmartThemeBlurTintColor) 0%, + var(--rpg-bg) 0%, transparent 100% ); } @@ -1241,7 +1241,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { right: 0; background: linear-gradient( to left, - var(--SmartThemeBlurTintColor) 0%, + var(--rpg-bg) 0%, transparent 100% ); } @@ -1281,16 +1281,16 @@ body:has(.rpg-panel.rpg-position-left) #sheld { width: 2rem; height: 2rem; font-size: 0.9rem; - border: 1px solid var(--SmartThemeBorderColor); - background: var(--SmartThemeBlurTintColor); - color: var(--SmartThemeBodyColor); + border: 1px solid var(--rpg-border); + background: var(--rpg-accent); + color: var(--rpg-text); border-radius: 6px; cursor: pointer; transition: all 0.2s; } .rpg-dashboard-btn:hover { - background: var(--SmartThemeQuoteColor); + background: var(--rpg-highlight); transform: translateY(-1px); } @@ -1309,10 +1309,10 @@ body:has(.rpg-panel.rpg-position-left) #sheld { right: 0; min-width: 200px; max-width: 300px; - background: var(--SmartThemeBlurTintColor); - border: 1px solid var(--SmartThemeBorderColor); + background: var(--rpg-bg); + border: 1px solid var(--rpg-border); border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 12px var(--rpg-shadow); z-index: 10003; /* Above modals (10000) and mobile toggle (10002) */ overflow: hidden; animation: slideDown 0.2s ease-out; @@ -1338,7 +1338,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { padding: 0.75rem 1rem; border: none; background: transparent; - color: var(--SmartThemeBodyColor); + color: var(--rpg-text); text-align: left; cursor: pointer; transition: background 0.15s; @@ -1347,7 +1347,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-dropdown-item:hover, .rpg-dropdown-item:focus { - background: var(--SmartThemeQuoteColor); + background: var(--rpg-highlight); outline: none; } @@ -1444,13 +1444,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Modal Content Container */ .rpg-modal-content { - background: var(--SmartThemeBlurTintColor); - border: 2px solid var(--SmartThemeBorderColor); + background: var(--rpg-bg); + border: 2px solid var(--rpg-border); border-radius: 8px; max-width: 600px; max-height: 80vh; overflow: auto; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + box-shadow: 0 4px 20px var(--rpg-shadow); } /* Modal Header */ @@ -1459,12 +1459,12 @@ body:has(.rpg-panel.rpg-position-left) #sheld { justify-content: space-between; align-items: center; padding: 1rem; - border-bottom: 1px solid var(--SmartThemeBorderColor); + border-bottom: 1px solid var(--rpg-border); } .rpg-modal-header h3 { margin: 0; - color: var(--SmartThemeBodyColor); + color: var(--rpg-text); display: flex; align-items: center; gap: 0.5rem; @@ -1473,7 +1473,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-modal-close { background: transparent; border: none; - color: var(--SmartThemeBodyColor); + color: var(--rpg-text); font-size: 1.5rem; cursor: pointer; padding: 0.25rem 0.5rem; @@ -1496,7 +1496,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { justify-content: flex-end; gap: 0.5rem; padding: 1rem; - border-top: 1px solid var(--SmartThemeBorderColor); + border-top: 1px solid var(--rpg-border); } /* Widget Grid in Modal */ @@ -1509,9 +1509,9 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Widget Card */ .rpg-widget-card { padding: 1rem; - border: 2px solid var(--SmartThemeBorderColor); + border: 2px solid var(--rpg-border); border-radius: 8px; - background: var(--SmartThemeQuoteColor); + background: var(--rpg-accent); text-align: center; transition: all 0.2s; display: flex; @@ -1522,7 +1522,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-widget-card:hover { border-color: var(--rpg-highlight); transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 12px var(--rpg-shadow); } .rpg-widget-card-icon { @@ -1533,12 +1533,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-widget-card-name { font-size: 0.95rem; font-weight: 600; - color: var(--SmartThemeBodyColor); + color: var(--rpg-text); } .rpg-widget-card-description { font-size: 0.75rem; - color: var(--SmartThemeFastUISliderColColor); + color: var(--rpg-text); + opacity: 0.7; line-height: 1.3; } @@ -1587,12 +1588,12 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-btn-secondary { background: transparent; - border: 1px solid var(--SmartThemeBorderColor); - color: var(--SmartThemeBodyColor); + border: 1px solid var(--rpg-border); + color: var(--rpg-text); } .rpg-btn-secondary:hover { - background: var(--SmartThemeQuoteColor); + background: var(--rpg-accent); } /* Tab Drop Zone Highlight */ @@ -1603,6 +1604,87 @@ body:has(.rpg-panel.rpg-position-left) #sheld { box-shadow: 0 0 12px var(--rpg-highlight); } +/* ======================================== + Confirmation Dialog Styles + ======================================== */ + +/* Confirmation Dialog Content */ +.rpg-confirm-content { + max-width: 450px; + width: 90%; +} + +/* Confirmation Dialog Header */ +.rpg-confirm-header-content { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.rpg-confirm-icon { + font-size: 1.75rem; + flex-shrink: 0; +} + +/* Confirmation Dialog Body */ +.rpg-confirm-body { + padding: 1.5rem 1rem; +} + +.rpg-confirm-body p { + margin: 0; + line-height: 1.5; + color: var(--rpg-text); +} + +/* Confirmation Dialog Footer */ +.rpg-confirm-footer { + padding: 1rem; + display: flex; + gap: 0.75rem; + justify-content: flex-end; +} + +/* Variant Styles - Danger (Red) */ +.rpg-confirm-danger .rpg-confirm-icon { + color: #ff4444; +} + +.rpg-confirm-danger .rpg-btn-primary { + background: #ff4444; +} + +.rpg-confirm-danger .rpg-btn-primary:hover { + background: #cc0000; +} + +/* Variant Styles - Warning (Yellow/Orange) */ +.rpg-confirm-warning .rpg-confirm-icon { + color: #ffaa00; +} + +.rpg-confirm-warning .rpg-btn-primary { + background: #ffaa00; + color: #1a1a1a; +} + +.rpg-confirm-warning .rpg-btn-primary:hover { + background: #cc8800; +} + +/* Variant Styles - Info (Blue) */ +.rpg-confirm-info .rpg-confirm-icon { + color: #00aaff; +} + +.rpg-confirm-info .rpg-btn-primary { + background: #00aaff; +} + +.rpg-confirm-info .rpg-btn-primary:hover { + background: #0088cc; +} + .rpg-dashboard-grid { position: relative; width: 100%;