From f1bdce317f019be1eb26e8221f4d21821b45971b Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 5 Feb 2026 18:09:05 -0400 Subject: [PATCH] Exportar excel con imagen y ahora guarda las imagenes como .png --- apps/api/.env.example | 4 +- apps/api/package.json | 4 +- .../features/training/training.controller.ts | 14 +- .../src/features/training/training.service.ts | 401 +++++++----------- .../dashboard/formulario/editar/[id]/page.tsx | 18 +- apps/web/app/dashboard/formulario/page.tsx | 25 +- apps/web/app/dashboard/page.tsx | 1 + apps/web/app/layout.tsx | 1 + .../feactures/auth/actions/login-action.ts | 10 +- .../training/actions/training-actions.ts | 2 +- .../feactures/training/components/form.tsx | 64 +-- .../training-tables/cell-action.tsx | 4 +- apps/web/lib/auth.config.ts | 34 +- 13 files changed, 250 insertions(+), 332 deletions(-) diff --git a/apps/api/.env.example b/apps/api/.env.example index 8b043fc..7076d29 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -15,5 +15,5 @@ DATABASE_URL="postgresql://postgres:local**@localhost:5432/caja_ahorro" #url con #Mail Configuration MAIL_HOST=gmail -MAIL_USERNAME= -MAIL_PASSWORD= +MAIL_USERNAME="123" +MAIL_PASSWORD="123" diff --git a/apps/api/package.json b/apps/api/package.json index c32d8ff..98aef39 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -42,6 +42,7 @@ "@nestjs/platform-express": "11.0.0", "dotenv": "16.5.0", "drizzle-orm": "0.40.0", + "exceljs": "^4.4.0", "express": "5.1.0", "joi": "17.13.3", "moment": "2.30.1", @@ -50,8 +51,7 @@ "pino-pretty": "13.0.0", "reflect-metadata": "0.2.0", "rxjs": "7.8.1", - "sharp": "^0.34.5", - "xlsx-populate": "^1.21.0" + "sharp": "^0.34.5" }, "devDependencies": { "@nestjs-modules/mailer": "^2.0.2", diff --git a/apps/api/src/features/training/training.controller.ts b/apps/api/src/features/training/training.controller.ts index de5eada..edeff68 100644 --- a/apps/api/src/features/training/training.controller.ts +++ b/apps/api/src/features/training/training.controller.ts @@ -13,6 +13,7 @@ import { StreamableFile, Header } from '@nestjs/common'; +import { Readable } from 'stream'; import { FilesInterceptor } from '@nestjs/platform-express'; import { ApiConsumes, @@ -33,12 +34,13 @@ import { Public } from '@/common/decorators'; export class TrainingController { constructor(private readonly trainingService: TrainingService) { } + // export training with excel @Public() @Get('export/:id') - @ApiOperation({ summary: 'Export training template' }) + @ApiOperation({ summary: 'Export training with excel' }) @ApiResponse({ status: 200, - description: 'Return training template.', + description: 'Return training with excel.', content: { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { schema: { type: 'string', format: 'binary' } } } }) @Header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') @@ -48,9 +50,10 @@ export class TrainingController { throw new Error('ID is required'); } const data = await this.trainingService.exportTemplate(Number(id)); - return new StreamableFile(data); + return new StreamableFile(Readable.from([data])); } + // get all training records @Get() @ApiOperation({ summary: 'Get all training records with pagination and filters', @@ -68,6 +71,7 @@ export class TrainingController { }; } + // get training statistics @Get('statistics') @ApiOperation({ summary: 'Get training statistics' }) @ApiResponse({ status: 200, description: 'Return training statistics.' }) @@ -76,6 +80,7 @@ export class TrainingController { return { message: 'Training statistics fetched successfully', data }; } + // get training record by id @Get(':id') @ApiOperation({ summary: 'Get a training record by ID' }) @ApiResponse({ status: 200, description: 'Return the training record.' }) @@ -85,6 +90,7 @@ export class TrainingController { return { message: 'Training record fetched successfully', data }; } + // create training record @Post() @UseInterceptors(FilesInterceptor('files', 3)) @ApiConsumes('multipart/form-data') @@ -101,6 +107,7 @@ export class TrainingController { return { message: 'Training record created successfully', data }; } + // update training record @Patch(':id') @UseInterceptors(FilesInterceptor('files', 3)) @ApiConsumes('multipart/form-data') @@ -123,6 +130,7 @@ export class TrainingController { return { message: 'Training record updated successfully', data }; } + // delete training record @Delete(':id') @ApiOperation({ summary: 'Delete a training record' }) @ApiResponse({ diff --git a/apps/api/src/features/training/training.service.ts b/apps/api/src/features/training/training.service.ts index 9941144..23b5b97 100644 --- a/apps/api/src/features/training/training.service.ts +++ b/apps/api/src/features/training/training.service.ts @@ -1,16 +1,19 @@ import { HttpException, HttpStatus, Inject, Injectable, NotFoundException } from '@nestjs/common'; -import { and, eq, gte, ilike, lte, or, SQL, sql } from 'drizzle-orm'; +import { and, eq, getTableColumns, gte, ilike, lte, or, SQL, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import * as fs from 'fs'; import * as path from 'path'; import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider'; import * as schema from 'src/database/index'; import { municipalities, parishes, states, trainingSurveys } from 'src/database/index'; -import XlsxPopulate from 'xlsx-populate'; +// import XlsxPopulate from 'xlsx-populate'; +import ExcelJS from 'exceljs'; import { PaginationDto } from '../../common/dto/pagination.dto'; import { CreateTrainingDto } from './dto/create-training.dto'; import { TrainingStatisticsFilterDto } from './dto/training-statistics-filter.dto'; import { UpdateTrainingDto } from './dto/update-training.dto'; +import sharp from 'sharp'; + @Injectable() export class TrainingService { @@ -215,9 +218,17 @@ export class TrainingService { async findOne(id: number) { const find = await this.drizzle - .select() + .select({ + ...getTableColumns(trainingSurveys), + stateName: states.name, + municipalityName: municipalities.name, + parishName: parishes.name, + }) .from(trainingSurveys) - .where(eq(trainingSurveys.id, id)); + .leftJoin(states, eq(trainingSurveys.state, states.id)) + .leftJoin(municipalities, eq(trainingSurveys.municipality, municipalities.id)) + .leftJoin(parishes, eq(trainingSurveys.parish, parishes.id)) + .where(eq(trainingSurveys.id, id)) if (find.length === 0) { throw new HttpException( @@ -239,9 +250,14 @@ export class TrainingService { const savedPaths: string[] = []; for (const file of files) { - const fileName = `${Date.now()}-${Math.round(Math.random() * 1e9)}${path.extname(file.originalname)}`; + const fileName = `${Date.now()}-${Math.round(Math.random() * 1e9)}.png`; const filePath = path.join(uploadDir, fileName); - fs.writeFileSync(filePath, file.buffer); + + // Convertir a PNG usando sharp antes de guardar + await sharp(file.buffer) + .png() + .toFile(filePath); + savedPaths.push(`/assets/training/${fileName}`); } return savedPaths; @@ -270,7 +286,6 @@ export class TrainingService { const photoPaths = await this.saveFiles(files); // 2. Extraer solo visitDate para formatearlo. - // Ya NO extraemos state, municipality, etc. porque no vienen en el DTO. const { visitDate, state, municipality, parish, ...rest } = createTrainingDto; const [newRecord] = await this.drizzle @@ -288,7 +303,7 @@ export class TrainingService { photo3: photoPaths[2] ?? null, state: Number(state) ?? null, municipality: Number(municipality) ?? null, - parish: Number(parish) ?? null, + parish: Number(parish) ?? null, }) .returning(); @@ -362,200 +377,18 @@ export class TrainingService { }; } - // async exportTemplate() { - - // const templatePath = path.join( - // __dirname, - // 'export_template', - // 'excel.osp.xlsx', - // ); - // const templateBuffer = fs.readFileSync(templatePath); - - // const workbook: any = await XlsxPopulate.fromDataAsync(templateBuffer); - // const sheet = workbook.sheet(0); - - // const records = await this.drizzle - // .select({ - // firstname: trainingSurveys.firstname, - // lastname: trainingSurveys.lastname, - // visitDate: trainingSurveys.visitDate, - // stateName: states.name, - // municipalityName: municipalities.name, - // parishName: parishes.name, - // communeName: trainingSurveys.communeName, - // siturCodeCommune: trainingSurveys.siturCodeCommune, - // communalCouncil: trainingSurveys.communalCouncil, - // siturCodeCommunalCouncil: trainingSurveys.siturCodeCommunalCouncil, - // productiveActivity: trainingSurveys.productiveActivity, - // ospName: trainingSurveys.ospName, - // ospAddress: trainingSurveys.ospAddress, - // ospRif: trainingSurveys.ospRif, - // ospType: trainingSurveys.ospType, - // currentStatus: trainingSurveys.currentStatus, - // companyConstitutionYear: trainingSurveys.companyConstitutionYear, - // ospResponsibleFullname: trainingSurveys.ospResponsibleFullname, - // ospResponsibleCedula: trainingSurveys.ospResponsibleCedula, - // ospResponsibleRif: trainingSurveys.ospResponsibleRif, - // ospResponsiblePhone: trainingSurveys.ospResponsiblePhone, - // ospResponsibleEmail: trainingSurveys.ospResponsibleEmail, - // civilState: trainingSurveys.civilState, - // familyBurden: trainingSurveys.familyBurden, - // numberOfChildren: trainingSurveys.numberOfChildren, - // generalObservations: trainingSurveys.generalObservations, - // paralysisReason: trainingSurveys.paralysisReason, - // productList: trainingSurveys.productList, - // infrastructureMt2: trainingSurveys.infrastructureMt2, - // photo1: trainingSurveys.photo1, - // photo2: trainingSurveys.photo2, - // photo3: trainingSurveys.photo3, - // }) - // .from(trainingSurveys) - // .leftJoin(states, eq(trainingSurveys.state, states.id)) - // .leftJoin( - // municipalities, - // eq(trainingSurveys.municipality, municipalities.id), - // ) - // .leftJoin(parishes, eq(trainingSurveys.parish, parishes.id)) - // .execute(); - - // let currentRow = 2; - - // for (const record of records) { - // const date = new Date(record.visitDate); - // const dateStr = date.toLocaleDateString('es-VE'); - // const timeStr = date.toLocaleTimeString('es-VE'); - - // sheet.cell(`A${currentRow}`).value(record.firstname); - // sheet.cell(`B${currentRow}`).value(record.lastname); - // sheet.cell(`C${currentRow}`).value(dateStr); - // sheet.cell(`D${currentRow}`).value(timeStr); - // sheet.cell(`E${currentRow}`).value(record.stateName || ''); - // sheet.cell(`F${currentRow}`).value(record.municipalityName || ''); - // sheet.cell(`G${currentRow}`).value(record.parishName || ''); - // sheet.cell(`H${currentRow}`).value(record.communeName); - // sheet.cell(`I${currentRow}`).value(record.siturCodeCommune); - // sheet.cell(`J${currentRow}`).value(record.communalCouncil); - // sheet.cell(`K${currentRow}`).value(record.siturCodeCommunalCouncil); - // sheet.cell(`L${currentRow}`).value(record.productiveActivity); - // sheet.cell(`M${currentRow}`).value(''); // requerimiento financiero description - // sheet.cell(`N${currentRow}`).value(record.ospName); - // sheet.cell(`O${currentRow}`).value(record.ospAddress); - // sheet.cell(`P${currentRow}`).value(record.ospRif); - // sheet.cell(`Q${currentRow}`).value(record.ospType); - // sheet.cell(`R${currentRow}`).value(record.currentStatus); - // sheet.cell(`S${currentRow}`).value(record.companyConstitutionYear); - - // const products = (record.productList as any[]) || []; - // const totalProducers = products.reduce( - // (sum, p) => - // sum + (Number(p.menCount) || 0) + (Number(p.womenCount) || 0), - // 0, - // ); - // const productsDesc = products.map((p) => p.name).join(', '); - - // sheet.cell(`T${currentRow}`).value(totalProducers); - // sheet.cell(`U${currentRow}`).value(productsDesc); - // sheet.cell(`V${currentRow}`).value(record.infrastructureMt2); - // sheet.cell(`W${currentRow}`).value(''); - // sheet.cell(`X${currentRow}`).value(record.paralysisReason || ''); - // sheet.cell(`Y${currentRow}`).value(record.ospResponsibleFullname); - // sheet.cell(`Z${currentRow}`).value(record.ospResponsibleCedula); - // sheet.cell(`AA${currentRow}`).value(record.ospResponsibleRif); - // sheet.cell(`AB${currentRow}`).value(record.ospResponsiblePhone); - // sheet.cell(`AC${currentRow}`).value(record.ospResponsibleEmail); - // sheet.cell(`AD${currentRow}`).value(record.civilState); - // sheet.cell(`AE${currentRow}`).value(record.familyBurden); - // sheet.cell(`AF${currentRow}`).value(record.numberOfChildren); - // sheet.cell(`AG${currentRow}`).value(record.generalObservations || ''); - - // sheet.cell(`AH${currentRow}`).value(record.photo1 || ''); - // sheet.cell(`AI${currentRow}`).value(record.photo2 || ''); - // sheet.cell(`AJ${currentRow}`).value(record.photo3 || ''); - - // currentRow++; - // } - - // return await workbook.outputAsync(); - // } - async exportTemplate(id: number) { - // Validar que el registro exista - const exist = await this.findOne(id); - if (!exist) throw new NotFoundException(`No se encontro el registro`); + const record = await this.findOne(id); + if (!record) throw new NotFoundException(`No se encontró el registro`); - // Obtener los datos del registro - const records = await this.drizzle - .select({ - // id: trainingSurveys.id, - visitDate: trainingSurveys.visitDate, - ospName: trainingSurveys.ospName, - productiveSector: trainingSurveys.productiveSector, - ospAddress: trainingSurveys.ospAddress, - ospRif: trainingSurveys.ospRif, - - siturCodeCommune: trainingSurveys.siturCodeCommune, - communeEmail: trainingSurveys.communeEmail, - communeRif: trainingSurveys.communeRif, - communeSpokespersonName: trainingSurveys.communeSpokespersonName, - communeSpokespersonPhone: trainingSurveys.communeSpokespersonPhone, - - siturCodeCommunalCouncil: trainingSurveys.siturCodeCommunalCouncil, - communalCouncilRif: trainingSurveys.communalCouncilRif, - communalCouncilSpokespersonName: trainingSurveys.communalCouncilSpokespersonName, - communalCouncilSpokespersonPhone: trainingSurveys.communalCouncilSpokespersonPhone, - - ospType: trainingSurveys.ospType, - productiveActivity: trainingSurveys.productiveActivity, // Sector Productivo - companyConstitutionYear: trainingSurveys.companyConstitutionYear, - infrastructureMt2: trainingSurveys.infrastructureMt2, - - hasTransport: trainingSurveys.hasTransport, - structureType: trainingSurveys.structureType, - isOpenSpace: trainingSurveys.isOpenSpace, - - ospResponsibleFullname: trainingSurveys.ospResponsibleFullname, - ospResponsibleCedula: trainingSurveys.ospResponsibleCedula, - ospResponsiblePhone: trainingSurveys.ospResponsiblePhone, - - productList: trainingSurveys.productList, - equipmentList: trainingSurveys.equipmentList, - productionList: trainingSurveys.productionList, - - // photo1: trainingSurveys.photo1 - }) - .from(trainingSurveys) - .where(eq(trainingSurveys.id, id)) - // .leftJoin(states, eq(trainingSurveys.state, states.id)) - // .leftJoin(municipalities,eq(trainingSurveys.municipality, municipalities.id)) - // .leftJoin(parishes, eq(trainingSurveys.parish, parishes.id)) - - let equipmentList: any[] = Array.isArray(records[0].equipmentList) ? records[0].equipmentList : []; - let productList: any[] = Array.isArray(records[0].productList) ? records[0].productList : []; - let productionList: any[] = Array.isArray(records[0].productionList) ? records[0].productionList : []; - - console.log('equipmentList', equipmentList); - console.log('productList', productList); - console.log('productionList', productionList); - - let equipmentListArray: any[] = []; - let productListArray: any[] = []; - let productionListArray: any[] = []; - - const equipmentListCount = equipmentList.length; - for (let i = 0; i < equipmentListCount; i++) { - equipmentListArray.push([equipmentList[i].machine, '', equipmentList[i].quantity]); - } - - const productListCount = productList.length; - for (let i = 0; i < productListCount; i++) { - productListArray.push([productList[i].productName, productList[i].dailyCount, productList[i].weeklyCount, productList[i].monthlyCount]); - } - - const productionListCount = productionList.length; - for (let i = 0; i < productionListCount; i++) { - productionListArray.push([productionList[i].rawMaterial, '', productionList[i].quantity]); - } + // Formatear fecha y hora + const dateObj = new Date(record.visitDate); + const fechaFormateada = dateObj.toLocaleDateString('es-ES'); + const horaFormateada = dateObj.toLocaleTimeString('es-ES', { + hour: '2-digit', + minute: '2-digit', + }); // Ruta de la plantilla const templatePath = path.join( @@ -564,53 +397,143 @@ export class TrainingService { 'excel.osp.xlsx', ); - // Cargar la plantilla - const book = await XlsxPopulate.fromFileAsync(templatePath); + // Cargar la plantilla con ExcelJS + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.readFile(templatePath); + const worksheet = workbook.getWorksheet(1); // Usar la primera hoja - const isoString = records[0].visitDate; - const dateObj = new Date(isoString); - const fechaFormateada = dateObj.toLocaleDateString('es-ES'); - const horaFormateada = dateObj.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); + if (!worksheet) { + throw new Error('No se pudo encontrar la hoja de trabajo en la plantilla'); + } - // Llenar los datos - book.sheet(0).cell('A6').value(records[0].productiveSector); - book.sheet(0).cell('D6').value(records[0].ospName); - book.sheet(0).cell('L5').value(fechaFormateada); - book.sheet(0).cell('L6').value(horaFormateada); - book.sheet(0).cell('B10').value(records[0].ospAddress); - book.sheet(0).cell('C11').value(records[0].communeEmail); - book.sheet(0).cell('C12').value(records[0].communeSpokespersonName); - book.sheet(0).cell('G11').value(records[0].communeRif); - book.sheet(0).cell('G12').value(records[0].communeSpokespersonPhone); - book.sheet(0).cell('C13').value(records[0].siturCodeCommune); - book.sheet(0).cell('G13').value(records[0].siturCodeCommunalCouncil); - book.sheet(0).cell('G14').value(records[0].communalCouncilRif); - book.sheet(0).cell('C15').value(records[0].communalCouncilSpokespersonName); - book.sheet(0).cell('G15').value(records[0].communalCouncilSpokespersonPhone); - book.sheet(0).cell('C16').value(records[0].ospType); - book.sheet(0).cell('C17').value(records[0].ospName); - book.sheet(0).cell('C18').value(records[0].productiveActivity); - book.sheet(0).cell('C19').value('Proveedores'); - book.sheet(0).cell('C20').value(records[0].companyConstitutionYear); - book.sheet(0).cell('C21').value(records[0].infrastructureMt2); - book.sheet(0).cell('G17').value(records[0].ospRif); + // Llenar los datos principales + worksheet.getCell('A6').value = record.productiveSector; + worksheet.getCell('B8').value = record.stateName; + worksheet.getCell('E8').value = record.municipalityName; + worksheet.getCell('B9').value = record.parishName; + worksheet.getCell('D6').value = record.ospName; + worksheet.getCell('L5').value = fechaFormateada; + worksheet.getCell('L6').value = horaFormateada; + worksheet.getCell('B10').value = record.ospAddress; + worksheet.getCell('C11').value = record.communeEmail; + worksheet.getCell('C12').value = record.communeSpokespersonName; + worksheet.getCell('G11').value = record.communeRif; + worksheet.getCell('G12').value = record.communeSpokespersonPhone; + worksheet.getCell('C13').value = record.siturCodeCommune; + worksheet.getCell('G13').value = record.siturCodeCommunalCouncil; + worksheet.getCell('G14').value = record.communalCouncilRif; + worksheet.getCell('C15').value = record.communalCouncilSpokespersonName; + worksheet.getCell('G15').value = record.communalCouncilSpokespersonPhone; + worksheet.getCell('C16').value = record.ospType; + worksheet.getCell('C17').value = record.ospName; + worksheet.getCell('C18').value = record.productiveActivity; + worksheet.getCell('C19').value = 'Proveedores'; + worksheet.getCell('C20').value = record.companyConstitutionYear; + worksheet.getCell('C21').value = record.infrastructureMt2; + worksheet.getCell('G17').value = record.ospRif; - book.sheet(0).cell(records[0].hasTransport === true ? 'J19' : 'L19').value('X'); - book.sheet(0).cell(records[0].structureType === 'CASA' ? 'J20' : 'L20').value('X'); - book.sheet(0).cell(records[0].isOpenSpace === true ? 'J21' : 'L21').value('X'); + worksheet.getCell(record.hasTransport === true ? 'J19' : 'L19').value = 'X'; + worksheet.getCell(record.structureType === 'CASA' ? 'J20' : 'L20').value = + 'X'; + worksheet.getCell(record.isOpenSpace === true ? 'J21' : 'L21').value = 'X'; - book.sheet(0).cell('A24').value(records[0].ospResponsibleFullname); - book.sheet(0).cell('C24').value(records[0].ospResponsibleCedula); - book.sheet(0).cell('E24').value(records[0].ospResponsiblePhone); + worksheet.getCell('A24').value = record.ospResponsibleFullname; + worksheet.getCell('C24').value = record.ospResponsibleCedula; + worksheet.getCell('E24').value = record.ospResponsiblePhone; + worksheet.getCell('J24').value = 'N Femenino'; // Placeholder si no hay dato + worksheet.getCell('L24').value = 'N Masculino'; // Placeholder si no hay dato - book.sheet(0).cell('J24').value('N Femenino'); - book.sheet(0).cell('L24').value('N Masculino'); + // const photo1 = record.photo1; + // const photo2 = record.photo2; + // const photo3 = record.photo3; - book.sheet(0).range(`A28:C${equipmentListCount + 28}`).value(equipmentListArray); - book.sheet(0).range(`E28:G${productionListCount + 28}`).value(productionListArray); - book.sheet(0).range(`I28:L${productListCount + 28}`).value(productListArray); + if (record.photo1) { + const image = record.photo1.slice(17); + const extension = image.split('.')[1]; - return book.outputAsync(); + // Validar que sea una imagen png, gif o jpeg + if (extension === 'png' || extension === 'gif' || extension === 'jpeg') { + // Ruta de la imagen + const imagePath = path.join( + __dirname, + '../../../', + `uploads/training/${image}`, + ); + + // Add an image to the workbook from a file buffer + const logoId = workbook.addImage({ + filename: imagePath, + extension: extension, + }); + + // Anchor the image to a specific cell (e.g., A1) + worksheet.addImage(logoId, `I7:L17`); // Spans from A1 to C3 + } + } + + // let i = 1; + // while (i <= 3) { + // const element = record[`photo${i}`]; + // if (element) { + // const image = element.slice(17); + // const extension: extensionType = image.split('.')[1]; + + // // Validar que sea una imagen png, gif o jpeg + // if (extension === 'png' || extension === 'gif' || extension === 'jpeg') { + // // Ruta de la imagen + // const imagePath = path.join( + // __dirname, + // '../../../', + // `uploads/training/${image}`, + // ); + + // // Add an image to the workbook from a file buffer + // const logoId = workbook.addImage({ + // filename: imagePath, + // extension: extension, + // }); + + // // Anchor the image to a specific cell (e.g., A1) + // worksheet.addImage(logoId, `I7:L17`); // Spans from A1 to C3 + // i = 4; + // } + // } + // i++; + // } + + // Listas (Equipos, Materia Prima, Productos) + const equipmentList = Array.isArray(record.equipmentList) + ? record.equipmentList + : []; + const productionList = Array.isArray(record.productionList) + ? record.productionList + : []; + const productList = Array.isArray(record.productList) + ? record.productList + : []; + + // Colocar listas empezando en la fila 28 + equipmentList.forEach((item: any, i: number) => { + const row = 28 + i; + worksheet.getCell(`A${row}`).value = item.machine; + worksheet.getCell(`C${row}`).value = item.quantity; + }); + + productionList.forEach((item: any, i: number) => { + const row = 28 + i; + worksheet.getCell(`E${row}`).value = item.rawMaterial; + worksheet.getCell(`G${row}`).value = item.quantity; + }); + + productList.forEach((item: any, i: number) => { + const row = 28 + i; + worksheet.getCell(`I${row}`).value = item.productName; + worksheet.getCell(`J${row}`).value = item.dailyCount; + worksheet.getCell(`K${row}`).value = item.weeklyCount; + worksheet.getCell(`L${row}`).value = item.monthlyCount; + }); + + return await workbook.xlsx.writeBuffer(); } } diff --git a/apps/web/app/dashboard/formulario/editar/[id]/page.tsx b/apps/web/app/dashboard/formulario/editar/[id]/page.tsx index c279469..6953637 100644 --- a/apps/web/app/dashboard/formulario/editar/[id]/page.tsx +++ b/apps/web/app/dashboard/formulario/editar/[id]/page.tsx @@ -21,14 +21,14 @@ export default function EditTrainingPage() { } return ( - -
- router.push('/dashboard/formulario')} - onCancel={() => router.back()} - /> -
-
+ // +
+ router.push('/dashboard/formulario')} + onCancel={() => router.back()} + /> +
+ //
); } diff --git a/apps/web/app/dashboard/formulario/page.tsx b/apps/web/app/dashboard/formulario/page.tsx index e963ddf..5cb7b5d 100644 --- a/apps/web/app/dashboard/formulario/page.tsx +++ b/apps/web/app/dashboard/formulario/page.tsx @@ -23,17 +23,18 @@ export default async function Page({ searchParams }: PageProps) { } = searchParamsCache.parse(await searchParams); return ( - -
- - - -
-
+ // + //
+ < div className="p-6 space-y-6" > + + + +
+ //
); } diff --git a/apps/web/app/dashboard/page.tsx b/apps/web/app/dashboard/page.tsx index 8fbbe21..0b20007 100644 --- a/apps/web/app/dashboard/page.tsx +++ b/apps/web/app/dashboard/page.tsx @@ -2,6 +2,7 @@ import { auth } from '@/lib/auth'; import { redirect } from 'next/navigation'; export default async function Dashboard() { + console.log('La sesion es llamada'); const session = await auth(); if (!session?.user) { diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 3c50ecc..15fb2cc 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -44,6 +44,7 @@ const RootLayout = async ({ }: Readonly<{ children: ReactNode; }>) => { + console.log('La sesion es llamada'); const session = await auth(); return ( diff --git a/apps/web/feactures/auth/actions/login-action.ts b/apps/web/feactures/auth/actions/login-action.ts index 24102f6..8ae18b8 100644 --- a/apps/web/feactures/auth/actions/login-action.ts +++ b/apps/web/feactures/auth/actions/login-action.ts @@ -1,5 +1,5 @@ 'use server'; -import { safeFetchApi } from '@/lib'; +import { safeFetchApi } from '@/lib/fetch.api'; import { loginResponseSchema, UserFormValue } from '../schemas/login'; type LoginActionSuccess = { @@ -20,9 +20,9 @@ type LoginActionSuccess = { } type LoginActionError = { - type: 'API_ERROR' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR'; // **Asegúrate de que el tipo de `type` sea este aquí** - message: string; - details?: any; + type: 'API_ERROR' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR'; // **Asegúrate de que el tipo de `type` sea este aquí** + message: string; + details?: any; }; // Si SignInAction también puede devolver null, asegúralo en su tipo de retorno @@ -37,7 +37,7 @@ export const SignInAction = async (payload: UserFormValue): Promise 0 + dataCoorMunicipality.data.length > 0 ? dataCoorMunicipality.data : [{ id: 0, stateId: 0, name: 'Sin Municipios' }]; @@ -268,7 +268,7 @@ export function CreateTrainingForm({ // 1. Definimos las claves que NO queremos enviar en el bucle general // 'files' se procesa aparte. // 'photo1/2/3' son strings (urls viejas) que no queremos reenviar como texto. - const excludedKeys = ['files', 'photo1', 'photo2', 'photo3', 'coorState', 'coorMunicipality', 'coorParish']; + const excludedKeys = ['files', 'photo1', 'photo2', 'photo3', 'coorState', 'coorMunicipality', 'coorParish']; Object.entries(formData).forEach(([key, value]) => { // 2. Condición actualizada: Si la key está en la lista de excluidos, la saltamos @@ -287,17 +287,17 @@ export function CreateTrainingForm({ } }); - - // 2. Mapeo manual y conversión a numérico - // if (formData.state) { - // data.append('state', Number(formData.state).toString()); - // } - // if (formData.municipality) { - // data.append('municipality', Number(formData.municipality).toString()); - // } - // if (formData.parish) { - // data.append('parish', Number(formData.parish).toString()); - // } + + // 2. Mapeo manual y conversión a numérico + // if (formData.state) { + // data.append('state', Number(formData.state).toString()); + // } + // if (formData.municipality) { + // data.append('municipality', Number(formData.municipality).toString()); + // } + // if (formData.parish) { + // data.append('parish', Number(formData.parish).toString()); + // } if (defaultValues?.id) { data.append('id', defaultValues.id.toString()); } @@ -305,24 +305,24 @@ export function CreateTrainingForm({ // 4. Aquí se agregan las NUEVAS fotos (binary) si el usuario seleccionó alguna selectedFiles.forEach((file) => { data.append('files', file); - }); - console.log(data); + console.log(file); + }); const mutation = defaultValues?.id ? updateTraining : createTraining; - // mutation(data as any, { - // onSuccess: () => { - // form.reset(); - // setSelectedFiles([]); - // onSuccess?.(); - // }, - // onError: (e) => { - // console.error(e); - // form.setError('root', { - // type: 'manual', - // message: 'Error al guardar el registro', - // }); - // }, - // }); + mutation(data as any, { + onSuccess: () => { + form.reset(); + setSelectedFiles([]); + onSuccess?.(); + }, + onError: (e) => { + console.error(e); + form.setError('root', { + type: 'manual', + message: 'Error al guardar el registro', + }); + }, + }); }; return ( @@ -417,7 +417,7 @@ export function CreateTrainingForm({ )} /> - ( @@ -921,7 +921,7 @@ export function CreateTrainingForm({ 3. Detalles de la ubicación - + = ({ data, apiUrl }) => { - {/* +