Merge branch 'dev' into UH-registroGuardaSeguridad

This commit is contained in:
Mariela34 2022-07-21 23:10:36 -06:00 committed by GitHub
commit 01c1512bf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 16585 additions and 280 deletions

View File

@ -100,16 +100,15 @@ export class AppController {
@Body('canton') canton: string,
@Body('district') district: string,
@Body('num_houses') num_houses: number,
@Body('phone') phone: number,
@Body('quote') quote: number,
@Body('phone') phone: string,
@Body('status') status: string,
@Body('date_entry') date_entry: Date,
@Body('houses') houses: [{}],
@Body('houses') houses: [],
) {
return this.appService.createCommunity(name, province, canton,
district, num_houses, phone,
quote, status, date_entry, houses);
status, date_entry, houses);
}
@Get('community/allCommunities')
@ -132,6 +131,14 @@ export class AppController {
}
@Post('community/findCommunityAdmin')
findCommunityAdmin(
@Body('community_id') community_id: string,
) {
return this.appService.findCommunityAdmin(community_id);
}
// #==== API Common Areas
@Post('commonArea/createCommonArea')
createCommonArea(
@ -318,4 +325,38 @@ export class AppController {
) {
return this.appService.findReport(paramReport);
}
@Post('email/sendMail')
senMail(
@Body('email') email: string,
) {
return this.appService.sendMail(email);
}
@Post('email/html')
html(
@Body('email') email: string,
@Body('name') name: string,
) {
return this.appService.html(email, name);
}
// #==== API Users
@Post('user/testSendMail')
testSendMail(
@Body('dni') dni: string,
@Body('name') name: string,
@Body('last_name') last_name: string,
@Body('email') email: string,
@Body('phone') phone: number,
@Body('password') password: string,
@Body('user_type') user_type: string,
@Body('status') status: string,
@Body('date_entry') date_entry: Date,
) {
return this.appService.testSendMail(dni, name, last_name, email, phone, password,
user_type, status, date_entry);
}
}

View File

@ -56,6 +56,7 @@ export class AppService {
const payload = {
dni: dni, name: name, last_name: last_name, email: email, phone: phone,
password: password, user_type: user_type, status: status, date_entry: date_entry, community_id
};
return this.clientUserApp
.send<string>(pattern, payload)
@ -125,15 +126,27 @@ export class AppService {
);
}
//GET parameter from API
findCommunityAdmin(community_id: string) {
const pattern = { cmd: 'findCommunityAdmin' };
const payload = { community_id: community_id };
return this.clientCommunityApp
.send<string>(pattern, payload)
.pipe(
map((message: string) => ({ message })),
);
}
// ====================== COMMUNITIES ===============================
//POST parameter from API
createCommunity(name: string, province: string, canton: string, district: string
, num_houses: number, phone: number, quote: number, status: string, date_entry: Date, houses: [{}]) {
, num_houses: number, phone: string, status: string, date_entry: Date, houses: []) {
const pattern = { cmd: 'createCommunity' };
const payload = {
name: name, province: province, canton: canton, district: district, num_houses: num_houses,
phone: phone, quote: quote, status: status, date_entry: date_entry, houses
phone: phone, status: status, date_entry: date_entry, houses: houses
};
return this.clientCommunityApp
.send<string>(pattern, payload)
@ -443,4 +456,25 @@ export class AppService {
map((message: string) => ({ message })),
);
}
sendMail(email: string) {
const pattern = { cmd: 'sendMail' };
const payload = { email: email};
return this.clientNotificationtApp
.send<string>(pattern, payload)
.pipe(
map((message: string) => ({ message })),
);
}
html(email: string, name: string) {
const pattern = { cmd: 'html' };
const payload = { email: email, name: name};
return this.clientNotificationtApp
.send<string>(pattern, payload)
.pipe(
map((message: string) => ({ message })),
);
}
}

View File

@ -3,13 +3,11 @@ import { AppModule } from './app.module';
const cors = require('cors');
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: 'http://localhost:3000',
methods: 'GET, PUT, POST, DELETE',
allowedHeaders: 'Content-Type, Authorization',
});
await app.listen(4000);
//app.use(cors(enableCors))
}
bootstrap();

View File

@ -29,6 +29,12 @@ export class CommunitiesController {
return this.communitiesService.findOneName(_id);
}
/* @MessagePattern({cmd: 'findCommunityAdmin'})
findCommunityAdmin(@Payload() community: any) {
let _community = community['community_id'];
return this.communitiesService.findCommunityAdmin(_community, "2");
}*/
@MessagePattern({cmd: 'updateCommunity'})
update(@Payload() community: CommunityDocument) {
return this.communitiesService.update(community.id, community);

View File

@ -2,12 +2,23 @@ import { Module } from '@nestjs/common';
import { CommunitiesService } from './communities.service';
import { CommunitiesController } from './communities.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { ClientsModule, Transport } from "@nestjs/microservices";
import { Community, CommunitySchema } from '../schemas/community.schema';
@Module({
imports: [
ClientsModule.register([
{
name: "SERVICIO_USUARIOS",
transport: Transport.TCP,
options: {
host: "127.0.0.1",
port: 3001
}
}
]),
MongooseModule.forFeature([{ name: Community.name, schema: CommunitySchema }]),
],
controllers: [CommunitiesController],

View File

@ -1,13 +1,20 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Inject } from '@nestjs/common';
import { Model } from 'mongoose';
import { Community, CommunityDocument } from 'src/schemas/community.schema';
import { InjectModel } from '@nestjs/mongoose';
import { RpcException, ClientProxy } from '@nestjs/microservices';
import { from, lastValueFrom, map, scan, mergeMap } from 'rxjs';
import { Admin } from 'src/schemas/admin.entity';
import { appendFileSync } from 'fs';
@Injectable()
export class CommunitiesService {
constructor(
@InjectModel(Community.name) private readonly communityModel: Model<CommunityDocument>,
@Inject('SERVICIO_USUARIOS') private readonly clientUserApp: ClientProxy,
) { }
async create(community: CommunityDocument): Promise<Community> {
@ -15,10 +22,25 @@ export class CommunitiesService {
}
async findAll(): Promise<Community[]> {
return this.communityModel
return await this.communityModel
.find()
.setOptions({ sanitizeFilter: true })
.exec();
.exec()
.then(async community => {
if (community) {
await Promise.all(community.map(async c => {
//buscar al usuario con el id de la comunidad anexado
let admin = await this.findCommunityAdmin(c["_id"], "2")
if (admin) {
c["id_admin"] = admin["_id"]
c["name_admin"] = admin["name"]
}
return c
}))
}
return community;
})
}
findOne(id: string): Promise<Community> {
@ -37,4 +59,19 @@ export class CommunitiesService {
async remove(id: string) {
return this.communityModel.findByIdAndRemove({ _id: id }).exec();
}
async findCommunityAdmin(community: string, user_type: string) {
const pattern = { cmd: 'findOneCommunityUser' }
const payload = { community_id: community, user_type: user_type }
let callback = await this.clientUserApp
.send<string>(pattern, payload)
.pipe(
map((response: string) => ({ response }))
)
const finalValue = await lastValueFrom(callback);
return finalValue['response'];
}
}

View File

@ -0,0 +1,4 @@
export interface Admin {
id_community: string;
user_type: string;
}

View File

@ -7,6 +7,11 @@ export type CommunityDocument = Community & Document;
@Schema({ collection: 'communities' })
export class Community {
@Prop()
id_admin: string;
@Prop()
name_admin: string ;
@Prop()
name: string;
@ -24,10 +29,7 @@ export class Community {
num_houses: number;
@Prop()
phone: number;
@Prop()
quote: number;
phone: string;
@Prop()
status: string;
@ -37,6 +39,7 @@ export class Community {
@Prop({ type: [HouseSchema] })
houses: Array<House>;
}

View File

@ -1,5 +1,7 @@
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import e from 'express';
import { Document } from 'mongoose';
import { empty } from 'rxjs';
import { Tenant, TenantSchema } from './tenant.schema';
@ -7,12 +9,12 @@ import { Tenant, TenantSchema } from './tenant.schema';
@Schema()
export class House extends Document {
@Prop({ default: " " })
number: string;
number_house: string;
@Prop({ default: " " })
description: string;
@Prop({ default: "desocupada" })
state: string;
@Prop({ type: TenantSchema, default: " " })
@Prop({ type: TenantSchema })
tenants: Tenant;
}
export const HouseSchema = SchemaFactory.createForClass(House);

View File

@ -4,7 +4,7 @@ import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Tenant {
@Prop()
@Prop( {default: ''})
tenant_id: string;
}

View File

@ -0,0 +1,8 @@
# mail
MAIL_HOST=smtp.gmail.com
MAIL_USER=mbonilla.guti@gmail.com
MAIL_PASSWORD=laofghlofgffmyry
MAIL_FROM=noreply@example.com
# optional
MAIL_TRANSPORT=smtp://${MAIL_USER}:${MAIL_PASSWORD}@${MAIL_HOST}

View File

@ -1,5 +1,9 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src"
"sourceRoot": "src",
"compilerOptions": {
"assets": ["mails/**/*"],
"watchAssets": true
}
}

File diff suppressed because it is too large Load Diff

View File

@ -21,12 +21,16 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs-modules/mailer": "^1.7.1",
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^8.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/microservices": "^8.4.7",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.2.1",
"handlebars": "^4.7.7",
"nodemailer": "^6.7.7",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",

View File

@ -1,11 +1,45 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { NotificationsModule } from './notifications/notifications.module';
import { MailerModule } from '@nestjs-modules/mailer';
import { ClientsModule, Transport } from "@nestjs/microservices";
import { AuthModule } from './auth/auth.module';
import { EmailController } from './email.controller';
import { join } from 'path';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
MailerModule.forRootAsync({
// imports: [ConfigModule], // import module if not enabled globally
useFactory: async (config: ConfigService) => ({
// transport: config.get("MAIL_TRANSPORT"),
// or
transport: {
host: config.get('MAIL_HOST'),
secure: false,
auth: {
user: config.get('MAIL_USER'),
pass: config.get('MAIL_PASSWORD'),
},
},
defaults: {
from: `"No Reply" <${config.get('MAIL_USER')}>`,
},
template: {
dir: join(__dirname, 'mails'),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
}),
inject: [ConfigService],
}),
ConfigModule.forRoot({
isGlobal: true, // no need to import into other modules
}),
ClientsModule.register([
{
name: "SERVICIO_NOTIFICACIONES",
@ -16,8 +50,8 @@ import { ClientsModule, Transport } from "@nestjs/microservices";
}
}
]),
NotificationsModule],
controllers: [AppController],
AuthModule],
controllers: [AppController, EmailController],
providers: [],
})
export class AppModule {}

View File

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
//import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
@Module({
//controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get<AuthService>(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,13 @@
import { Injectable } from '@nestjs/common';
import { User } from './../user/user.entity';
@Injectable()
export class AuthService {
async signUp(user: User) {
const token = Math.floor(1000 + Math.random() * 9000).toString();
// create user in db
// ...
// send confirmation mail
}
}

View File

@ -0,0 +1,46 @@
import { Controller, Get, Query } from '@nestjs/common';
import { MessagePattern, Payload } from '@nestjs/microservices';
import { MailerService } from '@nestjs-modules/mailer';
import { User } from './user/user.entity';
@Controller()
export class EmailController {
constructor(private mailService: MailerService) { }
@MessagePattern({ cmd: 'sendMail' })
sendMail(@Payload() toEmail: string) {
var response = this.mailService.sendMail({
to: toEmail["email"],
from: "mbonilla.guti@gmail.com",
subject: 'Plain Text Email ✔',
text: 'Welcome NestJS Email Sending Tutorial',
});
return response;
}
@MessagePattern({ cmd: 'html' })
async postHTMLEmail(@Payload() user: any) {
const url = "http://localhost:3000/";
const image = "images/email.ong";
var response = await this.mailService.sendMail({
to: user["email"],
from: "mbonilla.guti@gmail.com",
subject: 'HTML Dynamic Template',
template: 'templateEmail',
context: {
name: user["name"],
url
},
attachments: [
{
filename: 'email.png',
path: __dirname +'/mails/images/email.png',
cid: 'logo' //my mistake was putting "cid:logo@cid" here!
}
]
});
return response;
}
}

View File

@ -0,0 +1,7 @@
<p>Hey {{ name }},</p>
<p>Please click below to confirm your email</p>
<p>
<a href="{{ url }}">Confirm</a>
</p>
<p>If you did not request this email you can safely ignore it.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,452 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
<title></title> <!-- The title tag shows in email notifications, like Android 4.4. -->
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700" rel="stylesheet">
<!-- CSS Reset : BEGIN -->
<style>
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
background: #f1f1f1;
}
/* What it does: Stops email clients resizing small text. */
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
/* What it does: Centers email on Android 4.4 */
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
/* What it does: Stops Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* What it does: Fixes webkit padding issue. */
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
/* What it does: Uses a better rendering method when resizing images in IE. */
img {
-ms-interpolation-mode: bicubic;
}
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
/* What it does: A work-around for email clients meddling in triggered links. */
*[x-apple-data-detectors],
/* iOS */
.unstyle-auto-detected-links *,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* What it does: Prevents Gmail from displaying a download button on large, non-linked images. */
.a6S {
display: none !important;
opacity: 0.01 !important;
}
/* What it does: Prevents Gmail from changing the text color in conversation threads. */
.im {
color: inherit !important;
}
/* If the above doesn't work, add a .g-img class to any image in question. */
img.g-img+div {
display: none !important;
}
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
/* Create one of these media queries for each additional viewport size you'd like to fix */
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!-- CSS Reset : END -->
<!-- Progressive Enhancements : BEGIN -->
<style>
.primary {
background: #30e3ca;
}
.bg_white {
background: #ffffff;
}
.bg_light {
background: #fafafa;
}
.bg_black {
background: #000000;
}
.bg_dark {
background: rgba(0, 0, 0, .8);
}
.email-section {
padding: 2.5em;
}
/*BUTTON*/
.btn {
padding: 10px 15px;
display: inline-block;
}
.btn.btn-primary {
border-radius: 5px;
background: #30e3ca;
color: #ffffff;
}
.btn.btn-white {
border-radius: 5px;
background: #ffffff;
color: #000000;
}
.btn.btn-white-outline {
border-radius: 5px;
background: transparent;
border: 1px solid #fff;
color: #fff;
}
.btn.btn-black-outline {
border-radius: 0px;
background: transparent;
border: 2px solid #000;
color: #000;
font-weight: 700;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'Lato', sans-serif;
color: #000000;
margin-top: 0;
font-weight: 400;
}
body {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
line-height: 1.8;
color: rgba(0, 0, 0, .4);
}
a {
color: #30e3ca;
}
table {}
/*LOGO*/
.logo h1 {
margin: 0;
}
.logo h1 a {
color: #30e3ca;
font-size: 24px;
font-weight: 700;
font-family: 'Lato', sans-serif;
}
/*HERO*/
.hero {
position: relative;
z-index: 0;
}
.hero .text {
color: rgba(0, 0, 0, .3);
}
.hero .text h2 {
color: #000;
font-size: 40px;
margin-bottom: 0;
font-weight: 400;
line-height: 1.4;
}
.hero .text h3 {
font-size: 24px;
font-weight: 300;
}
.hero .text h2 span {
font-weight: 600;
color: #30e3ca;
}
/*HEADING SECTION*/
.heading-section {}
.heading-section h2 {
color: #000000;
font-size: 28px;
margin-top: 0;
line-height: 1.4;
font-weight: 400;
}
.heading-section .subheading {
margin-bottom: 20px !important;
display: inline-block;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 2px;
color: rgba(0, 0, 0, .4);
position: relative;
}
.heading-section .subheading::after {
position: absolute;
left: 0;
right: 0;
bottom: -10px;
content: '';
width: 100%;
height: 2px;
background: #30e3ca;
margin: 0 auto;
}
.heading-section-white {
color: rgba(255, 255, 255, .8);
}
.heading-section-white h2 {
font-family:
line-height: 1;
padding-bottom: 0;
}
.heading-section-white h2 {
color: #ffffff;
}
.heading-section-white .subheading {
margin-bottom: 0;
display: inline-block;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 2px;
color: rgba(255, 255, 255, .4);
}
ul.social {
padding: 0;
}
ul.social li {
display: inline-block;
margin-right: 10px;
}
/*FOOTER*/
.footer {
border-top: 1px solid rgba(0, 0, 0, .05);
color: rgba(0, 0, 0, .5);
}
.footer .heading {
color: #000;
font-size: 20px;
}
.footer ul {
margin: 0;
padding: 0;
}
.footer ul li {
list-style: none;
margin-bottom: 10px;
}
.footer ul li a {
color: rgba(0, 0, 0, 1);
}
@media screen and (max-width: 500px) {}
</style>
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f1f1f1;">
<center style="width: 100%; background-color: #f1f1f1;">
<div
style="display: none; font-size: 1px;max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;
</div>
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!-- BEGIN BODY -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td valign="top" class="bg_white" style="padding: 1em 2.5em 0 2.5em;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td class="logo" style="text-align: center;">
<h1><a href="#">e-Verify</a></h1>
</td>
</tr>
</table>
</td>
</tr><!-- end tr -->
<tr>
<td valign="middle" class="hero bg_white" style="padding: 3em 0 2em 0;">
<img src="cid:logo" alt=""
style="width: 300px; max-width: 600px; height: auto; margin: auto; display: block;">
</td>
</tr><!-- end tr -->
<tr>
<td valign="middle" class="hero bg_white" style="padding: 2em 0 4em 0;">
<table>
<tr>
<td>
<div class="text" style="padding: 0 2.5em; text-align: center;">
<h2>Hi, {{ name }}</h2>
<h2>Please verify your email</h2>
<h3>Amazing deals, updates, interesting news right in your inbox</h3>
<p><a href="{{ url }}" class="btn btn-primary">Yes! Subscribe Me</a></p>
</div>
</td>
</tr>
</table>
</td>
</tr><!-- end tr -->
<!-- 1 Column Text + Button : END -->
</table>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td valign="middle" class="bg_light footer email-section">
<table>
<tr>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-right: 10px;">
<h3 class="heading">About</h3>
<p>A small river named Duden flows by their place and supplies it with
the necessary regelialia.</p>
</td>
</tr>
</table>
</td>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-left: 5px; padding-right: 5px;">
<h3 class="heading">Contact Info</h3>
<ul>
<li><span class="text">203 Fake St. Mountain View, San Francisco,
California, USA</span></li>
<li><span class="text">+2 392 3929 210</span></a></li>
</ul>
</td>
</tr>
</table>
</td>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-left: 10px;">
<h3 class="heading">Useful Links</h3>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Work</a></li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr><!-- end: tr -->
<tr>
<td class="bg_light" style="text-align: center;">
<p>No longer want to receive these email? You can <a href="#"
style="color: rgba(0,0,0,.8);">Unsubscribe here</a></p>
</td>
</tr>
</table>
</div>
</center>
</body>
</html>

View File

@ -0,0 +1,4 @@
export interface User {
email: string;
name: string;
}

View File

@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
"name": "servicio-usuarios",
"version": "0.0.1",
"license": "UNLICENSED",
"dependencies": {

View File

@ -22,6 +22,7 @@ export class UsersController {
return this.userService.create(user);
}
@MessagePattern({ cmd: 'findAllUsers' })
findAll() {
return this.userService.findAll();
@ -39,6 +40,7 @@ export class UsersController {
return this.userService.findGuardsCommunity(pcommunity_id);
}
@MessagePattern({ cmd: 'updateUser' })
update(@Payload() user: UserDocument) {
return this.userService.update(user.id, user);
@ -69,4 +71,19 @@ export class UsersController {
allUsersAdminComunidad() {
return this.userService.allUsersAdminComunidad();
}
//Prueba de envio de correo despues de registro, llamando a microservicio notificaciones
@MessagePattern({ cmd: 'testSendMail' })
testSendMail(@Payload() user: UserDocument) {
return this.userService.testSendMail(user);
}
//buscar usuario de una comunidad
@MessagePattern({ cmd: 'findOneCommunityUser' })
findCommunityUser(@Payload() user: any) {
return this.userService.findCommunityUser(user["community_id"], user["user_type"]);
}
}

View File

@ -4,9 +4,20 @@ import { MongooseModule } from '@nestjs/mongoose';
import { UsersController } from './users.controller';
import { User, UserSchema } from '../schemas/user.schema';
import { ClientsModule, Transport } from "@nestjs/microservices";
@Module({
imports: [
ClientsModule.register([
{
name: "SERVICIO_NOTIFICACIONES",
transport: Transport.TCP,
options: {
host: "127.0.0.1",
port: 3009
}
}
]),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
controllers: [UsersController],

View File

@ -1,13 +1,20 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Inject } from '@nestjs/common';
import { Model } from 'mongoose';
import { User, UserDocument } from '../schemas/user.schema';
import { InjectModel } from '@nestjs/mongoose';
import { Md5 } from "md5-typescript";
import { map } from 'rxjs/operators';
import { RpcException, ClientProxy } from '@nestjs/microservices';
@Injectable()
export class UsersService {
constructor(
@InjectModel(User.name) private readonly userModel: Model<UserDocument>,
@Inject('SERVICIO_NOTIFICACIONES') private readonly clientNotificationtApp: ClientProxy,
) { }
private publicKey: string;
async create(user: UserDocument): Promise<User> {
@ -70,6 +77,7 @@ export class UsersService {
return this.userModel.find({ user_type: 1 }).exec();
}
//find admin del sistema
async findGuardsCommunity(pcommunity_id: string): Promise<User[]> {
return this.userModel.find({ user_type: 4 }).exec();
@ -79,5 +87,33 @@ export class UsersService {
return this.userModel.find({ user_type: 2 }).exec();
}
//find admin de comunidad
async allUsersAdminComunidad(): Promise<User[]> {
return this.userModel.find({ user_type: 2 }).exec();
}
async testSendMail(user: UserDocument) {
let passwordEncriptada = Md5.init(user.password);
user.password = passwordEncriptada;
this.userModel.create(user)
/*.then(() => {
} );*/
const pattern = { cmd: 'html' };
const payload = { email: user['email'], name: user['name'] };
return this.clientNotificationtApp
.send<string>(pattern, payload)
.pipe(
map((message: string) => ({ message })),
);
}
async findCommunityUser(community_id: string, user_type: number): Promise<User> {
return this.userModel.findOne({ community_id: community_id, user_type: user_type }).exec();
}
}

View File

@ -0,0 +1,489 @@
[
{
"name": "San José",
"code": "101",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Escazú",
"code": "102",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Desamparados",
"code": "103",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Puriscal",
"code": "104",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Tarrazú",
"code": "105",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Aserrí­",
"code": "106",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Mora",
"code": "107",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Goicoechea",
"code": "108",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Santa Ana",
"code": "109",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Alajuelita",
"code": "110",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Vasquez de Coronado",
"code": "111",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Acosta",
"code": "112",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Tibás",
"code": "113",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Moravia",
"code": "114",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Montes de Oca",
"code": "115",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Turrubares",
"code": "116",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Dota",
"code": "117",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Curridabat",
"code": "118",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Pérez Zeledón",
"code": "119",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "León Cortés",
"code": "120",
"parentCode": "1",
"isoParent": "SJ"
},
{
"name": "Alajuela",
"code": "201",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "San Ramón",
"code": "202",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Grecia",
"code": "203",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "San Mateo",
"code": "204",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Atenas",
"code": "205",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Naranjo",
"code": "206",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Palmares",
"code": "207",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Poás",
"code": "208",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Orotina",
"code": "209",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "San Carlos",
"code": "210",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Alfaro Ruiz",
"code": "211",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Valverde Vega",
"code": "212",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Upala",
"code": "213",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Los Chiles",
"code": "214",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Guatuso",
"code": "215",
"parentCode": "2",
"isoParent": "ALAJ"
},
{
"name": "Cartago",
"code": "301",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Paraí­so",
"code": "302",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "La Unión",
"code": "303",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Jiménez",
"code": "304",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Turrialba",
"code": "305",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Alvarado",
"code": "306",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Oreamuno",
"code": "307",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "El Guarco",
"code": "308",
"parentCode": "3",
"isoParent": "CAR"
},
{
"name": "Heredia",
"code": "401",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Barva",
"code": "402",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Santo Domingo",
"code": "403",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Santa Bárbara",
"code": "404",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "San Rafael",
"code": "405",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "San Isidro",
"code": "406",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Belén",
"code": "407",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Flores",
"code": "408",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "San Pablo",
"code": "409",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Sarapiquí­ ",
"code": "410",
"parentCode": "4",
"isoParent": "HER"
},
{
"name": "Liberia",
"code": "501",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Nicoya",
"code": "502",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Santa Cruz",
"code": "503",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Bagaces",
"code": "504",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Carrillo",
"code": "505",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Cañas",
"code": "506",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Abangares",
"code": "507",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Tilarán",
"code": "508",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Nandayure",
"code": "509",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "La Cruz",
"code": "510",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Hojancha",
"code": "511",
"parentCode": "5",
"isoParent": "GUANA"
},
{
"name": "Puntarenas",
"code": "601",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Esparza",
"code": "602",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Buenos Aires",
"code": "603",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Montes de Oro",
"code": "604",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Osa",
"code": "605",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Aguirre",
"code": "606",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Golfito",
"code": "607",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Coto Brus",
"code": "608",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Parrita",
"code": "609",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Corredores",
"code": "610",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Garabito",
"code": "611",
"parentCode": "6",
"isoParent": "PUNT"
},
{
"name": "Limón",
"code": "701",
"parentCode": "7",
"isoParent": "LIM"
},
{
"name": "Pococí­",
"code": "702",
"parentCode": "7",
"isoParent": "LIM"
},
{
"name": "Siquirres ",
"code": "703",
"parentCode": "7",
"isoParent": "LIM"
},
{
"name": "Talamanca",
"code": "704",
"parentCode": "7",
"isoParent": "LIM"
},
{
"name": "Matina",
"code": "705",
"parentCode": "7",
"isoParent": "LIM"
},
{
"name": "Guácimo",
"code": "706",
"parentCode": "7",
"isoParent": "LIM"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
[
{
"name":"San José",
"code":"1"
},
{
"name":"Alajuela",
"code":"2"
},
{
"name":"Cartago",
"code":"3"
},
{
"name":"Heredia",
"code":"4"
},
{
"name":"Guanacaste",
"code":"5"
},
{
"name":"Puntarenas",
"code":"6"
},
{
"name":"Limón",
"code":"7"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,14 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>PrimeReact - Sakai</title>
<title>Katoikia</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="icon" type="image/x-icon" href="%PUBLIC_URL%/favicon.ico" />
<link id="theme-link" rel="stylesheet" href="%PUBLIC_URL%/assets/themes/lara-light-indigo/theme.css">
<link id="theme-link" rel="stylesheet" href="%PUBLIC_URL%/assets/themes/khaki/theme.css">
</head>
<body>

View File

@ -31,6 +31,7 @@ import IconsDemo from './templates/IconsDemo';
import AdministradoresSistema from './components/AdministradoresSistema';
import AdministradoresComunidad from './components/AdministradoresComunidad';
import GuardasSeguridad from './components/GuardasSeguridad';
import Communities from './components/ComunidadViviendas';
import Crud from './pages/Crud';
import EmptyPage from './pages/EmptyPage';
@ -167,6 +168,7 @@ const App = () => {
{label: 'Administradores del sistema', icon: 'pi pi-fw pi-id-card', to: '/administradoresSistema'},
{label: 'Administradores de comunidad', icon: 'pi pi-fw pi-id-card', to: '/administradoresComunidad'},
{label: 'Guardas de seguridad', icon: 'pi pi-fw pi-id-card', to: '/guardasSeguridad'},
{label: 'Comunidadades', icon: 'pi pi-fw pi-id-card', to: '/comunidadesViviendas'},
{label: 'Log in', icon: 'pi pi-fw pi-id-card', to: '/logIn'}
]
},
@ -323,6 +325,7 @@ const App = () => {
<Route path="/administradoresSistema" component={AdministradoresSistema} />
<Route path="/administradoresComunidad" component={AdministradoresComunidad} />
<Route path="/guardasSeguridad" component={GuardasSeguridad} />
<Route path="/comunidadesViviendas" component={Communities} />
<Route path="/logIn" component={LogIn} />
</div>

View File

@ -9,7 +9,7 @@ export const AppConfig = (props) => {
const [active, setActive] = useState(false);
const [scale, setScale] = useState(14);
const [scales] = useState([12, 13, 14, 15, 16]);
const [theme, setTheme] = useState('lara-light-indigo');
const [theme, setTheme] = useState('khaki');
const config = useRef(null);
let outsideClickListener = useRef(null);
@ -187,6 +187,11 @@ export const AppConfig = (props) => {
<h6>Material Design</h6>
<div className="grid free-themes">
<div className="col-3 text-center">
<button className="p-link" onClick={e => changeTheme(e, 'md-light-indigo', 'light')}>
<img src="assets/layout/images/themes/md-light-indigo.svg" alt="Material Light Indigo" />
</button>
</div>
<div className="col-3 text-center">
<button className="p-link" onClick={e => changeTheme(e, 'md-light-indigo', 'light')}>
<img src="assets/layout/images/themes/md-light-indigo.svg" alt="Material Light Indigo" />
@ -253,6 +258,11 @@ export const AppConfig = (props) => {
<h6>PrimeOne Design - 2022</h6>
<div className="grid free-themes">
<div className="col-3 text-center">
<button className="p-link" onClick={(e) => changeTheme(e, 'khaki', 'light')}>
<img src="assets/layout/images/themes/lara-light-indigo.png" alt="Khaki" />
</button>
</div>
<div className="col-3 text-center">
<button className="p-link" onClick={(e) => changeTheme(e, 'lara-light-indigo', 'light')}>
<img src="assets/layout/images/themes/lara-light-indigo.png" alt="Lara Light Indigo" />

View File

@ -0,0 +1,346 @@
import React, { useEffect, useState, useRef } from 'react';
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Dropdown } from 'primereact/dropdown';
import { Toast } from 'primereact/toast';
import classNames from 'classnames';
const Communities = () => {
let emptyCommunity = {
name: '',
province: provinciaId,
canton: cantonId,
district: districtId,
phone: '',
num_houses: 0,
status: 'activo',
date_entry: new Date(),
houses: [],
};
const [communitiesList, setCommunitiesList] = useState([]);
const [community, setCommunity] = useState(emptyCommunity);
const [housesList, setHousesList] = useState([]);
const [provincesList, setProvincesList] = useState([]);
const [provinciaId, setProvinciaId] = useState(null);
const [cantonsList, setCantonsList] = useState([]);
const [cantonId, setCantonId] = useState(null);
const [districtsList, setDistrictsList] = useState([]);
const [districtId, setDistrictId] = useState(null);
const [codeHouses, setCodeHouses] = useState(null);
const [submitted, setSubmitted] = useState(false);
const toast = useRef(null);
const dt = useRef(null);
const p = provincesList.map((item) => ({
label: item.name,
value: item.code
}))
const c = cantonsList.map((item) => ({
label: item.name,
value: item.code,
parent: item.parentCode
}))
const d = districtsList.map((item) => ({
label: item.name,
value: item.code,
parent: item.parentCode
}))
useEffect(() => {
fillProvinces();
}, [])
useEffect(() => {
fillCantons();
}, [provinciaId])
useEffect(() => {
fillDistricts();
}, [cantonId])
async function getProvinces() {
const response = await fetch('assets/demo/data/provincias.json', { method: 'GET' });
return await response.json();
}
async function fillProvinces() {
const getP = await getProvinces();
setProvincesList(await getP);
}
async function getCantons() {
const response = await fetch('assets/demo/data/cantones.json', { method: 'GET' });
return await response.json();
}
async function fillCantons() {
const resJson = await getCantons();
const cantones = await resJson.filter(function (i, n) {
return i.parentCode === provinciaId;
});
setCantonsList(await cantones);
}
async function getDistricts() {
const response = await fetch('assets/demo/data/distritos.json', { method: 'GET' });
return await response.json();
}
async function fillDistricts() {
const resJson = await getDistricts();
const districts = await resJson.filter(function (i, n) {
return i.parentCode === cantonId;
});
setDistrictsList(await districts);
}
const handleProvinces = (event) => {
const getprovinciaId = event.target.value;
setProvinciaId(getprovinciaId);
}
const handleCanton = (event) => {
const getCantonId = event.target.value;
setCantonId(getCantonId);
}
const handleDistrict = (event) => {
const getDistrictId = event.target.value;
setDistrictId(getDistrictId);
}
const handleCodeHouses = (event) => {
const getcodehouse = event.target.value;
setCodeHouses(getcodehouse);
}
async function getCommunites() {
let response = await fetch('http://localhost:4000/community/allCommunities', { method: 'GET' });
let resJson = await response.json();
let pList = await getProvinces();
let cList = await getCantons();
let dList = await getDistricts();
await resJson.message.map((item) => {
item.province = pList.find(p => p.code === item.province).name
item.canton = cList.find(p => p.code === item.canton).name
item.district = dList.find(p => p.code === item.district).name
})
setCommunitiesList(await resJson.message);
}
useEffect(() => {
getCommunites();
}, [])
const saveCommunity = () => {
setSubmitted(true);
if (community.name.trim()) {
let _communities = [...communitiesList];
let _community = { ...community };
_community.province = provinciaId;
_community.canton = cantonId;
_community.district = districtId;
for (let i = 0; i < _community.num_houses; i++){
_community.houses.push({
number_house: codeHouses + (i+1),
})
}
// console.log(houses)
fetch('http://localhost:4000/community/createCommunity', {
cache: 'no-cache',
method: 'POST',
body: JSON.stringify(_community),
headers: {
'Content-Type': 'application/json'
}
})
.then(
function (response) {
if (response.status != 201)
console.log('Ocurrió un error con el servicio: ' + response.status);
else
return response.json();
}
)
.then(() => {
_community.province = provincesList.find(p => p.code === _community.province).name
_community.canton = cantonsList.find(p => p.code === _community.canton).name
_community.district = districtsList.find(p => p.code === _community.district).name
_communities.push(_community);
toast.current.show({ severity: 'success', summary: 'Registro exitoso', detail: 'Comunidad de vivienda Creada', life: 3000 });
setCommunitiesList(_communities);
setProvinciaId('');
setCantonId('');
setDistrictId('');
setCodeHouses('');
setCommunity(emptyCommunity);
})
.catch(
err => console.log('Ocurrió un error con el fetch', err)
);
}
}
const onInputChange = (e, name) => {
const val = (e.target && e.target.value) || '';
let _community = { ...community };
_community[`${name}`] = val;
setCommunity(_community);
}
return (
<div className="grid">
<div className="col-12">
<div className="card">
<h5>Comunidades de Viviendas</h5>
<DataTable value={communitiesList} scrollable scrollHeight="400px" scrollDirection="both" className="mt-3">
<Column field="name" header="Nombre" style={{ flexGrow: 1, flexBasis: '160px' }}></Column>
<Column field="province" header="Provincia" style={{ flexGrow: 1, flexBasis: '160px' }}></Column>
<Column field="canton" header="Cantón" style={{ flexGrow: 1, flexBasis: '160px' }}></Column>
<Column field="district" header="Distrito" style={{ flexGrow: 1, flexBasis: '160px' }}></Column>
<Column field="phone" header="Telefóno" style={{ flexGrow: 1, flexBasis: '180px' }}></Column>
<Column field="num_houses" header="Número de viviendas" style={{ flexGrow: 1, flexBasis: '180px' }}></Column>
<Column field="name_admin" header="Administrador" style={{ flexGrow: 1, flexBasis: '180px' }}></Column>
</DataTable>
</div>
</div>
<div className="col-12">
<div className="card">
<Toast ref={toast} />
<h5>Registro de comunidad de viviendas</h5>
<div className="p-fluid formgrid grid">
<div className="field col-12 md:col-12">
<label htmlFor="name">Nombre</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-home"></i>
</span>
<InputText id="name" value={community.name} onChange={(e) => onInputChange(e, 'name')} required autoFocus className={classNames( submitted && community.name==='' ? 'p-invalid' : '' )} />
</div>
{submitted && community.name==='' && <small className="p-invalid">Nombre es requirido.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="provinces">Provincia</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-map-marker"></i>
</span>
<Dropdown placeholder="--Seleccione Provincia--" value={provinciaId} options={p} onChange={handleProvinces} required autoFocus className={classNames({ 'p-invalid': submitted && !provinciaId } )} />
</div>
{submitted && !provinciaId && <small className="p-invalid">Provincia es requirido.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="cantons">Cantón</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-map-marker"></i>
</span>
<Dropdown placeholder="--Seleccione Cantón--" value={cantonId} options={c} onChange={handleCanton} required autoFocus className={classNames({ 'p-invalid': submitted && !cantonId } )}/>
</div>
{submitted && !cantonId && <small className="p-invalid">Cantón es requirido.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="districts">Distrito</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-map-marker"></i>
</span>
<Dropdown placeholder="--Seleccione Distrito--" value={districtId} options={d} onChange={handleDistrict} required autoFocus className={classNames({ 'p-invalid': submitted && !districtId } )}/>
</div>
{submitted && !districtId && <small className="p-invalid">Distrito es requirido.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="telefono">Número de Teléfono</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-phone"></i>
</span>
<InputText id="phone" value={community.phone} onChange={(e) => onInputChange(e, 'phone')} required autoFocus className={classNames({ 'p-invalid': submitted && community.phone==='' } )} />
</div>
{submitted && community.phone==='' && <small className="p-invalid">Número de teléfono es requirido.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="numHouse">Numero de Viviendas</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-hashtag"></i>
</span>
<InputText id="num_houses" value={community.num_houses} onChange={(e) => onInputChange(e, 'num_houses')} required autoFocus className={classNames({ 'p-invalid': submitted && community.num_houses < 1 } )} />
</div>
{submitted && community.num_houses < 1 && <small className="p-invalid">Número de viviendas es requirido y debe ser mayor que 0.</small>}
</div>
</div>
<div className="field col-12 md:col-6">
<label htmlFor="numHouse">Ingrese el prefijo para el código de las viviendas</label>
<div className="p-0 col-12 md:col-12">
<div className="p-inputgroup">
<span className="p-inputgroup-addon p-button p-icon-input-khaki">
<i className="pi pi-hashtag"></i>
</span>
<InputText id="code_houses" value={codeHouses} onChange={handleCodeHouses} required autoFocus className={classNames({ 'p-invalid': submitted && !codeHouses } )} />
</div>
{submitted && !codeHouses && <small className="p-invalid">El código para las viviendas es requirido.</small>}
</div>
</div>
<div className="col-12 md:col-12 py-2">
<Button label="Registrar" icon="pi pi-check" onClick={saveCommunity}></Button>
</div>
</div>
</div>
</div>
</div >
)
}
export default React.memo(Communities);