Se modifico seccion de reportes
This commit is contained in:
440
src/data/datastageModels.json
Normal file
440
src/data/datastageModels.json
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
{
|
||||||
|
"models": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "Registro501",
|
||||||
|
"name": "Datos generales",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "tipo_operacion", "clave_documento",
|
||||||
|
"seccion_aduanera_entrada", "curp_contribuyente", "rfc", "curp_agente_a", "tipo_cambio",
|
||||||
|
"total_fletes", "total_seguros", "total_embalajes", "total_incrementables", "total_deducibles",
|
||||||
|
"peso_bruto_mercancia", "medio_transporte_salida", "medio_transporte_arribo",
|
||||||
|
"medio_transporte_entrada_salida", "destino_mercancia", "nombre_contribuyente",
|
||||||
|
"calle_contribuyente", "num_interior_contribuyente", "num_exterior_contribuyente",
|
||||||
|
"cp_contribuyente", "municipio_contribuyente", "entidad_fed_contribuyente", "pais_contribuyente",
|
||||||
|
"tipo_pedimento", "fecha_recepcion_pedimento", "fecha_pago_real", "organizacion", "consulta",
|
||||||
|
"datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"pedimento": "",
|
||||||
|
"seccion_aduanera": "",
|
||||||
|
"tipo_operacion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro502",
|
||||||
|
"name": "Transporte de las mercancías",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "pedimento", "seccion_aduanera", "rfc_transportista", "curp_transportista",
|
||||||
|
"nombre_transportista", "pais_transporte", "identificador_transporte", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "consulta", "datastage", "patente"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"pedimento": "",
|
||||||
|
"fecha_pago_real": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro503",
|
||||||
|
"name": "Guías",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "numero_guia", "tipo_guia",
|
||||||
|
"fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"pedimento": "",
|
||||||
|
"fecha_pago_real": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro504",
|
||||||
|
"name": "Contenedores",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "num_contenedor", "tipo_contenedor",
|
||||||
|
"fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"pedimento": "",
|
||||||
|
"num_contenedor": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro505",
|
||||||
|
"name": "Facturas",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "pedimento", "seccion_aduanera", "fecha_facturacion", "numero_factura",
|
||||||
|
"termino_facturacion", "moneda_facturacion", "valor_dolares", "valor_moneda_extranjera",
|
||||||
|
"pais_facturacion", "entidad_fed_facturacion", "indent_fiscal_proveedor",
|
||||||
|
"proveedor_mercancia", "calle_proveedor", "num_interior_proveedor", "num_exterior_proveedor",
|
||||||
|
"cp_proveedor", "municipio_proveedor", "fecha_pago_real", "organizacion", "created_by",
|
||||||
|
"consulta", "datastage", "patente"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"pedimento": "",
|
||||||
|
"numero_factura": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro506",
|
||||||
|
"name": "Fechas del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "tipo_fecha", "fecha_operacion",
|
||||||
|
"fecha_validacion_pago_r", "organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fecha_operacion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro507",
|
||||||
|
"name": "Casos del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_caso", "identificador_caso",
|
||||||
|
"tipo_pedimento", "complemento_caso", "fecha_validacion_pago_r", "organizacion",
|
||||||
|
"created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"clave_caso": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro508",
|
||||||
|
"name": "Cuentas aduaneras de garantía del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "institucion_emisora", "numero_cuenta",
|
||||||
|
"folio_constancia", "fecha_constancia", "tipo_cuenta", "clave_garantia",
|
||||||
|
"valor_unitario_titulo", "total_garantia", "cantidad_unidades", "titulos_asignados",
|
||||||
|
"fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"institucion_emisora": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro509",
|
||||||
|
"name": "Tasas del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion",
|
||||||
|
"tasa_contribucion", "tipo_tasa", "tipo_pedimento", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"clave_contribucion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro510",
|
||||||
|
"name": "Contribuciones del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion",
|
||||||
|
"tasa_contribucion", "tipo_tasa", "tipo_pedimento", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "datastage", "consulta", "forma_pago", "importe_pago"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"clave_contribucion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro511",
|
||||||
|
"name": "Observaciones del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "secuencia_observacion",
|
||||||
|
"observaciones", "tipo_pedimento", "fecha_validacion_pago_r", "organizacion",
|
||||||
|
"created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"secuencia_observacion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro512",
|
||||||
|
"name": "Descargos de mercancías",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "patente_aduanal_orig",
|
||||||
|
"pedimento_original", "seccion_aduanera_desp_orig", "documento_original",
|
||||||
|
"fecha_operacion_orig", "fraccion_original", "unidad_medida", "mercancia_descargada",
|
||||||
|
"tipo_pedimento", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"patente_aduanal_orig": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro520",
|
||||||
|
"name": "Destinatarios de la mercancía",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "indent_fiscal_destinatario",
|
||||||
|
"nombre_destinatario_mercancia", "calle_destinatario", "num_interior_destinatario",
|
||||||
|
"num_exterior_destinatario", "cp_destinatario", "municpio_destinatario",
|
||||||
|
"pais_destinatario", "fecha_pago_real", "organizacion", "created_at",
|
||||||
|
"consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"indent_fiscal_destinatario": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro551",
|
||||||
|
"name": "Partidas",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"subdivision_fraccion", "descripcion_mercancia", "precio_unitario", "valor_aduana",
|
||||||
|
"valor_comercial", "valor_dolares", "cantidad_um_comercial", "unidad_medida_comercial",
|
||||||
|
"cantidad_um_tarifa", "unidad_medida_tarifa", "valor_agregado", "clave_vinculacion",
|
||||||
|
"metodo_valorizacion", "codigo_mercancia_producto", "marca_mercancia_producto",
|
||||||
|
"modelo_mercancia_producto", "pais_origen_destino", "pais_comprador_vendedor",
|
||||||
|
"entidad_fed_origen", "entidad_fed_comprador", "entidad_fed_vendedor",
|
||||||
|
"tipo_operacion", "clave_documento", "fecha_pago_real", "organizacion",
|
||||||
|
"created_by", "consulta", "datastage", "entidad_fed_destino"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro552",
|
||||||
|
"name": "Mercancías",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"vin_numero_serie", "kilometraje_vehiculo", "fecha_pago_real", "organizacion",
|
||||||
|
"created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro553",
|
||||||
|
"name": "Permiso de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"clave_permiso", "firma_descargo", "numero_permiso", "valor_comercial_dolares",
|
||||||
|
"cantidad_mum_tarifa", "fecha_pago_real", "organizacion", "created_by",
|
||||||
|
"datastage", "consulta"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro554",
|
||||||
|
"name": "Casos de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"clave_caso", "identificador_caso", "complemento_caso", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro555",
|
||||||
|
"name": "Cuentas aduaneras de garantía de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"institucion_emisora", "numero_cuenta", "folio_constancia", "fecha_constancia",
|
||||||
|
"clave_garantia", "valor_unitario_titulo", "total_garantia", "cantidad_unidades_medida",
|
||||||
|
"titulos_asignados", "fecha_pago_real", "organizacion", "datastage", "created_by",
|
||||||
|
"consulta"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro556",
|
||||||
|
"name": "Tasas de las contribuciones de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion",
|
||||||
|
"tasa_contribucion", "tipo_tasa", "fecha_pago_real", "organizacion",
|
||||||
|
"created_by", "consulta", "datastage", "fraccion", "secuencia_fraccion"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"clave_contribucion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro557",
|
||||||
|
"name": "Contribuciones de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"clave_contribucion", "forma_pago", "importe_pago", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "datastage", "consulta"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro558",
|
||||||
|
"name": "Observaciones de la partida",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion",
|
||||||
|
"secuencia_observacion", "observaciones", "fecha_pago_real", "organizacion",
|
||||||
|
"created_by", "datastage", "consulta"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"fraccion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "RegistroSel",
|
||||||
|
"name": "Registros de selección",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "consecutivo_remesa",
|
||||||
|
"numero_seleccion", "fecha_seleccion", "hora_seleccion", "semaforo_fiscal",
|
||||||
|
"clave_documento", "tipo_operacion", "organizacion", "created_by",
|
||||||
|
"consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"consecutivo_remesa": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro701",
|
||||||
|
"name": "Rectificaciones",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_documento",
|
||||||
|
"fecha_pago", "pedimento_anterior", "patente_anterior", "seccion_aduanera_anterior",
|
||||||
|
"documento_anterior", "fecha_operacion_anterior", "pedimento_original",
|
||||||
|
"patente_aduanal_orig", "seccion_aduanera_desp_orig", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"pedimento_anterior": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "Registro702",
|
||||||
|
"name": "Diferencias de contribuciones del pedimento",
|
||||||
|
"module": "datastage",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion",
|
||||||
|
"forma_pago", "importe_pago", "tipo_pedimento", "fecha_pago_real",
|
||||||
|
"organizacion", "created_by", "consulta", "datastage"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"patente": "",
|
||||||
|
"clave_contribucion": "",
|
||||||
|
"organizacion": "",
|
||||||
|
"datastage": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
50
src/data/pedimentosModels.json
Normal file
50
src/data/pedimentosModels.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"model": "Pedimento",
|
||||||
|
"name": "Pedimentos cargados",
|
||||||
|
"module": "customs",
|
||||||
|
"type": "excel",
|
||||||
|
"fields": [
|
||||||
|
"id",
|
||||||
|
"pedimento",
|
||||||
|
"pedimento_app",
|
||||||
|
"patente",
|
||||||
|
"aduana",
|
||||||
|
"regimen",
|
||||||
|
"tipo_operacion",
|
||||||
|
"clave_pedimento",
|
||||||
|
"fecha_inicio",
|
||||||
|
"fecha_fin",
|
||||||
|
"fecha_pago",
|
||||||
|
"alerta",
|
||||||
|
"contribuyente",
|
||||||
|
"agente_aduanal",
|
||||||
|
"curp_apoderado",
|
||||||
|
"importe_total",
|
||||||
|
"saldo_disponible",
|
||||||
|
"importe_pedimento",
|
||||||
|
"existe_expediente",
|
||||||
|
"remesas",
|
||||||
|
"numero_partidas",
|
||||||
|
"numero_operacion",
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
],
|
||||||
|
"filters": {
|
||||||
|
"pedimento": "",
|
||||||
|
"patente": "",
|
||||||
|
"aduana": "",
|
||||||
|
"fecha_inicio": "",
|
||||||
|
"fecha_fin": "",
|
||||||
|
"fecha_pago": "",
|
||||||
|
"tipo_operacion": "",
|
||||||
|
"clave_pedimento": "",
|
||||||
|
"contribuyente": "",
|
||||||
|
"alerta": "",
|
||||||
|
"existe_expediente": "",
|
||||||
|
"remesas": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,80 +1,102 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from 'react';
|
||||||
import { fetchWithAuth } from '../fetchWithAuth';
|
import { fetchWithAuth } from '../fetchWithAuth';
|
||||||
|
import datastageModelsData from '../data/datastageModels.json';
|
||||||
|
import pedimentosModelsData from '../data/pedimentosModels.json';
|
||||||
|
|
||||||
const API_URL = import.meta.env.VITE_EFC_API_URL;
|
const API_URL = import.meta.env.VITE_EFC_API_URL;
|
||||||
|
|
||||||
// Animación fade-in/slide-up para bloques
|
// Animaciones
|
||||||
const fadeInSlideUp = `@keyframes fadein-slideup {
|
const animations = `
|
||||||
0% { opacity: 0; transform: translateY(40px); }
|
@keyframes fadein-slideup {
|
||||||
100% { opacity: 1; transform: translateY(0); }
|
0% { opacity: 0; transform: translateY(40px); }
|
||||||
}`;
|
100% { opacity: 1; transform: translateY(0); }
|
||||||
if (typeof document !== 'undefined' && !document.getElementById('fadein-slideup-reports')) {
|
}
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { transform: translateX(-100%); }
|
||||||
|
100% { transform: translateX(100%); }
|
||||||
|
}
|
||||||
|
@keyframes bounce-slow {
|
||||||
|
0%, 100% { transform: translateY(0) scale(1); }
|
||||||
|
50% { transform: translateY(-8px) scale(1.05); }
|
||||||
|
}
|
||||||
|
@keyframes pulse-soft {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.8; }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Inyectar estilos de animación
|
||||||
|
if (typeof document !== 'undefined' && !document.getElementById('reports-animations')) {
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.id = 'fadein-slideup-reports';
|
style.id = 'reports-animations';
|
||||||
style.innerHTML = fadeInSlideUp;
|
style.innerHTML = animations;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Reports() {
|
export default function Reports() {
|
||||||
const [isExporting, setIsExporting] = useState(false);
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
|
const [exportFormat, setExportFormat] = useState('excel');
|
||||||
|
const [showExportSuccess, setShowExportSuccess] = useState(false);
|
||||||
|
|
||||||
|
// Estado para formato de exportación personalizado
|
||||||
|
const [showFormatSelector, setShowFormatSelector] = useState(false);
|
||||||
|
|
||||||
// Estado para pestañas
|
// Estado para pestañas
|
||||||
const [activeTab, setActiveTab] = useState('pedimentos');
|
const [activeTab, setActiveTab] = useState('pedimentos');
|
||||||
|
|
||||||
// JSON de modelos para Datastage
|
// Importar modelos
|
||||||
const datastageModels = [
|
const datastageModels = datastageModelsData?.models || [];
|
||||||
{ model: "Registro500", name: "Validación y reconocimiento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "consecutivo_remesa", "numero_seleccion", "fecha_inicio_reconocimiento", "hora_inicio_reconocimiento", "fecha_fin_reconocimiento", "hora_fin_reconocimiento", "fraccion", "secuencia_fraccion", "clave_documento", "tipo_operacion", "grado_incidencia", "fecha_seleccion", "organizacion", "consulta", "datastage", "created_at", "updated_at"], filters: {patente: "", pedimento: "", seccion_aduanera: "", fecha_seleccion: "", organizacion: "", datastage: ""}},
|
const pedimentosModels = pedimentosModelsData?.models || [];
|
||||||
{ model: "Registro501", name: "Datos generales", fields: ["id", "patente", "pedimento", "seccion_aduanera", "tipo_operacion", "clave_documento", "seccion_aduanera_entrada", "curp_contribuyente", "rfc", "curp_agente_a", "tipo_cambio", "total_fletes", "total_seguros", "total_embalajes", "total_incrementables", "total_deducibles", "peso_bruto_mercancia", "medio_transporte_salida", "medio_transporte_arribo", "medio_transporte_entrada_salida", "destino_mercancia", "nombre_contribuyente", "calle_contribuyente", "num_interior_contribuyente", "num_exterior_contribuyente", "cp_contribuyente", "municipio_contribuyente", "entidad_fed_contribuyente", "pais_contribuyente", "tipo_pedimento", "fecha_recepcion_pedimento", "fecha_pago_real", "organizacion", "consulta", "datastage"], filters: {patente: "", pedimento: "", seccion_aduanera: "", tipo_operacion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro502", name: "Transporte de las mercancías", fields: ["id", "pedimento", "seccion_aduanera", "rfc_transportista", "curp_transportista", "nombre_transportista", "pais_transporte", "identificador_transporte", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage", "patente"], filters: {patente: "", pedimento: "", fecha_pago_real: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro503", name: "Guías", fields: ["id", "patente", "pedimento", "seccion_aduanera", "numero_guia", "tipo_guia", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", pedimento: "", fecha_pago_real: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro504", name: "Contenedores", fields: ["id", "patente", "pedimento", "seccion_aduanera", "num_contenedor", "tipo_contenedor", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", pedimento: "", num_contenedor: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro505", name: "Facturas", fields: ["id", "pedimento", "seccion_aduanera", "fecha_facturacion", "numero_factura", "termino_facturacion", "moneda_facturacion", "valor_dolares", "valor_moneda_extranjera", "pais_facturacion", "entidad_fed_facturacion", "indent_fiscal_proveedor", "proveedor_mercancia", "calle_proveedor", "num_interior_proveedor", "num_exterior_proveedor", "cp_proveedor", "municipio_proveedor", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage", "patente"], filters: {pedimento: "", numero_factura: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro506", name: "Fechas del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "tipo_fecha", "fecha_operacion", "fecha_validacion_pago_r", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", fecha_operacion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro507", name: "Casos del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_caso", "identificador_caso", "tipo_pedimento", "complemento_caso", "fecha_validacion_pago_r", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", clave_caso: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro508", name: "Cuentas aduaneras de garantía del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "institucion_emisora", "numero_cuenta", "folio_constancia", "fecha_constancia", "tipo_cuenta", "clave_garantia", "valor_unitario_titulo", "total_garantia", "cantidad_unidades", "titulos_asignados", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", institucion_emisora: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro509", name: "Tasas del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion", "tasa_contribucion", "tipo_tasa", "tipo_pedimento", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", clave_contribucion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro510", name: "Contribuciones del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion", "tasa_contribucion", "tipo_tasa", "tipo_pedimento", "fecha_pago_real", "organizacion", "created_by", "datastage", "consulta", "forma_pago", "importe_pago"], filters: {patente: "", clave_contribucion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro511", name: "Observaciones del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "secuencia_observacion", "observaciones", "tipo_pedimento", "fecha_validacion_pago_r", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", secuencia_observacion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro512", name: "Descargos de mercancías", fields: ["id", "patente", "pedimento", "seccion_aduanera", "patente_aduanal_orig", "pedimento_original", "seccion_aduanera_desp_orig", "documento_original", "fecha_operacion_orig", "fraccion_original", "unidad_medida", "mercancia_descargada", "tipo_pedimento", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", patente_aduanal_orig: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro520", name: "Destinatarios de la mercancía", fields: ["id", "patente", "pedimento", "seccion_aduanera", "indent_fiscal_destinatario", "nombre_destinatario_mercancia", "calle_destinatario", "num_interior_destinatario", "num_exterior_destinatario", "cp_destinatario", "municpio_destinatario", "pais_destinatario", "fecha_pago_real", "organizacion", "created_at", "consulta", "datastage"], filters: {patente: "", indent_fiscal_destinatario: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro551", name: "Partidas", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "subdivision_fraccion", "descripcion_mercancia", "precio_unitario", "valor_aduana", "valor_comercial", "valor_dolares", "cantidad_um_comercial", "unidad_medida_comercial", "cantidad_um_tarifa", "unidad_medida_tarifa", "valor_agregado", "clave_vinculacion", "metodo_valorizacion", "codigo_mercancia_producto", "marca_mercancia_producto", "modelo_mercancia_producto", "pais_origen_destino", "pais_comprador_vendedor", "entidad_fed_origen", "entidad_fed_comprador", "entidad_fed_vendedor", "tipo_operacion", "clave_documento", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage", "entidad_fed_destino"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro552", name: "Mercancías", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "vin_numero_serie", "kilometraje_vehiculo", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro553", name: "Permiso de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "clave_permiso", "firma_descargo", "numero_permiso", "valor_comercial_dolares", "cantidad_mum_tarifa", "fecha_pago_real", "organizacion", "created_by", "datastage", "consulta"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro554", name: "Casos de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "clave_caso", "identificador_caso", "complemento_caso", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro555", name: "Cuentas aduaneras de garantía de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "institucion_emisora", "numero_cuenta", "folio_constancia", "fecha_constancia", "clave_garantia", "valor_unitario_titulo", "total_garantia", "cantidad_unidades_medida", "titulos_asignados", "fecha_pago_real", "organizacion", "datastage", "created_by", "consulta"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro556", name: "Tasas de las contribuciones de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion", "tasa_contribucion", "tipo_tasa", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage", "fraccion", "secuencia_fraccion"], filters: {patente: "", clave_contribucion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro557", name: "Contribuciones de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "clave_contribucion", "forma_pago", "importe_pago", "fecha_pago_real", "organizacion", "created_by", "datastage", "consulta"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro558", name: "Observaciones de la partida", fields: ["id", "patente", "pedimento", "seccion_aduanera", "fraccion", "secuencia_fraccion", "secuencia_observacion", "observaciones", "fecha_pago_real", "organizacion", "created_by", "datastage", "consulta"], filters: {patente: "", fraccion: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "RegistroSel", fields: ["id", "patente", "pedimento", "seccion_aduanera", "consecutivo_remesa", "numero_seleccion", "fecha_seleccion", "hora_seleccion", "semaforo_fiscal", "clave_documento", "tipo_operacion", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", consecutivo_remesa: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro701", name: "Rectificaciones", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_documento", "fecha_pago", "pedimento_anterior", "patente_anterior", "seccion_aduanera_anterior", "documento_anterior", "fecha_operacion_anterior", "pedimento_original", "patente_aduanal_orig", "seccion_aduanera_desp_orig", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", pedimento_anterior: "", organizacion: "", datastage: ""}},
|
|
||||||
{ model: "Registro702", name: "Diferencias de contribuciones del pedimento", fields: ["id", "patente", "pedimento", "seccion_aduanera", "clave_contribucion", "forma_pago", "importe_pago", "tipo_pedimento", "fecha_pago_real", "organizacion", "created_by", "consulta", "datastage"], filters: {patente: "", clave_contribucion: "", organizacion: "", datastage: ""}},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
// Obtener el modelo inicial según la pestaña activa
|
||||||
|
const initialModels = activeTab === 'pedimentos' ? pedimentosModels : datastageModels;
|
||||||
|
const defaultModel = initialModels[0] || { model: '', fields: [], filters: {} };
|
||||||
|
|
||||||
// Estado para modelo seleccionado en Datastage
|
// Estado para modelo seleccionado
|
||||||
const [selectedModel, setSelectedModel] = useState(datastageModels[0].model);
|
const [selectedModel, setSelectedModel] = useState(defaultModel.model);
|
||||||
|
|
||||||
// Estado para campos seleccionados
|
// Estado para campos seleccionados
|
||||||
const [selectedFields, setSelectedFields] = useState(datastageModels[0].fields);
|
const [selectedFields, setSelectedFields] = useState(defaultModel.fields);
|
||||||
|
|
||||||
// Estado para campo seleccionado en lista disponible
|
// Estado para campo seleccionado en lista disponible
|
||||||
const [availableSelected, setAvailableSelected] = useState(null);
|
const [availableSelected, setAvailableSelected] = useState(null);
|
||||||
|
|
||||||
// Estado para los filtros
|
// Estado para los filtros
|
||||||
const [filters, setFilters] = useState(datastageModels[0].filters);
|
const [filters, setFilters] = useState(defaultModel.filters);
|
||||||
|
|
||||||
// Actualizar campos seleccionados al cambiar de modelo
|
// Actualizar campos seleccionados al cambiar de modelo o pestaña
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const modelObj = datastageModels.find(m => m.model === selectedModel);
|
const models = activeTab === 'pedimentos' ? pedimentosModels : datastageModels;
|
||||||
setSelectedFields(modelObj.fields);
|
// Al cambiar de pestaña, seleccionar el primer modelo de la pestaña actual
|
||||||
setAvailableSelected(null);
|
const newModel = models[0]?.model || '';
|
||||||
setFilters(modelObj.filters);
|
setSelectedModel(newModel);
|
||||||
|
|
||||||
|
const modelObj = models.find(m => m.model === newModel);
|
||||||
|
if (modelObj) {
|
||||||
|
setSelectedFields(modelObj.fields);
|
||||||
|
setAvailableSelected(null);
|
||||||
|
setFilters(modelObj.filters);
|
||||||
|
}
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
|
// Efecto separado para cambios de modelo dentro de la misma pestaña
|
||||||
|
React.useEffect(() => {
|
||||||
|
const models = activeTab === 'pedimentos' ? pedimentosModels : datastageModels;
|
||||||
|
const modelObj = models.find(m => m.model === selectedModel);
|
||||||
|
if (modelObj) {
|
||||||
|
setSelectedFields(modelObj.fields);
|
||||||
|
setAvailableSelected(null);
|
||||||
|
setFilters(modelObj.filters);
|
||||||
|
}
|
||||||
}, [selectedModel]);
|
}, [selectedModel]);
|
||||||
|
|
||||||
// Encontrar el modelo actual
|
// Obtener los modelos según la pestaña activa
|
||||||
const currentModel = datastageModels.find(m => m.model === selectedModel);
|
const models = activeTab === 'pedimentos' ? pedimentosModels : datastageModels;
|
||||||
|
|
||||||
|
// Encontrar el modelo actual dentro de los modelos de la pestaña activa
|
||||||
|
const currentModel = models.find(m => m.model === selectedModel) || defaultModel;
|
||||||
|
|
||||||
// Campos disponibles (no seleccionados)
|
// Campos disponibles (no seleccionados) del modelo actual
|
||||||
const availableFields = currentModel.fields.filter(f => !selectedFields.includes(f));
|
const availableFields = currentModel.fields.filter(f => !selectedFields.includes(f));
|
||||||
|
|
||||||
// Mover campo de disponible a seleccionado
|
// Mover campo de disponible a seleccionado
|
||||||
@@ -112,32 +134,63 @@ export default function Reports() {
|
|||||||
|
|
||||||
// Función para manejar la exportación del modelo
|
// Función para manejar la exportación del modelo
|
||||||
const handleExportModel = async () => {
|
const handleExportModel = async () => {
|
||||||
|
if (selectedFields.length === 0) {
|
||||||
|
// Mostrar mensaje de error con estilo
|
||||||
|
const errorDiv = document.createElement('div');
|
||||||
|
errorDiv.className = 'fixed top-4 right-4 bg-red-50 border-l-4 border-red-500 p-4 rounded shadow-lg';
|
||||||
|
errorDiv.innerHTML = `
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm text-red-700">Por favor selecciona al menos un campo para exportar</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(errorDiv);
|
||||||
|
setTimeout(() => {
|
||||||
|
errorDiv.style.opacity = '0';
|
||||||
|
errorDiv.style.transform = 'translateX(100%)';
|
||||||
|
setTimeout(() => errorDiv.remove(), 300);
|
||||||
|
}, 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsExporting(true);
|
setIsExporting(true);
|
||||||
try {
|
try {
|
||||||
// Construir el objeto de filtros con los valores no vacíos
|
// Mostrar indicador de progreso
|
||||||
const nonEmptyFilters = {};
|
const progressDiv = document.createElement('div');
|
||||||
for (const [key, value] of Object.entries(filters)) {
|
progressDiv.className = 'fixed bottom-4 right-4 bg-white p-4 rounded-lg shadow-xl border border-blue-100';
|
||||||
if (value && value.trim() !== '') {
|
progressDiv.innerHTML = `
|
||||||
nonEmptyFilters[key] = value.trim();
|
<div class="flex items-center gap-3">
|
||||||
}
|
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-blue-500"></div>
|
||||||
}
|
<p class="text-sm text-gray-600">Preparando exportación...</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(progressDiv);
|
||||||
|
|
||||||
// Construir el payload
|
// Construir filtros no vacíos
|
||||||
|
const nonEmptyFilters = Object.entries(filters)
|
||||||
|
.reduce((acc, [key, value]) => {
|
||||||
|
if (value?.trim()) {
|
||||||
|
acc[key] = value.trim();
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Construir payload
|
||||||
const payload = {
|
const payload = {
|
||||||
model: currentModel.model,
|
model: currentModel.model,
|
||||||
|
module: currentModel.module,
|
||||||
fields: selectedFields,
|
fields: selectedFields,
|
||||||
type: 'csv', // Usamos CSV como formato por defecto
|
type: exportFormat,
|
||||||
|
...(Object.keys(nonEmptyFilters).length > 0 && { filters: nonEmptyFilters }),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Solo agregar filtros si hay alguno con valor
|
// Realizar la petición
|
||||||
if (Object.keys(nonEmptyFilters).length > 0) {
|
|
||||||
payload.filters = nonEmptyFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log para debug
|
|
||||||
console.log('Sending payload:', payload);
|
|
||||||
|
|
||||||
// Realizar la petición POST
|
|
||||||
const response = await fetchWithAuth(`${API_URL}/reports/exportmodel/`, {
|
const response = await fetchWithAuth(`${API_URL}/reports/exportmodel/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -147,45 +200,51 @@ export default function Reports() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// Intentar obtener el mensaje de error del backend
|
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
console.error('Error response:', {
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
errorData
|
|
||||||
});
|
|
||||||
throw new Error(errorData.error || errorData.message || 'Error al exportar el modelo');
|
throw new Error(errorData.error || errorData.message || 'Error al exportar el modelo');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener el blob del archivo según el tipo
|
// Actualizar mensaje de progreso
|
||||||
|
progressDiv.innerHTML = `
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-green-500"></div>
|
||||||
|
<p class="text-sm text-gray-600">Preparando archivo...</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const contentType = payload.type === 'excel'
|
const contentType = exportFormat === 'excel'
|
||||||
? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
: 'text/csv';
|
: 'text/csv';
|
||||||
|
|
||||||
// Crear URL del blob
|
const url = window.URL.createObjectURL(new Blob([blob], { type: contentType }));
|
||||||
const url = window.URL.createObjectURL(
|
|
||||||
new Blob([blob], { type: contentType })
|
|
||||||
);
|
|
||||||
|
|
||||||
// Crear elemento <a> temporal para la descarga
|
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
|
const extension = exportFormat === 'excel' ? 'xlsx' : 'csv';
|
||||||
|
const fileName = `${currentModel.model}_${new Date().toISOString().split('T')[0]}.${extension}`;
|
||||||
|
|
||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
||||||
// Obtener el nombre del archivo del header Content-Disposition o usar uno por defecto
|
|
||||||
const contentDisposition = response.headers.get('Content-Disposition');
|
|
||||||
const extension = payload.type === 'csv' ? 'csv' : 'xlsx';
|
|
||||||
const fileName = contentDisposition
|
|
||||||
? contentDisposition.match(/filename="(.+?)"/)?.pop() || `${currentModel.model}.${extension}`
|
|
||||||
: `${currentModel.model}.${extension}`;
|
|
||||||
|
|
||||||
link.setAttribute('download', fileName);
|
link.setAttribute('download', fileName);
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
// Limpiar recursos
|
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
// Mostrar mensaje de éxito
|
||||||
|
progressDiv.innerHTML = `
|
||||||
|
<div class="flex items-center gap-3 text-green-600">
|
||||||
|
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm">¡Exportación completada!</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
setTimeout(() => {
|
||||||
|
progressDiv.style.opacity = '0';
|
||||||
|
progressDiv.style.transform = 'translateY(100%)';
|
||||||
|
setTimeout(() => progressDiv.remove(), 300);
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
setShowExportSuccess(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error al exportar:', error);
|
console.error('Error al exportar:', error);
|
||||||
alert(error.message || 'Error al exportar el modelo. Por favor intente nuevamente.');
|
alert(error.message || 'Error al exportar el modelo. Por favor intente nuevamente.');
|
||||||
@@ -194,115 +253,334 @@ export default function Reports() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderFields = () => (
|
const renderFields = () => {
|
||||||
<div className="mb-4 flex flex-col lg:flex-row gap-4">
|
const formatFieldName = (field) => {
|
||||||
{/* Lista de campos disponibles */}
|
return field.split('_')
|
||||||
<div className="flex-1 bg-white/95 backdrop-blur-sm rounded-xl border border-blue-100 shadow-lg p-4">
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 mb-3">
|
.join(' ');
|
||||||
<span className="font-bold text-blue-800 text-sm">Campos disponibles</span>
|
};
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
return (
|
||||||
className="px-3 py-1.5 text-xs bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 font-semibold transition-all duration-200 hover:shadow-md"
|
<div className="mb-6">
|
||||||
onClick={includeAllFields}
|
<div className="bg-white rounded-xl shadow-lg overflow-hidden border border-blue-100">
|
||||||
>
|
{/* Header con búsqueda y contador */}
|
||||||
Incluir todos
|
<div className="p-4 border-b border-blue-50">
|
||||||
</button>
|
<div className="flex items-center justify-between mb-4">
|
||||||
<button
|
<div className="flex items-center gap-3">
|
||||||
className="inline-flex items-center px-3 py-1.5 bg-blue-500 text-white rounded-lg shadow-md hover:bg-blue-600 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed text-xs font-semibold hover:shadow-lg"
|
<div className="h-10 w-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||||
onClick={() => availableSelected && addField(availableSelected)}
|
<svg className="w-6 h-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
disabled={!availableSelected}
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||||
title="Incluir campo seleccionado"
|
</svg>
|
||||||
>
|
</div>
|
||||||
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" /></svg>
|
<div>
|
||||||
Incluir
|
<h3 className="font-bold text-gray-900">Campos del reporte</h3>
|
||||||
</button>
|
<p className="text-sm text-gray-500">Selecciona los campos a incluir</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="inline-flex items-center px-3 py-1 rounded-full bg-blue-50 text-blue-700 text-sm font-medium">
|
||||||
|
{selectedFields.length} seleccionados
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Panel de campos */}
|
||||||
|
<div className="flex gap-4 flex-col lg:flex-row">
|
||||||
|
{/* Panel izquierdo - Campos disponibles */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h4 className="text-sm font-medium text-gray-700">Campos disponibles</h4>
|
||||||
|
<button
|
||||||
|
onClick={includeAllFields}
|
||||||
|
disabled={availableFields.length === 0}
|
||||||
|
className="inline-flex items-center px-2.5 py-1.5 text-xs font-medium rounded-md text-blue-700 bg-blue-50 hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||||
|
</svg>
|
||||||
|
Incluir todos
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-50 rounded-lg p-2 max-h-80 overflow-y-auto custom-scrollbar">
|
||||||
|
{availableFields.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-8 text-gray-400">
|
||||||
|
<svg className="w-12 h-12 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||||
|
</svg>
|
||||||
|
<p className="text-sm">Todos los campos están incluidos</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 gap-1">
|
||||||
|
{availableFields.map(field => (
|
||||||
|
<div
|
||||||
|
key={field}
|
||||||
|
onClick={() => setAvailableSelected(field)}
|
||||||
|
onDoubleClick={() => addField(field)}
|
||||||
|
className={`group flex items-center justify-between p-2 rounded-md cursor-pointer transition-all duration-200 ${
|
||||||
|
availableSelected === field
|
||||||
|
? 'bg-blue-50 text-blue-700'
|
||||||
|
: 'hover:bg-gray-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-sm font-medium">{formatFieldName(field)}</span>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
addField(field);
|
||||||
|
}}
|
||||||
|
className="opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-blue-100 text-blue-600 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Panel derecho - Campos seleccionados */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h4 className="text-sm font-medium text-gray-700">Campos incluidos</h4>
|
||||||
|
<button
|
||||||
|
onClick={removeAllFields}
|
||||||
|
disabled={selectedFields.length === 0}
|
||||||
|
className="inline-flex items-center px-2.5 py-1.5 text-xs font-medium rounded-md text-red-700 bg-red-50 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
|
</svg>
|
||||||
|
Quitar todos
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-50 rounded-lg p-2 max-h-80 overflow-y-auto custom-scrollbar">
|
||||||
|
{selectedFields.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-8 text-gray-400">
|
||||||
|
<svg className="w-12 h-12 mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<p className="text-sm">No hay campos seleccionados</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 gap-1">
|
||||||
|
{selectedFields.map(field => (
|
||||||
|
<div
|
||||||
|
key={field}
|
||||||
|
className={`group flex items-center justify-between p-2 rounded-md cursor-pointer transition-all duration-200 ${
|
||||||
|
includedSelected === field
|
||||||
|
? 'bg-red-50 text-red-700'
|
||||||
|
: 'hover:bg-gray-100'
|
||||||
|
}`}
|
||||||
|
onClick={() => setIncludedSelected(field)}
|
||||||
|
onDoubleClick={() => removeField(field)}
|
||||||
|
>
|
||||||
|
<span className="text-sm font-medium">{formatFieldName(field)}</span>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeField(field);
|
||||||
|
}}
|
||||||
|
className="opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-red-100 text-red-600 transition-all duration-200"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<ul className="max-h-56 overflow-y-auto divide-y divide-blue-50">
|
|
||||||
{availableFields.length === 0 && (
|
|
||||||
<li className="text-gray-400 text-xs py-2">Todos los campos incluidos</li>
|
|
||||||
)}
|
|
||||||
{availableFields.map(field => (
|
|
||||||
<li
|
|
||||||
key={field}
|
|
||||||
className={`px-2 py-1 cursor-pointer rounded transition-all duration-100 select-none ${availableSelected === field ? 'bg-blue-100 text-blue-700' : 'hover:bg-blue-50'}`}
|
|
||||||
onClick={() => setAvailableSelected(field)}
|
|
||||||
onDoubleClick={() => addField(field)}
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
{field}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{/* Lista de campos seleccionados */}
|
|
||||||
<div className="flex-1 bg-white/95 backdrop-blur-sm rounded-xl border border-green-100 shadow-lg p-4">
|
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 mb-3">
|
|
||||||
<span className="font-bold text-green-800 text-sm">Campos incluidos</span>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
|
||||||
className="px-3 py-1.5 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 font-semibold transition-all duration-200 hover:shadow-md disabled:opacity-40 disabled:cursor-not-allowed"
|
|
||||||
onClick={removeAllFields}
|
|
||||||
disabled={selectedFields.length === 0}
|
|
||||||
>
|
|
||||||
Quitar todos
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="px-3 py-1.5 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 font-semibold transition-all duration-200 hover:shadow-md disabled:opacity-40 disabled:cursor-not-allowed"
|
|
||||||
onClick={removeSelectedField}
|
|
||||||
disabled={!includedSelected}
|
|
||||||
>
|
|
||||||
Quitar
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="max-h-56 overflow-y-auto divide-y divide-green-50">
|
|
||||||
{selectedFields.length === 0 && (
|
<style jsx>{`
|
||||||
<li className="text-gray-400 text-xs py-2">No hay campos seleccionados</li>
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
)}
|
width: 4px;
|
||||||
{selectedFields.map(field => (
|
}
|
||||||
<li
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
key={field}
|
background: #f3f4f6;
|
||||||
className={`px-2 py-1 cursor-pointer rounded transition-all duration-100 select-none ${includedSelected === field ? 'bg-green-100 text-green-700' : 'hover:bg-green-50'}`}
|
border-radius: 2px;
|
||||||
onClick={() => setIncludedSelected(field)}
|
}
|
||||||
onDoubleClick={() => removeField(field)}
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
tabIndex={0}
|
background: #cbd5e1;
|
||||||
title="Doble click para quitar"
|
border-radius: 2px;
|
||||||
>
|
}
|
||||||
{field}
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
</li>
|
background: #94a3b8;
|
||||||
))}
|
}
|
||||||
</ul>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
const renderFilters = () => (
|
const renderFilters = () => (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-4 bg-gradient-to-br from-blue-50/60 to-blue-100/30 p-4 sm:p-5 rounded-xl border border-blue-100 shadow-lg backdrop-blur-sm">
|
<div className="mb-4">
|
||||||
{Object.entries(currentModel.filters).map(([key, value]) => (
|
<div className="bg-white rounded-xl shadow-lg overflow-hidden border border-blue-100">
|
||||||
<div key={key} className="flex flex-col">
|
<div className="p-4 border-b border-blue-50">
|
||||||
<label className="block text-xs font-semibold text-blue-800 mb-1.5">{key}</label>
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<div className="h-10 w-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||||
type="text"
|
<svg className="w-6 h-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
value={filters[key] || ''}
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
||||||
onChange={(e) => setFilters(prev => ({
|
</svg>
|
||||||
...prev,
|
</div>
|
||||||
[key]: e.target.value
|
<div>
|
||||||
}))}
|
<h3 className="font-bold text-gray-900">Filtros de búsqueda</h3>
|
||||||
className="w-full border border-blue-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 bg-white/90 shadow-md transition-all duration-200 hover:shadow-lg"
|
<p className="text-sm text-gray-500">Refina los resultados del reporte</p>
|
||||||
/>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="p-4">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{Object.entries(currentModel.filters).map(([key, value]) => {
|
||||||
|
const label = key.split('_').map(word =>
|
||||||
|
word.charAt(0).toUpperCase() + word.slice(1)
|
||||||
|
).join(' ');
|
||||||
|
|
||||||
|
const isDate = key.toLowerCase().includes('fecha');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={key} className="group">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<div className="relative rounded-lg shadow-sm">
|
||||||
|
<input
|
||||||
|
type={isDate ? "date" : "text"}
|
||||||
|
value={filters[key] || ''}
|
||||||
|
onChange={(e) => setFilters(prev => ({
|
||||||
|
...prev,
|
||||||
|
[key]: e.target.value
|
||||||
|
}))}
|
||||||
|
className="block w-full rounded-lg border-gray-300 pl-3 pr-10 py-2.5 text-gray-900 placeholder-gray-500
|
||||||
|
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 sm:text-sm
|
||||||
|
transition-all duration-200 bg-white"
|
||||||
|
placeholder={`Buscar por ${label.toLowerCase()}`}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||||
|
{isDate ? (
|
||||||
|
<svg className="h-5 w-5 text-gray-400 group-focus-within:text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="h-5 w-5 text-gray-400 group-focus-within:text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contenido de cada pestaña
|
// Contenido de cada pestaña
|
||||||
const tabContents = {
|
const tabContents = {
|
||||||
pedimentos: (
|
pedimentos: (
|
||||||
<div className="p-6">
|
<div className="p-6 bg-white/95 rounded-2xl shadow-xl border border-blue-100">
|
||||||
<h2 className="text-xl font-bold mb-2 text-blue-900">Generar reporte de pedimentos cargados</h2>
|
<div className="relative mb-8">
|
||||||
<p className="mb-4 text-gray-700">Aquí puedes generar y descargar el reporte de pedimentos cargados.</p>
|
<div className="absolute -left-2 -top-2 w-12 h-12 bg-gradient-to-br from-blue-600 to-blue-700 rounded-2xl shadow-lg flex items-center justify-center transform -rotate-6">
|
||||||
{/* Aquí va la lógica y UI específica para pedimentos */}
|
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="pl-12">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900">Generar reporte de pedimentos</h2>
|
||||||
|
<p className="text-gray-600 mt-1">Selecciona los campos y ajusta los filtros para generar el reporte.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="block text-xs font-semibold text-blue-800 mb-2">Formato de Exportación</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setExportFormat('excel')}
|
||||||
|
className={`flex-1 py-2.5 rounded-lg transition-all duration-200 border ${
|
||||||
|
exportFormat === 'excel'
|
||||||
|
? 'bg-gradient-to-br from-green-50 to-green-100 border-green-200 shadow-lg shadow-green-100 scale-[1.02]'
|
||||||
|
: 'bg-white border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<svg className={`w-5 h-5 ${exportFormat === 'excel' ? 'text-green-600' : 'text-gray-500'}`} viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2v6h6M8 13h8M8 17h8M8 9h2"/>
|
||||||
|
</svg>
|
||||||
|
<span className={exportFormat === 'excel' ? 'font-semibold text-green-700' : 'text-gray-700'}>Excel</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setExportFormat('csv')}
|
||||||
|
className={`flex-1 py-2.5 rounded-lg transition-all duration-200 border ${
|
||||||
|
exportFormat === 'csv'
|
||||||
|
? 'bg-gradient-to-br from-blue-50 to-blue-100 border-blue-200 shadow-lg shadow-blue-100 scale-[1.02]'
|
||||||
|
: 'bg-white border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<svg className={`w-5 h-5 ${exportFormat === 'csv' ? 'text-blue-600' : 'text-gray-500'}`} viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2v6h6M10 9v10M14 9v10M6 9v10"/>
|
||||||
|
</svg>
|
||||||
|
<span className={exportFormat === 'csv' ? 'font-semibold text-blue-700' : 'text-gray-700'}>CSV</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<h3 className="text-md font-bold text-blue-800 mb-3">Campos</h3>
|
||||||
|
{renderFields()}
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<h3 className="text-md font-bold text-blue-800 mb-3">Filtros</h3>
|
||||||
|
{renderFilters()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleExportModel}
|
||||||
|
disabled={isExporting}
|
||||||
|
className={`group relative w-full py-3 text-lg font-semibold ${
|
||||||
|
isExporting
|
||||||
|
? 'bg-gray-400 cursor-not-allowed'
|
||||||
|
: exportFormat === 'excel'
|
||||||
|
? 'bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700'
|
||||||
|
: 'bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700'
|
||||||
|
} text-white rounded-xl transition-all duration-300 transform hover:scale-[1.02] shadow-lg hover:shadow-xl ${
|
||||||
|
exportFormat === 'excel'
|
||||||
|
? 'shadow-green-500/20 hover:shadow-green-500/30'
|
||||||
|
: 'shadow-blue-500/20 hover:shadow-blue-500/30'
|
||||||
|
} overflow-hidden`}
|
||||||
|
>
|
||||||
|
<div className={`absolute inset-0 w-full h-full ${
|
||||||
|
isExporting
|
||||||
|
? 'bg-gradient-to-r from-gray-300/0 via-gray-300/30 to-gray-300/0'
|
||||||
|
: exportFormat === 'excel'
|
||||||
|
? 'bg-gradient-to-r from-green-400/0 via-green-400/30 to-green-400/0'
|
||||||
|
: 'bg-gradient-to-r from-blue-400/0 via-blue-400/30 to-blue-400/0'
|
||||||
|
} skeleton-animation`}></div>
|
||||||
|
<span className="relative inline-flex items-center justify-center gap-2 px-4">
|
||||||
|
{isExporting ? (
|
||||||
|
<svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5 transform group-hover:scale-110 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
<span>{isExporting ? 'Generando archivo...' : `Generar y descargar ${exportFormat.toUpperCase()}`}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
datastage: (
|
datastage: (
|
||||||
@@ -318,24 +596,65 @@ export default function Reports() {
|
|||||||
<p className="text-gray-600 mt-1">Selecciona el modelo, revisa los campos y ajusta los filtros para generar el reporte.</p>
|
<p className="text-gray-600 mt-1">Selecciona el modelo, revisa los campos y ajusta los filtros para generar el reporte.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-6">
|
<div className="mb-6 space-y-4">
|
||||||
<label className="block text-xs font-semibold text-blue-800 mb-2">Modelo</label>
|
<div>
|
||||||
<div className="relative">
|
<label className="block text-xs font-semibold text-blue-800 mb-2">Modelo</label>
|
||||||
<select
|
<div className="relative">
|
||||||
value={selectedModel}
|
<select
|
||||||
onChange={e => setSelectedModel(e.target.value)}
|
value={selectedModel}
|
||||||
className="w-full border border-blue-200 rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 bg-white/90 shadow-md appearance-none pr-10 transition-all duration-200 hover:shadow-lg"
|
onChange={e => setSelectedModel(e.target.value)}
|
||||||
>
|
className="w-full border border-blue-200 rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 bg-white/90 shadow-md appearance-none pr-10 transition-all duration-200 hover:shadow-lg"
|
||||||
{datastageModels.map(m => (
|
>
|
||||||
<option key={m.model} value={m.model} className="py-1">
|
{datastageModels.map(m => (
|
||||||
{m.model} - {m.name}
|
<option key={m.model} value={m.model} className="py-1">
|
||||||
</option>
|
{m.model} - {m.name}
|
||||||
))}
|
</option>
|
||||||
</select>
|
))}
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
</select>
|
||||||
<svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
<svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
</svg>
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-semibold text-blue-800 mb-2">Formato de Exportación</label>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setExportFormat('excel')}
|
||||||
|
className={`flex-1 py-2.5 rounded-lg transition-all duration-200 border ${
|
||||||
|
exportFormat === 'excel'
|
||||||
|
? 'bg-gradient-to-br from-green-50 to-green-100 border-green-200 shadow-lg shadow-green-100 scale-[1.02]'
|
||||||
|
: 'bg-white border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<svg className={`w-5 h-5 ${exportFormat === 'excel' ? 'text-green-600' : 'text-gray-500'}`} viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2v6h6M8 13h8M8 17h8M8 9h2"/>
|
||||||
|
</svg>
|
||||||
|
<span className={exportFormat === 'excel' ? 'font-semibold text-green-700' : 'text-gray-700'}>Excel</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setExportFormat('csv')}
|
||||||
|
className={`flex-1 py-2.5 rounded-lg transition-all duration-200 border ${
|
||||||
|
exportFormat === 'csv'
|
||||||
|
? 'bg-gradient-to-br from-blue-50 to-blue-100 border-blue-200 shadow-lg shadow-blue-100 scale-[1.02]'
|
||||||
|
: 'bg-white border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<svg className={`w-5 h-5 ${exportFormat === 'csv' ? 'text-blue-600' : 'text-gray-500'}`} viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M14 2v6h6M10 9v10M14 9v10M6 9v10"/>
|
||||||
|
</svg>
|
||||||
|
<span className={exportFormat === 'csv' ? 'font-semibold text-blue-700' : 'text-gray-700'}>CSV</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -353,13 +672,21 @@ export default function Reports() {
|
|||||||
className={`group relative w-full py-3 text-lg font-semibold ${
|
className={`group relative w-full py-3 text-lg font-semibold ${
|
||||||
isExporting
|
isExporting
|
||||||
? 'bg-gray-400 cursor-not-allowed'
|
? 'bg-gray-400 cursor-not-allowed'
|
||||||
: 'bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700'
|
: exportFormat === 'excel'
|
||||||
} text-white rounded-xl transition-all duration-300 transform hover:scale-[1.02] shadow-lg hover:shadow-xl shadow-green-500/20 hover:shadow-green-500/30 overflow-hidden`}
|
? 'bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700'
|
||||||
|
: 'bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700'
|
||||||
|
} text-white rounded-xl transition-all duration-300 transform hover:scale-[1.02] shadow-lg hover:shadow-xl ${
|
||||||
|
exportFormat === 'excel'
|
||||||
|
? 'shadow-green-500/20 hover:shadow-green-500/30'
|
||||||
|
: 'shadow-blue-500/20 hover:shadow-blue-500/30'
|
||||||
|
} overflow-hidden`}
|
||||||
>
|
>
|
||||||
<div className={`absolute inset-0 w-full h-full ${
|
<div className={`absolute inset-0 w-full h-full ${
|
||||||
isExporting
|
isExporting
|
||||||
? 'bg-gradient-to-r from-gray-300/0 via-gray-300/30 to-gray-300/0'
|
? 'bg-gradient-to-r from-gray-300/0 via-gray-300/30 to-gray-300/0'
|
||||||
: 'bg-gradient-to-r from-green-400/0 via-green-400/30 to-green-400/0'
|
: exportFormat === 'excel'
|
||||||
|
? 'bg-gradient-to-r from-green-400/0 via-green-400/30 to-green-400/0'
|
||||||
|
: 'bg-gradient-to-r from-blue-400/0 via-blue-400/30 to-blue-400/0'
|
||||||
} skeleton-animation`}></div>
|
} skeleton-animation`}></div>
|
||||||
<span className="relative inline-flex items-center justify-center gap-2 px-4">
|
<span className="relative inline-flex items-center justify-center gap-2 px-4">
|
||||||
{isExporting ? (
|
{isExporting ? (
|
||||||
@@ -372,7 +699,7 @@ export default function Reports() {
|
|||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
<span>{isExporting ? 'Generando archivo...' : 'Generar y descargar CSV'}</span>
|
<span>{isExporting ? 'Generando archivo...' : `Generar y descargar ${exportFormat.toUpperCase()}`}</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user