summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/lib/camera/component/dispatchCamera.tsx61
-rw-r--r--app/lib/camera/component/pakageCamera.tsx64
-rw-r--r--app/lib/camera/component/sjCamera.tsx73
3 files changed, 149 insertions, 49 deletions
diff --git a/app/lib/camera/component/dispatchCamera.tsx b/app/lib/camera/component/dispatchCamera.tsx
index 2591413..6780c12 100644
--- a/app/lib/camera/component/dispatchCamera.tsx
+++ b/app/lib/camera/component/dispatchCamera.tsx
@@ -3,17 +3,68 @@ import useCameraStore from "../hooks/useCameraStore";
import { IconButton } from "@mui/material";
import { LocalShipping } from "@mui/icons-material";
+const MAX_WIDTH = 1024;
+const JPEG_QUALITY = 0.7;
+
const DispatchCamera: React.FC = () => {
const { setImageDispatch } = useCameraStore();
- const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => {
- const file = event.target.files?.[0];
- if (file) {
+
+ const compressImage = (file: File): Promise<string> => {
+ return new Promise((resolve, reject) => {
const reader = new FileReader();
- reader.onloadend = () => {
- setImageDispatch(reader.result as string);
+
+ reader.onload = () => {
+ const img = new Image();
+ img.onload = () => {
+ let { width, height } = img;
+
+ if (width > MAX_WIDTH) {
+ height = (height * MAX_WIDTH) / width;
+ width = MAX_WIDTH;
+ }
+
+ const canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+
+ const ctx = canvas.getContext("2d");
+ if (!ctx) {
+ reject("Canvas not supported");
+ return;
+ }
+
+ ctx.drawImage(img, 0, 0, width, height);
+
+ const compressedBase64 = canvas.toDataURL("image/jpeg", JPEG_QUALITY);
+
+ resolve(compressedBase64);
+ };
+
+ img.onerror = reject;
+ img.src = reader.result as string;
};
+
+ reader.onerror = reject;
reader.readAsDataURL(file);
+ });
+ };
+ const handleCapture = async (event: React.ChangeEvent<HTMLInputElement>) => {
+ const file = event.target.files?.[0];
+ if (!file) return;
+
+ try {
+ const compressesd = await compressImage(file);
+ setImageDispatch(compressesd);
+ } catch (err) {
+ console.error("Image compress failed", err);
}
+ // if (file) {
+ // const reader = new FileReader();
+ // reader.onloadend = () => {
+ // setImageDispatch(reader.result as string);
+ // };
+ // reader.readAsDataURL(file);
+ // }
};
return (
diff --git a/app/lib/camera/component/pakageCamera.tsx b/app/lib/camera/component/pakageCamera.tsx
index 23ed9a4..7efabb6 100644
--- a/app/lib/camera/component/pakageCamera.tsx
+++ b/app/lib/camera/component/pakageCamera.tsx
@@ -3,34 +3,76 @@ import useCameraStore from "../hooks/useCameraStore";
import { IconButton } from "@mui/material";
import { PhotoCameraFrontOutlined } from "@mui/icons-material";
+const MAX_WIDTH = 1024;
+const JPEG_QUALITY = 0.7;
+
const PackageCamera: React.FC = () => {
const { setImagePackage } = useCameraStore();
- const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => {
- const file = event.target.files?.[0];
- if (file) {
+
+ const compressImage = (file: File): Promise<string> => {
+ return new Promise((resolve, reject) => {
const reader = new FileReader();
- reader.onloadend = () => {
- setImagePackage(reader.result as string);
+
+ reader.onload = () => {
+ const img = new Image();
+ img.onload = () => {
+ let { width, height } = img;
+
+ if (width > MAX_WIDTH) {
+ height = (height * MAX_WIDTH) / width;
+ width = MAX_WIDTH;
+ }
+
+ const canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+
+ const ctx = canvas.getContext("2d");
+ if (!ctx) {
+ reject("Canvas not supported");
+ return;
+ }
+
+ ctx.drawImage(img, 0, 0, width, height);
+
+ const compressedBase64 = canvas.toDataURL("image/jpeg", JPEG_QUALITY);
+
+ resolve(compressedBase64);
+ };
+
+ img.onerror = reject;
+ img.src = reader.result as string;
};
+
+ reader.onerror = reject;
reader.readAsDataURL(file);
+ });
+ };
+
+ const handleCapture = async (event: React.ChangeEvent<HTMLInputElement>) => {
+ const file = event.target.files?.[0];
+ if (!file) return;
+
+ try {
+ const compressed = await compressImage(file);
+ setImagePackage(compressed);
+ } catch (err) {
+ console.error("Image compress failed", err);
}
};
return (
- <div className="px-4 py-8 items-center border-2 rounded-md shadow-sm w-[49%] text-center ">
+ <div className="px-4 py-8 items-center border-2 rounded-md shadow-sm w-[49%] text-center">
<input
type="file"
accept="image/*"
+ capture="environment"
onChange={handleCapture}
className="hidden"
id="pakageCameraInput"
/>
<label htmlFor="pakageCameraInput" className="text-gray-600">
- <IconButton
- color="primary"
- aria-label="upload picture"
- component="span"
- >
+ <IconButton color="primary" component="span">
<PhotoCameraFrontOutlined fontSize="large" />
</IconButton>
<br />
diff --git a/app/lib/camera/component/sjCamera.tsx b/app/lib/camera/component/sjCamera.tsx
index 73062e2..0cb43ac 100644
--- a/app/lib/camera/component/sjCamera.tsx
+++ b/app/lib/camera/component/sjCamera.tsx
@@ -3,41 +3,51 @@ import useCameraStore from "../hooks/useCameraStore";
import { IconButton } from "@mui/material";
import { PendingActions } from "@mui/icons-material";
+const compressImage = (
+ file: File,
+ maxWidth = 1600,
+ quality = 0.6
+): Promise<string> => {
+ return new Promise((resolve) => {
+ const img = new Image();
+ const reader = new FileReader();
+
+ reader.onload = () => {
+ img.src = reader.result as string;
+ };
+
+ img.onload = () => {
+ const scale = Math.min(1, maxWidth / img.width);
+ const canvas = document.createElement("canvas");
+
+ canvas.width = img.width * scale;
+ canvas.height = img.height * scale;
+
+ const ctx = canvas.getContext("2d")!;
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
+
+ resolve(canvas.toDataURL("image/jpeg", quality));
+ };
+
+ reader.readAsDataURL(file);
+ });
+};
+
const SjCamera: React.FC = () => {
const { setImageSj } = useCameraStore();
const fileRef = useRef<HTMLInputElement | null>(null);
- const readFilesAsDataURL = (files: FileList | null): Promise<string[]> =>
- new Promise((resolve) => {
- if (!files || files.length === 0) return resolve([]);
- const list = Array.from(files);
- const out: string[] = [];
- let done = 0;
- list.forEach((f) => {
- const fr = new FileReader();
- fr.onload = (e) => {
- const dataUrl = (e.target?.result as string) || "";
- if (dataUrl) out.push(dataUrl);
- done += 1;
- if (done === list.length) resolve(out);
- };
- fr.onerror = () => {
- done += 1;
- if (done === list.length) resolve(out);
- };
- fr.readAsDataURL(f);
- });
- });
-
const handleSuratJalanCapture = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
- const imgs = await readFilesAsDataURL(event.target.files);
- if (imgs.length > 0) {
- // APPEND: panggil setImageSj untuk tiap foto
- imgs.forEach((img) => setImageSj(img));
- }
- // reset supaya bisa pilih file yang sama lagi
+ const imgs = await Promise.all(
+ Array.from(event.target.files ?? []).map((file) =>
+ compressImage(file, 1600, 0.6)
+ )
+ );
+
+ imgs.forEach((img) => setImageSj(img));
+
if (fileRef.current) fileRef.current.value = "";
};
@@ -49,6 +59,7 @@ const SjCamera: React.FC = () => {
type="file"
accept="image/*"
multiple
+ capture="environment"
className="hidden"
onChange={handleSuratJalanCapture}
/>
@@ -56,11 +67,7 @@ const SjCamera: React.FC = () => {
htmlFor="suratJalanInput"
className="text-gray-600 cursor-pointer select-none"
>
- <IconButton
- color="primary"
- aria-label="upload picture"
- component="span"
- >
+ <IconButton color="primary" component="span">
<PendingActions fontSize="large" />
</IconButton>
<br />