fix: discard button in sticky save bar instead of browser confirm

This commit is contained in:
Usman Baig
2026-03-25 20:51:39 +01:00
parent 7181d68d85
commit 81fafcf711
5 changed files with 84 additions and 32 deletions

View File

@@ -7,7 +7,7 @@ import { useSite, useBotFilterStats, useSessions } from '@/lib/swr/dashboard'
import { updateSite } from '@/lib/api/sites'
import { botFilterSessions, botUnfilterSessions } from '@/lib/api/bot-filter'
export default function SiteBotSpamTab({ siteId, onDirtyChange }: { siteId: string; onDirtyChange?: (dirty: boolean) => void }) {
export default function SiteBotSpamTab({ siteId, onDirtyChange, hasPendingAction, onDiscard }: { siteId: string; onDirtyChange?: (dirty: boolean) => void; hasPendingAction?: boolean; onDiscard?: () => void }) {
const { data: site, mutate } = useSite(siteId)
const { data: botStats, mutate: mutateBotStats } = useBotFilterStats(siteId)
const [filterBots, setFilterBots] = useState(false)
@@ -226,10 +226,17 @@ export default function SiteBotSpamTab({ siteId, onDirtyChange }: { siteId: stri
{/* Sticky save bar */}
{isDirty && (
<div className="sticky bottom-0 -mx-6 -mb-6 px-6 py-3 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-800 flex items-center justify-between">
<span className="text-xs text-neutral-400">Unsaved changes</span>
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
<span className="text-xs text-neutral-400">{hasPendingAction ? 'Save or discard to continue' : 'Unsaved changes'}</span>
<div className="flex items-center gap-2">
{hasPendingAction && (
<button onClick={onDiscard} className="px-3 py-1.5 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
Discard
</button>
)}
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</div>
)}
</div>

View File

@@ -28,7 +28,7 @@ const TIMEZONES = [
{ value: 'Australia/Sydney', label: 'Australia/Sydney (AEST)' },
]
export default function SiteGeneralTab({ siteId, onDirtyChange }: { siteId: string; onDirtyChange?: (dirty: boolean) => void }) {
export default function SiteGeneralTab({ siteId, onDirtyChange, hasPendingAction, onDiscard }: { siteId: string; onDirtyChange?: (dirty: boolean) => void; hasPendingAction?: boolean; onDiscard?: () => void }) {
const router = useRouter()
const { user } = useAuth()
const { closeUnifiedSettings: closeSettings } = useUnifiedSettings()
@@ -180,10 +180,17 @@ export default function SiteGeneralTab({ siteId, onDirtyChange }: { siteId: stri
{/* Sticky save bar */}
{isDirty && (
<div className="sticky bottom-0 -mx-6 -mb-6 px-6 py-3 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-800 flex items-center justify-between">
<span className="text-xs text-neutral-400">Unsaved changes</span>
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
<span className="text-xs text-neutral-400">{hasPendingAction ? 'Save or discard to continue' : 'Unsaved changes'}</span>
<div className="flex items-center gap-2">
{hasPendingAction && (
<button onClick={onDiscard} className="px-3 py-1.5 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
Discard
</button>
)}
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</div>
)}

View File

@@ -28,7 +28,7 @@ function PrivacyToggle({ label, desc, checked, onToggle }: { label: string; desc
)
}
export default function SitePrivacyTab({ siteId, onDirtyChange }: { siteId: string; onDirtyChange?: (dirty: boolean) => void }) {
export default function SitePrivacyTab({ siteId, onDirtyChange, hasPendingAction, onDiscard }: { siteId: string; onDirtyChange?: (dirty: boolean) => void; hasPendingAction?: boolean; onDiscard?: () => void }) {
const { data: site, mutate } = useSite(siteId)
const { data: subscription, error: subscriptionError, mutate: mutateSubscription } = useSubscription()
const { data: psiConfig, mutate: mutatePSIConfig } = usePageSpeedConfig(siteId)
@@ -253,10 +253,17 @@ export default function SitePrivacyTab({ siteId, onDirtyChange }: { siteId: stri
{/* Sticky save bar — only visible when dirty */}
{isDirty && (
<div className="sticky bottom-0 -mx-6 -mb-6 px-6 py-3 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-800 flex items-center justify-between">
<span className="text-xs text-neutral-400">Unsaved changes</span>
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
<span className="text-xs text-neutral-400">{hasPendingAction ? 'Save or discard to continue' : 'Unsaved changes'}</span>
<div className="flex items-center gap-2">
{hasPendingAction && (
<button onClick={onDiscard} className="px-3 py-1.5 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
Discard
</button>
)}
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</div>
)}
</div>

View File

@@ -9,7 +9,7 @@ import { updateSite } from '@/lib/api/sites'
const APP_URL = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3003'
export default function SiteVisibilityTab({ siteId, onDirtyChange }: { siteId: string; onDirtyChange?: (dirty: boolean) => void }) {
export default function SiteVisibilityTab({ siteId, onDirtyChange, hasPendingAction, onDiscard }: { siteId: string; onDirtyChange?: (dirty: boolean) => void; hasPendingAction?: boolean; onDiscard?: () => void }) {
const { data: site, mutate } = useSite(siteId)
const [isPublic, setIsPublic] = useState(false)
const [password, setPassword] = useState('')
@@ -149,10 +149,17 @@ export default function SiteVisibilityTab({ siteId, onDirtyChange }: { siteId: s
{/* Sticky save bar */}
{isDirty && (
<div className="sticky bottom-0 -mx-6 -mb-6 px-6 py-3 bg-neutral-900/95 backdrop-blur-sm border-t border-neutral-800 flex items-center justify-between">
<span className="text-xs text-neutral-400">Unsaved changes</span>
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
<span className="text-xs text-neutral-400">{hasPendingAction ? 'Save or discard to continue' : 'Unsaved changes'}</span>
<div className="flex items-center gap-2">
{hasPendingAction && (
<button onClick={onDiscard} className="px-3 py-1.5 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
Discard
</button>
)}
<Button onClick={handleSave} variant="primary" disabled={saving} className="text-sm">
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</div>
)}
</div>