mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-06 06:25:28 +02:00
- Restored the type import for UploadFileToContainer in the upload file modal component. - Updated useForm to correctly utilize the UploadFileToContainer type for improved type safety.
188 lines
4.7 KiB
TypeScript
188 lines
4.7 KiB
TypeScript
import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema";
|
|
import { Upload } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { toast } from "sonner";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog";
|
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
|
import { Dropzone } from "@/components/ui/dropzone";
|
|
import {
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormMessage,
|
|
} from "@/components/ui/form";
|
|
import { Input } from "@/components/ui/input";
|
|
import { api } from "@/utils/api";
|
|
import {
|
|
uploadFileToContainerSchema,
|
|
type UploadFileToContainer,
|
|
} from "@/utils/schema";
|
|
|
|
interface Props {
|
|
containerId: string;
|
|
serverId?: string;
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
export const UploadFileModal = ({ children, containerId, serverId }: Props) => {
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const { mutateAsync: uploadFile, isPending: isLoading } =
|
|
api.docker.uploadFileToContainer.useMutation({
|
|
onSuccess: () => {
|
|
toast.success("File uploaded successfully");
|
|
setOpen(false);
|
|
form.reset();
|
|
},
|
|
onError: (error) => {
|
|
toast.error(error.message || "Failed to upload file to container");
|
|
},
|
|
});
|
|
|
|
const form = useForm({
|
|
resolver: zodResolver(uploadFileToContainerSchema),
|
|
defaultValues: {
|
|
containerId,
|
|
destinationPath: "/",
|
|
serverId: serverId || undefined,
|
|
},
|
|
});
|
|
|
|
const file = form.watch("file");
|
|
|
|
const onSubmit = async (values: UploadFileToContainer) => {
|
|
if (!values.file) {
|
|
toast.error("Please select a file to upload");
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append("containerId", values.containerId);
|
|
formData.append("file", values.file);
|
|
formData.append("destinationPath", values.destinationPath);
|
|
if (values.serverId) {
|
|
formData.append("serverId", values.serverId);
|
|
}
|
|
|
|
await uploadFile(formData);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
<DropdownMenuItem
|
|
className="w-full cursor-pointer space-x-3"
|
|
onSelect={(e) => e.preventDefault()}
|
|
>
|
|
{children}
|
|
</DropdownMenuItem>
|
|
</DialogTrigger>
|
|
<DialogContent className="sm:max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2">
|
|
<Upload className="h-5 w-5" />
|
|
Upload File to Container
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
Upload a file directly into the container's filesystem
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
<FormField
|
|
control={form.control}
|
|
name="destinationPath"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Destination Path</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
placeholder="/path/to/file"
|
|
className="font-mono"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
<p className="text-xs text-muted-foreground">
|
|
Enter the full path where the file should be uploaded in the
|
|
container (e.g., /app/config.json)
|
|
</p>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="file"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>File</FormLabel>
|
|
<FormControl>
|
|
<Dropzone
|
|
{...field}
|
|
dropMessage="Drop file here or click to browse"
|
|
onChange={(files) => {
|
|
if (files && files.length > 0) {
|
|
field.onChange(files[0]);
|
|
} else {
|
|
field.onChange(null);
|
|
}
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
{file instanceof File && (
|
|
<div className="flex items-center gap-2 p-2 bg-muted rounded-md">
|
|
<span className="text-sm text-muted-foreground flex-1">
|
|
{file.name} ({(file.size / 1024).toFixed(2)} KB)
|
|
</span>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => field.onChange(null)}
|
|
>
|
|
Remove
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<DialogFooter>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setOpen(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
isLoading={isLoading}
|
|
disabled={!file || isLoading}
|
|
>
|
|
Upload File
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</Form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|