ux: in-app confirm for asset/sitesmith delete + safe copy
- AssetsPanel: asset delete now requires an in-app two-step confirm (tile-button turns into "Delete?" + cancel, auto-resets after 4s or on click-elsewhere) instead of deleting with no confirmation at all. - AssetsPanel: copyUrl uses a new copyToClipboard() helper that tries the async Clipboard API and falls back to a hidden-textarea execCommand copy in non-secure contexts, surfacing a visible "Copy failed" state instead of silently doing nothing. - SitesmithModal: replaced window.confirm(...) for "Clear chat" with the same in-app two-step confirm pattern -- no native dialogs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import { useEditorConfig } from '../../state/EditorConfigContext';
|
||||
import { useSitesmith } from '../../hooks/useSitesmith';
|
||||
@@ -28,6 +28,14 @@ export const SitesmithModal: React.FC<Props> = ({ onClose, target }) => {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [pendingReplace, setPendingReplace] = useState<SitesmithResponse | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [confirmClearChat, setConfirmClearChat] = useState(false);
|
||||
const clearChatTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (clearChatTimeoutRef.current) clearTimeout(clearChatTimeoutRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const canChat = summary && (summary.status === 'OK_BONUS' || summary.status === 'OK_MONTHLY');
|
||||
|
||||
@@ -67,6 +75,23 @@ export const SitesmithModal: React.FC<Props> = ({ onClose, target }) => {
|
||||
if (!r.ok) setError(r.message || 'Apply failed');
|
||||
};
|
||||
|
||||
const cancelClearChat = () => {
|
||||
if (clearChatTimeoutRef.current) clearTimeout(clearChatTimeoutRef.current);
|
||||
setConfirmClearChat(false);
|
||||
};
|
||||
|
||||
const handleClearChatClick = async () => {
|
||||
if (!confirmClearChat) {
|
||||
setConfirmClearChat(true);
|
||||
if (clearChatTimeoutRef.current) clearTimeout(clearChatTimeoutRef.current);
|
||||
clearChatTimeoutRef.current = setTimeout(() => setConfirmClearChat(false), 4000);
|
||||
return;
|
||||
}
|
||||
cancelClearChat();
|
||||
const r = await clearHistory();
|
||||
if (!r.ok) setError(r.error || 'Failed to clear history');
|
||||
};
|
||||
|
||||
return (
|
||||
<div role="dialog" aria-modal="true" style={overlay}>
|
||||
<div style={panel}>
|
||||
@@ -80,17 +105,29 @@ export const SitesmithModal: React.FC<Props> = ({ onClose, target }) => {
|
||||
)}
|
||||
<div style={{ flex: 1 }} />
|
||||
{messages.length > 0 && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (!window.confirm('Clear all Sitesmith chat history for this site? The canvas is unaffected.')) return;
|
||||
const r = await clearHistory();
|
||||
if (!r.ok) setError(r.error || 'Failed to clear history');
|
||||
}}
|
||||
style={clearBtn}
|
||||
title="Clear chat history"
|
||||
>
|
||||
Clear chat
|
||||
</button>
|
||||
confirmClearChat ? (
|
||||
<>
|
||||
<button
|
||||
onClick={handleClearChatClick}
|
||||
style={{ ...clearBtn, background: '#b91c1c', color: '#fff', borderColor: '#b91c1c' }}
|
||||
title="Click again to permanently clear chat history"
|
||||
autoFocus
|
||||
>
|
||||
Confirm clear?
|
||||
</button>
|
||||
<button onClick={cancelClearChat} style={clearBtn} title="Cancel">
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleClearChatClick}
|
||||
style={clearBtn}
|
||||
title="Clear all Sitesmith chat history for this site (canvas is unaffected)"
|
||||
>
|
||||
Clear chat
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
<button onClick={onClose} aria-label="Close" style={closeBtn}>✕</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user