Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add modify page #12

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 319 additions & 0 deletions src/app/dashboard/[id]/modify/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
"use client";

import { useForm, FormProvider, SubmitHandler } from "react-hook-form";
import toast from "react-hot-toast";
import Button from "@/components/buttons/Button";
import LabelText from "@/components/form/LabelText";
import "@react-pdf-viewer/core/lib/styles/index.css";
import { Worker, Viewer, SpecialZoomLevel } from "@react-pdf-viewer/core";
import { useEffect, useRef, useState } from "react";

import Modal from "@/components/modal/Modal";

import { useDisclosure } from "@nextui-org/modal";
import { ModifyModal } from "../modal/modifyModal";
import Input from "@/components/form/Input";
import { usePathname, useRouter, useSearchParams } from "next/navigation";

export type EditData = {
id: string;
x: string;
y: string;
w: string;
password: string;
confirmpassword?: string;
};

export default function ModifyPage() {
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();

const pathname = usePathname();

const parseQueryParams = (pathname: string) => {
const queryParams = pathname.split("?")[1]; // Get everything after the "?"
if (!queryParams) return { id: null, url: null };

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const params = queryParams.split("&").reduce((acc: any, param: string) => {
const [key, value] = param.split("=");
acc[key] = decodeURIComponent(value); // Decode the URL-encoded value
return acc;
}, {});

return { id: params.id, fileUrl: params.url };
};

const { id, fileUrl } = parseQueryParams(pathname);

console.log(id);

const methods = useForm<EditData>({ mode: "onChange" });
const { reset, handleSubmit } = methods;

const [selection, setSelection] = useState<{
x: number;
y: number;
w: number;
height: number;
} | null>(null);
const [startPoint, setStartPoint] = useState<{ x: number; y: number } | null>(
null,
);
const [isMdScreen, setIsMdScreen] = useState(false);
const animationFrameId = useRef<number | null>(null);

useEffect(() => {
const handleResize = () => {
setIsMdScreen(window.innerWidth >= 768);
};

window.addEventListener("resize", handleResize);
handleResize();

return () => window.removeEventListener("resize", handleResize);
}, []);

const handleMouseDown = (e: React.MouseEvent, page: HTMLElement) => {
const rect = page.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;

setStartPoint({ x, y });
};

const handleMouseMove = (e: React.MouseEvent, page: HTMLElement) => {
if (startPoint) {
if (animationFrameId.current) {
cancelAnimationFrame(animationFrameId.current);
}

animationFrameId.current = requestAnimationFrame(() => {
const rect = page.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;

const w = currentX - startPoint.x;
const height = currentY - startPoint.y;

setSelection({
x: startPoint.x,
y: startPoint.y,
w,
height,
});
});
}
};

const handleMouseUp = () => {
setStartPoint(null);
if (animationFrameId.current) {
cancelAnimationFrame(animationFrameId.current);
animationFrameId.current = null;
}
};

const [width, setWidth] = useState(60);
const [height, setHeight] = useState(35);

const handlePageClick = (e: React.MouseEvent, page: HTMLElement) => {
const rect = page.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;

setSelection({ x, y, w: width, height: height });
};

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const renderPage = (props: any) => (
<div
id="preview-page"
style={{
position: "relative",
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100%",
overflow: "hidden",
cursor: isMdScreen ? "crosshair" : "default",
zIndex: 100,
}}
onMouseDown={
isMdScreen ? (e) => handleMouseDown(e, e.currentTarget) : undefined
}
onMouseMove={
isMdScreen ? (e) => handleMouseMove(e, e.currentTarget) : undefined
}
onMouseUp={isMdScreen ? handleMouseUp : undefined}
onClick={
!isMdScreen ? (e) => handlePageClick(e, e.currentTarget) : undefined
}
>
{props.canvasLayer.children}
{isMdScreen && selection && (
<div
style={{
position: "absolute",
left: selection.x,
top: selection.y,
width: selection.w,
height: selection.height,
border: "2px dashed blue",
backgroundColor: "rgba(0, 0, 255, 0.1)",
pointerEvents: "none",
}}
/>
)}
{!isMdScreen && selection && (
<div
style={{
position: "absolute",
left: selection.x,
top: selection.y,
width: selection.w,
height: selection.height,
border: "2px dashed red",
backgroundColor: "rgba(255, 0, 0, 0.1)",
pointerEvents: "none",
}}
/>
)}
</div>
);

const [modalData, setModalData] = useState<EditData>({
id: "",
x: "0",
y: "0",
w: "0",
password: "",
confirmpassword: undefined,
});

const onSubmit: SubmitHandler<EditData> = () => {
if (!selection) {
toast.error("Please select coordinates on the preview");
return;
}

const page = document.getElementById("preview-page");
if (!page) return;

const rect = page.getBoundingClientRect();

const scaleX = 595 / rect.width;
const scaleY = 842 / rect.height;

const scaledX = selection.x * scaleX;
const scaledY = selection.y * scaleY;
const scaledW = selection.w * scaleX;

// const preparedData: EditData = {
// id: id,
// x: scaledX.toFixed(0),
// y: scaledY.toFixed(0),
// w: scaledW.toFixed(0),
// password: "",
// confirmpassword: "",
// };

// setModalData(preparedData);
};

// const fileUrl = url;

return (
<div>
Edit Letak Tanda Tangan
<FormProvider {...methods}>
<form
onSubmit={handleSubmit(onSubmit)}
className="space-y-5 h-full mb-4"
>
<LabelText labelTextClasname="mt-4">
Select Coordinates <span className="md:hidden">Area Size</span>
<div className="md:hidden">
<div className="flex items-center mt-2 gap-2 w-[50%]">
Width:
<Input
id="width"
type="number"
value={width}
onChange={(e) => setWidth(parseInt(e.target.value))}
className="px-2 py-1"
/>
</div>
<div className="flex items-center gap-1 w-[50%]">
Height:
<Input
id="height"
type="number"
value={height}
onChange={(e) => setHeight(parseInt(e.target.value))}
className="px-2 py-1"
/>
</div>
</div>
<span className="max-md:hidden">on the Preview</span>
</LabelText>
<div className="w-full h-[45vh] bg-gray-100 mt-6 p-4 border-3 border-primary border-dashed rounded-lg overflow-auto">
{fileUrl ? (
<Worker
workerUrl={`https://unpkg.com/[email protected]/build/pdf.worker.min.js`}
>
<Viewer
fileUrl={fileUrl}
defaultScale={SpecialZoomLevel.PageFit}
renderPage={renderPage}
/>
</Worker>
) : (
<p className="text-center text-gray-500">File not uploaded</p>
)}
</div>
<div className="mt-4 text-gray-700">
<LabelText>
Selected Coordinates: X: {selection?.x.toFixed(2)}, Y:{" "}
{selection?.y.toFixed(2)},{" "}
{isMdScreen && <>Width: {selection?.w.toFixed(2)}</>}
</LabelText>
</div>
</form>
</FormProvider>
<div className="flex w-full justify-center gap-[10px] px-[30px]">
<Button
variant="outline"
size="base"
onClick={() => {
reset();
setSelection(null);
onClose();
}}
className="w-1/2"
>
Batal
</Button>
<ModifyModal data={modalData}>
{({ openModal }) => (
<Button
variant="primary"
size="base"
onClick={() => {
if (!selection) {
toast.error("Please select coordinates on the preview");
} else {
onSubmit(modalData);
openModal();
}
}}
className="w-1/2"
>
Simpan
</Button>
)}
</ModifyModal>
</div>
</div>
);
}
19 changes: 18 additions & 1 deletion src/app/dashboard/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { usePathname } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import api from "@/lib/api";
import Button from "@/components/buttons/Button";
Expand All @@ -13,11 +13,20 @@ import { AcceptModal } from "./modal/acceptModal";
import { RejectModal } from "./modal/rejectModal";
import { EditModal } from "./modal/editModal";
import { useEffect, useState } from "react";
import ModifyPage from "./modify/page";

export default function TambahAjuan() {
const path = usePathname();
const pathId = path.split("/").pop();

const router = useRouter();

const handleNavigate = () => {
router.push(
`/dashboard/modify?id=${data?.ID}&url=${encodeURIComponent(fileUrl)}`,
);
};

const breadCrumbs = [
{ href: "/dashboard", Title: "Dashboard" },
{ href: `/dashboard/${pathId}`, Title: "Detail" },
Expand Down Expand Up @@ -190,6 +199,14 @@ export default function TambahAjuan() {
</Button>
)}
</RejectModal>
<Button
variant="yellow"
size="base"
className="min-h-8 max-w-24 px-9 py-0.5"
onClick={handleNavigate}
>
Edit
</Button>
<EditModal id={data?.ID} url={fileUrl}>
{({ openModal }) => (
<Button
Expand Down
Loading
Loading