Skip to content

Commit

Permalink
feat(ui): Add a resize button to the image content card (#142)
Browse files Browse the repository at this point in the history
* feat(ui): Add a resize button to the image content card

Add a resize button that opens a modal and let the user change both the width and height of the image

* fix(ui): use the previous toast id instead of dismissing it when resizing images
  • Loading branch information
luckramos authored Sep 9, 2024
1 parent 00ce21f commit f69a174
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 3 deletions.
36 changes: 36 additions & 0 deletions ui/src/actions/handleResizeImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ImageDimensions } from "@/types/imageDimensions";
import axios from "axios";
import { FormEvent } from "react";
import toast from "react-hot-toast";

const handleResizeImage = (e: FormEvent, newDimensions: ImageDimensions, filename: string) => {
e.preventDefault();

const {width, height} = newDimensions;

if(!width || !height) {
toast.error("Width and height are required!")
return;
}

const data = {
filename,
width: Math.abs(Math.floor(width)),
height: Math.abs(Math.floor(height))
};

const toastId = toast.loading("Renaming file...");
axios.put(`/api/cdn/resize/image`, JSON.stringify(data)).then((res) => {
if (res.status === 200) {
toast.success("File resized!", { id: toastId, duration: 1500 });

setTimeout(() => {
location.reload();
}, 1500)
}
}).catch((err) => {
toast.error("Error: " + err.response.data, { id: toastId, duration: 4000 });
})
}

export default handleResizeImage
10 changes: 7 additions & 3 deletions ui/src/components/content-card.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import axios from "axios";
import { useAtom } from "jotai";
import { DownloadCloud, FileText, Files, Trash2 } from "lucide-react";
import { toast } from "react-hot-toast";
import { getFiles } from "../actions/getFiles";
import { useAtom } from "jotai";
import { filesAtom, sizeAtom } from "../store";
import { getSize } from "../actions/getSize";
import { filesAtom, sizeAtom } from "../store";
import FileDataModal from "./file-data-modal";
import RenameModal from "./rename-modal";
import ResizeModal from "./resize-modal";
import { Button } from "./ui/button";
import { Dialog, DialogTrigger } from "./ui/dialog";
import FileDataModal from "./file-data-modal";

type TContentCardProps = {
file_name?: string;
Expand Down Expand Up @@ -100,6 +101,9 @@ const ContentCard: React.FC<TContentCardProps> = ({
<DownloadCloud className="inline" size="24" />
</a>
<RenameModal type={type} filename={file_name} />
{type === 'images' && (
<ResizeModal filename={file_name ?? ''} />
)}
</div>
{/* Destructive buttons */}
<div className="flex gap-2">
Expand Down
118 changes: 118 additions & 0 deletions ui/src/components/resize-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import handleResizeImage from "@/actions/handleResizeImage";
import { FileMetadata } from "@/types/fileMetadata";
import { ImageDimensions } from "@/types/imageDimensions";
import axios from "axios";
import { Scaling } from "lucide-react";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { Button } from "./ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
import { Input } from "./ui/input";
import { Label } from "./ui/label";

type ResizeModalProps = {
filename: string;
};


const ResizeModal: React.FC<ResizeModalProps> = ({ filename }) => {
const [resizeFormData, setResizeFormData] = useState<ImageDimensions>({
width: 0,
height: 0,
});

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;

const formattedValue = value.replace(/\D/g, '');
const positiveIntegerValue = formattedValue === '' ? 0 : parseInt(formattedValue, 10);

setResizeFormData({
...resizeFormData,
[name]: positiveIntegerValue,
});
}

useEffect(() => {
if (filename) {
axios
.get<FileMetadata>(
`/api/cdn/image/${filename}`
)
.then((res) => {
toast.dismiss();
if (res.status === 200) {
setResizeFormData({
width: res.data.width ?? 0,
height: res.data.height ?? 0,
})
}
})
.catch((err) => {
toast.dismiss();
toast.error(err.message);
});
}
axios;
}, [filename]);

return (
<Dialog>
<DialogTrigger asChild className="text-sky-600">
<Button size="icon" variant="ghost">
<Scaling className="inline" size="24" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<form
onSubmit={(e) => handleResizeImage(e, resizeFormData, filename)}
>
<DialogHeader>
<DialogTitle>Resize image</DialogTitle>
<DialogDescription>
Change the height and width of the image. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="height" className="text-right">
Height
</Label>
<Input
name="height"
value={resizeFormData.height}
onChange={handleInputChange}
className="col-span-3"
/>
</div>

<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="width" className="text-right">
Width
</Label>
<Input
name="width"
value={resizeFormData.width}
onChange={handleInputChange}
className="col-span-3"
/>
</div>
</div>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
};

export default ResizeModal;
4 changes: 4 additions & 0 deletions ui/src/types/imageDimensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ImageDimensions = {
width: number;
height: number;
}

0 comments on commit f69a174

Please sign in to comment.