mirror of
https://github.com/willchen96/mike.git
synced 2026-06-14 20:55:13 +02:00
feat: add OpenAI model support and harden OSS security defaults
This commit is contained in:
parent
adc2cf2370
commit
bef75b082d
24 changed files with 1301 additions and 364 deletions
|
|
@ -1,7 +1,24 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Download, Eye, EyeOff, FolderMinus, Hash, History, Pencil, Trash2, Upload } from "lucide-react";
|
||||
import {
|
||||
Download,
|
||||
Eye,
|
||||
EyeOff,
|
||||
FolderMinus,
|
||||
FolderPlus,
|
||||
Hash,
|
||||
History,
|
||||
Pencil,
|
||||
Trash2,
|
||||
Upload,
|
||||
} from "lucide-react";
|
||||
|
||||
const CLOSE_ROW_ACTIONS_EVENT = "mike:close-row-actions";
|
||||
|
||||
export function closeRowActionMenus() {
|
||||
document.dispatchEvent(new Event(CLOSE_ROW_ACTIONS_EVENT));
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onDelete?: () => void;
|
||||
|
|
@ -11,12 +28,130 @@ interface Props {
|
|||
onRemoveFromFolder?: () => void;
|
||||
onShowAllVersions?: () => void;
|
||||
onUploadNewVersion?: () => void;
|
||||
onNewSubfolder?: () => void;
|
||||
deleting?: boolean;
|
||||
onRename?: () => void;
|
||||
onUpdateCmNumber?: () => void;
|
||||
newSubfolderLabel?: string;
|
||||
renameLabel?: string;
|
||||
deleteLabel?: string;
|
||||
}
|
||||
|
||||
export function RowActions({ onDelete, onHide, onUnhide, onDownload, onRemoveFromFolder, onShowAllVersions, onUploadNewVersion, deleting, onRename, onUpdateCmNumber }: Props) {
|
||||
export function RowActionMenuItems({
|
||||
onDelete,
|
||||
onHide,
|
||||
onUnhide,
|
||||
onDownload,
|
||||
onRemoveFromFolder,
|
||||
onShowAllVersions,
|
||||
onUploadNewVersion,
|
||||
onNewSubfolder,
|
||||
deleting,
|
||||
onRename,
|
||||
onUpdateCmNumber,
|
||||
newSubfolderLabel = "New subfolder",
|
||||
renameLabel = "Rename",
|
||||
deleteLabel = "Delete",
|
||||
onClose,
|
||||
}: Props & { onClose: () => void }) {
|
||||
return (
|
||||
<>
|
||||
{onNewSubfolder && (
|
||||
<button
|
||||
onClick={() => { onClose(); onNewSubfolder(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<FolderPlus className="h-3.5 w-3.5 shrink-0" />
|
||||
{newSubfolderLabel}
|
||||
</button>
|
||||
)}
|
||||
{onRename && (
|
||||
<button
|
||||
onClick={() => { onClose(); onRename(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
{renameLabel}
|
||||
</button>
|
||||
)}
|
||||
{onUpdateCmNumber && (
|
||||
<button
|
||||
onClick={() => { onClose(); onUpdateCmNumber(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Hash className="h-3.5 w-3.5" />
|
||||
Edit CM No.
|
||||
</button>
|
||||
)}
|
||||
{onDownload && (
|
||||
<button
|
||||
onClick={() => { onClose(); onDownload(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
Download
|
||||
</button>
|
||||
)}
|
||||
{onShowAllVersions && (
|
||||
<button
|
||||
onClick={() => { onClose(); onShowAllVersions(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<History className="h-3.5 w-3.5 shrink-0" />
|
||||
Show all versions
|
||||
</button>
|
||||
)}
|
||||
{onUploadNewVersion && (
|
||||
<button
|
||||
onClick={() => { onClose(); onUploadNewVersion(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Upload className="h-3.5 w-3.5 shrink-0" />
|
||||
Upload new version
|
||||
</button>
|
||||
)}
|
||||
{onRemoveFromFolder && (
|
||||
<button
|
||||
onClick={() => { onClose(); onRemoveFromFolder(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<FolderMinus className="h-3.5 w-3.5 shrink-0" />
|
||||
Remove from subfolder
|
||||
</button>
|
||||
)}
|
||||
{onUnhide && (
|
||||
<button
|
||||
onClick={() => { onClose(); onUnhide(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5" />
|
||||
Unhide
|
||||
</button>
|
||||
)}
|
||||
{onHide && (
|
||||
<button
|
||||
onClick={() => { onClose(); onHide(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<EyeOff className="h-3.5 w-3.5" />
|
||||
Hide
|
||||
</button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<button
|
||||
onClick={() => { onClose(); onDelete(); }}
|
||||
disabled={deleting}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-red-500 hover:bg-red-50 transition-colors disabled:opacity-40"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
{deleteLabel}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function RowActions(props: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [coords, setCoords] = useState({ top: 0, right: 0 });
|
||||
const btnRef = useRef<HTMLButtonElement>(null);
|
||||
|
|
@ -30,16 +165,33 @@ export function RowActions({ onDelete, onHide, onUnhide, onDownload, onRemoveFro
|
|||
return () => document.removeEventListener("click", handleClick);
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleCloseRowActions() {
|
||||
setOpen(false);
|
||||
}
|
||||
document.addEventListener(CLOSE_ROW_ACTIONS_EVENT, handleCloseRowActions);
|
||||
return () =>
|
||||
document.removeEventListener(
|
||||
CLOSE_ROW_ACTIONS_EVENT,
|
||||
handleCloseRowActions,
|
||||
);
|
||||
}, []);
|
||||
|
||||
function handleToggle(e: React.MouseEvent) {
|
||||
e.stopPropagation();
|
||||
if (!open && btnRef.current) {
|
||||
if (open) {
|
||||
setOpen(false);
|
||||
return;
|
||||
}
|
||||
closeRowActionMenus();
|
||||
if (btnRef.current) {
|
||||
const rect = btnRef.current.getBoundingClientRect();
|
||||
setCoords({
|
||||
top: rect.bottom + 4,
|
||||
right: window.innerWidth - rect.right,
|
||||
});
|
||||
}
|
||||
setOpen((o) => !o);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -55,91 +207,13 @@ export function RowActions({ onDelete, onHide, onUnhide, onDownload, onRemoveFro
|
|||
{open && (
|
||||
<div
|
||||
style={{ position: "fixed", top: coords.top, right: coords.right }}
|
||||
className="z-50 w-48 rounded-xl border border-gray-100 bg-white shadow-lg overflow-hidden"
|
||||
className="z-[120] w-48 rounded-xl border border-gray-100 bg-white shadow-lg overflow-hidden"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{onRename && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onRename(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
Rename
|
||||
</button>
|
||||
)}
|
||||
{onUpdateCmNumber && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onUpdateCmNumber(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Hash className="h-3.5 w-3.5" />
|
||||
Edit CM No.
|
||||
</button>
|
||||
)}
|
||||
{onDownload && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onDownload(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
Download
|
||||
</button>
|
||||
)}
|
||||
{onShowAllVersions && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onShowAllVersions(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<History className="h-3.5 w-3.5 shrink-0" />
|
||||
Show all versions
|
||||
</button>
|
||||
)}
|
||||
{onUploadNewVersion && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onUploadNewVersion(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Upload className="h-3.5 w-3.5 shrink-0" />
|
||||
Upload new version
|
||||
</button>
|
||||
)}
|
||||
{onRemoveFromFolder && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onRemoveFromFolder(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<FolderMinus className="h-3.5 w-3.5 shrink-0" />
|
||||
Remove from subfolder
|
||||
</button>
|
||||
)}
|
||||
{onUnhide && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onUnhide(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5" />
|
||||
Unhide
|
||||
</button>
|
||||
)}
|
||||
{onHide && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onHide(); }}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<EyeOff className="h-3.5 w-3.5" />
|
||||
Hide
|
||||
</button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<button
|
||||
onClick={() => { setOpen(false); onDelete(); }}
|
||||
disabled={deleting}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 text-xs text-red-500 hover:bg-red-50 transition-colors disabled:opacity-40"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
<RowActionMenuItems
|
||||
{...props}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue