From efa17262230e7707abdb74d1743ec08b2f972818 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 1 Dec 2025 18:23:18 -0400 Subject: [PATCH 1/4] formulario de capacitacion --- apps/api/src/app.module.ts | 10 +- .../database/migrations/0008_plain_scream.sql | 37 + .../migrations/meta/0008_snapshot.json | 1778 +++++++++++++++++ .../database/migrations/meta/_journal.json | 7 + apps/api/src/database/schema/surveys.ts | 42 +- apps/api/src/features/auth/auth.service.ts | 144 +- .../features/pictures/pictures.controller.ts | 19 - .../src/features/pictures/pictures.module.ts | 10 - .../src/features/pictures/pictures.service.ts | 41 - .../training/dto/create-training.dto.ts | 124 ++ .../training/dto/update-training.dto.ts | 4 + .../features/training/training.controller.ts | 58 + .../src/features/training/training.module.ts | 10 + .../src/features/training/training.service.ts | 118 ++ .../configuraciones/caja-ahorro/page.tsx | 19 - apps/web/app/dashboard/formulario/page.tsx | 15 + apps/web/components/layout/app-sidebar.tsx | 18 +- apps/web/constants/{data.ts => routes.ts} | 18 +- .../web/feactures/training/actions/actions.ts | 148 ++ .../training/actions/training-actions.ts | 94 + .../feactures/training/components/form.tsx | 431 ++++ .../training/hooks/use-mutation-users.ts | 45 + .../feactures/training/hooks/use-training.ts | 29 + .../feactures/training/schemas/training.ts | 60 + apps/web/feactures/training/schemas/users.ts | 67 + 25 files changed, 3165 insertions(+), 181 deletions(-) create mode 100644 apps/api/src/database/migrations/0008_plain_scream.sql create mode 100644 apps/api/src/database/migrations/meta/0008_snapshot.json delete mode 100644 apps/api/src/features/pictures/pictures.controller.ts delete mode 100644 apps/api/src/features/pictures/pictures.module.ts delete mode 100644 apps/api/src/features/pictures/pictures.service.ts create mode 100644 apps/api/src/features/training/dto/create-training.dto.ts create mode 100644 apps/api/src/features/training/dto/update-training.dto.ts create mode 100644 apps/api/src/features/training/training.controller.ts create mode 100644 apps/api/src/features/training/training.module.ts create mode 100644 apps/api/src/features/training/training.service.ts delete mode 100644 apps/web/app/dashboard/configuraciones/caja-ahorro/page.tsx create mode 100644 apps/web/app/dashboard/formulario/page.tsx rename apps/web/constants/{data.ts => routes.ts} (77%) create mode 100644 apps/web/feactures/training/actions/actions.ts create mode 100644 apps/web/feactures/training/actions/training-actions.ts create mode 100644 apps/web/feactures/training/components/form.tsx create mode 100644 apps/web/feactures/training/hooks/use-mutation-users.ts create mode 100644 apps/web/feactures/training/hooks/use-training.ts create mode 100644 apps/web/feactures/training/schemas/training.ts create mode 100644 apps/web/feactures/training/schemas/users.ts diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index e73e01d..5914eb0 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -13,13 +13,13 @@ import { ThrottlerGuard } from '@nestjs/throttler'; import { DrizzleModule } from './database/drizzle.module'; import { AuthModule } from './features/auth/auth.module'; import { ConfigurationsModule } from './features/configurations/configurations.module'; -import { LocationModule} from './features/location/location.module' +import { LocationModule } from './features/location/location.module' import { MailModule } from './features/mail/mail.module'; import { RolesModule } from './features/roles/roles.module'; import { UserRolesModule } from './features/user-roles/user-roles.module'; import { SurveysModule } from './features/surveys/surveys.module'; -import {InventoryModule} from './features/inventory/inventory.module' -import { PicturesModule } from './features/pictures/pictures.module'; +import { InventoryModule } from './features/inventory/inventory.module'; +import { TrainingModule } from './features/training/training.module'; @Module({ providers: [ @@ -61,7 +61,7 @@ import { PicturesModule } from './features/pictures/pictures.module'; SurveysModule, LocationModule, InventoryModule, - PicturesModule + TrainingModule ], }) -export class AppModule {} +export class AppModule { } diff --git a/apps/api/src/database/migrations/0008_plain_scream.sql b/apps/api/src/database/migrations/0008_plain_scream.sql new file mode 100644 index 0000000..58fef4f --- /dev/null +++ b/apps/api/src/database/migrations/0008_plain_scream.sql @@ -0,0 +1,37 @@ +CREATE TABLE "training_surveys" ( + "id" serial PRIMARY KEY NOT NULL, + "firstname" text NOT NULL, + "lastname" text NOT NULL, + "visit_date" timestamp NOT NULL, + "productive_activity" text NOT NULL, + "financial_requirement_description" text NOT NULL, + "situr_code_commune" text NOT NULL, + "communal_council" text NOT NULL, + "situr_code_communal_council" text NOT NULL, + "osp_name" text NOT NULL, + "osp_address" text NOT NULL, + "osp_rif" text NOT NULL, + "osp_type" text NOT NULL, + "current_status" text NOT NULL, + "company_constitution_year" integer NOT NULL, + "producer_count" integer NOT NULL, + "product_description" text NOT NULL, + "installed_capacity" text NOT NULL, + "operational_capacity" text NOT NULL, + "osp_responsible_fullname" text NOT NULL, + "osp_responsible_cedula" text NOT NULL, + "osp_responsible_rif" text NOT NULL, + "osp_responsible_phone" text NOT NULL, + "civil_state" text NOT NULL, + "family_burden" integer NOT NULL, + "number_of_children" integer NOT NULL, + "general_observations" text NOT NULL, + "photo1" text NOT NULL, + "photo2" text NOT NULL, + "photo3" text NOT NULL, + "paralysis_reason" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp (3) +); +--> statement-breakpoint +CREATE INDEX "training_surveys_index_00" ON "training_surveys" USING btree ("firstname"); \ No newline at end of file diff --git a/apps/api/src/database/migrations/meta/0008_snapshot.json b/apps/api/src/database/migrations/meta/0008_snapshot.json new file mode 100644 index 0000000..d2f6dde --- /dev/null +++ b/apps/api/src/database/migrations/meta/0008_snapshot.json @@ -0,0 +1,1778 @@ +{ + "id": "3680a83d-f6d6-408a-bb21-6a54da326385", + "prevId": "c8138b84-b544-4a39-8cb7-b5c5fb558092", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.activity_logs": { + "name": "activity_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "activityLogs_idx": { + "name": "activityLogs_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_logs_user_id_users_id_fk": { + "name": "activity_logs_user_id_users_id_fk", + "tableFrom": "activity_logs", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "auth.roles": { + "name": "roles", + "schema": "auth", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "roles_idx": { + "name": "roles_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "auth.sessions": { + "name": "sessions", + "schema": "auth", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "session_token": { + "name": "session_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "sessions_idx": { + "name": "sessions_idx", + "columns": [ + { + "expression": "session_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "auth.users": { + "name": "users", + "schema": "auth", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullname": { + "name": "fullname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "municipality": { + "name": "municipality", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parish": { + "name": "parish", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_two_factor_enabled": { + "name": "is_two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "two_factor_secret": { + "name": "two_factor_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_email_verified": { + "name": "is_email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_idx": { + "name": "users_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_state_states_id_fk": { + "name": "users_state_states_id_fk", + "tableFrom": "users", + "tableTo": "states", + "columnsFrom": [ + "state" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "users_municipality_municipalities_id_fk": { + "name": "users_municipality_municipalities_id_fk", + "tableFrom": "users", + "tableTo": "municipalities", + "columnsFrom": [ + "municipality" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "users_parish_parishes_id_fk": { + "name": "users_parish_parishes_id_fk", + "tableFrom": "users", + "tableTo": "parishes", + "columnsFrom": [ + "parish" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "auth.user_role": { + "name": "user_role", + "schema": "auth", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "role_id": { + "name": "role_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_role_idx": { + "name": "user_role_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_role_user_id_users_id_fk": { + "name": "user_role_user_id_users_id_fk", + "tableFrom": "user_role", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_role_role_id_roles_id_fk": { + "name": "user_role_role_id_roles_id_fk", + "tableFrom": "user_role", + "tableTo": "roles", + "schemaTo": "auth", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "auth.verificationToken": { + "name": "verificationToken", + "schema": "auth", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.category_type": { + "name": "category_type", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "group": { + "name": "group", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "category_typeIx0": { + "name": "category_typeIx0", + "columns": [ + { + "expression": "group", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "category_typeIx1": { + "name": "category_typeIx1", + "columns": [ + { + "expression": "description", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.localities": { + "name": "localities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "municipality_id": { + "name": "municipality_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parish_id": { + "name": "parish_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "localities_index_03": { + "name": "localities_index_03", + "columns": [ + { + "expression": "state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "municipality_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parish_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "localities_index_00": { + "name": "localities_index_00", + "columns": [ + { + "expression": "state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "localities_index_01": { + "name": "localities_index_01", + "columns": [ + { + "expression": "municipality_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "localities_index_02": { + "name": "localities_index_02", + "columns": [ + { + "expression": "parish_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "localities_state_id_states_id_fk": { + "name": "localities_state_id_states_id_fk", + "tableFrom": "localities", + "tableTo": "states", + "columnsFrom": [ + "state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "localities_municipality_id_municipalities_id_fk": { + "name": "localities_municipality_id_municipalities_id_fk", + "tableFrom": "localities", + "tableTo": "municipalities", + "columnsFrom": [ + "municipality_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "localities_parish_id_parishes_id_fk": { + "name": "localities_parish_id_parishes_id_fk", + "tableFrom": "localities", + "tableTo": "parishes", + "columnsFrom": [ + "parish_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "localities_name_unique": { + "name": "localities_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.municipalities": { + "name": "municipalities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "municipalities_index_00": { + "name": "municipalities_index_00", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "municipalities_state_id_states_id_fk": { + "name": "municipalities_state_id_states_id_fk", + "tableFrom": "municipalities", + "tableTo": "states", + "columnsFrom": [ + "state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.parishes": { + "name": "parishes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "municipality_id": { + "name": "municipality_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "parishes_index_00": { + "name": "parishes_index_00", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "municipality_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "parishes_municipality_id_municipalities_id_fk": { + "name": "parishes_municipality_id_municipalities_id_fk", + "tableFrom": "parishes", + "tableTo": "municipalities", + "columnsFrom": [ + "municipality_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.states": { + "name": "states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "states_index_00": { + "name": "states_index_00", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "stock": { + "name": "stock", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url_img": { + "name": "url_img", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gallery": { + "name": "gallery", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'BORRADOR'" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "products_user_id_users_id_fk": { + "name": "products_user_id_users_id_fk", + "tableFrom": "products", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.answers_surveys": { + "name": "answers_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "survey_id": { + "name": "survey_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "answers": { + "name": "answers", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "answers_index_00": { + "name": "answers_index_00", + "columns": [ + { + "expression": "answers", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "answers_index_01": { + "name": "answers_index_01", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "answers_index_02": { + "name": "answers_index_02", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "answers_surveys_survey_id_surveys_id_fk": { + "name": "answers_surveys_survey_id_surveys_id_fk", + "tableFrom": "answers_surveys", + "tableTo": "surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "answers_surveys_user_id_users_id_fk": { + "name": "answers_surveys_user_id_users_id_fk", + "tableFrom": "answers_surveys", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.surveys": { + "name": "surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_audience": { + "name": "target_audience", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "closing_date": { + "name": "closing_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "published": { + "name": "published", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "questions": { + "name": "questions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "surveys_index_00": { + "name": "surveys_index_00", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.training_surveys": { + "name": "training_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "firstname": { + "name": "firstname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastname": { + "name": "lastname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visit_date": { + "name": "visit_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "productive_activity": { + "name": "productive_activity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "financial_requirement_description": { + "name": "financial_requirement_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "situr_code_commune": { + "name": "situr_code_commune", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "communal_council": { + "name": "communal_council", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "situr_code_communal_council": { + "name": "situr_code_communal_council", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_name": { + "name": "osp_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_address": { + "name": "osp_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_rif": { + "name": "osp_rif", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_type": { + "name": "osp_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_status": { + "name": "current_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "company_constitution_year": { + "name": "company_constitution_year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "producer_count": { + "name": "producer_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "product_description": { + "name": "product_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installed_capacity": { + "name": "installed_capacity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operational_capacity": { + "name": "operational_capacity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_responsible_fullname": { + "name": "osp_responsible_fullname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_responsible_cedula": { + "name": "osp_responsible_cedula", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_responsible_rif": { + "name": "osp_responsible_rif", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "osp_responsible_phone": { + "name": "osp_responsible_phone", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "civil_state": { + "name": "civil_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "family_burden": { + "name": "family_burden", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number_of_children": { + "name": "number_of_children", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "general_observations": { + "name": "general_observations", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo1": { + "name": "photo1", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo2": { + "name": "photo2", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo3": { + "name": "photo3", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "paralysis_reason": { + "name": "paralysis_reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "training_surveys_index_00": { + "name": "training_surveys_index_00", + "columns": [ + { + "expression": "firstname", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "auth.gender": { + "name": "gender", + "schema": "auth", + "values": [ + "FEMENINO", + "MASCULINO" + ] + }, + "public.nationality": { + "name": "nationality", + "schema": "public", + "values": [ + "VENEZOLANO", + "EXTRANJERO" + ] + }, + "auth.status": { + "name": "status", + "schema": "auth", + "values": [ + "ACTIVE", + "INACTIVE" + ] + } + }, + "schemas": { + "auth": "auth" + }, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "auth.user_access_view": { + "columns": { + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "role_name": { + "name": "role_name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n SELECT\n u.id AS user_id,\n u.username,\n u.email,\n u.fullname,\n r.id AS role_id,\n r.name AS role_name\nFROM\n auth.users u\nLEFT JOIN\n auth.user_role ur ON u.id = ur.user_id \nLEFT JOIN\n auth.roles r ON ur.role_id = r.id", + "name": "user_access_view", + "schema": "auth", + "isExisting": false, + "materialized": false + }, + "public.v_product_store": { + "columns": { + "product_id": { + "name": "product_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "stock": { + "name": "stock", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "url_img": { + "name": "url_img", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gallery": { + "name": "gallery", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "fullname": { + "name": "fullname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n select p.id as product_id, p.title, p.description, p.price, p.stock, p.url_img, p.gallery, p.address, p.status, p.user_id, u.fullname, u.email, u.phone\n from products p\n left join auth.users as u on u.id = p.user_id", + "name": "v_product_store", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.v_surveys": { + "columns": { + "survey_id": { + "name": "survey_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "closing_date": { + "name": "closing_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "target_audience": { + "name": "target_audience", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select id as survey_id, title, description, created_at, closing_date, target_audience from surveys\nwhere published = true", + "name": "v_surveys", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/src/database/migrations/meta/_journal.json b/apps/api/src/database/migrations/meta/_journal.json index 9e4acf0..5658ebb 100644 --- a/apps/api/src/database/migrations/meta/_journal.json +++ b/apps/api/src/database/migrations/meta/_journal.json @@ -57,6 +57,13 @@ "when": 1754420096323, "tag": "0007_curved_fantastic_four", "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1764623430844, + "tag": "0008_plain_scream", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/api/src/database/schema/surveys.ts b/apps/api/src/database/schema/surveys.ts index ccbede3..b5eb413 100644 --- a/apps/api/src/database/schema/surveys.ts +++ b/apps/api/src/database/schema/surveys.ts @@ -44,7 +44,47 @@ export const answersSurveys = t.pgTable( }), ); - +// Tabla training_surveys +export const trainingSurveys = t.pgTable( + 'training_surveys', + { + id: t.serial('id').primaryKey(), + firstname: t.text('firstname').notNull(), + lastname: t.text('lastname').notNull(), + visitDate: t.timestamp('visit_date').notNull(), + productiveActivity: t.text('productive_activity').notNull(), + financialRequirementDescription: t.text('financial_requirement_description').notNull(), + siturCodeCommune: t.text('situr_code_commune').notNull(), + communalCouncil: t.text('communal_council').notNull(), + siturCodeCommunalCouncil: t.text('situr_code_communal_council').notNull(), + ospName: t.text('osp_name').notNull(), + ospAddress: t.text('osp_address').notNull(), + ospRif: t.text('osp_rif').notNull(), + ospType: t.text('osp_type').notNull(), + currentStatus: t.text('current_status').notNull(), + companyConstitutionYear: t.integer('company_constitution_year').notNull(), + producerCount: t.integer('producer_count').notNull(), + productDescription: t.text('product_description').notNull(), + installedCapacity: t.text('installed_capacity').notNull(), + operationalCapacity: t.text('operational_capacity').notNull(), + ospResponsibleFullname: t.text('osp_responsible_fullname').notNull(), + ospResponsibleCedula: t.text('osp_responsible_cedula').notNull(), + ospResponsibleRif: t.text('osp_responsible_rif').notNull(), + ospResponsiblePhone: t.text('osp_responsible_phone').notNull(), + civilState: t.text('civil_state').notNull(), + familyBurden: t.integer('family_burden').notNull(), + numberOfChildren: t.integer('number_of_children').notNull(), + generalObservations: t.text('general_observations').notNull(), + photo1: t.text('photo1').notNull(), + photo2: t.text('photo2').notNull(), + photo3: t.text('photo3').notNull(), + paralysisReason: t.text('paralysis_reason').notNull(), + ...timestamps, + }, + (trainingSurveys) => ({ + trainingSurveysIndex: t.index('training_surveys_index_00').on(trainingSurveys.firstname), + }) +); export const viewSurveys = t.pgView('v_surveys', { surverId: t.integer('survey_id'), diff --git a/apps/api/src/features/auth/auth.service.ts b/apps/api/src/features/auth/auth.service.ts index 833166c..d05c149 100644 --- a/apps/api/src/features/auth/auth.service.ts +++ b/apps/api/src/features/auth/auth.service.ts @@ -40,7 +40,7 @@ export class AuthService { private readonly config: ConfigService, @Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase, private readonly mailService: MailService, - ) {} + ) { } //Decode Tokens // Método para decodificar el token y obtener los datos completos @@ -89,7 +89,7 @@ export class AuthService { }, { secret: envs.access_token_secret, - expiresIn: envs.access_token_expiration, + expiresIn: envs.access_token_expiration as any, }, ), this.jwtService.signAsync( @@ -99,7 +99,7 @@ export class AuthService { }, { secret: envs.refresh_token_secret, - expiresIn: envs.refresh_token_expiration, + expiresIn: envs.refresh_token_expiration as any, }, ), ]); @@ -197,7 +197,7 @@ export class AuthService { //Sign In User Account async signIn(dto: SignInUserDto): Promise { - + const user = await this.validateUser(dto); const tokens = await this.generateTokens(user); const decodeAccess = this.decodeToken(tokens.access_token); @@ -267,7 +267,7 @@ export class AuthService { // const user_id = 1; const validation = await this.jwtService.verifyAsync(refresh_token, { - secret: envs.refresh_token_secret, + secret: envs.refresh_token_secret, }); if (!validation) throw new UnauthorizedException('Invalid refresh token'); @@ -278,21 +278,21 @@ export class AuthService { .where( and( eq(sessions.userId, user_id) && - eq(sessions.sessionToken, dto.refresh_token), + eq(sessions.sessionToken, dto.refresh_token), ), ); // console.log(session.length); - + if (session.length === 0) throw new NotFoundException('session not found'); const user = await this.findUserById(user_id); if (!user) throw new NotFoundException('User not found'); - + // Genera token const tokens = await this.generateTokens(user); const decodeAccess = this.decodeToken(tokens.access_token); const decodeRefresh = this.decodeToken(tokens.refresh_token); - + // Actualiza session await this.drizzle .update(sessions) @@ -308,75 +308,75 @@ export class AuthService { } async singUp(createUserDto: SingUpUserDto): Promise { - // Check if username or email exists - const data = await this.drizzle + // Check if username or email exists + const data = await this.drizzle + .select({ + id: users.id, + username: users.username, + email: users.email + }) + .from(users) + .where(or(eq(users.username, createUserDto.username), eq(users.email, createUserDto.email))); + + if (data.length > 0) { + if (data[0].username === createUserDto.username) { + throw new HttpException('Username already exists', HttpStatus.BAD_REQUEST); + } + if (data[0].email === createUserDto.email) { + throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST); + } + } + + // Hash the password + const hashedPassword = await bcrypt.hash(createUserDto.password, 10); + + // Start a transaction + return await this.drizzle.transaction(async (tx) => { + // Create the user + const [newUser] = await tx + .insert(users) + .values({ + username: createUserDto.username, + email: createUserDto.email, + password: hashedPassword, + fullname: createUserDto.fullname, + isActive: true, + state: createUserDto.state, + municipality: createUserDto.municipality, + parish: createUserDto.parish, + phone: createUserDto.phone, + isEmailVerified: false, + isTwoFactorEnabled: false, + }) + .returning(); + + // check if user role is admin + const role = createUserDto.role <= 2 ? 5 : createUserDto.role; + + // Assign role to user + await tx.insert(usersRole).values({ + userId: newUser.id, + roleId: role, + }); + + // Return the created user with role + const [userWithRole] = await tx .select({ id: users.id, username: users.username, - email: users.email + email: users.email, + fullname: users.fullname, + phone: users.phone, + isActive: users.isActive, + role: roles.name, }) .from(users) - .where(or(eq(users.username, createUserDto.username), eq(users.email, createUserDto.email))); - - if (data.length > 0) { - if (data[0].username === createUserDto.username) { - throw new HttpException('Username already exists', HttpStatus.BAD_REQUEST); - } - if (data[0].email === createUserDto.email) { - throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST); - } - } + .leftJoin(usersRole, eq(usersRole.userId, users.id)) + .leftJoin(roles, eq(roles.id, usersRole.roleId)) + .where(eq(users.id, newUser.id)); - // Hash the password - const hashedPassword = await bcrypt.hash(createUserDto.password, 10); - - // Start a transaction - return await this.drizzle.transaction(async (tx) => { - // Create the user - const [newUser] = await tx - .insert(users) - .values({ - username: createUserDto.username, - email: createUserDto.email, - password: hashedPassword, - fullname: createUserDto.fullname, - isActive: true, - state: createUserDto.state, - municipality: createUserDto.municipality, - parish: createUserDto.parish, - phone: createUserDto.phone, - isEmailVerified: false, - isTwoFactorEnabled: false, - }) - .returning(); + return userWithRole; + }) - // check if user role is admin - const role = createUserDto.role <= 2 ? 5 : createUserDto.role; - - // Assign role to user - await tx.insert(usersRole).values({ - userId: newUser.id, - roleId: role, - }); - - // Return the created user with role - const [userWithRole] = await tx - .select({ - id: users.id, - username: users.username, - email: users.email, - fullname: users.fullname, - phone: users.phone, - isActive: users.isActive, - role: roles.name, - }) - .from(users) - .leftJoin(usersRole, eq(usersRole.userId, users.id)) - .leftJoin(roles, eq(roles.id, usersRole.roleId)) - .where(eq(users.id, newUser.id)); - - return userWithRole; - }) - } } diff --git a/apps/api/src/features/pictures/pictures.controller.ts b/apps/api/src/features/pictures/pictures.controller.ts deleted file mode 100644 index ee54831..0000000 --- a/apps/api/src/features/pictures/pictures.controller.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Controller, Post, UploadedFiles, UseInterceptors, Body } from '@nestjs/common'; -import { FilesInterceptor } from '@nestjs/platform-express'; -import { PicturesService } from './pictures.service'; - -@Controller('pictures') -export class PicturesController { - constructor(private readonly picturesService: PicturesService) {} - - @Post('upload') - @UseInterceptors(FilesInterceptor('urlImg')) - async uploadFile(@UploadedFiles() files: Express.Multer.File[], @Body() body: any) { - // Aquí puedes acceder a los campos del formulario - // console.log('Archivos:', files); - // console.log('Otros campos del formulario:', body); - const result = await this.picturesService.saveImages(files); - - return { data: result }; - } -} \ No newline at end of file diff --git a/apps/api/src/features/pictures/pictures.module.ts b/apps/api/src/features/pictures/pictures.module.ts deleted file mode 100644 index 274177c..0000000 --- a/apps/api/src/features/pictures/pictures.module.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import { Module } from '@nestjs/common'; -import { PicturesController } from './pictures.controller'; -import { PicturesService } from './pictures.service'; - -@Module({ - controllers: [PicturesController], - providers: [PicturesService], -}) -export class PicturesModule {} diff --git a/apps/api/src/features/pictures/pictures.service.ts b/apps/api/src/features/pictures/pictures.service.ts deleted file mode 100644 index 6ec7537..0000000 --- a/apps/api/src/features/pictures/pictures.service.ts +++ /dev/null @@ -1,41 +0,0 @@ - -import { Injectable } from '@nestjs/common'; -import { writeFile } from 'fs/promises'; -import { join } from 'path'; - -@Injectable() -export class PicturesService { - /** - * Guarda una imagen en el directorio de imágenes. - * @param file - El archivo de imagen a guardar. - * @returns La ruta de la imagen guardada. - */ - async saveImages(file: Express.Multer.File[]): Promise { - // Construye la ruta al directorio de imágenes. - - const picturesPath = join(__dirname, '..', '..', '..', '..', 'uploads','pict'); - - console.log(picturesPath); - - let images : string[] = []; - - let count = 0; - - file.forEach(async (file) => { - count++ - // Crea un nombre de archivo único para la imagen. - const fileName = `${Date.now()}-${count}-${file.originalname}`; - images.push(fileName); - // console.log(fileName); - - // Construye la ruta completa al archivo de imagen. - const filePath = join(picturesPath, fileName); - - // Escribe el archivo de imagen en el disco. - await writeFile(filePath, file.buffer); - }); - // Devuelve la ruta de la imagen guardada. - // return [file[0].originalname] - return images; - } -} diff --git a/apps/api/src/features/training/dto/create-training.dto.ts b/apps/api/src/features/training/dto/create-training.dto.ts new file mode 100644 index 0000000..cf64d27 --- /dev/null +++ b/apps/api/src/features/training/dto/create-training.dto.ts @@ -0,0 +1,124 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsString, IsDateString, IsOptional } from 'class-validator'; + +export class CreateTrainingDto { + @ApiProperty() + @IsString() + firstname: string; + + @ApiProperty() + @IsString() + lastname: string; + + @ApiProperty() + @IsDateString() + visitDate: Date; + + @ApiProperty() + @IsString() + productiveActivity: string; + + @ApiProperty() + @IsString() + financialRequirementDescription: string; + + @ApiProperty() + @IsString() + siturCodeCommune: string; + + @ApiProperty() + @IsString() + communalCouncil: string; + + @ApiProperty() + @IsString() + siturCodeCommunalCouncil: string; + + @ApiProperty() + @IsString() + ospName: string; + + @ApiProperty() + @IsString() + ospAddress: string; + + @ApiProperty() + @IsString() + ospRif: string; + + @ApiProperty() + @IsString() + ospType: string; + + @ApiProperty() + @IsString() + currentStatus: string; + + @ApiProperty() + @IsInt() + companyConstitutionYear: number; + + @ApiProperty() + @IsInt() + producerCount: number; + + @ApiProperty() + @IsString() + productDescription: string; + + @ApiProperty() + @IsString() + installedCapacity: string; + + @ApiProperty() + @IsString() + operationalCapacity: string; + + @ApiProperty() + @IsString() + ospResponsibleFullname: string; + + @ApiProperty() + @IsString() + ospResponsibleCedula: string; + + @ApiProperty() + @IsString() + ospResponsibleRif: string; + + @ApiProperty() + @IsString() + ospResponsiblePhone: string; + + @ApiProperty() + @IsString() + civilState: string; + + @ApiProperty() + @IsInt() + familyBurden: number; + + @ApiProperty() + @IsInt() + numberOfChildren: number; + + @ApiProperty() + @IsString() + generalObservations: string; + + @ApiProperty() + @IsString() + photo1: string; + + @ApiProperty() + @IsString() + photo2: string; + + @ApiProperty() + @IsString() + photo3: string; + + @ApiProperty() + @IsString() + paralysisReason: string; +} diff --git a/apps/api/src/features/training/dto/update-training.dto.ts b/apps/api/src/features/training/dto/update-training.dto.ts new file mode 100644 index 0000000..4fd5c69 --- /dev/null +++ b/apps/api/src/features/training/dto/update-training.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateTrainingDto } from './create-training.dto'; + +export class UpdateTrainingDto extends PartialType(CreateTrainingDto) { } diff --git a/apps/api/src/features/training/training.controller.ts b/apps/api/src/features/training/training.controller.ts new file mode 100644 index 0000000..8f88032 --- /dev/null +++ b/apps/api/src/features/training/training.controller.ts @@ -0,0 +1,58 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common'; +import { TrainingService } from './training.service'; +import { CreateTrainingDto } from './dto/create-training.dto'; +import { UpdateTrainingDto } from './dto/update-training.dto'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { PaginationDto } from '../../common/dto/pagination.dto'; + +@ApiTags('training') +@Controller('training') +export class TrainingController { + constructor(private readonly trainingService: TrainingService) { } + + @Get() + @ApiOperation({ summary: 'Get all training records with pagination and filters' }) + @ApiResponse({ status: 200, description: 'Return paginated training records.' }) + async findAll(@Query() paginationDto: PaginationDto) { + const result = await this.trainingService.findAll(paginationDto); + return { + message: 'Training records fetched successfully', + data: result.data, + meta: result.meta + }; + } + + @Get(':id') + @ApiOperation({ summary: 'Get a training record by ID' }) + @ApiResponse({ status: 200, description: 'Return the training record.' }) + @ApiResponse({ status: 404, description: 'Training record not found.' }) + async findOne(@Param('id') id: string) { + const data = await this.trainingService.findOne(+id); + return { message: 'Training record fetched successfully', data }; + } + + @Post() + @ApiOperation({ summary: 'Create a new training record' }) + @ApiResponse({ status: 201, description: 'Training record created successfully.' }) + async create(@Body() createTrainingDto: CreateTrainingDto) { + const data = await this.trainingService.create(createTrainingDto); + return { message: 'Training record created successfully', data }; + } + + @Patch(':id') + @ApiOperation({ summary: 'Update a training record' }) + @ApiResponse({ status: 200, description: 'Training record updated successfully.' }) + @ApiResponse({ status: 404, description: 'Training record not found.' }) + async update(@Param('id') id: string, @Body() updateTrainingDto: UpdateTrainingDto) { + const data = await this.trainingService.update(+id, updateTrainingDto); + return { message: 'Training record updated successfully', data }; + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete a training record' }) + @ApiResponse({ status: 200, description: 'Training record deleted successfully.' }) + @ApiResponse({ status: 404, description: 'Training record not found.' }) + async remove(@Param('id') id: string) { + return await this.trainingService.remove(+id); + } +} diff --git a/apps/api/src/features/training/training.module.ts b/apps/api/src/features/training/training.module.ts new file mode 100644 index 0000000..9b13731 --- /dev/null +++ b/apps/api/src/features/training/training.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TrainingService } from './training.service'; +import { TrainingController } from './training.controller'; + +@Module({ + controllers: [TrainingController], + providers: [TrainingService], + exports: [TrainingService], +}) +export class TrainingModule { } diff --git a/apps/api/src/features/training/training.service.ts b/apps/api/src/features/training/training.service.ts new file mode 100644 index 0000000..5052551 --- /dev/null +++ b/apps/api/src/features/training/training.service.ts @@ -0,0 +1,118 @@ +import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider'; +import { Inject, Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { NodePgDatabase } from 'drizzle-orm/node-postgres'; +import * as schema from 'src/database/index'; +import { trainingSurveys } from 'src/database/index'; +import { eq, like, or, SQL, sql } from 'drizzle-orm'; +import { CreateTrainingDto } from './dto/create-training.dto'; +import { UpdateTrainingDto } from './dto/update-training.dto'; +import { PaginationDto } from '../../common/dto/pagination.dto'; + +@Injectable() +export class TrainingService { + constructor( + @Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase, + ) { } + + async findAll(paginationDto?: PaginationDto) { + const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {}; + + const offset = (page - 1) * limit; + + let searchCondition: SQL | undefined; + if (search) { + searchCondition = or( + like(trainingSurveys.firstname, `%${search}%`), + like(trainingSurveys.lastname, `%${search}%`), + like(trainingSurveys.ospName, `%${search}%`), + like(trainingSurveys.ospRif, `%${search}%`) + ); + } + + const orderBy = sortOrder === 'asc' + ? sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} asc` + : sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} desc`; + + const totalCountResult = await this.drizzle + .select({ count: sql`count(*)` }) + .from(trainingSurveys) + .where(searchCondition); + + const totalCount = Number(totalCountResult[0].count); + const totalPages = Math.ceil(totalCount / limit); + + const data = await this.drizzle + .select() + .from(trainingSurveys) + .where(searchCondition) + .orderBy(orderBy) + .limit(limit) + .offset(offset); + + const meta = { + page, + limit, + totalCount, + totalPages, + hasNextPage: page < totalPages, + hasPreviousPage: page > 1, + nextPage: page < totalPages ? page + 1 : null, + previousPage: page > 1 ? page - 1 : null, + }; + + return { data, meta }; + } + + async findOne(id: number) { + const find = await this.drizzle + .select() + .from(trainingSurveys) + .where(eq(trainingSurveys.id, id)); + + if (find.length === 0) { + throw new HttpException('Training record not found', HttpStatus.NOT_FOUND); + } + + return find[0]; + } + + async create(createTrainingDto: CreateTrainingDto) { + const [newRecord] = await this.drizzle + .insert(trainingSurveys) + .values({ + ...createTrainingDto, + visitDate: new Date(createTrainingDto.visitDate), + }) + .returning(); + + return newRecord; + } + + async update(id: number, updateTrainingDto: UpdateTrainingDto) { + await this.findOne(id); + + const updateData: any = { ...updateTrainingDto }; + if (updateTrainingDto.visitDate) { + updateData.visitDate = new Date(updateTrainingDto.visitDate); + } + + const [updatedRecord] = await this.drizzle + .update(trainingSurveys) + .set(updateData) + .where(eq(trainingSurveys.id, id)) + .returning(); + + return updatedRecord; + } + + async remove(id: number) { + await this.findOne(id); + + const [deletedRecord] = await this.drizzle + .delete(trainingSurveys) + .where(eq(trainingSurveys.id, id)) + .returning(); + + return { message: 'Training record deleted successfully', data: deletedRecord }; + } +} diff --git a/apps/web/app/dashboard/configuraciones/caja-ahorro/page.tsx b/apps/web/app/dashboard/configuraciones/caja-ahorro/page.tsx deleted file mode 100644 index 4344857..0000000 --- a/apps/web/app/dashboard/configuraciones/caja-ahorro/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import PageContainer from '@/components/layout/page-container'; - -const Page = () => { - return ( - -
- En mantenimiento -
- -
- //
- //
- - //
- //
- ); -}; - -export default Page; diff --git a/apps/web/app/dashboard/formulario/page.tsx b/apps/web/app/dashboard/formulario/page.tsx new file mode 100644 index 0000000..a612102 --- /dev/null +++ b/apps/web/app/dashboard/formulario/page.tsx @@ -0,0 +1,15 @@ +'use client'; + +import PageContainer from '@/components/layout/page-container'; +import { CreateTrainingForm } from '@/feactures/training/components/form'; + +const Page = () => { + return ( + +
+ +
+ ); +}; + +export default Page; diff --git a/apps/web/components/layout/app-sidebar.tsx b/apps/web/components/layout/app-sidebar.tsx index efe1b70..c28016d 100644 --- a/apps/web/components/layout/app-sidebar.tsx +++ b/apps/web/components/layout/app-sidebar.tsx @@ -1,7 +1,7 @@ 'use client'; -import { NavMain as GeneralMain, NavMain as AdministrationMain, NavMain as StatisticsMain, } from '@/components/nav-main'; -import { GeneralItems, AdministrationItems, StatisticsItems } from '@/constants/data'; +import { NavMain as GeneralMain, NavMain as AdministrationMain, NavMain as StatisticsMain, } from '@/components/nav-main'; +import { GeneralItems, AdministrationItems, StatisticsItems } from '@/constants/routes'; import { Sidebar, SidebarContent, @@ -24,9 +24,9 @@ export const company = { export function AppSidebar({ ...props }: React.ComponentProps) { const { data: session } = useSession(); - const userRole = session?.user.role[0]?.rol ? session.user.role[0].rol :''; + const userRole = session?.user.role[0]?.rol ? session.user.role[0].rol : ''; // console.log(AdministrationItems[0]?.role); - + return ( @@ -42,14 +42,14 @@ export function AppSidebar({ ...props }: React.ComponentProps) { - - + + {StatisticsItems[0]?.role?.includes(userRole) && - + } - + {AdministrationItems[0]?.role?.includes(userRole) && - + } {/* */} diff --git a/apps/web/constants/data.ts b/apps/web/constants/routes.ts similarity index 77% rename from apps/web/constants/data.ts rename to apps/web/constants/routes.ts index d391faf..f466f98 100644 --- a/apps/web/constants/data.ts +++ b/apps/web/constants/routes.ts @@ -18,6 +18,14 @@ export const GeneralItems: NavItem[] = [ isActive: false, items: [], // No child items }, + { + title: 'Formulario', + url: '/dashboard/formulario/', + icon: 'notepadText', + shortcut: ['p', 'p'], + isActive: false, + items: [], // No child items + }, ]; @@ -27,7 +35,7 @@ export const AdministrationItems: NavItem[] = [ url: '#', // Placeholder as there is no direct link for the parent icon: 'settings2', isActive: true, - role:['admin','superadmin','manager','user'], // sumatoria de los roles que si tienen acceso + role: ['admin', 'superadmin', 'manager', 'user'], // sumatoria de los roles que si tienen acceso items: [ { @@ -35,14 +43,14 @@ export const AdministrationItems: NavItem[] = [ url: '/dashboard/administracion/usuario', icon: 'userPen', shortcut: ['m', 'm'], - role:['admin','superadmin'], + role: ['admin', 'superadmin'], }, { title: 'Encuestas', shortcut: ['l', 'l'], url: '/dashboard/administracion/encuestas', icon: 'login', - role:['admin','superadmin','manager','user'], + role: ['admin', 'superadmin', 'manager', 'user'], }, ], }, @@ -54,7 +62,7 @@ export const StatisticsItems: NavItem[] = [ url: '#', // Placeholder as there is no direct link for the parent icon: 'chartColumn', isActive: true, - role:['admin','superadmin','autoridad'], + role: ['admin', 'superadmin', 'autoridad'], items: [ // { @@ -69,7 +77,7 @@ export const StatisticsItems: NavItem[] = [ shortcut: ['l', 'l'], url: '/dashboard/estadisticas/encuestas', icon: 'notepadText', - role:['admin','superadmin','autoridad'], + role: ['admin', 'superadmin', 'autoridad'], }, ], }, diff --git a/apps/web/feactures/training/actions/actions.ts b/apps/web/feactures/training/actions/actions.ts new file mode 100644 index 0000000..4d9f698 --- /dev/null +++ b/apps/web/feactures/training/actions/actions.ts @@ -0,0 +1,148 @@ +'use server'; +import { safeFetchApi } from '@/lib/fetch.api'; +import { + surveysApiResponseSchema, + CreateUser, + UsersMutate, + UpdateUser +} from '../schemas/users'; + +import { auth } from '@/lib/auth'; + + +export const getProfileAction = async () => { + const session = await auth() + const id = session?.user?.id + + const [error, response] = await safeFetchApi( + UsersMutate, + `/users/${id}`, + 'GET' + ); + if (error) throw new Error(error.message); + return response; +}; + +export const updateProfileAction = async (payload: UpdateUser) => { + const { id, ...payloadWithoutId } = payload; + + const [error, data] = await safeFetchApi( + UsersMutate, + `/users/profile/${id}`, + 'PATCH', + payloadWithoutId, + ); + + console.log(payload); + if (error) { + if (error.message === 'Email already exists') { + throw new Error('Ese correo ya está en uso'); + } + // console.error('Error:', error); + throw new Error('Error al crear el usuario'); + } + return data; +}; + +export const getUsersAction = async (params: { + page?: number; + limit?: number; + search?: string; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +}) => { + + const searchParams = new URLSearchParams({ + page: (params.page || 1).toString(), + limit: (params.limit || 10).toString(), + ...(params.search && { search: params.search }), + ...(params.sortBy && { sortBy: params.sortBy }), + ...(params.sortOrder && { sortOrder: params.sortOrder }), + }); + + const [error, response] = await safeFetchApi( + surveysApiResponseSchema, + `/users?${searchParams}`, + 'GET', + ); + + if (error) throw new Error(error.message); + + // const transformedData = response?.data ? transformSurvey(response?.data) : undefined; + + return { + data: response?.data || [], + meta: response?.meta || { + page: 1, + limit: 10, + totalCount: 0, + totalPages: 1, + hasNextPage: false, + hasPreviousPage: false, + nextPage: null, + previousPage: null, + }, + }; +} + +export const createUserAction = async (payload: CreateUser) => { + const { id, confirmPassword, ...payloadWithoutId } = payload; + + const [error, data] = await safeFetchApi( + UsersMutate, + '/users', + 'POST', + payloadWithoutId, + ); + + if (error) { + if (error.message === 'Username already exists') { + throw new Error('Ese usuario ya existe'); + } + if (error.message === 'Email already exists') { + throw new Error('Ese correo ya está en uso'); + } + // console.error('Error:', error); + throw new Error('Error al crear el usuario'); + } + + return payloadWithoutId; +}; + +export const updateUserAction = async (payload: UpdateUser) => { + try { + const { id, ...payloadWithoutId } = payload; + + const [error, data] = await safeFetchApi( + UsersMutate, + `/users/${id}`, + 'PATCH', + payloadWithoutId, + ); + + // console.log(data); + if (error) { + console.error(error); + + throw new Error(error?.message || 'Error al actualizar el usuario'); + } + return data; + } catch (error) { + console.error(error); + } +} + +export const deleteUserAction = async (id: Number) => { + const [error] = await safeFetchApi( + UsersMutate, + `/users/${id}`, + 'DELETE' + ) + + console.log(error); + + + // if (error) throw new Error(error.message || 'Error al eliminar el usuario') + + return true; +} \ No newline at end of file diff --git a/apps/web/feactures/training/actions/training-actions.ts b/apps/web/feactures/training/actions/training-actions.ts new file mode 100644 index 0000000..ce410f1 --- /dev/null +++ b/apps/web/feactures/training/actions/training-actions.ts @@ -0,0 +1,94 @@ +'use server'; +import { safeFetchApi } from '@/lib/fetch.api'; +import { + TrainingSchema, + TrainingMutate, + trainingApiResponseSchema +} from '../schemas/training'; + +export const getTrainingAction = async (params: { + page?: number; + limit?: number; + search?: string; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +}) => { + + const searchParams = new URLSearchParams({ + page: (params.page || 1).toString(), + limit: (params.limit || 10).toString(), + ...(params.search && { search: params.search }), + ...(params.sortBy && { sortBy: params.sortBy }), + ...(params.sortOrder && { sortOrder: params.sortOrder }), + }); + + const [error, response] = await safeFetchApi( + trainingApiResponseSchema, + `/training?${searchParams}`, + 'GET', + ); + + if (error) throw new Error(error.message); + + return { + data: response?.data || [], + meta: response?.meta || { + page: 1, + limit: 10, + totalCount: 0, + totalPages: 1, + hasNextPage: false, + hasPreviousPage: false, + nextPage: null, + previousPage: null, + }, + }; +} + +export const createTrainingAction = async (payload: TrainingSchema) => { + const { id, ...payloadWithoutId } = payload; + + const [error, data] = await safeFetchApi( + TrainingMutate, + '/training', + 'POST', + payloadWithoutId, + ); + + if (error) { + throw new Error(error.message || 'Error al crear el registro'); + } + + return data; +}; + +export const updateTrainingAction = async (payload: TrainingSchema) => { + const { id, ...payloadWithoutId } = payload; + + if (!id) throw new Error('ID es requerido para actualizar'); + + const [error, data] = await safeFetchApi( + TrainingMutate, + `/training/${id}`, + 'PATCH', + payloadWithoutId, + ); + + if (error) { + throw new Error(error.message || 'Error al actualizar el registro'); + } + + return data; +}; + +export const deleteTrainingAction = async (id: number) => { + const [error] = await safeFetchApi( + TrainingMutate, + `/training/${id}`, + 'DELETE' + ) + + if (error) throw new Error(error.message || 'Error al eliminar el registro'); + + return true; +} diff --git a/apps/web/feactures/training/components/form.tsx b/apps/web/feactures/training/components/form.tsx new file mode 100644 index 0000000..323bf6e --- /dev/null +++ b/apps/web/feactures/training/components/form.tsx @@ -0,0 +1,431 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { Button } from '@repo/shadcn/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@repo/shadcn/form'; +import { Input } from '@repo/shadcn/input'; +import { Textarea } from '@repo/shadcn/textarea'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@repo/shadcn/select'; +import { useForm } from 'react-hook-form'; +import { useCreateTraining } from "../hooks/use-training"; +import { TrainingSchema, trainingSchema } from '../schemas/training'; + +const PRODUCTIVE_ACTIVITIES = [ + 'Agricola', + 'Textil', + 'Bloquera', + 'Carpinteria', + 'Unidad de suministro' +]; + +interface CreateTrainingFormProps { + onSuccess?: () => void; + onCancel?: () => void; + defaultValues?: Partial; +} + +export function CreateTrainingForm({ + onSuccess, + onCancel, + defaultValues, +}: CreateTrainingFormProps) { + const { + mutate: saveTraining, + isPending: isSaving, + } = useCreateTraining(); + + const form = useForm({ + resolver: zodResolver(trainingSchema), + defaultValues: { + firstname: defaultValues?.firstname || '', + lastname: defaultValues?.lastname || '', + visitDate: defaultValues?.visitDate || new Date().toISOString().split('T')[0], + productiveActivity: defaultValues?.productiveActivity || '', + financialRequirementDescription: defaultValues?.financialRequirementDescription || '', + siturCodeCommune: defaultValues?.siturCodeCommune || '', + communalCouncil: defaultValues?.communalCouncil || '', + siturCodeCommunalCouncil: defaultValues?.siturCodeCommunalCouncil || '', + ospName: defaultValues?.ospName || '', + ospAddress: defaultValues?.ospAddress || '', + ospRif: defaultValues?.ospRif || '', + ospType: defaultValues?.ospType || '', + currentStatus: defaultValues?.currentStatus || '', + companyConstitutionYear: defaultValues?.companyConstitutionYear || new Date().getFullYear(), + producerCount: defaultValues?.producerCount || 0, + productDescription: defaultValues?.productDescription || '', + installedCapacity: defaultValues?.installedCapacity || '', + operationalCapacity: defaultValues?.operationalCapacity || '', + ospResponsibleFullname: defaultValues?.ospResponsibleFullname || '', + ospResponsibleCedula: defaultValues?.ospResponsibleCedula || '', + ospResponsibleRif: defaultValues?.ospResponsibleRif || '', + ospResponsiblePhone: defaultValues?.ospResponsiblePhone || '', + civilState: defaultValues?.civilState || '', + familyBurden: defaultValues?.familyBurden || 0, + numberOfChildren: defaultValues?.numberOfChildren || 0, + generalObservations: defaultValues?.generalObservations || '', + photo1: defaultValues?.photo1 || '', + photo2: defaultValues?.photo2 || '', + photo3: defaultValues?.photo3 || '', + paralysisReason: defaultValues?.paralysisReason || '', + state: defaultValues?.state || '', + municipality: defaultValues?.municipality || '', + parish: defaultValues?.parish || '', + }, + mode: 'onChange', + }); + + const onSubmit = async (formData: TrainingSchema) => { + saveTraining(formData, { + onSuccess: () => { + form.reset(); + onSuccess?.(); + }, + onError: (e) => { + console.error(e); + form.setError('root', { + type: 'manual', + message: 'Error al guardar el registro', + }); + }, + }); + }; + + return ( +
+ + {form.formState.errors.root && ( +
+ {form.formState.errors.root.message} +
+ )} + +
+ {/* Datos Personales */} +
+

Datos Básicos

+
+ + ( + + Nombre + + + + )} /> + + ( + + Apellido + + + + )} /> + + ( + + Fecha de la visita + + + + )} /> + + {/* Ubicación */} +
+

Ubicación

+
+ + ( + + Estado + + + + )} /> + + ( + + Municipio + + + + )} /> + + ( + + Parroquia + + + + )} /> + + ( + + Código SITUR Comuna + + + + )} /> + + ( + + Consejo Comunal + + + + )} /> + + ( + + Código SITUR Consejo Comunal + + + + )} /> + + {/* Datos de la OSP */} +
+

Datos de la Organización Socioproductiva (OSP)

+
+ + ( + + Nombre de la Organización + + + + )} /> + + ( + + Dirección + + + + )} /> + + ( + + RIF + + + + )} /> + + ( + + Tipo de Organización + + + + )} /> + + ( + + Actividad Productiva + + + + )} /> + + ( + + Estatus Actual + + + + )} /> + + ( + + Año de Constitución + + + + )} /> + + ( + + Cantidad de Productores + + + + )} /> + + ( + + Breve descripción del producto o servicio +