nuevas correciones al formulario y esquema base de datos para osp
This commit is contained in:
@@ -108,10 +108,19 @@ export class MinioService implements OnModuleInit {
|
|||||||
|
|
||||||
async delete(objectName: string): Promise<void> {
|
async delete(objectName: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.minioClient.removeObject(this.bucketName, objectName);
|
// Ensure we don't have a leading slash which can cause issues with removeObject
|
||||||
this.logger.log(`Object "${objectName}" deleted successfully.`);
|
const cleanedName = objectName.startsWith('/')
|
||||||
|
? objectName.slice(1)
|
||||||
|
: objectName;
|
||||||
|
|
||||||
|
await this.minioClient.removeObject(this.bucketName, cleanedName);
|
||||||
|
this.logger.log(
|
||||||
|
`Object "${cleanedName}" deleted successfully from bucket "${this.bucketName}".`,
|
||||||
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Error deleting file: ${error.message}`);
|
this.logger.error(
|
||||||
|
`Error deleting file "${objectName}": ${error.message}`,
|
||||||
|
);
|
||||||
// We don't necessarily want to throw if the file is already gone
|
// We don't necessarily want to throw if the file is already gone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "internal_distribution_zone" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "is_exporting" boolean DEFAULT false NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "external_country" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "external_city" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "external_description" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "external_quantity" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "external_unit" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "women_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "men_count" integer DEFAULT 0 NOT NULL;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
DROP INDEX "training_surveys_index_00";--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" ADD COLUMN "coor_full_name" text NOT NULL;--> statement-breakpoint
|
||||||
|
CREATE INDEX "training_surveys_index_00" ON "training_surveys" USING btree ("coor_full_name");--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" DROP COLUMN "firstname";--> statement-breakpoint
|
||||||
|
ALTER TABLE "training_surveys" DROP COLUMN "lastname";
|
||||||
2098
apps/api/src/database/migrations/meta/0022_snapshot.json
Normal file
2098
apps/api/src/database/migrations/meta/0022_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2092
apps/api/src/database/migrations/meta/0023_snapshot.json
Normal file
2092
apps/api/src/database/migrations/meta/0023_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -155,6 +155,20 @@
|
|||||||
"when": 1771901546945,
|
"when": 1771901546945,
|
||||||
"tag": "0021_warm_machine_man",
|
"tag": "0021_warm_machine_man",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 22,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1772031518006,
|
||||||
|
"tag": "0022_nervous_dragon_lord",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 23,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1772032122473,
|
||||||
|
"tag": "0023_sticky_slayback",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,7 @@ export const trainingSurveys = t.pgTable(
|
|||||||
{
|
{
|
||||||
// === 1. IDENTIFICADORES Y DATOS DE VISITA ===
|
// === 1. IDENTIFICADORES Y DATOS DE VISITA ===
|
||||||
id: t.serial('id').primaryKey(),
|
id: t.serial('id').primaryKey(),
|
||||||
firstname: t.text('firstname').notNull(),
|
coorFullName: t.text('coor_full_name').notNull(),
|
||||||
lastname: t.text('lastname').notNull(),
|
|
||||||
visitDate: t.timestamp('visit_date').notNull(),
|
visitDate: t.timestamp('visit_date').notNull(),
|
||||||
coorPhone: t.text('coor_phone'),
|
coorPhone: t.text('coor_phone'),
|
||||||
|
|
||||||
@@ -133,6 +132,20 @@ export const trainingSurveys = t.pgTable(
|
|||||||
familyBurden: t.integer('family_burden'),
|
familyBurden: t.integer('family_burden'),
|
||||||
numberOfChildren: t.integer('number_of_children'),
|
numberOfChildren: t.integer('number_of_children'),
|
||||||
generalObservations: t.text('general_observations'),
|
generalObservations: t.text('general_observations'),
|
||||||
|
|
||||||
|
// === 4. DATOS DE DISTRIBUCIÓN Y EXPORTACIÓN ===
|
||||||
|
internalDistributionZone: t.text('internal_distribution_zone'),
|
||||||
|
isExporting: t.boolean('is_exporting').notNull().default(false),
|
||||||
|
externalCountry: t.text('external_country'),
|
||||||
|
externalCity: t.text('external_city'),
|
||||||
|
externalDescription: t.text('external_description'),
|
||||||
|
externalQuantity: t.text('external_quantity'),
|
||||||
|
externalUnit: t.text('external_unit'),
|
||||||
|
|
||||||
|
// === 5. MANO DE OBRA ===
|
||||||
|
womenCount: t.integer('women_count').notNull().default(0),
|
||||||
|
menCount: t.integer('men_count').notNull().default(0),
|
||||||
|
|
||||||
// Fotos
|
// Fotos
|
||||||
photo1: t.text('photo1'),
|
photo1: t.text('photo1'),
|
||||||
photo2: t.text('photo2'),
|
photo2: t.text('photo2'),
|
||||||
@@ -149,7 +162,7 @@ export const trainingSurveys = t.pgTable(
|
|||||||
(trainingSurveys) => ({
|
(trainingSurveys) => ({
|
||||||
trainingSurveysIndex: t
|
trainingSurveysIndex: t
|
||||||
.index('training_surveys_index_00')
|
.index('training_surveys_index_00')
|
||||||
.on(trainingSurveys.firstname),
|
.on(trainingSurveys.coorFullName),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import { Transform, Type } from 'class-transformer';
|
import { Transform, Type } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
|
||||||
IsDateString,
|
IsDateString,
|
||||||
IsEmail,
|
IsEmail,
|
||||||
IsInt,
|
IsInt,
|
||||||
@@ -15,11 +14,7 @@ export class CreateTrainingDto {
|
|||||||
// === 1. DATOS BÁSICOS ===
|
// === 1. DATOS BÁSICOS ===
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
firstname: string;
|
coorFullName: string;
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@IsString()
|
|
||||||
lastname: string;
|
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
@@ -77,16 +72,14 @@ export class CreateTrainingDto {
|
|||||||
structureType?: string;
|
structureType?: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Transform(({ value }) => value === 'true' || value === true) // Convierte "false" -> false
|
hasTransport?: string;
|
||||||
hasTransport?: boolean;
|
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Transform(({ value }) => value === 'true' || value === true)
|
isOpenSpace?: string;
|
||||||
isOpenSpace?: boolean;
|
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -209,7 +202,56 @@ export class CreateTrainingDto {
|
|||||||
@IsEmail()
|
@IsEmail()
|
||||||
communalCouncilEmail?: string;
|
communalCouncilEmail?: string;
|
||||||
|
|
||||||
// === 6. LISTAS (Arrays JSON) ===
|
// === 6. DISTRIBUCIÓN Y EXPORTACIÓN ===
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
internalDistributionZone?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
isExporting?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
externalCountry?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
externalCity?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
externalDescription?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
externalQuantity?: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
externalUnit?: string;
|
||||||
|
|
||||||
|
// === 7. MANO DE OBRA ===
|
||||||
|
@ApiProperty()
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
womenCount?: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
menCount?: number;
|
||||||
|
|
||||||
|
// === 8. LISTAS (Arrays JSON) ===
|
||||||
// Reciben un string JSON '[{...}]' y lo convierten a Objeto JS real
|
// Reciben un string JSON '[{...}]' y lo convierten a Objeto JS real
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
|
|||||||
@@ -107,17 +107,7 @@ export class TrainingService {
|
|||||||
// 2. Total Productores (Columna plana que mantuviste)
|
// 2. Total Productores (Columna plana que mantuviste)
|
||||||
this.drizzle
|
this.drizzle
|
||||||
.select({
|
.select({
|
||||||
sum: sql<number>`
|
sum: sql<number>`SUM(${trainingSurveys.womenCount} + ${trainingSurveys.menCount})`,
|
||||||
SUM(
|
|
||||||
(
|
|
||||||
SELECT SUM(
|
|
||||||
COALESCE((item->>'menCount')::int, 0) +
|
|
||||||
COALESCE((item->>'womenCount')::int, 0)
|
|
||||||
)
|
|
||||||
FROM jsonb_array_elements(${trainingSurveys.productList}) as item
|
|
||||||
)
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
})
|
})
|
||||||
.from(trainingSurveys)
|
.from(trainingSurveys)
|
||||||
.where(whereCondition),
|
.where(whereCondition),
|
||||||
@@ -244,20 +234,25 @@ export class TrainingService {
|
|||||||
private async deleteFile(fileUrl: string) {
|
private async deleteFile(fileUrl: string) {
|
||||||
if (!fileUrl) return;
|
if (!fileUrl) return;
|
||||||
|
|
||||||
// Extract object name from URL
|
|
||||||
// URL format: http://endpoint:port/bucket/folder/filename
|
|
||||||
// Or it could be just the path if we decided that.
|
|
||||||
// Assuming fileUrl is the full public URL from getPublicUrl
|
|
||||||
try {
|
try {
|
||||||
const url = new URL(fileUrl);
|
// If it's a full URL, we need to extract the part after the bucket name
|
||||||
const pathname = url.pathname; // /bucket/folder/filename
|
if (fileUrl.startsWith('http')) {
|
||||||
const parts = pathname.split('/');
|
const url = new URL(fileUrl);
|
||||||
// parts[0] is '', parts[1] is bucket, parts[2..] is objectName
|
const pathname = url.pathname; // /bucket/folder/filename
|
||||||
const objectName = parts.slice(2).join('/');
|
const parts = pathname.split('/').filter(Boolean); // ['bucket', 'folder', 'filename']
|
||||||
|
|
||||||
await this.minioService.delete(objectName);
|
// The first part is the bucket name, the rest is the object name
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
const objectName = parts.slice(1).join('/');
|
||||||
|
await this.minioService.delete(objectName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not a URL or doesn't match the expected format, pass it as is
|
||||||
|
await this.minioService.delete(fileUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If it's not a valid URL, maybe it's just the object name stored from before
|
// Fallback if URL parsing fails
|
||||||
await this.minioService.delete(fileUrl);
|
await this.minioService.delete(fileUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,6 +287,9 @@ export class TrainingService {
|
|||||||
state: Number(state) ?? null,
|
state: Number(state) ?? null,
|
||||||
municipality: Number(municipality) ?? null,
|
municipality: Number(municipality) ?? null,
|
||||||
parish: Number(parish) ?? null,
|
parish: Number(parish) ?? null,
|
||||||
|
hasTransport: rest.hasTransport === 'true' ? true : false,
|
||||||
|
isOpenSpace: rest.isOpenSpace === 'true' ? true : false,
|
||||||
|
isExporting: rest.isExporting === 'true' ? true : false,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
updatedBy: userId,
|
updatedBy: userId,
|
||||||
})
|
})
|
||||||
@@ -359,6 +357,12 @@ export class TrainingService {
|
|||||||
|
|
||||||
// actualizamos el id del usuario que actualizo el registro
|
// actualizamos el id del usuario que actualizo el registro
|
||||||
updateData.updatedBy = userId;
|
updateData.updatedBy = userId;
|
||||||
|
updateData.hasTransport =
|
||||||
|
updateTrainingDto.hasTransport === 'true' ? true : false;
|
||||||
|
updateData.isOpenSpace =
|
||||||
|
updateTrainingDto.isOpenSpace === 'true' ? true : false;
|
||||||
|
updateData.isExporting =
|
||||||
|
updateTrainingDto.isExporting === 'true' ? true : false;
|
||||||
|
|
||||||
const [updatedRecord] = await this.drizzle
|
const [updatedRecord] = await this.drizzle
|
||||||
.update(trainingSurveys)
|
.update(trainingSurveys)
|
||||||
@@ -402,8 +406,7 @@ export class TrainingService {
|
|||||||
|
|
||||||
// const records = await this.drizzle
|
// const records = await this.drizzle
|
||||||
// .select({
|
// .select({
|
||||||
// firstname: trainingSurveys.firstname,
|
// coorFullName: trainingSurveys.coorFullName,
|
||||||
// lastname: trainingSurveys.lastname,
|
|
||||||
// visitDate: trainingSurveys.visitDate,
|
// visitDate: trainingSurveys.visitDate,
|
||||||
// stateName: states.name,
|
// stateName: states.name,
|
||||||
// municipalityName: municipalities.name,
|
// municipalityName: municipalities.name,
|
||||||
@@ -451,8 +454,7 @@ export class TrainingService {
|
|||||||
// const dateStr = date.toLocaleDateString('es-VE');
|
// const dateStr = date.toLocaleDateString('es-VE');
|
||||||
// const timeStr = date.toLocaleTimeString('es-VE');
|
// const timeStr = date.toLocaleTimeString('es-VE');
|
||||||
|
|
||||||
// sheet.cell(`A${currentRow}`).value(record.firstname);
|
// sheet.cell(`A${currentRow}`).value(record.coorFullName);
|
||||||
// sheet.cell(`B${currentRow}`).value(record.lastname);
|
|
||||||
// sheet.cell(`C${currentRow}`).value(dateStr);
|
// sheet.cell(`C${currentRow}`).value(dateStr);
|
||||||
// sheet.cell(`D${currentRow}`).value(timeStr);
|
// sheet.cell(`D${currentRow}`).value(timeStr);
|
||||||
// sheet.cell(`E${currentRow}`).value(record.stateName || '');
|
// sheet.cell(`E${currentRow}`).value(record.stateName || '');
|
||||||
|
|||||||
@@ -1,196 +1,195 @@
|
|||||||
export const COUNTRY_OPTIONS = [
|
export const COUNTRY_OPTIONS = [
|
||||||
'Afganistán',
|
'Afganistán',
|
||||||
'Albania',
|
'Albania',
|
||||||
'Alemania',
|
'Alemania',
|
||||||
'Andorra',
|
'Andorra',
|
||||||
'Angola',
|
'Angola',
|
||||||
'Antigua y Barbuda',
|
'Antigua y Barbuda',
|
||||||
'Arabia Saudita',
|
'Arabia Saudita',
|
||||||
'Argelia',
|
'Argelia',
|
||||||
'Argentina',
|
'Argentina',
|
||||||
'Armenia',
|
'Armenia',
|
||||||
'Australia',
|
'Australia',
|
||||||
'Austria',
|
'Austria',
|
||||||
'Azerbaiyán',
|
'Azerbaiyán',
|
||||||
'Bahamas',
|
'Bahamas',
|
||||||
'Bangladés',
|
'Bangladés',
|
||||||
'Barbados',
|
'Barbados',
|
||||||
'Baréin',
|
'Baréin',
|
||||||
'Bélgica',
|
'Bélgica',
|
||||||
'Belice',
|
'Belice',
|
||||||
'Benín',
|
'Benín',
|
||||||
'Bielorrusia',
|
'Bielorrusia',
|
||||||
'Birmania',
|
'Birmania',
|
||||||
'Bolivia',
|
'Bolivia',
|
||||||
'Bosnia y Herzegovina',
|
'Bosnia y Herzegovina',
|
||||||
'Botsuana',
|
'Botsuana',
|
||||||
'Brasil',
|
'Brasil',
|
||||||
'Brunéi',
|
'Brunéi',
|
||||||
'Bulgaria',
|
'Bulgaria',
|
||||||
'Burkina Faso',
|
'Burkina Faso',
|
||||||
'Burundi',
|
'Burundi',
|
||||||
'Bután',
|
'Bután',
|
||||||
'Cabo Verde',
|
'Cabo Verde',
|
||||||
'Camboya',
|
'Camboya',
|
||||||
'Camerún',
|
'Camerún',
|
||||||
'Canadá',
|
'Canadá',
|
||||||
'Catar',
|
'Catar',
|
||||||
'Chad',
|
'Chad',
|
||||||
'Chile',
|
'Chile',
|
||||||
'China',
|
'China',
|
||||||
'Chipre',
|
'Chipre',
|
||||||
'Ciudad del Vaticano',
|
'Ciudad del Vaticano',
|
||||||
'Colombia',
|
'Colombia',
|
||||||
'Comoras',
|
'Comoras',
|
||||||
'Corea del Norte',
|
'Corea del Norte',
|
||||||
'Corea del Sur',
|
'Corea del Sur',
|
||||||
'Costa de Marfil',
|
'Costa de Marfil',
|
||||||
'Costa Rica',
|
'Costa Rica',
|
||||||
'Croacia',
|
'Croacia',
|
||||||
'Cuba',
|
'Cuba',
|
||||||
'Dinamarca',
|
'Dinamarca',
|
||||||
'Dominica',
|
'Dominica',
|
||||||
'Ecuador',
|
'Ecuador',
|
||||||
'Egipto',
|
'Egipto',
|
||||||
'El Salvador',
|
'El Salvador',
|
||||||
'Emiratos Árabes Unidos',
|
'Emiratos Árabes Unidos',
|
||||||
'Eritrea',
|
'Eritrea',
|
||||||
'Eslovaquia',
|
'Eslovaquia',
|
||||||
'Eslovenia',
|
'Eslovenia',
|
||||||
'España',
|
'España',
|
||||||
'Estados Unidos',
|
'Estados Unidos',
|
||||||
'Estonia',
|
'Estonia',
|
||||||
'Etiopía',
|
'Etiopía',
|
||||||
'Filipinas',
|
'Filipinas',
|
||||||
'Finlandia',
|
'Finlandia',
|
||||||
'Fiyi',
|
'Fiyi',
|
||||||
'Francia',
|
'Francia',
|
||||||
'Gabón',
|
'Gabón',
|
||||||
'Gambia',
|
'Gambia',
|
||||||
'Georgia',
|
'Georgia',
|
||||||
'Ghana',
|
'Ghana',
|
||||||
'Granada',
|
'Granada',
|
||||||
'Grecia',
|
'Grecia',
|
||||||
'Guatemala',
|
'Guatemala',
|
||||||
'Guyana',
|
'Guyana',
|
||||||
'Guinea',
|
'Guinea',
|
||||||
'Guinea Ecuatorial',
|
'Guinea Ecuatorial',
|
||||||
'Guinea-Bisáu',
|
'Guinea-Bisáu',
|
||||||
'Haití',
|
'Haití',
|
||||||
'Honduras',
|
'Honduras',
|
||||||
'Hungría',
|
'Hungría',
|
||||||
'India',
|
'India',
|
||||||
'Indonesia',
|
'Indonesia',
|
||||||
'Irak',
|
'Irak',
|
||||||
'Irán',
|
'Irán',
|
||||||
'Irlanda',
|
'Irlanda',
|
||||||
'Islandia',
|
'Islandia',
|
||||||
'Islas Marshall',
|
'Islas Marshall',
|
||||||
'Islas Salomón',
|
'Islas Salomón',
|
||||||
'Israel',
|
'Israel',
|
||||||
'Italia',
|
'Italia',
|
||||||
'Jamaica',
|
'Jamaica',
|
||||||
'Japón',
|
'Japón',
|
||||||
'Jordania',
|
'Jordania',
|
||||||
'Kazajistán',
|
'Kazajistán',
|
||||||
'Kenia',
|
'Kenia',
|
||||||
'Kirguistán',
|
'Kirguistán',
|
||||||
'Kiribati',
|
'Kiribati',
|
||||||
'Kuwait',
|
'Kuwait',
|
||||||
'Laos',
|
'Laos',
|
||||||
'Lesoto',
|
'Lesoto',
|
||||||
'Letonia',
|
'Letonia',
|
||||||
'Líbano',
|
'Líbano',
|
||||||
'Liberia',
|
'Liberia',
|
||||||
'Libia',
|
'Libia',
|
||||||
'Liechtenstein',
|
'Liechtenstein',
|
||||||
'Lituania',
|
'Lituania',
|
||||||
'Luxemburgo',
|
'Luxemburgo',
|
||||||
'Madagascar',
|
'Madagascar',
|
||||||
'Malasia',
|
'Malasia',
|
||||||
'Malaui',
|
'Malaui',
|
||||||
'Maldivas',
|
'Maldivas',
|
||||||
'Malí',
|
'Malí',
|
||||||
'Malta',
|
'Malta',
|
||||||
'Marruecos',
|
'Marruecos',
|
||||||
'Mauricio',
|
'Mauricio',
|
||||||
'Mauritania',
|
'Mauritania',
|
||||||
'México',
|
'México',
|
||||||
'Micronesia',
|
'Micronesia',
|
||||||
'Moldavia',
|
'Moldavia',
|
||||||
'Mónaco',
|
'Mónaco',
|
||||||
'Mongolia',
|
'Mongolia',
|
||||||
'Montenegro',
|
'Montenegro',
|
||||||
'Mozambique',
|
'Mozambique',
|
||||||
'Namibia',
|
'Namibia',
|
||||||
'Nauru',
|
'Nauru',
|
||||||
'Nepal',
|
'Nepal',
|
||||||
'Nicaragua',
|
'Nicaragua',
|
||||||
'Níger',
|
'Níger',
|
||||||
'Nigeria',
|
'Nigeria',
|
||||||
'Noruega',
|
'Noruega',
|
||||||
'Nueva Zelanda',
|
'Nueva Zelanda',
|
||||||
'Omán',
|
'Omán',
|
||||||
'Países Bajos',
|
'Países Bajos',
|
||||||
'Pakistán',
|
'Pakistán',
|
||||||
'Palaos',
|
'Palaos',
|
||||||
'Panamá',
|
'Panamá',
|
||||||
'Papúa Nueva Guinea',
|
'Papúa Nueva Guinea',
|
||||||
'Paraguay',
|
'Paraguay',
|
||||||
'Perú',
|
'Perú',
|
||||||
'Polonia',
|
'Polonia',
|
||||||
'Portugal',
|
'Portugal',
|
||||||
'Reino Unido',
|
'Reino Unido',
|
||||||
'República Centroafricana',
|
'República Centroafricana',
|
||||||
'República Checa',
|
'República Checa',
|
||||||
'República de Macedonia',
|
'República de Macedonia',
|
||||||
'República del Congo',
|
'República del Congo',
|
||||||
'República Democrática del Congo',
|
'República Democrática del Congo',
|
||||||
'República Dominicana',
|
'República Dominicana',
|
||||||
'República Sudafricana',
|
'República Sudafricana',
|
||||||
'Ruanda',
|
'Ruanda',
|
||||||
'Rumanía',
|
'Rumanía',
|
||||||
'Rusia',
|
'Rusia',
|
||||||
'Samoa',
|
'Samoa',
|
||||||
'San Cristóbal y Nieves',
|
'San Cristóbal y Nieves',
|
||||||
'San Marino',
|
'San Marino',
|
||||||
'San Vicente y las Granadinas',
|
'San Vicente y las Granadinas',
|
||||||
'Santa Lucía',
|
'Santa Lucía',
|
||||||
'Santo Tomé y Príncipe',
|
'Santo Tomé y Príncipe',
|
||||||
'Senegal',
|
'Senegal',
|
||||||
'Serbia',
|
'Serbia',
|
||||||
'Seychelles',
|
'Seychelles',
|
||||||
'Sierra Leona',
|
'Sierra Leona',
|
||||||
'Singapur',
|
'Singapur',
|
||||||
'Siria',
|
'Siria',
|
||||||
'Somalia',
|
'Somalia',
|
||||||
'Sri Lanka',
|
'Sri Lanka',
|
||||||
'Suazilandia',
|
'Suazilandia',
|
||||||
'Sudán',
|
'Sudán',
|
||||||
'Sudán del Sur',
|
'Sudán del Sur',
|
||||||
'Suecia',
|
'Suecia',
|
||||||
'Suiza',
|
'Suiza',
|
||||||
'Surinam',
|
'Surinam',
|
||||||
'Tailandia',
|
'Tailandia',
|
||||||
'Tanzania',
|
'Tanzania',
|
||||||
'Tayikistán',
|
'Tayikistán',
|
||||||
'Timor Oriental',
|
'Timor Oriental',
|
||||||
'Togo',
|
'Togo',
|
||||||
'Tonga',
|
'Tonga',
|
||||||
'Trinidad y Tobago',
|
'Trinidad y Tobago',
|
||||||
'Túnez',
|
'Túnez',
|
||||||
'Turkmenistán',
|
'Turkmenistán',
|
||||||
'Turquía',
|
'Turquía',
|
||||||
'Tuvalu',
|
'Tuvalu',
|
||||||
'Ucrania',
|
'Ucrania',
|
||||||
'Uganda',
|
'Uganda',
|
||||||
'Uruguay',
|
'Uruguay',
|
||||||
'Uzbekistán',
|
'Uzbekistán',
|
||||||
'Vanuatu',
|
'Vanuatu',
|
||||||
'Venezuela',
|
'Vietnam',
|
||||||
'Vietnam',
|
'Yemen',
|
||||||
'Yemen',
|
'Yibuti',
|
||||||
'Yibuti',
|
'Zambia',
|
||||||
'Zambia',
|
'Zimbabue',
|
||||||
'Zimbabue'
|
];
|
||||||
];
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export const createTrainingAction = async (
|
|||||||
payloadToSend = rest as any;
|
payloadToSend = rest as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(payloadToSend);
|
console.log(payloadToSend);
|
||||||
|
|
||||||
const [error, data] = await safeFetchApi(
|
const [error, data] = await safeFetchApi(
|
||||||
TrainingMutate,
|
TrainingMutate,
|
||||||
@@ -124,6 +124,8 @@ export const updateTrainingAction = async (
|
|||||||
|
|
||||||
if (!id) throw new Error('ID es requerido para actualizar');
|
if (!id) throw new Error('ID es requerido para actualizar');
|
||||||
|
|
||||||
|
console.log(payloadToSend);
|
||||||
|
|
||||||
const [error, data] = await safeFetchApi(
|
const [error, data] = await safeFetchApi(
|
||||||
TrainingMutate,
|
TrainingMutate,
|
||||||
`/training/${id}`,
|
`/training/${id}`,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { COUNTRY_OPTIONS } from '@/constants/countries';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { Button } from '@repo/shadcn/button';
|
import { Button } from '@repo/shadcn/button';
|
||||||
|
import { Separator } from '@repo/shadcn/components/ui/separator';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -18,6 +20,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@repo/shadcn/select';
|
} from '@repo/shadcn/select';
|
||||||
|
import { Switch } from '@repo/shadcn/switch';
|
||||||
import { Textarea } from '@repo/shadcn/textarea';
|
import { Textarea } from '@repo/shadcn/textarea';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
@@ -98,8 +101,7 @@ export function CreateTrainingForm({
|
|||||||
const form = useForm<TrainingSchema>({
|
const form = useForm<TrainingSchema>({
|
||||||
resolver: zodResolver(trainingSchema),
|
resolver: zodResolver(trainingSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
firstname: defaultValues?.firstname || '',
|
coorFullName: defaultValues?.coorFullName || '',
|
||||||
lastname: defaultValues?.lastname || '',
|
|
||||||
coorState: defaultValues?.coorState || undefined,
|
coorState: defaultValues?.coorState || undefined,
|
||||||
coorMunicipality: defaultValues?.coorMunicipality || undefined,
|
coorMunicipality: defaultValues?.coorMunicipality || undefined,
|
||||||
coorParish: defaultValues?.coorParish || undefined,
|
coorParish: defaultValues?.coorParish || undefined,
|
||||||
@@ -157,6 +159,17 @@ export function CreateTrainingForm({
|
|||||||
state: defaultValues?.state || undefined,
|
state: defaultValues?.state || undefined,
|
||||||
municipality: defaultValues?.municipality || undefined,
|
municipality: defaultValues?.municipality || undefined,
|
||||||
parish: defaultValues?.parish || undefined,
|
parish: defaultValues?.parish || undefined,
|
||||||
|
|
||||||
|
internalDistributionZone: defaultValues?.internalDistributionZone || '',
|
||||||
|
isExporting: defaultValues?.isExporting || false,
|
||||||
|
externalCountry: defaultValues?.externalCountry || '',
|
||||||
|
externalCity: defaultValues?.externalCity || '',
|
||||||
|
externalDescription: defaultValues?.externalDescription || '',
|
||||||
|
externalQuantity: defaultValues?.externalQuantity || '',
|
||||||
|
externalUnit: defaultValues?.externalUnit || '',
|
||||||
|
|
||||||
|
womenCount: defaultValues?.womenCount || 0,
|
||||||
|
menCount: defaultValues?.menCount || 0,
|
||||||
},
|
},
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
@@ -213,23 +226,6 @@ export function CreateTrainingForm({
|
|||||||
mainProductiveActivity,
|
mainProductiveActivity,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { data: dataCoorState } = useStateQuery();
|
|
||||||
const { data: dataCoorMunicipality } = useMunicipalityQuery(coorState);
|
|
||||||
const { data: dataCoorParish } = useParishQuery(coorMunicipality);
|
|
||||||
|
|
||||||
const coorStateOptions = dataCoorState?.data || [
|
|
||||||
{ id: 0, name: 'Sin estados' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const coorMunicipalityOptions = dataCoorMunicipality?.data?.length
|
|
||||||
? dataCoorMunicipality.data
|
|
||||||
: [{ id: 0, stateId: 0, name: 'Sin Municipios' }];
|
|
||||||
|
|
||||||
const coorParishOptions =
|
|
||||||
Array.isArray(dataCoorParish?.data) && dataCoorParish?.data?.length
|
|
||||||
? dataCoorParish.data
|
|
||||||
: [{ id: 0, stateId: 0, name: 'Sin Parroquias' }];
|
|
||||||
|
|
||||||
const stateOptions = dataState?.data || [{ id: 0, name: 'Sin estados' }];
|
const stateOptions = dataState?.data || [{ id: 0, name: 'Sin estados' }];
|
||||||
|
|
||||||
const municipalityOptions = dataMunicipality?.data?.length
|
const municipalityOptions = dataMunicipality?.data?.length
|
||||||
@@ -313,6 +309,7 @@ export function CreateTrainingForm({
|
|||||||
selectedFiles.forEach((file) => {
|
selectedFiles.forEach((file) => {
|
||||||
data.append('files', file);
|
data.append('files', file);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mutation = defaultValues?.id ? updateTraining : createTraining;
|
const mutation = defaultValues?.id ? updateTraining : createTraining;
|
||||||
|
|
||||||
mutation(data as any, {
|
mutation(data as any, {
|
||||||
@@ -359,26 +356,14 @@ export function CreateTrainingForm({
|
|||||||
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="firstname"
|
name="coorFullName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Nombre del Coordinador Estadal</FormLabel>
|
<FormLabel>
|
||||||
|
Nombre y Apellido del Coordinador Estadal
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="Ej. Juan" />
|
<Input {...field} placeholder="Ej. Juan Pérez" />
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="lastname"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Apellido del Coordinador Estadal</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} placeholder="Ej. Pérez" />
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -825,7 +810,7 @@ export function CreateTrainingForm({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(val) => field.onChange(val === 'true')}
|
onValueChange={(val) => field.onChange(val === 'true')}
|
||||||
defaultValue={field.value ? 'true' : 'false'}
|
value={field.value ? 'true' : 'false'}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
@@ -881,7 +866,7 @@ export function CreateTrainingForm({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(val) => field.onChange(val === 'true')}
|
onValueChange={(val) => field.onChange(val === 'true')}
|
||||||
defaultValue={field.value ? 'true' : 'false'}
|
value={field.value ? 'true' : 'false'}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
@@ -936,6 +921,212 @@ export function CreateTrainingForm({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Distribución y Exportación */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Zona de Distribución y Exportación</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="internalDistributionZone"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
Breve Descripción de la Zona de Distribución
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="Ej. Mercado local y regional"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="isExporting"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel className="text-base">
|
||||||
|
¿El producto es para exportación?
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{form.watch('isExporting') && (
|
||||||
|
<div className="space-y-4 pt-4 border-t">
|
||||||
|
<h4 className="font-semibold text-sm">
|
||||||
|
Datos de Exportación
|
||||||
|
</h4>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalCountry"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>País</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value ?? undefined}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Seleccione País" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{COUNTRY_OPTIONS.map((country: string) => (
|
||||||
|
<SelectItem key={country} value={country}>
|
||||||
|
{country}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalCity"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Ciudad</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} value={field.value ?? ''} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalDescription"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Breve Descripción</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} value={field.value ?? ''} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalQuantity"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Cantidad</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
{...field}
|
||||||
|
value={field.value ?? ''}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="externalUnit"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Unidad</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value ?? undefined}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Unidad" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{[
|
||||||
|
'KG',
|
||||||
|
'TON',
|
||||||
|
'UNID',
|
||||||
|
'LT',
|
||||||
|
'MTS',
|
||||||
|
'QQ',
|
||||||
|
'HM2',
|
||||||
|
'SACOS',
|
||||||
|
].map((unit) => (
|
||||||
|
<SelectItem key={unit} value={unit}>
|
||||||
|
{unit}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Mano de Obra */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Mano de Obra</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="womenCount"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Mujeres (cantidad)</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="number" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="menCount"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Hombres (cantidad)</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="number" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* 3. Detalles de la ubicación */}
|
{/* 3. Detalles de la ubicación */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -963,12 +1154,14 @@ export function CreateTrainingForm({
|
|||||||
name="ospGoogleMapsLink"
|
name="ospGoogleMapsLink"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="col-span-1 lg:col-span-2 flex flex-col space-y-2">
|
<FormItem className="col-span-1 lg:col-span-2 flex flex-col space-y-2">
|
||||||
<FormLabel>Dirección Link Google Maps</FormLabel>
|
<FormLabel>
|
||||||
|
Coordenadas de la Ubicación (Google Maps)
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
value={field.value ?? ''}
|
value={field.value ?? ''}
|
||||||
placeholder="https://maps.google.com/..."
|
placeholder="10.123456, -66.123456"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
import { COUNTRY_OPTIONS } from '@/constants/countries';
|
|
||||||
import {
|
|
||||||
useMunicipalityQuery,
|
|
||||||
useParishQuery,
|
|
||||||
useStateQuery,
|
|
||||||
} from '@/feactures/location/hooks/use-query-location';
|
|
||||||
import { Button } from '@repo/shadcn/button';
|
import { Button } from '@repo/shadcn/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -23,15 +17,6 @@ import {
|
|||||||
} from '@repo/shadcn/components/ui/table';
|
} from '@repo/shadcn/components/ui/table';
|
||||||
import { Input } from '@repo/shadcn/input';
|
import { Input } from '@repo/shadcn/input';
|
||||||
import { Label } from '@repo/shadcn/label';
|
import { Label } from '@repo/shadcn/label';
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@repo/shadcn/select';
|
|
||||||
import { SelectSearchable } from '@repo/shadcn/select-searchable';
|
|
||||||
import { Switch } from '@repo/shadcn/switch';
|
|
||||||
import { Trash2 } from 'lucide-react';
|
import { Trash2 } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
@@ -58,48 +43,8 @@ export function ProductActivityList() {
|
|||||||
dailyCount: '',
|
dailyCount: '',
|
||||||
weeklyCount: '',
|
weeklyCount: '',
|
||||||
monthlyCount: '',
|
monthlyCount: '',
|
||||||
internalDistributionZone: '',
|
|
||||||
|
|
||||||
// Internal dist
|
|
||||||
internalState: null,
|
|
||||||
internalMunicipality: null,
|
|
||||||
internalParish: null,
|
|
||||||
internalDescription: '',
|
|
||||||
internalQuantity: '',
|
|
||||||
internalUnit: '',
|
|
||||||
|
|
||||||
// External dist
|
|
||||||
externalCountry: '',
|
|
||||||
externalState: null,
|
|
||||||
externalMunicipality: null,
|
|
||||||
externalParish: null,
|
|
||||||
externalCity: '',
|
|
||||||
externalDescription: '',
|
|
||||||
externalQuantity: '',
|
|
||||||
externalUnit: '',
|
|
||||||
|
|
||||||
// Workforce
|
|
||||||
womenCount: '',
|
|
||||||
menCount: '',
|
|
||||||
isExporting: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Location logic for Internal Validation
|
|
||||||
const [internalStateId, setInternalStateId] = useState(0);
|
|
||||||
const [internalMuniId, setInternalMuniId] = useState(0);
|
|
||||||
|
|
||||||
const { data: statesData } = useStateQuery();
|
|
||||||
const { data: internalMuniData } = useMunicipalityQuery(internalStateId);
|
|
||||||
const { data: internalParishData } = useParishQuery(internalMuniId);
|
|
||||||
|
|
||||||
// Location logic for External Validation
|
|
||||||
const [externalStateId, setExternalStateId] = useState(0);
|
|
||||||
const [externalMuniId, setExternalMuniId] = useState(0);
|
|
||||||
const { data: externalMuniData } = useMunicipalityQuery(externalStateId);
|
|
||||||
const { data: externalParishData } = useParishQuery(externalMuniId);
|
|
||||||
|
|
||||||
const isVenezuela = newItem.externalCountry === 'Venezuela';
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
if (newItem.description) {
|
if (newItem.description) {
|
||||||
append(newItem);
|
append(newItem);
|
||||||
@@ -108,39 +53,11 @@ export function ProductActivityList() {
|
|||||||
dailyCount: '',
|
dailyCount: '',
|
||||||
weeklyCount: '',
|
weeklyCount: '',
|
||||||
monthlyCount: '',
|
monthlyCount: '',
|
||||||
internalDistributionZone: '',
|
|
||||||
internalState: null,
|
|
||||||
internalMunicipality: null,
|
|
||||||
internalParish: null,
|
|
||||||
internalDescription: '',
|
|
||||||
internalQuantity: '',
|
|
||||||
internalUnit: '',
|
|
||||||
externalCountry: '',
|
|
||||||
externalState: null,
|
|
||||||
externalMunicipality: null,
|
|
||||||
externalParish: null,
|
|
||||||
externalCity: '',
|
|
||||||
externalDescription: '',
|
|
||||||
externalQuantity: '',
|
|
||||||
externalUnit: '',
|
|
||||||
womenCount: '',
|
|
||||||
menCount: '',
|
|
||||||
isExporting: false,
|
|
||||||
});
|
});
|
||||||
setInternalStateId(0);
|
|
||||||
setInternalMuniId(0);
|
|
||||||
setExternalStateId(0);
|
|
||||||
setExternalMuniId(0);
|
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stateOptions = statesData?.data || [];
|
|
||||||
const internalMuniOptions = internalMuniData?.data || [];
|
|
||||||
const internalParishOptions = internalParishData?.data || [];
|
|
||||||
const externalMuniOptions = externalMuniData?.data || [];
|
|
||||||
const externalParishOptions = externalParishData?.data || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@@ -203,209 +120,6 @@ export function ProductActivityList() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
|
||||||
<h4 className="font-semibold">Zona de Distribucción</h4>
|
|
||||||
<div className="grid grid-cols-1 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Breve Descripción de la Zona de Distribucción</Label>
|
|
||||||
<Input
|
|
||||||
value={newItem.internalDistributionZone}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
internalDistributionZone: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Switch
|
|
||||||
id="export-toggle"
|
|
||||||
checked={newItem.isExporting}
|
|
||||||
onCheckedChange={(val: boolean) =>
|
|
||||||
setNewItem({ ...newItem, isExporting: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="export-toggle">
|
|
||||||
¿El producto es para exportación?
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{newItem.isExporting && (
|
|
||||||
<>
|
|
||||||
<h4 className="font-semibold text-sm">
|
|
||||||
Datos de Exportación
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>País</Label>
|
|
||||||
<Select
|
|
||||||
value={newItem.externalCountry}
|
|
||||||
onValueChange={(val) =>
|
|
||||||
setNewItem({ ...newItem, externalCountry: val })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-full">
|
|
||||||
<SelectValue placeholder="Seleccione País" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{COUNTRY_OPTIONS.map((country: string) => (
|
|
||||||
<SelectItem key={country} value={country}>
|
|
||||||
{country}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
{!isVenezuela && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Ciudad</Label>
|
|
||||||
<Input
|
|
||||||
value={newItem.externalCity}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
externalCity: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isVenezuela && (
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Estado</Label>
|
|
||||||
<SelectSearchable
|
|
||||||
options={stateOptions.map((s) => ({
|
|
||||||
value: String(s.id),
|
|
||||||
label: s.name,
|
|
||||||
}))}
|
|
||||||
onValueChange={(val) => {
|
|
||||||
const id = Number(val);
|
|
||||||
setExternalStateId(id);
|
|
||||||
setNewItem({ ...newItem, externalState: id });
|
|
||||||
}}
|
|
||||||
placeholder="Estado"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Municipio</Label>
|
|
||||||
<SelectSearchable
|
|
||||||
options={externalMuniOptions.map((s) => ({
|
|
||||||
value: String(s.id),
|
|
||||||
label: s.name,
|
|
||||||
}))}
|
|
||||||
onValueChange={(val) => {
|
|
||||||
const id = Number(val);
|
|
||||||
setExternalMuniId(id);
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
externalMunicipality: id,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
placeholder="Municipio"
|
|
||||||
disabled={!externalStateId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Parroquia</Label>
|
|
||||||
<SelectSearchable
|
|
||||||
options={externalParishOptions.map((s) => ({
|
|
||||||
value: String(s.id),
|
|
||||||
label: s.name,
|
|
||||||
}))}
|
|
||||||
onValueChange={(val) =>
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
externalParish: Number(val),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="Parroquia"
|
|
||||||
disabled={!externalMuniId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Breve Descripción</Label>
|
|
||||||
<Input
|
|
||||||
value={newItem.externalDescription}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
externalDescription: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Cantidad Numérica</Label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
className="flex-1"
|
|
||||||
value={newItem.externalQuantity}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({
|
|
||||||
...newItem,
|
|
||||||
externalQuantity: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Select
|
|
||||||
value={newItem.externalUnit}
|
|
||||||
onValueChange={(val) =>
|
|
||||||
setNewItem({ ...newItem, externalUnit: val })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-[120px]">
|
|
||||||
<SelectValue placeholder="Unidad" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{UNIT_OPTIONS.map((unit) => (
|
|
||||||
<SelectItem key={unit} value={unit}>
|
|
||||||
{unit}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<h4 className="font-semibold">Mano de Obra</h4>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Mujer (cantidad)</Label>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
value={newItem.womenCount}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({ ...newItem, womenCount: e.target.value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Hombre (cantidad)</Label>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
value={newItem.menCount}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewItem({ ...newItem, menCount: e.target.value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-end gap-4">
|
<div className="flex justify-end gap-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -427,6 +141,8 @@ export function ProductActivityList() {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Producto/Descripción</TableHead>
|
<TableHead>Producto/Descripción</TableHead>
|
||||||
|
<TableHead>Producción Diario</TableHead>
|
||||||
|
<TableHead>Producción Semanal</TableHead>
|
||||||
<TableHead>Producción Mensual</TableHead>
|
<TableHead>Producción Mensual</TableHead>
|
||||||
<TableHead className="w-[50px]"></TableHead>
|
<TableHead className="w-[50px]"></TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -455,75 +171,10 @@ export function ProductActivityList() {
|
|||||||
{...register(`productList.${index}.monthlyCount`)}
|
{...register(`productList.${index}.monthlyCount`)}
|
||||||
defaultValue={field.monthlyCount ?? ''}
|
defaultValue={field.monthlyCount ?? ''}
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(
|
|
||||||
`productList.${index}.internalDistributionZone`,
|
|
||||||
)}
|
|
||||||
defaultValue={field.internalDistributionZone ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.internalQuantity`)}
|
|
||||||
defaultValue={field.internalQuantity ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.internalUnit`)}
|
|
||||||
defaultValue={field.internalUnit ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalCountry`)}
|
|
||||||
defaultValue={field.externalCountry ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalState`)}
|
|
||||||
defaultValue={field.externalState ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalMunicipality`)}
|
|
||||||
defaultValue={field.externalMunicipality ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalParish`)}
|
|
||||||
defaultValue={field.externalParish ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalCity`)}
|
|
||||||
defaultValue={field.externalCity ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalDescription`)}
|
|
||||||
defaultValue={field.externalDescription ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalQuantity`)}
|
|
||||||
defaultValue={field.externalQuantity ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.externalUnit`)}
|
|
||||||
defaultValue={field.externalUnit ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.womenCount`)}
|
|
||||||
defaultValue={field.womenCount ?? ''}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
{...register(`productList.${index}.menCount`)}
|
|
||||||
defaultValue={field.menCount ?? ''}
|
|
||||||
/>
|
|
||||||
{field.description}
|
{field.description}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>{field.dailyCount}</TableCell>
|
||||||
|
<TableCell>{field.weeklyCount}</TableCell>
|
||||||
<TableCell>{field.monthlyCount}</TableCell>
|
<TableCell>{field.monthlyCount}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@repo/shadcn/components/ui/dialog';
|
} from '@repo/shadcn/components/ui/dialog';
|
||||||
import { ScrollArea } from '@repo/shadcn/components/ui/scroll-area';
|
import { ScrollArea } from '@repo/shadcn/components/ui/scroll-area';
|
||||||
import { Separator } from '@repo/shadcn/components/ui/separator';
|
|
||||||
import {
|
import {
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Factory,
|
Factory,
|
||||||
@@ -129,10 +128,7 @@ export function TrainingViewModal({
|
|||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{/* 1. Datos de la Visita */}
|
{/* 1. Datos de la Visita */}
|
||||||
<Section title="Datos de la Visita">
|
<Section title="Datos de la Visita">
|
||||||
<DetailItem
|
<DetailItem label="Coordinador" value={data.coorFullName} />
|
||||||
label="Coordinador"
|
|
||||||
value={`${data.firstname} ${data.lastname}`}
|
|
||||||
/>
|
|
||||||
<DetailItem label="Teléfono Coord." value={data.coorPhone} />
|
<DetailItem label="Teléfono Coord." value={data.coorPhone} />
|
||||||
<DetailItem
|
<DetailItem
|
||||||
label="Fecha Visita"
|
label="Fecha Visita"
|
||||||
@@ -209,7 +205,11 @@ export function TrainingViewModal({
|
|||||||
className="gap-2"
|
className="gap-2"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={data.ospGoogleMapsLink}
|
href={
|
||||||
|
data.ospGoogleMapsLink.startsWith('http')
|
||||||
|
? data.ospGoogleMapsLink
|
||||||
|
: `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(data.ospGoogleMapsLink)}`
|
||||||
|
}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
@@ -229,80 +229,36 @@ export function TrainingViewModal({
|
|||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<CardTitle className="text-lg flex items-center gap-2">
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
<Package className="h-5 w-5" />
|
<Package className="h-5 w-5" />
|
||||||
Productos y Mano de Obra
|
Productos Registrados
|
||||||
<Badge variant="secondary" className="ml-2">
|
<Badge variant="secondary" className="ml-2">
|
||||||
{data.productList?.length || 0}
|
{data.productList?.length || 0}
|
||||||
</Badge>
|
</Badge>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-4">
|
<CardContent className="grid gap-4">
|
||||||
{data.productList?.map((prod: any, idx: number) => (
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div
|
{data.productList?.map((prod: any, idx: number) => (
|
||||||
key={idx}
|
<div
|
||||||
className="bg-muted/40 p-4 rounded-lg border text-sm"
|
key={idx}
|
||||||
>
|
className="bg-muted/40 p-4 rounded-lg border text-sm"
|
||||||
<div className="flex justify-between items-start mb-2">
|
>
|
||||||
<h4 className="font-bold text-base text-primary">
|
<h4 className="font-bold text-base text-primary mb-2">
|
||||||
{prod.productName}
|
{prod.description}
|
||||||
</h4>
|
</h4>
|
||||||
<Badge variant="outline">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
Mano de obra:{' '}
|
<DetailItem label="Diario" value={prod.dailyCount} />
|
||||||
{Number(prod.menCount || 0) +
|
<DetailItem
|
||||||
Number(prod.womenCount || 0)}
|
label="Semanal"
|
||||||
</Badge>
|
value={prod.weeklyCount}
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Mensual"
|
||||||
|
value={prod.monthlyCount}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-muted-foreground mb-3">
|
))}
|
||||||
{prod.description}
|
</div>
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-3">
|
|
||||||
<DetailItem label="Diario" value={prod.dailyCount} />
|
|
||||||
<DetailItem label="Semanal" value={prod.weeklyCount} />
|
|
||||||
<DetailItem label="Mensual" value={prod.monthlyCount} />
|
|
||||||
<DetailItem
|
|
||||||
label="Hombres / Mujeres"
|
|
||||||
value={`${prod.menCount || 0} / ${prod.womenCount || 0}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Detalles de distribución si existen */}
|
|
||||||
{(prod.internalQuantity || prod.externalQuantity) && (
|
|
||||||
<>
|
|
||||||
<Separator className="my-2" />
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
{prod.internalQuantity && (
|
|
||||||
<div>
|
|
||||||
<span className="text-xs font-bold text-muted-foreground block mb-1">
|
|
||||||
DISTRIBUCIÓN INTERNA
|
|
||||||
</span>
|
|
||||||
<p>
|
|
||||||
Cant: {prod.internalQuantity}{' '}
|
|
||||||
{prod.internalUnit}
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{prod.internalDescription}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{prod.externalQuantity && (
|
|
||||||
<div>
|
|
||||||
<span className="text-xs font-bold text-muted-foreground block mb-1">
|
|
||||||
EXPORTACIÓN ({prod.externalCountry})
|
|
||||||
</span>
|
|
||||||
<p>
|
|
||||||
Cant: {prod.externalQuantity}{' '}
|
|
||||||
{prod.externalUnit}
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{prod.externalDescription}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{(!data.productList || data.productList.length === 0) && (
|
{(!data.productList || data.productList.length === 0) && (
|
||||||
<p className="text-sm text-muted-foreground italic">
|
<p className="text-sm text-muted-foreground italic">
|
||||||
No hay productos registrados.
|
No hay productos registrados.
|
||||||
@@ -311,6 +267,64 @@ export function TrainingViewModal({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* DISTRIBUCIÓN, EXPORTACIÓN Y MANO DE OBRA */}
|
||||||
|
<Section title="Distribución, Exportación y Mano de Obra">
|
||||||
|
<div className="col-span-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h4 className="text-sm font-bold border-b pb-1">
|
||||||
|
Distribución Interna
|
||||||
|
</h4>
|
||||||
|
<DetailItem
|
||||||
|
label="Zona de Distribución"
|
||||||
|
value={data.internalDistributionZone}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h4 className="text-sm font-bold border-b pb-1">
|
||||||
|
Mano de Obra
|
||||||
|
</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<DetailItem label="Mujeres" value={data.womenCount} />
|
||||||
|
<DetailItem label="Hombres" value={data.menCount} />
|
||||||
|
<DetailItem
|
||||||
|
label="Total"
|
||||||
|
value={
|
||||||
|
Number(data.womenCount || 0) +
|
||||||
|
Number(data.menCount || 0)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h4 className="text-sm font-bold border-b pb-1 flex items-center gap-2">
|
||||||
|
Exportación <BooleanBadge value={data.isExporting} />
|
||||||
|
</h4>
|
||||||
|
{data.isExporting && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<DetailItem label="País" value={data.externalCountry} />
|
||||||
|
<DetailItem label="Ciudad" value={data.externalCity} />
|
||||||
|
<DetailItem
|
||||||
|
label="Descripción"
|
||||||
|
value={data.externalDescription}
|
||||||
|
/>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<DetailItem
|
||||||
|
label="Cantidad"
|
||||||
|
value={data.externalQuantity}
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Unidad"
|
||||||
|
value={data.externalUnit}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
{/* EQUIPAMIENTO Y PRODUCCIÓN */}
|
{/* EQUIPAMIENTO Y PRODUCCIÓN */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
|
|||||||
@@ -7,25 +7,6 @@ const productItemSchema = z.object({
|
|||||||
dailyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
dailyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
||||||
weeklyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
weeklyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
||||||
monthlyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
monthlyCount: z.coerce.string().or(z.number()).optional().nullable(),
|
||||||
|
|
||||||
// Distribución Interna
|
|
||||||
internalDistributionZone: z.string().optional().nullable(),
|
|
||||||
internalQuantity: z.coerce.string().or(z.number()).optional().nullable(),
|
|
||||||
internalUnit: z.string().optional().nullable(),
|
|
||||||
|
|
||||||
// Distribución Externa
|
|
||||||
externalCountry: z.string().optional().nullable(),
|
|
||||||
externalState: z.number().optional().nullable(),
|
|
||||||
externalMunicipality: z.number().optional().nullable(),
|
|
||||||
externalParish: z.number().optional().nullable(),
|
|
||||||
externalCity: z.string().optional().nullable(),
|
|
||||||
externalDescription: z.string().optional().nullable(),
|
|
||||||
externalQuantity: z.coerce.string().or(z.number()).optional().nullable(),
|
|
||||||
externalUnit: z.string().optional().nullable(),
|
|
||||||
|
|
||||||
// Mano de obra
|
|
||||||
womenCount: z.coerce.string().or(z.number()).optional().nullable(),
|
|
||||||
menCount: z.coerce.string().or(z.number()).optional().nullable(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const productionItemSchema = z.object({
|
const productionItemSchema = z.object({
|
||||||
@@ -42,8 +23,9 @@ const equipmentItemSchema = z.object({
|
|||||||
export const trainingSchema = z.object({
|
export const trainingSchema = z.object({
|
||||||
//Datos de la visita
|
//Datos de la visita
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
firstname: z.string().min(1, { message: 'Nombre es requerido' }),
|
coorFullName: z
|
||||||
lastname: z.string().min(1, { message: 'Apellido es requerido' }),
|
.string()
|
||||||
|
.min(1, { message: 'Nombre del coordinador es requerido' }),
|
||||||
coorPhone: z
|
coorPhone: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -76,14 +58,22 @@ export const trainingSchema = z.object({
|
|||||||
.default('ACTIVA'),
|
.default('ACTIVA'),
|
||||||
infrastructureMt2: z.string().optional().or(z.literal('')).nullable(),
|
infrastructureMt2: z.string().optional().or(z.literal('')).nullable(),
|
||||||
hasTransport: z
|
hasTransport: z
|
||||||
.preprocess((val) => val === 'true' || val === true, z.boolean())
|
.preprocess(
|
||||||
|
(val) => val === 'true' || val === true || val === 1 || val === '1',
|
||||||
|
z.boolean(),
|
||||||
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.nullable(),
|
.nullable()
|
||||||
|
.default(false),
|
||||||
structureType: z.string().optional().or(z.literal('')).nullable(),
|
structureType: z.string().optional().or(z.literal('')).nullable(),
|
||||||
isOpenSpace: z
|
isOpenSpace: z
|
||||||
.preprocess((val) => val === 'true' || val === true, z.boolean())
|
.preprocess(
|
||||||
|
(val) => val === 'true' || val === true || val === 1 || val === '1',
|
||||||
|
z.boolean(),
|
||||||
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.nullable(),
|
.nullable()
|
||||||
|
.default(false),
|
||||||
paralysisReason: z.string().optional().nullable(),
|
paralysisReason: z.string().optional().nullable(),
|
||||||
|
|
||||||
//Datos del Equipamiento
|
//Datos del Equipamiento
|
||||||
@@ -95,6 +85,31 @@ export const trainingSchema = z.object({
|
|||||||
// Datos de Actividad Productiva
|
// Datos de Actividad Productiva
|
||||||
productList: z.array(productItemSchema).optional().default([]),
|
productList: z.array(productItemSchema).optional().default([]),
|
||||||
|
|
||||||
|
// Distribución y Exportación
|
||||||
|
internalDistributionZone: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: 'Zona de distribución es requerida' }),
|
||||||
|
isExporting: z
|
||||||
|
.preprocess(
|
||||||
|
(val) => val === 'true' || val === true || val === 1 || val === '1',
|
||||||
|
z.boolean(),
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.default(false),
|
||||||
|
externalCountry: z.string().optional().nullable(),
|
||||||
|
externalCity: z.string().optional().nullable(),
|
||||||
|
externalDescription: z.string().optional().nullable(),
|
||||||
|
externalQuantity: z.coerce.string().or(z.number()).optional().nullable(),
|
||||||
|
externalUnit: z.string().optional().nullable(),
|
||||||
|
|
||||||
|
// Mano de obra
|
||||||
|
womenCount: z.coerce
|
||||||
|
.number()
|
||||||
|
.min(0, { message: 'Cantidad de mujeres es requerida' }),
|
||||||
|
menCount: z.coerce
|
||||||
|
.number()
|
||||||
|
.min(0, { message: 'Cantidad de hombres es requerida' }),
|
||||||
|
|
||||||
//Detalles de la ubicación
|
//Detalles de la ubicación
|
||||||
ospAddress: z
|
ospAddress: z
|
||||||
.string()
|
.string()
|
||||||
|
|||||||
Reference in New Issue
Block a user