From 8f41579972eeaefc9d6c04e978488e18c00819ee Mon Sep 17 00:00:00 2001 From: phuongdm Date: Wed, 24 Sep 2025 16:13:57 +0700 Subject: [PATCH] add send cmd page --- package-lock.json | 90 +++++++----- package.json | 3 +- src/components/command-form.tsx | 90 ++++++++++++ src/components/ui/alert.tsx | 66 +++++++++ src/components/ui/badge.tsx | 46 ++++++ src/components/upload-dialog.tsx | 146 ++++++++++++++------ src/config/api.ts | 2 + src/hooks/useMutationData.ts | 44 +++--- src/layouts/app-layout.tsx | 3 +- src/routeTree.gen.ts | 38 ++++- src/routes/_authenticated/agent/index.tsx | 60 +++++--- src/routes/_authenticated/apps/index.tsx | 104 ++++++++++---- src/routes/_authenticated/command/index.tsx | 46 ++++++ src/routes/index.tsx | 7 - src/template/app-manager-template.tsx | 5 +- src/template/form-submit-template.tsx | 53 +++++++ 16 files changed, 643 insertions(+), 160 deletions(-) create mode 100644 src/components/command-form.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/routes/_authenticated/command/index.tsx create mode 100644 src/template/form-submit-template.tsx diff --git a/package-lock.json b/package-lock.json index f126cf5..67e4174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/vite": "^4.1.11", - "@tanstack/react-form": "^1.15.0", + "@tanstack/react-form": "^1.23.0", "@tanstack/react-query": "^5.83.0", "@tanstack/react-router": "^1.121.2", "@tanstack/react-router-devtools": "^1.121.2", @@ -31,6 +31,7 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", + "zod": "^3.25.76", "zustand": "^5.0.6" }, "devDependencies": { @@ -2385,12 +2386,26 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tanstack/devtools-event-client": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.2.5.tgz", + "integrity": "sha512-iVdqw879KETXyyPHc3gQR5Ld0GjlPLk7bKenBUhzr3+z1FiQZvsbfgYfRRokTSPcgwANAV7aA2Uv05nx5xWT8A==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/form-core": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.15.0.tgz", - "integrity": "sha512-zMNyxb/J/JnFmW4Gzb1TSxaXmwNhvsaF9p3dGRpE93TMGp2ojPKK7V5LZ43ZV7iFTYWTL8NOIU8ZXuf9qZVkmw==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.23.0.tgz", + "integrity": "sha512-aBJnO5iA1TtMK/SSpAvd/y6cVsBgbu5Br2FftvW6r7TkHCcZ/lB2DMx3G2dk6SG7sW0g8s6F7iaMjrvqBGooDw==", "dependencies": { - "@tanstack/store": "^0.7.2" + "@tanstack/devtools-event-client": "^0.2.5", + "@tanstack/store": "^0.7.5", + "uuid": "^13.0.0" }, "funding": { "type": "github", @@ -2419,30 +2434,26 @@ } }, "node_modules/@tanstack/react-form": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-form/-/react-form-1.15.0.tgz", - "integrity": "sha512-bAawFDxR1wLn+eXli6MSyS4Nw0vTyHuW3CybjZGtk7NIcYxyaAm9cW/jPUX2j/KMDtjVNg5RMpfmufxDrsNHyA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-form/-/react-form-1.23.0.tgz", + "integrity": "sha512-wZ/cBjqj5dgWG79TQOsilbs8WzdouJj3iXw1DAZvQ4nejobsTGl0huU7SZZ0mnFtdNHbre1bFSdWyw+GqFcIhQ==", "dependencies": { - "@tanstack/form-core": "1.15.0", - "@tanstack/react-store": "^0.7.3", + "@tanstack/form-core": "1.23.0", + "@tanstack/react-store": "^0.7.5", "decode-formdata": "^0.9.0", - "devalue": "^5.1.1" + "devalue": "^5.3.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-start": "^1.112.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "vinxi": "^0.5.0" + "@tanstack/react-start": "^1.130.10", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@tanstack/react-start": { "optional": true - }, - "vinxi": { - "optional": true } } }, @@ -2506,11 +2517,11 @@ } }, "node_modules/@tanstack/react-store": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.3.tgz", - "integrity": "sha512-3Dnqtbw9P2P0gw8uUM8WP2fFfg8XMDSZCTsywRPZe/XqqYW8PGkXKZTvP0AHkE4mpqP9Y43GpOg9vwO44azu6Q==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.7.tgz", + "integrity": "sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==", "dependencies": { - "@tanstack/store": "0.7.2", + "@tanstack/store": "0.7.7", "use-sync-external-store": "^1.5.0" }, "funding": { @@ -2685,9 +2696,9 @@ } }, "node_modules/@tanstack/store": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.2.tgz", - "integrity": "sha512-RP80Z30BYiPX2Pyo0Nyw4s1SJFH2jyM6f9i3HfX4pA+gm5jsnYryscdq2aIQLnL4TaGuQMO+zXmN9nh1Qck+Pg==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.7.tgz", + "integrity": "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -3215,9 +3226,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -3897,9 +3908,9 @@ "license": "MIT" }, "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", + "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==" }, "node_modules/diff": { "version": "8.0.2", @@ -7232,6 +7243,18 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -7241,10 +7264,9 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "license": "MIT", + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", + "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/package.json b/package.json index d13ff78..f3d9afd 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/vite": "^4.1.11", - "@tanstack/react-form": "^1.15.0", + "@tanstack/react-form": "^1.23.0", "@tanstack/react-query": "^5.83.0", "@tanstack/react-router": "^1.121.2", "@tanstack/react-router-devtools": "^1.121.2", @@ -35,6 +35,7 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", + "zod": "^3.25.76", "zustand": "^5.0.6" }, "devDependencies": { diff --git a/src/components/command-form.tsx b/src/components/command-form.tsx new file mode 100644 index 0000000..315620b --- /dev/null +++ b/src/components/command-form.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState } from "react"; +import { useForm } from "@tanstack/react-form"; +import { z } from "zod"; + +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Loader2, Terminal, AlertTriangle, CheckCircle } from "lucide-react"; + +interface ShellCommandFormProps { + onExecute: (command: string) => Promise<{ success: boolean; output: string }>; +} + +export function ShellCommandForm({ onExecute }: ShellCommandFormProps) { + const [isLoading, setIsLoading] = useState(false); + + // init form + const form = useForm({ + defaultValues: { + command: "", + }, + onSubmit: async ({ value }) => { + setIsLoading(true); + try { + const res = await onExecute(value.command); + if (res.success) { + form.reset(); + } + } finally { + setIsLoading(false); + } + }, + }); + + return ( +
+ + {/* Form */} +
{ + e.preventDefault(); + form.handleSubmit(); + }} + className="space-y-5" + > + {/* Field: command */} + ( +
+