npm-pkg-hook 1.11.3 → 1.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.env +5 -1
  2. package/package.json +3 -2
  3. package/src/config/client/errors.js +1 -1
  4. package/src/config/content/en.json +5 -0
  5. package/src/config/content/es.json +5 -0
  6. package/src/config/content/index.js +16 -0
  7. package/src/hooks/index.js +6 -0
  8. package/src/hooks/newStoreOrderSubscription/index.js +1 -12
  9. package/src/hooks/updateExtProductFoodsOptional/index.js +2 -2
  10. package/src/hooks/useCatWithProduct/queries.js +50 -24
  11. package/src/hooks/useCategoriesProduct/index.js +1 -0
  12. package/src/hooks/useCategoriesProduct/queries.js +2 -2
  13. package/src/hooks/useCategoryInStore/queries.js +2 -2
  14. package/src/hooks/useClients/queries.js +72 -23
  15. package/src/hooks/useCreateProduct/helpers/index.js +2 -2
  16. package/src/hooks/useCreateProduct/index.js +45 -46
  17. package/src/hooks/useDessert/index.js +20 -3
  18. package/src/hooks/useDessertWithPrice/helpers/index.js +2 -2
  19. package/src/hooks/useDessertWithPrice/index.js +70 -60
  20. package/src/hooks/useDessertWithPrice/queries.js +2 -2
  21. package/src/hooks/useDevices/queries.js +16 -7
  22. package/src/hooks/useDevices/useGetDevices.js +12 -19
  23. package/src/hooks/useDevices/useRegisterDevices.js +3 -3
  24. package/src/hooks/useDownloadReports/helpers/downloadFileFromResponse.ts +21 -0
  25. package/src/hooks/useDownloadReports/index.ts +2 -0
  26. package/src/hooks/useDownloadReports/useDownloadReportByDay/index.ts +103 -0
  27. package/src/hooks/useDownloadReports/useGetReportByDateRange/index.ts +115 -0
  28. package/src/hooks/useEditCategory/index.js +10 -10
  29. package/src/hooks/useFormTools/index.js +2 -1
  30. package/src/hooks/useFormatDate/index.js +56 -3
  31. package/src/hooks/useGetStoreCookie/index.js +1 -1
  32. package/src/hooks/useImageUploaderProduct/helper/canvasUtils.ts +130 -0
  33. package/src/hooks/useImageUploaderProduct/helper/getOrientation.ts +53 -0
  34. package/src/hooks/useImageUploaderProduct/helper/index.ts +5 -0
  35. package/src/hooks/useImageUploaderProduct/index.ts +292 -0
  36. package/src/hooks/useImagesStore/index.js +100 -58
  37. package/src/hooks/useImagesStore/queries.js +2 -2
  38. package/src/hooks/useImagesStore/utils/index.js +4 -0
  39. package/src/hooks/useInventory/queries.js +1 -1
  40. package/src/hooks/useLocationManager/index.js +3 -1
  41. package/src/hooks/useLogout/helpers/fetchData.js +1 -1
  42. package/src/hooks/useLogout/helpers/logger.js +8 -8
  43. package/src/hooks/useLogout/index.js +6 -4
  44. package/src/hooks/useManageNewOrder/index.js +3 -3
  45. package/src/hooks/useManageQueryParams/index.js +28 -28
  46. package/src/hooks/useMobile/index.js +38 -8
  47. package/src/hooks/useOrderStatusTypes/index.ts +2 -0
  48. package/src/hooks/useOrderStatusTypes/useOrderStatusTypes/index.ts +52 -0
  49. package/src/hooks/useOrderStatusTypes/useUpdateOrderStatusPriorities/index.ts +97 -0
  50. package/src/hooks/useOrders/index.js +2 -74
  51. package/src/hooks/useOrders/queries.js +31 -195
  52. package/src/hooks/useOrders/useChangeOrderState/index.ts +125 -0
  53. package/src/hooks/useOrders/useOrdersFromStore/index.ts +83 -0
  54. package/src/hooks/usePortFetcher/index.ts +33 -0
  55. package/src/hooks/useProductsFood/index.js +3 -3
  56. package/src/hooks/useProductsFood/queriesStore.js +120 -64
  57. package/src/hooks/useProductsFood/useEditProduct.js +42 -2
  58. package/src/hooks/useProductsFood/usetagsProducts.js +47 -43
  59. package/src/hooks/useRemoveExtraProductFoodsOptional/queries.js +4 -4
  60. package/src/hooks/useSales/index.js +29 -19
  61. package/src/hooks/useSales/queries.js +140 -162
  62. package/src/hooks/useSales/useGetSale.js +3 -3
  63. package/src/hooks/useSaveAvailableProduct/index.js +1 -0
  64. package/src/hooks/useSetImageProducts/index.js +42 -13
  65. package/src/hooks/useSetImageProducts/queries.js +5 -0
  66. package/src/hooks/useStore/index.js +5 -1
  67. package/src/hooks/useStore/queries.js +71 -72
  68. package/src/hooks/useTagProducts/index.ts +3 -0
  69. package/src/hooks/useTagProducts/useDeleteOneTag.ts +106 -0
  70. package/src/hooks/useTagProducts/useGetAllTags.ts +68 -0
  71. package/src/hooks/useTagProducts/useRegisterMultipleTags.ts +156 -0
  72. package/src/hooks/useUpdateDashboardComponent/index.ts +91 -0
  73. package/src/hooks/useUpdateExistingOrders/index.js +3 -9
  74. package/src/hooks/useUpdateExtProductFoodsSubOptional/index.js +25 -3
  75. package/src/hooks/useUpdateMultipleExtProduct/index.js +33 -0
  76. package/src/hooks/useUpdateMultipleExtProduct/queries.js +33 -0
  77. package/src/hooks/useUploadProducts/index.js +0 -1
  78. package/src/hooks/useUser/queries.js +0 -1
  79. package/src/utils/index.js +97 -5
  80. package/tsconfig.json +5 -0
  81. package/src/hooks/useUpdateMultipleExtProductFoods/index.js +0 -21
  82. package/src/hooks/useUpdateMultipleExtProductFoods/queries.js +0 -19
