Compare commits
2 Commits
v0.1.90-wi
...
v0.1.92
| Author | SHA1 | Date | |
|---|---|---|---|
| bf8ef3dba1 | |||
| 418afe00ed |
@@ -36,11 +36,10 @@ pub async fn list_container_files(
|
|||||||
let cmd = vec![
|
let cmd = vec![
|
||||||
"find".to_string(),
|
"find".to_string(),
|
||||||
path.clone(),
|
path.clone(),
|
||||||
|
"-mindepth".to_string(),
|
||||||
|
"1".to_string(),
|
||||||
"-maxdepth".to_string(),
|
"-maxdepth".to_string(),
|
||||||
"1".to_string(),
|
"1".to_string(),
|
||||||
"-not".to_string(),
|
|
||||||
"-name".to_string(),
|
|
||||||
".".to_string(),
|
|
||||||
"-printf".to_string(),
|
"-printf".to_string(),
|
||||||
"%f\t%y\t%s\t%T@\t%m\n".to_string(),
|
"%f\t%y\t%s\t%T@\t%m\n".to_string(),
|
||||||
];
|
];
|
||||||
|
|||||||
55
app/src/components/projects/ConfirmRemoveModal.tsx
Normal file
55
app/src/components/projects/ConfirmRemoveModal.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { useEffect, useRef, useCallback } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
projectName: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ConfirmRemoveModal({ projectName, onConfirm, onCancel }: Props) {
|
||||||
|
const overlayRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Escape") onCancel();
|
||||||
|
};
|
||||||
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [onCancel]);
|
||||||
|
|
||||||
|
const handleOverlayClick = useCallback(
|
||||||
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (e.target === overlayRef.current) onCancel();
|
||||||
|
},
|
||||||
|
[onCancel],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={overlayRef}
|
||||||
|
onClick={handleOverlayClick}
|
||||||
|
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||||
|
>
|
||||||
|
<div className="bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg p-6 w-[24rem] shadow-xl">
|
||||||
|
<h2 className="text-lg font-semibold mb-3">Remove Project</h2>
|
||||||
|
<p className="text-sm text-[var(--text-secondary)] mb-5">
|
||||||
|
Are you sure you want to remove <strong className="text-[var(--text-primary)]">{projectName}</strong>? This will delete the container, config volume, and stored credentials.
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
onClick={onCancel}
|
||||||
|
className="px-4 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onConfirm}
|
||||||
|
className="px-4 py-2 text-sm text-white bg-[var(--error)] hover:opacity-80 rounded transition-colors"
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import PortMappingsModal from "./PortMappingsModal";
|
|||||||
import ClaudeInstructionsModal from "./ClaudeInstructionsModal";
|
import ClaudeInstructionsModal from "./ClaudeInstructionsModal";
|
||||||
import ContainerProgressModal from "./ContainerProgressModal";
|
import ContainerProgressModal from "./ContainerProgressModal";
|
||||||
import FileManagerModal from "./FileManagerModal";
|
import FileManagerModal from "./FileManagerModal";
|
||||||
|
import ConfirmRemoveModal from "./ConfirmRemoveModal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
project: Project;
|
project: Project;
|
||||||
@@ -32,7 +33,7 @@ export default function ProjectCard({ project }: Props) {
|
|||||||
const [progressMsg, setProgressMsg] = useState<string | null>(null);
|
const [progressMsg, setProgressMsg] = useState<string | null>(null);
|
||||||
const [activeOperation, setActiveOperation] = useState<"starting" | "stopping" | "resetting" | null>(null);
|
const [activeOperation, setActiveOperation] = useState<"starting" | "stopping" | "resetting" | null>(null);
|
||||||
const [operationCompleted, setOperationCompleted] = useState(false);
|
const [operationCompleted, setOperationCompleted] = useState(false);
|
||||||
const [showRemoveConfirm, setShowRemoveConfirm] = useState(false);
|
const [showRemoveModal, setShowRemoveModal] = useState(false);
|
||||||
const [isEditingName, setIsEditingName] = useState(false);
|
const [isEditingName, setIsEditingName] = useState(false);
|
||||||
const [editName, setEditName] = useState(project.name);
|
const [editName, setEditName] = useState(project.name);
|
||||||
const isSelected = selectedProjectId === project.id;
|
const isSelected = selectedProjectId === project.id;
|
||||||
@@ -435,34 +436,12 @@ export default function ProjectCard({ project }: Props) {
|
|||||||
disabled={false}
|
disabled={false}
|
||||||
label={showConfig ? "Hide" : "Config"}
|
label={showConfig ? "Hide" : "Config"}
|
||||||
/>
|
/>
|
||||||
{showRemoveConfirm ? (
|
<ActionButton
|
||||||
<span className="inline-flex items-center gap-1 text-xs">
|
onClick={() => setShowRemoveModal(true)}
|
||||||
<span className="text-[var(--text-secondary)]">Remove?</span>
|
disabled={loading}
|
||||||
<button
|
label="Remove"
|
||||||
onClick={async (e) => {
|
danger
|
||||||
e.stopPropagation();
|
/>
|
||||||
setShowRemoveConfirm(false);
|
|
||||||
await remove(project.id);
|
|
||||||
}}
|
|
||||||
className="px-1.5 py-0.5 rounded text-white bg-[var(--error)] hover:opacity-80 transition-colors"
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={(e) => { e.stopPropagation(); setShowRemoveConfirm(false); }}
|
|
||||||
className="px-1.5 py-0.5 rounded text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-primary)] transition-colors"
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<ActionButton
|
|
||||||
onClick={() => setShowRemoveConfirm(true)}
|
|
||||||
disabled={loading}
|
|
||||||
label="Remove"
|
|
||||||
danger
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Config panel */}
|
{/* Config panel */}
|
||||||
@@ -925,6 +904,17 @@ export default function ProjectCard({ project }: Props) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showRemoveModal && (
|
||||||
|
<ConfirmRemoveModal
|
||||||
|
projectName={project.name}
|
||||||
|
onConfirm={async () => {
|
||||||
|
setShowRemoveModal(false);
|
||||||
|
await remove(project.id);
|
||||||
|
}}
|
||||||
|
onCancel={() => setShowRemoveModal(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{activeOperation && (
|
{activeOperation && (
|
||||||
<ContainerProgressModal
|
<ContainerProgressModal
|
||||||
projectName={project.name}
|
projectName={project.name}
|
||||||
|
|||||||
Reference in New Issue
Block a user