Skip to content

Commit

Permalink
feat(frontend): add dialog to the switch toggle + green switch
Browse files Browse the repository at this point in the history
  • Loading branch information
Shirajuki committed Sep 12, 2024
1 parent 72906ba commit 158b4fe
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 150 deletions.
330 changes: 181 additions & 149 deletions frontend/src/routes/exploits.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,167 +6,199 @@ import { Loader2Icon, PlayIcon } from "lucide-react";
import { useAtom } from "jotai";
import { exploitsAtom } from "../utils/atoms";
import { updateExploit, executeExploit } from "../services/rest";
import { Exploit } from "../services/models";
import type { Exploit } from "../services/models";
import { toast } from "sonner";
import { Switch } from "../components/Switch";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "../components/Dialog";

export const Route = createLazyFileRoute("/exploits")({
component: () => Exploits(),
component: () => Exploits(),
});

function Exploits() {
const [executeOpen, setExecuteOpen] = useState(false);
const [executeLoading, setExecuteLoading] = useState(false);
const [selectedExploit, setSelectedExploit] = useState<Exploit>();
const [exploits, setExploits] = useAtom(exploitsAtom);
const [openDialog, setOpenDialog] = useState(false);
const [selectedExploit, setSelectedExploit] = useState<{
type: "toggle" | "execute";
exploit: Exploit;
}>();
const [isLoading, setIsLoading] = useState(false);
const [exploits, setExploits] = useAtom(exploitsAtom);

const onToggleExploitHandler = useCallback(
(exploit: Exploit, checked: boolean) => {
const updatedExploit = {
...exploit,
manifest: { ...exploit.manifest, enabled: checked },
};
const updatedExploits = [
...exploits!.filter((e) => e.manifest.name != exploit.manifest.name),
updatedExploit,
].sort((a, b) => a.manifest.service.localeCompare(b.manifest.service));
const onToggleExploitHandler = useCallback(
(exploit: Exploit, checked: boolean) => {
if (isLoading) return;
setIsLoading(true);

toast.promise(updateExploit(updatedExploit), {
loading: "Loading...",
success: (_) => {
setExploits(updatedExploits);
return `${exploit.manifest.name} successfully ${checked ? "enabled" : "disabled"}`;
},
error: `Couldn't toggle exploit ${exploit.manifest.name}`,
});
},
[exploits, setExploits]
);
const updatedExploit = {
...exploit,
manifest: { ...exploit.manifest, enabled: checked },
};
const updatedExploits =
exploits &&
[
...exploits.filter((e) => e.manifest.name !== exploit.manifest.name),
updatedExploit,
].sort((a, b) => a.manifest.service.localeCompare(b.manifest.service));

const onExecuteExploitHandler = useCallback(
async (exploit: Exploit) => {
if (executeLoading) return;
setExecuteLoading(true);
toast.promise(updateExploit(updatedExploit), {
loading: "Loading...",
success: (_) => {
setExploits(updatedExploits);
setOpenDialog(false);
setIsLoading(false);
return `${exploit.manifest.name} successfully ${checked ? "enabled" : "disabled"}`;
},
error: (_) => {
setIsLoading(false);
return `Couldn't toggle exploit ${exploit.manifest.name}`;
},
});
},
[exploits, setExploits, isLoading],
);

try {
await executeExploit(exploit);
toast.success(`${exploit.manifest.name} successfully executed`);
setExecuteOpen(false);
} catch (_) {
toast.error(`Couldn't execute exploit ${exploit.manifest.name}`);
}
setExecuteLoading(false);
},
[exploits, setExecuteOpen, executeLoading, setExecuteLoading]
);
const onExecuteExploitHandler = useCallback(
async (exploit: Exploit) => {
if (isLoading) return;
setIsLoading(true);

return (
<main className="relative grid grid-rows-1 gap-3 min-h-0 h-[calc(100vh-6rem)]">
<div className="flex flex-col h-full relative rounded-md">
<AutoSizer>
{({ height, width }) => (
<List
height={height}
itemCount={exploits?.length ?? 0}
itemSize={64}
width={width}
innerElementType={forwardRef(({ children, ...rest }, ref) => (
<table ref={ref} {...rest} className="relative">
<thead className="sticky top-0 bg-primary-bg h-10 z-10">
<tr className="flex mb-2 gap-2 text-left">
<th className="min-w-16 h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm" />
<th className="min-w-24 max-w-48 w-full h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm">
Service
</th>
<th className="min-w-48 w-full h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm">
Exploit
</th>
<th className="min-w-36 h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm" />
</tr>
</thead>
<tbody>{children}</tbody>
</table>
))}
>
{({ index, style }) => {
const exploit = exploits![index];
const { manifest } = exploit;
return (
<tr
key={`key-${index}`}
style={{ ...style }}
className="flex min-w-full !h-14 mt-12 gap-2 bg-slate-950 bg-opacity-30 border-slate-950 border-opacity-20 border-2"
>
<td className="flex min-w-16 h-full justify-center items-center">
<Switch
checked={manifest.enabled}
onCheckedChange={(checked) =>
onToggleExploitHandler(exploit, checked)
}
/>
</td>
<td className="min-w-24 max-w-48 w-full h-14">
<div className="w-full flex items-center p-1.5 h-full">
<p className="truncate" title={manifest.service}>
{manifest.service}
</p>
</div>
</td>
<td className="min-w-48 w-full h-14">
<div className="w-full flex items-center p-1.5 h-full">
<p className="truncate" title={manifest.name}>
{manifest.name}
</p>
</div>
</td>
<td className="flex min-w-36 h-full justify-center items-center">
<button
className="flex justify-center items-center gap-2 py-2 px-4 bg-slate-950 bg-opacity-60 border-slate-950 border-opacity-20 border-2 rounded-md hover:bg-opacity-100"
onClick={() => {
setExecuteOpen(true);
setSelectedExploit(exploit);
}}
>
<PlayIcon className="w-4 h-4" />
Execute
</button>
</td>
</tr>
);
}}
</List>
)}
</AutoSizer>
<Dialog open={executeOpen} onOpenChange={setExecuteOpen}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Execute {selectedExploit?.manifest?.name}?</DialogTitle>
<DialogDescription>
Are you actually super duper very sure?
</DialogDescription>
<button
className="!mt-8 flex justify-center items-center gap-2 py-2 px-4 bg-slate-950 bg-opacity-60 border-slate-950 border-opacity-20 border-2 rounded-md hover:bg-opacity-100"
onClick={() => onExecuteExploitHandler(selectedExploit!)}
disabled={executeLoading}
>
{executeLoading ? (
<Loader2Icon className="h-4 w-4 animate-spin" />
) : (
<PlayIcon className="w-4 h-4" />
)}
Execute
</button>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
</main>
);
try {
await executeExploit(exploit);
toast.success(`${exploit.manifest.name} successfully executed`);
setOpenDialog(false);
} catch (_) {
toast.error(`Couldn't execute exploit ${exploit.manifest.name}`);
}
setIsLoading(false);
},
[isLoading],
);

return (
<main className="relative grid grid-rows-1 gap-3 min-h-0 h-[calc(100vh-6rem)]">
<div className="flex flex-col h-full relative rounded-md">
<AutoSizer>
{({ height, width }) => (
<List
height={height}
itemCount={exploits?.length ?? 0}
itemSize={64}
width={width}
innerElementType={forwardRef(({ children, ...rest }, ref) => (
<table ref={ref} {...rest} className="relative">
<thead className="sticky top-0 bg-primary-bg h-10 z-10">
<tr className="flex mb-2 gap-2 text-left">
<th className="min-w-16 h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm" />
<th className="min-w-24 max-w-48 w-full h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm">
Service
</th>
<th className="min-w-48 w-full h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm">
Exploit
</th>
<th className="min-w-36 h-10 items-center font-bold p-2 shadow-inner bg-slate-950/30 border-slate-950 border-opacity-20 border-2 rounded-sm" />
</tr>
</thead>
<tbody>{children}</tbody>
</table>
))}
>
{({ index, style }) => {
if (!exploits) return <></>;
const exploit = exploits[index];
const { manifest } = exploit;
return (
<tr
key={`key-${index}`}
style={{ ...style }}
className="flex min-w-full !h-14 mt-12 gap-2 bg-slate-950 bg-opacity-30 border-slate-950 border-opacity-20 border-2"
>
<td className="flex min-w-16 h-full justify-center items-center">
<Switch
className="data-[state=checked]:!bg-green-500/90"
checked={manifest.enabled}
onCheckedChange={() => {
setOpenDialog(true);
setSelectedExploit({
type: "toggle",
exploit: exploit,
});
}}
/>
</td>
<td className="min-w-24 max-w-48 w-full h-14">
<div className="w-full flex items-center p-1.5 h-full">
<p className="truncate" title={manifest.service}>
{manifest.service}
</p>
</div>
</td>
<td className="min-w-48 w-full h-14">
<div className="w-full flex items-center p-1.5 h-full">
<p className="truncate" title={manifest.name}>
{manifest.name}
</p>
</div>
</td>
<td className="flex min-w-36 h-full justify-center items-center">
<button
type="button"
className="flex justify-center items-center gap-2 py-2 px-4 bg-slate-950 bg-opacity-60 border-slate-950 border-opacity-20 border-2 rounded-md hover:bg-opacity-100"
onClick={() => {
setOpenDialog(true);
setSelectedExploit({
type: "execute",
exploit: exploit,
});
}}
>
<PlayIcon className="w-4 h-4" />
Execute
</button>
</td>
</tr>
);
}}
</List>
)}
</AutoSizer>
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>
{selectedExploit?.type === "execute"
? `Execute ${selectedExploit.exploit.manifest.name}?`
: `Toggle ${selectedExploit?.exploit.manifest.name}?`}
</DialogTitle>
<DialogDescription>
Are you actually super duper very sure?
</DialogDescription>
<button
type="button"
className="!mt-8 flex justify-center items-center gap-2 py-2 px-4 bg-slate-950 bg-opacity-60 border-slate-950 border-opacity-20 border-2 rounded-md hover:bg-opacity-100"
onClick={() => {
if (selectedExploit?.type === "execute")
onExecuteExploitHandler(selectedExploit.exploit);
else if (selectedExploit?.type === "toggle")
onToggleExploitHandler(
selectedExploit.exploit,
!selectedExploit.exploit.manifest.enabled,
);
}}
disabled={isLoading}
>
{isLoading && <Loader2Icon className="h-4 w-4 animate-spin" />}
Yes
</button>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
</main>
);
}
1 change: 0 additions & 1 deletion frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export default {
colors: {
"primary-bg": "#0f192e",
background: "#0f192e",
border: "hsl(var(--border))",
input: "rgb(148 163 184 / 0.4)", // bg-gray-200/70
primary: "rgb(229 231 235 / 0.7)", // bg-slate-400/40
red: {
Expand Down

0 comments on commit 158b4fe

Please sign in to comment.