@@ -1,13 +1,13 @@
1
1
  export const useFormatDate = ({
2
2
  date,
3
- local = 'ES'
3
+ local = 'ES-CO'
4
4
  } = {}) => {
5
5
  const dateToFormat = new Date(date ?? Date.now())
6
6
  const fullDate = dateToFormat.toLocaleDateString(local, { year: 'numeric', month: '2-digit', day: '2-digit' })
7
7
  const day = fullDate.split('/')[0]
8
8
  const month = fullDate.split('/')[1]
9
9
  const year = fullDate.split('/')[2]
10
- const yearMonthDay = dateToFormat.toLocaleDateString('en-CA')
10
+ const yearMonthDay = dateToFormat.toLocaleDateString('ES-CO')
11
11
  const numberDay = dateToFormat.getDay()
12
12
  const shortDayName = dateToFormat.toLocaleDateString(local, { weekday: 'short' })
13
13
  const longDayName = dateToFormat.toLocaleDateString(local, { weekday: 'long' })
@@ -17,9 +17,61 @@ export const useFormatDate = ({
17
17
  const hourPmAm = new Date(`1/1/1999 ${hour}`).toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true })
18
18
  return hour ? hourPmAm : ''
19
19
  }
20
+ /**
21
+ * Formats a date into Colombian time zone and Spanish locale for POS display.
22
+ * Example output: "15 de mayo de 2025 a las 14:23"
23
+ *
24
+ * @param {string | number | Date} dateInput - Date to format (ISO string, timestamp or Date object)
25
+ * @returns {string} - Formatted date string in Colombian time
26
+ */
27
+ function formatDateInTimeZone (dateInput) {
28
+ const timeZone = 'America/Bogota'
29
+ const locale = 'es-CO'
30
+ const options = {
31
+ timeZone,
32
+ year: 'numeric',
33
+ month: 'long',
34
+ day: '2-digit',
35
+ hour: '2-digit',
36
+ minute: '2-digit',
37
+ hour12: false
38
+ }
39
+
40
+ const date = new Date(dateInput)
41
+ const formatted = new Intl.DateTimeFormat(locale, options).format(date)
42
+
43
+ // Cambiar coma por "a las" para mejor legibilidad
44
+ return formatted.replace(',', ' a las')
45
+ }
46
+ /**
47
+ * Formats a date into YYYY-MM-DD using a given IANA timezone.
48
+ *
49
+ * @param {string | number | Date} inputDate - The date to format.
50
+ * @param {string} timeZone - IANA timezone string (default is "America/Bogota").
51
+ * @returns {string} Formatted date string in YYYY-MM-DD.
52
+ * @throws {Error} If the input date is invalid.
53
+ */
54
+ const formatToLocalDateYMD = (inputDate, timeZone = 'America/Bogota') => {
55
+ const date = new Date(inputDate)
56
+
57
+ if (isNaN(date.getTime())) {
58
+ throw new Error('Invalid date input provided to formatToLocalDateYMD')
59
+ }
60
+
61
+ const localDateStr = date.toLocaleString('en-US', { timeZone })
62
+ const localDate = new Date(localDateStr)
63
+
64
+ const year = localDate.getFullYear()
65
+ const month = String(localDate.getMonth() + 1).padStart(2, '0')
66
+ const day = String(localDate.getDate()).padStart(2, '0')
67
+
68
+ return `${year}-${month}-${day}`
69
+ }
70
+
20
71
  return {
21
72
  day,
22
73
  fullDate,
74
+ formatToLocalDateYMD,
23
75
  hourMinutes12,
24
76
  numberDay,
25
77
  yearMonthDay,
@@ -28,6 +80,7 @@ export const useFormatDate = ({
28
80
  shortDayName,
29
81
  month,
30
82
  year,
31
- handleHourPmAM
83
+ handleHourPmAM,
84
+ formatDateInTimeZone
32
85
  }
33
86
  }
@@ -6,7 +6,7 @@ export const useGetStoreCookie = () => {
6
6
 
7
7
  useEffect(() => {
8
8
  const getCookieValue = () => {
9
- const cookieValue = Cookies.get(process.env.SESSION_NAME)
9
+ const cookieValue = Cookies.get(process.env.NEXT_PUBLIC_SESSION_NAME)
10
10
  console.log('Cookie Value:', cookieValue)
11
11
 
12
12
  if (cookieValue) {
@@ -0,0 +1,130 @@
1
+ export const createImage = (url) =>
2
+ new Promise((resolve, reject) => {
3
+ const image = new Image()
4
+ image.addEventListener('load', () => resolve(image))
5
+ image.addEventListener('error', (error) => reject(error))
6
+ image.setAttribute('crossOrigin', 'anonymous') // needed to avoid cross-origin issues on CodeSandbox
7
+ image.src = url
8
+ })
9
+
10
+ export function getRadianAngle(degreeValue) {
11
+ return (degreeValue * Math.PI) / 180
12
+ }
13
+
14
+ /**
15
+ * Returns the new bounding area of a rotated rectangle.
16
+ */
17
+ export function rotateSize(width, height, rotation) {
18
+ const rotRad = getRadianAngle(rotation)
19
+
20
+ return {
21
+ width:
22
+ Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
23
+ height:
24
+ Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height),
25
+ }
26
+ }
27
+
28
+ /**
29
+ * This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop
30
+ */
31
+ /**
32
+ * This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop
33
+ */
34
+ export async function getCroppedImg(
35
+ imageSrc,
36
+ pixelCrop,
37
+ rotation = 0,
38
+ flip = { horizontal: false, vertical: false }
39
+ ) {
40
+ const image = await createImage(imageSrc)
41
+ const canvas = document.createElement('canvas')
42
+ const ctx = canvas.getContext('2d')
43
+
44
+ if (!ctx) {
45
+ return null
46
+ }
47
+
48
+ const rotRad = getRadianAngle(rotation)
49
+
50
+ // calculate bounding box of the rotated image
51
+ const { width: bBoxWidth, height: bBoxHeight } = rotateSize(
52
+ image.width,
53
+ image.height,
54
+ rotation
55
+ )
56
+
57
+ // set canvas size to match the bounding box
58
+ canvas.width = bBoxWidth
59
+ canvas.height = bBoxHeight
60
+
61
+ // translate canvas context to a central location to allow rotating and flipping around the center
62
+ ctx.translate(bBoxWidth / 2, bBoxHeight / 2)
63
+ ctx.rotate(rotRad)
64
+ ctx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1)
65
+ ctx.translate(-image.width / 2, -image.height / 2)
66
+
67
+ // draw rotated image
68
+ ctx.drawImage(image, 0, 0)
69
+
70
+ const croppedCanvas = document.createElement('canvas')
71
+
72
+ const croppedCtx = croppedCanvas.getContext('2d')
73
+
74
+ if (!croppedCtx) {
75
+ return null
76
+ }
77
+
78
+ // Set the size of the cropped canvas
79
+ croppedCanvas.width = pixelCrop.width
80
+ croppedCanvas.height = pixelCrop.height
81
+
82
+ // Draw the cropped image onto the new canvas
83
+ croppedCtx.drawImage(
84
+ canvas,
85
+ pixelCrop.x,
86
+ pixelCrop.y,
87
+ pixelCrop.width,
88
+ pixelCrop.height,
89
+ 0,
90
+ 0,
91
+ pixelCrop.width,
92
+ pixelCrop.height
93
+ )
94
+
95
+ // As Base64 string
96
+ // return croppedCanvas.toDataURL('image/jpeg');
97
+
98
+ // As a blob
99
+ return new Promise((resolve, reject) => {
100
+ croppedCanvas.toBlob((file) => {
101
+ resolve(URL.createObjectURL(file))
102
+ }, 'image/png')
103
+ })
104
+ }
105
+
106
+ export async function getRotatedImage(imageSrc, rotation = 0) {
107
+ const image = await createImage(imageSrc)
108
+ const canvas = document.createElement('canvas')
109
+ const ctx = canvas.getContext('2d')
110
+
111
+ const orientationChanged =
112
+ rotation === 90 || rotation === -90 || rotation === 270 || rotation === -270
113
+ if (orientationChanged) {
114
+ canvas.width = image.height
115
+ canvas.height = image.width
116
+ } else {
117
+ canvas.width = image.width
118
+ canvas.height = image.height
119
+ }
120
+
121
+ ctx.translate(canvas.width / 2, canvas.height / 2)
122
+ ctx.rotate((rotation * Math.PI) / 180)
123
+ ctx.drawImage(image, -image.width / 2, -image.height / 2)
124
+
125
+ return new Promise((resolve) => {
126
+ canvas.toBlob((file) => {
127
+ resolve(URL.createObjectURL(file))
128
+ }, 'image/png')
129
+ })
130
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Reads EXIF orientation from a JPEG file (native, no dependencies)
3
+ * @param {File} file
4
+ * @returns {Promise<number>} Orientation value from 1 to 8
5
+ */
6
+ export const getOrientation = (file: File, validTypes: string[]): Promise<number> => {
7
+ return new Promise((resolve, reject) => {
8
+ const reader = new FileReader()
9
+ reader.onload = (event) => {
10
+ const view = new DataView(event.target?.result as ArrayBuffer)
11
+
12
+ if (!validTypes.includes(file.type)) {
13
+ return reject(new Error(`Archivo no soportado: ${file.type}`))
14
+ }
15
+
16
+ let offset = 2
17
+ const length = view.byteLength
18
+
19
+ while (offset < length) {
20
+ if (view.getUint16(offset + 2, false) <= 8) return resolve(1)
21
+
22
+ const marker = view.getUint16(offset, false)
23
+ offset += 2
24
+
25
+ if (marker === 0xFFE1) {
26
+ const little = view.getUint16(offset + 8, false) === 0x4949
27
+ offset += 10
28
+
29
+ const tags = view.getUint16(offset, little)
30
+ offset += 2
31
+
32
+ for (let i = 0; i < tags; i++) {
33
+ const tagOffset = offset + i * 12
34
+ const tag = view.getUint16(tagOffset, little)
35
+ if (tag === 0x0112) {
36
+ const orientation = view.getUint16(tagOffset + 8, little)
37
+ return resolve(orientation)
38
+ }
39
+ }
40
+ } else if ((marker & 0xFF00) !== 0xFF00) {
41
+ break
42
+ } else {
43
+ offset += view.getUint16(offset, false)
44
+ }
45
+ }
46
+
47
+ resolve(1) // por defecto
48
+ }
49
+
50
+ reader.onerror = () => reject(new Error('Error leyendo archivo'))
51
+ reader.readAsArrayBuffer(file)
52
+ })
53
+ }
@@ -0,0 +1,5 @@
1
+ export const ORIENTATION_TO_ANGLE = {
2
+ 3: 180,
3
+ 6: 90,
4
+ 8: -90
5
+ }
@@ -0,0 +1,292 @@
1
+ import { useRef, useState } from 'react'
2
+ import { ORIENTATION_TO_ANGLE } from './helper'
3
+ import { getCroppedImg, getRotatedImage } from './helper/canvasUtils'
4
+ import { getOrientation } from './helper/getOrientation'
5
+
6
+ function readFile(file: File): Promise<string> {
7
+ return new Promise((resolve) => {
8
+ const reader = new FileReader()
9
+ reader.addEventListener('load', () => resolve(reader.result as string), false)
10
+ reader.readAsDataURL(file)
11
+ })
12
+ }
13
+
14
+ export type SendNotificationFn = (params: {
15
+ description: string
16
+ title: string
17
+ backgroundColor: string
18
+ }) => void
19
+
20
+ interface UseImageUploaderOptions {
21
+ maxSizeMB?: number
22
+ minHeight?: number
23
+ minWidth?: number
24
+ validTypes?: string[]
25
+ sendNotification: SendNotificationFn
26
+ }
27
+
28
+ interface UseImageUploaderResult {
29
+ crop: { x: number, y: number }
30
+ croppedImage: string | null
31
+ error: string
32
+ image: File
33
+ imageSrc: string | null
34
+ inputRef: React.RefObject<HTMLInputElement>
35
+ loading: boolean
36
+ open: boolean
37
+ preview: string | null
38
+ formattedList: string
39
+ rotation: number
40
+ zoom: number
41
+ validTypes: string[]
42
+ handleClose: () => void
43
+ handleRemoveImage: () => void
44
+ onCropComplete: (croppedArea: any, croppedPixels: any) => void
45
+ onFileChange: (e: React.ChangeEvent<HTMLInputElement>) => Promise<void>
46
+ setCrop: React.Dispatch<React.SetStateAction<{ x: number, y: number }>>
47
+ setRotation: React.Dispatch<React.SetStateAction<number>>
48
+ setZoom: React.Dispatch<React.SetStateAction<number>>
49
+ handleDrop: (event: React.DragEvent<HTMLDivElement>) => Promise<void>
50
+ showCroppedImage: () => Promise<void>
51
+ }
52
+
53
+ /**
54
+ * Hook for managing image selection, preview, validation and cropping
55
+ * @param {UseImageUploaderOptions} options
56
+ * @returns {UseImageUploaderResult}
57
+ */
58
+ export const useImageUploaderProduct = (
59
+ options?: UseImageUploaderOptions
60
+ ): UseImageUploaderResult => {
61
+ const {
62
+ validTypes = ['image/jpeg', 'image/jpg', 'image/png'],
63
+ maxSizeMB = 20,
64
+ minWidth = 300,
65
+ minHeight = 275,
66
+ sendNotification
67
+ } = options ?? {
68
+
69
+ }
70
+ const readableFormats = validTypes
71
+ .map((type) => type.split('/')[1].toUpperCase())
72
+
73
+ const formatter = new Intl.ListFormat('es', {
74
+ style: 'long',
75
+ type: 'conjunction'
76
+ })
77
+
78
+ const formattedList = formatter.format(readableFormats)
79
+
80
+ const [loading, setLoading] = useState(false)
81
+ const inputRef = useRef<HTMLInputElement>(null)
82
+ const [open, setOpen] = useState(false)
83
+ const [preview, setPreview] = useState<string | null>(null)
84
+ const [imageSrc, setImageSrc] = useState<string | null>(null)
85
+ const [croppedImage, setCroppedImage] = useState<string | null>(null)
86
+ const [crop, setCrop] = useState({ x: 0, y: 0 })
87
+ const [zoom, setZoom] = useState(1)
88
+ const [rotation, setRotation] = useState(0)
89
+ const [croppedAreaPixels, setCroppedAreaPixels] = useState(null)
90
+ const [error, setError] = useState('')
91
+ const [image, setImage] = useState<File | null>(null)
92
+
93
+
94
+ const onCropComplete = (_: any, croppedPixels: any) => {
95
+ setCroppedAreaPixels(croppedPixels)
96
+ }
97
+
98
+ const showCroppedImage = async () => {
99
+ if (!imageSrc || !croppedAreaPixels) return
100
+ try {
101
+ setLoading(true)
102
+ const base64Image = await getCroppedImg(imageSrc, croppedAreaPixels, rotation) as string
103
+
104
+ setCroppedImage(base64Image)
105
+ setPreview(base64Image)
106
+
107
+ // ✅ Convert base64 to blob and file
108
+ const blob = await (await fetch(base64Image)).blob()
109
+ const file = new File([blob], image?.name ?? 'cropped.jpeg', { type: blob.type })
110
+ setImage(file)
111
+ setLoading(false)
112
+ } catch (e) {
113
+ setLoading(false)
114
+ return sendNotification({
115
+ description: e ?? 'Ocurrió un error',
116
+ title: 'Error',
117
+ backgroundColor: 'error'
118
+ })
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Removes the selected image and resets relevant states
124
+ */
125
+ const handleRemoveImage = () => {
126
+ setImage(null)
127
+ setPreview(null)
128
+ setImageSrc(null)
129
+ setCroppedImage(null)
130
+ setZoom(1)
131
+ setCrop({ x: 0, y: 0 })
132
+ setRotation(0)
133
+ setError('')
134
+ if (inputRef.current) inputRef.current.value = ''
135
+ }
136
+
137
+ const onFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
138
+ const file = e.target.files?.[0]
139
+ handleRemoveImage()
140
+ if (!file) return
141
+
142
+ if (!validTypes.includes(file.type)) {
143
+
144
+
145
+ const error = `Formato inválido. Solo se permiten: ${formattedList}.`
146
+ sendNotification({
147
+ description: error,
148
+ title: 'Error',
149
+ backgroundColor: 'error'
150
+ })
151
+ return setError(error)
152
+ }
153
+
154
+ if (file.size > maxSizeMB * 1024 * 1024) {
155
+ const error = `El archivo supera los ${maxSizeMB} MB.`
156
+ sendNotification({
157
+ description: error,
158
+ title: 'Error',
159
+ backgroundColor: 'error'
160
+ })
161
+ return setError(`El archivo supera los ${maxSizeMB} MB.`)
162
+ }
163
+
164
+ let imageDataUrl = await readFile(file)
165
+
166
+ try {
167
+ const orientation = await getOrientation(file, validTypes)
168
+ const rotation = ORIENTATION_TO_ANGLE[orientation]
169
+ if (rotation) {
170
+ imageDataUrl = await getRotatedImage(imageDataUrl, rotation) as string
171
+ setRotation(rotation)
172
+ }
173
+ } catch (e) {
174
+ return sendNotification({
175
+ description: 'Error desconocido',
176
+ title: 'Error',
177
+ backgroundColor: 'error'
178
+ })
179
+ }
180
+
181
+ const image = new Image()
182
+ image.src = imageDataUrl
183
+ image.onload = () => {
184
+ if (image.width < minWidth || image.height < minHeight) {
185
+ const error = `Resolución mínima: ${minWidth}x${minHeight}px.`
186
+ sendNotification({
187
+ description: error,
188
+ title: 'Error',
189
+ backgroundColor: 'error'
190
+ })
191
+ setError(error)
192
+ return
193
+ }
194
+ setImageSrc(imageDataUrl)
195
+ setOpen(true)
196
+ }
197
+ }
198
+
199
+ const handleClose = () => {
200
+ setOpen(!open)
201
+ }
202
+
203
+ const handleDrop = async (event: React.DragEvent<HTMLDivElement>): Promise<void> => {
204
+ event.preventDefault()
205
+ handleRemoveImage()
206
+ const file = event.dataTransfer.files?.[0]
207
+ if (!file) return
208
+
209
+ if (!validTypes.includes(file.type)) {
210
+ const error = `Formato inválido. Solo se permiten: ${formattedList}.`
211
+ sendNotification({
212
+ description: error,
213
+ title: 'Error',
214
+ backgroundColor: 'error'
215
+ })
216
+ return setError(error)
217
+ }
218
+
219
+ if (file.size > maxSizeMB * 1024 * 1024) {
220
+ const error = `El archivo supera los ${maxSizeMB} MB.`
221
+ sendNotification({
222
+ description: error,
223
+ title: 'Error',
224
+ backgroundColor: 'error'
225
+ })
226
+ return setError(error)
227
+ }
228
+
229
+ let imageDataUrl = await readFile(file)
230
+
231
+ try {
232
+ const orientation = await getOrientation(file, validTypes)
233
+ const rotation = ORIENTATION_TO_ANGLE[orientation]
234
+ if (rotation) {
235
+ imageDataUrl = await getRotatedImage(imageDataUrl, rotation) as string
236
+ setRotation(rotation)
237
+ }
238
+ } catch (e) {
239
+ return sendNotification({
240
+ description: 'Error al procesar la orientación de la imagen',
241
+ title: 'Error',
242
+ backgroundColor: 'error'
243
+ })
244
+ }
245
+
246
+ const image = new Image()
247
+ image.src = imageDataUrl
248
+ image.onload = () => {
249
+ if (image.width < minWidth || image.height < minHeight) {
250
+ const error = `Resolución mínima: ${minWidth}x${minHeight}px.`
251
+ sendNotification({
252
+ description: error,
253
+ title: 'Error',
254
+ backgroundColor: 'error'
255
+ })
256
+ setError(error)
257
+ return
258
+ }
259
+ setImageSrc(imageDataUrl)
260
+ setImage(file)
261
+ setOpen(true)
262
+ }
263
+ }
264
+
265
+
266
+
267
+
268
+ return {
269
+ inputRef,
270
+ open,
271
+ imageSrc,
272
+ preview,
273
+ croppedImage,
274
+ crop,
275
+ zoom,
276
+ image,
277
+ rotation,
278
+ error,
279
+ formattedList,
280
+ loading,
281
+ validTypes,
282
+ onCropComplete,
283
+ showCroppedImage,
284
+ handleRemoveImage,
285
+ handleDrop,
286
+ onFileChange,
287
+ handleClose,
288
+ setCrop,
289
+ setZoom,
290
+ setRotation,
291
+ }
292
+ }