diff --git a/src/app/admin/affiliate-management/components/AffiliateCropModal.tsx b/src/app/admin/affiliate-management/components/AffiliateCropModal.tsx new file mode 100644 index 0000000..a598d4f --- /dev/null +++ b/src/app/admin/affiliate-management/components/AffiliateCropModal.tsx @@ -0,0 +1,132 @@ +'use client' +import React, { useState, useCallback } from 'react' +import Cropper from 'react-easy-crop' +import { Point, Area } from 'react-easy-crop' + +interface AffiliateCropModalProps { + isOpen: boolean + imageSrc: string + onClose: () => void + onCropComplete: (croppedImageBlob: Blob) => void +} + +export default function AffiliateCropModal({ isOpen, imageSrc, onClose, onCropComplete }: AffiliateCropModalProps) { + const [crop, setCrop] = useState({ x: 0, y: 0 }) + const [zoom, setZoom] = useState(1) + const [croppedAreaPixels, setCroppedAreaPixels] = useState(null) + + const onCropAreaComplete = useCallback((_croppedArea: Area, croppedAreaPixels: Area) => { + setCroppedAreaPixels(croppedAreaPixels) + }, []) + + const createCroppedImage = async () => { + if (!croppedAreaPixels) return + + const image = new Image() + image.src = imageSrc + await new Promise((resolve) => { + image.onload = resolve + }) + + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (!ctx) return + + // Set canvas size to cropped area + canvas.width = croppedAreaPixels.width + canvas.height = croppedAreaPixels.height + + ctx.drawImage( + image, + croppedAreaPixels.x, + croppedAreaPixels.y, + croppedAreaPixels.width, + croppedAreaPixels.height, + 0, + 0, + croppedAreaPixels.width, + croppedAreaPixels.height + ) + + return new Promise((resolve) => { + canvas.toBlob((blob) => { + if (blob) resolve(blob) + }, 'image/jpeg', 0.95) + }) + } + + const handleSave = async () => { + const croppedBlob = await createCroppedImage() + if (croppedBlob) { + onCropComplete(croppedBlob) + onClose() + } + } + + if (!isOpen) return null + + return ( +
+
+ {/* Header */} +
+

Crop Affiliate Logo

+ +
+ + {/* Crop Area */} +
+ +
+ + {/* Controls */} +
+
+
+ + setZoom(Number(e.target.value))} + className="w-full h-2 bg-blue-200 rounded-lg appearance-none cursor-pointer accent-blue-900" + /> +
+
+ + +
+
+
+
+
+ ) +} diff --git a/src/app/admin/subscriptions/components/ImageCropModal.tsx b/src/app/admin/subscriptions/components/ImageCropModal.tsx new file mode 100644 index 0000000..db37c64 --- /dev/null +++ b/src/app/admin/subscriptions/components/ImageCropModal.tsx @@ -0,0 +1,132 @@ +'use client' +import React, { useState, useCallback } from 'react' +import Cropper from 'react-easy-crop' +import { Point, Area } from 'react-easy-crop' + +interface ImageCropModalProps { + isOpen: boolean + imageSrc: string + onClose: () => void + onCropComplete: (croppedImageBlob: Blob) => void +} + +export default function ImageCropModal({ isOpen, imageSrc, onClose, onCropComplete }: ImageCropModalProps) { + const [crop, setCrop] = useState({ x: 0, y: 0 }) + const [zoom, setZoom] = useState(1) + const [croppedAreaPixels, setCroppedAreaPixels] = useState(null) + + const onCropAreaComplete = useCallback((_croppedArea: Area, croppedAreaPixels: Area) => { + setCroppedAreaPixels(croppedAreaPixels) + }, []) + + const createCroppedImage = async () => { + if (!croppedAreaPixels) return + + const image = new Image() + image.src = imageSrc + await new Promise((resolve) => { + image.onload = resolve + }) + + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (!ctx) return + + // Set canvas size to cropped area + canvas.width = croppedAreaPixels.width + canvas.height = croppedAreaPixels.height + + ctx.drawImage( + image, + croppedAreaPixels.x, + croppedAreaPixels.y, + croppedAreaPixels.width, + croppedAreaPixels.height, + 0, + 0, + croppedAreaPixels.width, + croppedAreaPixels.height + ) + + return new Promise((resolve) => { + canvas.toBlob((blob) => { + if (blob) resolve(blob) + }, 'image/jpeg', 0.95) + }) + } + + const handleSave = async () => { + const croppedBlob = await createCroppedImage() + if (croppedBlob) { + onCropComplete(croppedBlob) + onClose() + } + } + + if (!isOpen) return null + + return ( +
+
+ {/* Header */} +
+

Crop & Adjust Image

+ +
+ + {/* Crop Area */} +
+ +
+ + {/* Controls */} +
+
+
+ + setZoom(Number(e.target.value))} + className="w-full h-2 bg-blue-200 rounded-lg appearance-none cursor-pointer accent-blue-900" + /> +
+
+ + +
+
+
+
+
+ ) +}