Compare commits
20 Commits
admin
...
T2025-10-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 50716e363d | |||
| 4414923d04 | |||
| a78ed6f51b | |||
| 3f01709952 | |||
| 769a1fd4e8 | |||
| 70f0b38e93 | |||
| d8c23dcf09 | |||
| 756815983d | |||
| 33ca76c054 | |||
|
|
6acb55c563 | ||
| bdabc94974 | |||
|
|
5387eb25cf | ||
| 06c5d32ae0 | |||
| fd4fe5dc2b | |||
| 0c4a48a60b | |||
| f845629b81 | |||
| 5e50d6bac0 | |||
| 539954eb41 | |||
| 63ab45856f | |||
| 4660ed59a7 |
5
.env
5
.env
@@ -1,5 +0,0 @@
|
|||||||
VITE_DEBUG_MODE=true
|
|
||||||
|
|
||||||
VITE_EFC_API_URL=http://192.168.1.79:8000/api/v1
|
|
||||||
VITE_EFC_MICROSERVICE_URL=http://192.168.1.79:8001/api/v1
|
|
||||||
VITE_EFC_MICROSERVICE_URL_2=http://192.168.1.79:8001/api/v2
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,3 +23,4 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
.env
|
.env
|
||||||
|
*.bak
|
||||||
105
package-lock.json
generated
105
package-lock.json
generated
@@ -14,6 +14,8 @@
|
|||||||
"@tanstack/react-query": "^5.62.7",
|
"@tanstack/react-query": "^5.62.7",
|
||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"lucide-react": "^0.562.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
@@ -1706,6 +1708,12 @@
|
|||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -2420,6 +2428,12 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/imurmurhash": {
|
"node_modules/imurmurhash": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
@@ -2429,6 +2443,12 @@
|
|||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -2488,6 +2508,12 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
|
||||||
@@ -2531,6 +2557,27 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
@@ -2557,6 +2604,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.562.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz",
|
||||||
|
"integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/memoize-one": {
|
"node_modules/memoize-one": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||||
@@ -2703,6 +2759,12 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -2919,6 +2981,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.1.1",
|
"version": "19.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||||
@@ -3029,6 +3097,21 @@
|
|||||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -3069,6 +3152,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.26.0",
|
"version": "0.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||||
@@ -3082,11 +3171,26 @@
|
|||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/shallowequal": {
|
"node_modules/shallowequal": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width-cjs": {
|
"node_modules/string-width-cjs": {
|
||||||
"name": "string-width",
|
"name": "string-width",
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
@@ -3727,7 +3831,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
"@tanstack/react-query": "^5.62.7",
|
"@tanstack/react-query": "^5.62.7",
|
||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"lucide-react": "^0.562.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
|||||||
68
src/api/pedimentoCompleto.ts
Normal file
68
src/api/pedimentoCompleto.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// src\api\pedimentoCompleto.ts
|
||||||
|
import { fetchWithAuth } from '../fetchWithAuth';
|
||||||
|
|
||||||
|
export interface PedimentoCompleto {
|
||||||
|
id: string;
|
||||||
|
organizacion: string;
|
||||||
|
pedimento: string;
|
||||||
|
pedimento_numero: string;
|
||||||
|
archivo: string;
|
||||||
|
document_type: number;
|
||||||
|
size: number;
|
||||||
|
extension: string;
|
||||||
|
fuente: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PedimentoCompletoResponse {
|
||||||
|
count: number;
|
||||||
|
next: string | null;
|
||||||
|
previous: string | null;
|
||||||
|
results: PedimentoCompleto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DocumentFilters {
|
||||||
|
document_type?: string;
|
||||||
|
archivo__icontains?: string;
|
||||||
|
extension?: string;
|
||||||
|
created_at__date?: string;
|
||||||
|
ordering?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_URL = (import.meta as any).env.VITE_EFC_API_URL;
|
||||||
|
|
||||||
|
export async function fetchPedimentoCompleto(
|
||||||
|
pedimentoId: string,
|
||||||
|
page: number = 1,
|
||||||
|
pageSize: number = 10,
|
||||||
|
filters: DocumentFilters = {}
|
||||||
|
): Promise<PedimentoCompletoResponse> {
|
||||||
|
try {
|
||||||
|
// Construir URL con filtros
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
page: page.toString(),
|
||||||
|
page_size: pageSize.toString(),
|
||||||
|
pedimento: pedimentoId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Agregar filtros si existen
|
||||||
|
Object.entries(filters).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== '') {
|
||||||
|
params.append(key, value.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// const res = await fetchWithAuth(`${API_URL}/record/documents/?${params.toString()}`);
|
||||||
|
const res = await fetchWithAuth(`${API_URL}/record/pedimento-documents/?${params.toString()}`);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('No autorizado o error en la petición');
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in fetchPedimentoCompleto:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import fetchWithAuth from '../fetchWithAuth';
|
import fetchWithAuth from '../fetchWithAuth';
|
||||||
|
|
||||||
const initialFilters = {
|
const initialFilters = {
|
||||||
pedimento_app: '',
|
pedimento_app: '',
|
||||||
aduana: '',
|
aduana: '',
|
||||||
@@ -12,11 +11,48 @@ const initialFilters = {
|
|||||||
fecha_pago_lte: '',
|
fecha_pago_lte: '',
|
||||||
contribuyente__rfc: '',
|
contribuyente__rfc: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TableroAlmacenamiento() {
|
export default function TableroAlmacenamiento() {
|
||||||
const [filters, setFilters] = useState(initialFilters);
|
const [filters, setFilters] = useState(initialFilters);
|
||||||
const [summary, setSummary] = useState(null);
|
const [summary, setSummary] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [reports, setReports] = useState([]);
|
||||||
|
|
||||||
|
const handleGenerateReport = async () => {
|
||||||
|
try {
|
||||||
|
const params = Object.entries(filters)
|
||||||
|
.filter(([_, v]) => v)
|
||||||
|
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
||||||
|
.join('&');
|
||||||
|
const url = `${import.meta.env.VITE_EFC_API_URL}/reports/table-summary/${params ? `?${params}` : ''}`;
|
||||||
|
const res = await fetchWithAuth(url, { method: 'POST' });
|
||||||
|
if (!res.ok) throw new Error('Error al generar el reporte');
|
||||||
|
alert('Reporte solicitado correctamente. Aparecerá en el historial cuando esté listo.');
|
||||||
|
} catch (err) {
|
||||||
|
alert('No se pudo generar el reporte.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownloadReport = async (reportId) => {
|
||||||
|
try {
|
||||||
|
const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-download/${reportId}/`;
|
||||||
|
const res = await fetchWithAuth(url);
|
||||||
|
if (!res.ok) throw new Error('Error al descargar el reporte');
|
||||||
|
const blob = await res.blob();
|
||||||
|
let filename = `reporte_${reportId}.csv`;
|
||||||
|
const disposition = res.headers.get('Content-Disposition');
|
||||||
|
if (disposition && disposition.includes('filename=')) {
|
||||||
|
filename = disposition.split('filename=')[1].replace(/"/g, '').trim();
|
||||||
|
}
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
} catch (err) {
|
||||||
|
alert('No se pudo descargar el reporte.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Fetch summary data
|
// Fetch summary data
|
||||||
const fetchSummary = async () => {
|
const fetchSummary = async () => {
|
||||||
@@ -36,26 +72,30 @@ export default function TableroAlmacenamiento() {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch initial data
|
// Fetch report list from API
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchReports = async () => {
|
||||||
|
try {
|
||||||
|
const url = `${import.meta.env.VITE_EFC_API_URL}/reports/report-document-list/`;
|
||||||
|
const res = await fetchWithAuth(url);
|
||||||
|
if (!res.ok) throw new Error('Error al obtener el historial de reportes');
|
||||||
|
const data = await res.json();
|
||||||
|
setReports(data);
|
||||||
|
} catch (err) {
|
||||||
|
setReports([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchReports();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSummary();
|
fetchSummary();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle filter changes
|
|
||||||
const handleFilterChange = (e) => {
|
const handleFilterChange = (e) => {
|
||||||
setFilters({ ...filters, [e.target.name]: e.target.value });
|
setFilters({ ...filters, [e.target.name]: e.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Card components for different sizes
|
|
||||||
const Card = ({ title, children, icon, small }) => (
|
|
||||||
<div className={`bg-white rounded-lg shadow-sm border border-slate-200 p-4 flex flex-col w-full ${small ? 'min-h-[120px]' : 'min-h-[200px]'}`}>
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
{icon && <span className={`${small ? 'text-slate-600' : 'text-blue-600'}`}>{icon}</span>}
|
|
||||||
<span className={`text-sm font-semibold ${small ? 'text-slate-600' : 'text-slate-700'}`}>{title}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">{children}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-100">
|
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-100">
|
||||||
@@ -97,12 +137,21 @@ export default function TableroAlmacenamiento() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"
|
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"
|
||||||
>
|
>
|
||||||
Aplicar Filtros
|
Aplicar Filtros
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition-colors"
|
||||||
|
onClick={() => alert('Generar reporte (implementación pendiente)')}
|
||||||
|
>
|
||||||
|
Generar Reporte
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,203 +164,60 @@ export default function TableroAlmacenamiento() {
|
|||||||
<span className="text-slate-600">Cargando resumen...</span>
|
<span className="text-slate-600">Cargando resumen...</span>
|
||||||
</div>
|
</div>
|
||||||
) : summary ? (
|
) : summary ? (
|
||||||
|
<>
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||||
{/* Pedimentos */}
|
{/* ...Tarjetas existentes... */}
|
||||||
<Card
|
{/* ...aquí van las Card como antes... */}
|
||||||
title="Pedimentos"
|
{/* ...no se repite para brevedad... */}
|
||||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 17l4 4 4-4m-4-5v9" /></svg>}
|
</div>
|
||||||
|
{/* Tabla de reportes debajo de las tarjetas */}
|
||||||
|
<div className="mt-10">
|
||||||
|
<h2 className="text-lg font-bold text-slate-700 mb-4">Historial de Reportes</h2>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="min-w-full bg-white rounded-lg shadow border border-slate-200">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-slate-100">
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">ID</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">Estado</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">Creado</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">Finalizado</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">Error</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-semibold text-slate-600">Descargar</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{reports.length > 0 ? (
|
||||||
|
reports.map((r) => (
|
||||||
|
<tr key={r.report_id}>
|
||||||
|
<td className="px-4 py-2 text-xs text-slate-700">{r.report_id}</td>
|
||||||
|
<td className="px-4 py-2 text-xs text-slate-700">{r.status}</td>
|
||||||
|
<td className="px-4 py-2 text-xs text-slate-700">{r.created_at}</td>
|
||||||
|
<td className="px-4 py-2 text-xs text-slate-700">{r.finished_at}</td>
|
||||||
|
<td className="px-4 py-2 text-xs text-red-500">{r.error_message ? r.error_message : '-'}</td>
|
||||||
|
<td className="px-4 py-2 text-xs">
|
||||||
|
{r.status === 'ready' ? (
|
||||||
|
<button
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
onClick={() => handleDownloadReport(r.report_id)}
|
||||||
>
|
>
|
||||||
<div className="text-2xl font-bold text-blue-700">{summary.pedimentos?.total ?? '-'}</div>
|
Descargar
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2 text-xs">
|
</button>
|
||||||
<div>
|
) : (
|
||||||
<span className="block text-slate-500">Completos</span>
|
<span className="text-slate-400">-</span>
|
||||||
<span className="block font-semibold">{summary.pedimentos?.completos ?? '-'}</span>
|
)}
|
||||||
</div>
|
</td>
|
||||||
<div>
|
</tr>
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
))
|
||||||
<span className="block font-semibold">{summary.pedimentos?.pendientes ?? '-'}</span>
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={6} className="px-4 py-2 text-center text-slate-400">No hay reportes disponibles.</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
</>
|
||||||
<span className="block text-xs text-slate-500 mb-1">Cumplimiento</span>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-600 h-2 rounded"
|
|
||||||
style={{ width: `${summary.pedimentos?.cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-700 font-semibold mt-1">
|
|
||||||
{summary.pedimentos?.cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-2 pt-2 border-t grid grid-cols-2 gap-4">
|
|
||||||
{/* Documentos */}
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<svg className="w-4 h-4 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 16h8M8 12h8M8 8h8" />
|
|
||||||
</svg>
|
|
||||||
<span className="text-xs font-semibold text-slate-600">Documentos</span>
|
|
||||||
</div>
|
|
||||||
<div className="mt-1">
|
|
||||||
<div className="text-lg font-bold text-slate-700">{summary.documentos?.descargados ?? '-'}</div>
|
|
||||||
<span className="block text-xs text-slate-500">Descargados</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Remesas */}
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<svg className="w-4 h-4 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3" />
|
|
||||||
</svg>
|
|
||||||
<span className="text-xs font-semibold text-slate-600">Remesas</span>
|
|
||||||
</div>
|
|
||||||
<div className="mt-1">
|
|
||||||
<div className="text-lg font-bold text-slate-700">{summary.remesas?.total ?? '-'}</div>
|
|
||||||
<span className="block text-xs text-slate-500">Total</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Partidas */}
|
|
||||||
<Card
|
|
||||||
title="Partidas"
|
|
||||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" /></svg>}
|
|
||||||
>
|
|
||||||
<div className="text-2xl font-bold text-blue-700">{summary.partidas?.total ?? '-'}</div>
|
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2 text-xs">
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Descargadas</span>
|
|
||||||
<span className="block font-semibold">{summary.partidas?.partidas_descargadas ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
|
||||||
<span className="block font-semibold">{summary.partidas?.partidas_pendientes ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">
|
|
||||||
<span className="block text-xs text-slate-500 mb-1">Cumplimiento</span>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-600 h-2 rounded"
|
|
||||||
style={{ width: `${summary.partidas?.cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-700 font-semibold mt-1">
|
|
||||||
{summary.partidas?.cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* COVES */}
|
|
||||||
<Card
|
|
||||||
title="COVES"
|
|
||||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 8h10M7 12h10M7 16h10" /></svg>}
|
|
||||||
>
|
|
||||||
<div className="text-2xl font-bold text-blue-700">{summary.coves?.total ?? '-'}</div>
|
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2 text-xs">
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Procesados</span>
|
|
||||||
<span className="block font-semibold">{summary.coves?.coves_procesados ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
|
||||||
<span className="block font-semibold">{summary.coves?.coves_pendientes ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">
|
|
||||||
<span className="block text-xs text-slate-500 mb-1">Cumplimiento</span>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-600 h-2 rounded"
|
|
||||||
style={{ width: `${summary.coves?.coves_cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-700 font-semibold mt-1">
|
|
||||||
{summary.coves?.coves_cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 border-t pt-2">
|
|
||||||
<span className="block text-xs text-slate-500 mb-1">Acuses</span>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Procesados</span>
|
|
||||||
<span className="block font-semibold">{summary.coves?.acuse_coves_procesados ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
|
||||||
<span className="block font-semibold">{summary.coves?.acuse_coves_pendientes ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2 mt-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-400 h-2 rounded"
|
|
||||||
style={{ width: `${summary.coves?.acuse_coves_cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-400 font-semibold mt-1">
|
|
||||||
{summary.coves?.acuse_coves_cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* EDocuments */}
|
|
||||||
<Card
|
|
||||||
title="EDocuments"
|
|
||||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v16h16V4H4zm4 4h8v8H8V8z" /></svg>}
|
|
||||||
>
|
|
||||||
<div className="text-2xl font-bold text-blue-700">{summary.edocuments?.total ?? '-'}</div>
|
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2 text-xs">
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">asd</span>
|
|
||||||
<span className="block font-semibold">{summary.edocuments?.edocs_descargados ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
|
||||||
<span className="block font-semibold">{summary.edocuments?.edocs_pendientes ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">
|
|
||||||
<span className="block text-xs text-slate-500 mb-1">Cumplimiento</span>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-600 h-2 rounded"
|
|
||||||
style={{ width: `${summary.edocuments?.edocs_cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-700 font-semibold mt-1">
|
|
||||||
{summary.edocuments?.edocs_cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 border-t pt-2">
|
|
||||||
<span className="block text-xs text-slate-500 mb-1">Acuses</span>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Descargados</span>
|
|
||||||
<span className="block font-semibold">{summary.edocuments.acuse_descargados ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block text-slate-500">Pendientes</span>
|
|
||||||
<span className="block font-semibold">{summary.edocuments.acuses_pendientes ?? '-'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full bg-slate-100 rounded h-2 mt-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-400 h-2 rounded"
|
|
||||||
style={{ width: `${summary.edocuments.acuses_cumplimiento ?? 0}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="block text-xs text-right text-blue-400 font-semibold mt-1">
|
|
||||||
{summary.edocuments?.acuses_cumplimiento ?? 0}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center text-slate-500 py-12">No hay datos para mostrar.</div>
|
<div className="text-center text-slate-500 py-12">No hay datos para mostrar.</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user