diff --git a/.gitignore b/.gitignore index 7782e247adf6d07488035cd2c6d34d7cb396da0a..4c8aee9f16ba354c345e654dc17c8510b68420f5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /mobile/%ProgramData%/Microsoft/Windows/UUS/State/_active.uusver /mobile/assets/audio_prueba.mp3 .vscode/settings.json +web/.env diff --git a/backend/src/admin/admin.module.ts b/backend/src/admin/admin.module.ts index 59cc1c9007b2936d80be60b8b761415750395c74..0b35fe7f5093acdb01ec9566c382e68ddf2c5e08 100644 --- a/backend/src/admin/admin.module.ts +++ b/backend/src/admin/admin.module.ts @@ -7,11 +7,12 @@ import { Town } from 'src/town/entities/town.entity'; import { AuthAdminService } from 'src/auth/admin/authAdmin.service'; import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ controllers: [AdminController], providers: [AdminService, AuthAdminService, JwtService, EncryptionService], - imports: [TypeOrmModule.forFeature([Admin, Town])], + imports: [TypeOrmModule.forFeature([Admin, Town, AdminResetCode])], exports: [AdminService], }) export class AdminModule {} diff --git a/backend/src/admin/admin.service.ts b/backend/src/admin/admin.service.ts index 395962f7c60b0ca1d1e29145f4b85fe46944dab6..cd9e89d5e93740a4e1e331f7453bf94a9845e4e0 100644 --- a/backend/src/admin/admin.service.ts +++ b/backend/src/admin/admin.service.ts @@ -29,23 +29,29 @@ export class AdminService { } } + async adminExists(email: string): Promise { + const admin = await this.adminRepository.findOneBy({ email }); + if (admin) return true; + else return false; + } + async updatePassword(email: string, password: string) { await this.adminRepository.update({ email }, { password }); } - async findAllByTown(idTown : number) : Promise { + async findAllByTown(idTown: number): Promise { const res: any[] = await this.adminRepository .createQueryBuilder('admin') .leftJoinAndSelect('admin.idTown', 'town') .where('admin.idTown = :idTown', { idTown: idTown }) .getMany(); - const admins: GetAdminDto[] = res.map((admin) :GetAdminDto => { + const admins: GetAdminDto[] = res.map((admin): GetAdminDto => { return { email: admin.email, idTown: admin.idTown.townId, name: admin.name, lastName: admin.lastName, - status: admin.status + status: admin.status, }; }); return admins; diff --git a/backend/src/admin/entities/admin.entity.ts b/backend/src/admin/entities/admin.entity.ts index e5f847db9bad4bbcb2da7218da9435b339da50f3..78d90ff749ace982182c1bb7b493f70ada639f5c 100644 --- a/backend/src/admin/entities/admin.entity.ts +++ b/backend/src/admin/entities/admin.entity.ts @@ -1,7 +1,8 @@ +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; import { ADMIN_ROLE } from 'src/shared/enum/admin-role.enum'; import { UserStatus } from 'src/shared/enum/user-status.enum'; import { Town } from 'src/town/entities/town.entity'; -import { Entity, Column, PrimaryColumn, JoinColumn, ManyToOne } from 'typeorm'; +import { Entity, Column, PrimaryColumn, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; @Entity() export class Admin { @@ -34,4 +35,7 @@ export class Admin { default: 'active', }) status: UserStatus = UserStatus.ACTIVE; + + @OneToMany(() => AdminResetCode, (resetCode) => resetCode.admin) + resetCodes: AdminResetCode[]; } diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 4d8145bd3159c15e263a6c9195d2e0d45787eb7d..81fa18fa862dfe72676ce62934dfd92a67cd12f3 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -36,6 +36,7 @@ import { EmailService } from './email/email.service'; import { UserResetCode } from './auth/user/entities/user-reset-code.entity'; import { MailerModule } from '@nestjs-modules/mailer'; import { MailConstants } from './constants/mail.constants'; +import { AdminResetCode } from './auth/admin/entitites/admin-reset-code.entity'; import { UserConfirmCode } from './auth/user/entities/user-confirm-code.entity'; @Module({ @@ -63,6 +64,7 @@ import { UserConfirmCode } from './auth/user/entities/user-confirm-code.entity'; TravelPlace, Visited, UserResetCode, + AdminResetCode, UserConfirmCode, ], synchronize: DbConstants.DB_SYNC, diff --git a/backend/src/auth/admin/authAdmin.module.ts b/backend/src/auth/admin/authAdmin.module.ts index 8714d7afcd6fb48ddb869030381bc1d1af6703aa..19222fe3483cff3c52d70c767f255f63633bd5f1 100644 --- a/backend/src/auth/admin/authAdmin.module.ts +++ b/backend/src/auth/admin/authAdmin.module.ts @@ -8,11 +8,13 @@ import { AdminService } from 'src/admin/admin.service'; import { JwtService } from '@nestjs/jwt'; import { AuthAdminGuard } from './authAdmin.guard'; import { Town } from 'src/town/entities/town.entity'; +import { AdminResetCode } from './entitites/admin-reset-code.entity'; +import { EmailService } from 'src/email/email.service'; @Module({ controllers: [AuthAdminController], - providers: [AuthAdminService, AdminService, JwtService, EncryptionService, AuthAdminGuard], - imports: [TypeOrmModule.forFeature([Admin, Town])], + providers: [AuthAdminService, AdminService, JwtService, EncryptionService, AuthAdminGuard, EmailService], + imports: [TypeOrmModule.forFeature([Admin, Town, AdminResetCode])], exports: [AuthAdminService], }) export class AuthAdminModule {} diff --git a/backend/src/auth/admin/authAdmin.service.ts b/backend/src/auth/admin/authAdmin.service.ts index c11613a44a99bfa590e2159b75c1a17d2302c73e..6994cedc2434a7548b3d68270fc8b422b33c6aa2 100644 --- a/backend/src/auth/admin/authAdmin.service.ts +++ b/backend/src/auth/admin/authAdmin.service.ts @@ -10,10 +10,16 @@ import { Admin } from 'src/admin/entities/admin.entity'; import { ADMIN_ROLE } from 'src/shared/enum/admin-role.enum'; import { PayloadJwtDto } from 'src/shared/dto/payload-jwt.dto'; import { UpdatePwdDto } from '../user/dto/update-pwd.dto'; +import { randomInt } from 'crypto'; +import { InjectRepository } from '@nestjs/typeorm'; +import { AdminResetCode } from './entitites/admin-reset-code.entity'; +import { Repository } from 'typeorm'; +import { AdminResetPasswordDto } from './dto/admin-reset-password.dto'; @Injectable() export class AuthAdminService { constructor( + @InjectRepository(AdminResetCode) private adminResetCodeRepository: Repository, private adminService: AdminService, private jwtService: JwtService, private encryptionService: EncryptionService, @@ -26,6 +32,9 @@ export class AuthAdminService { }; const hashedPwd = await this.encryptionService.hashPassword(createAdminDto.password); createAdminDto.password = hashedPwd; + if (await this.adminService.adminExists(createAdminDto.email)) { + throw new HttpException('Admin already exists', HttpStatus.UNAUTHORIZED); + } await this.adminService.create(createAdminDto); const adminSigninResDto: AdminSigninResDto = await this.signIn(loginAdminDto); @@ -69,4 +78,42 @@ export class AuthAdminService { const hashedPwd = await this.encryptionService.hashPassword(updatePwdDto.newPassword); await this.adminService.updatePassword(email, hashedPwd); } + + async getResetPasswordCode(email: string): Promise { + const admin: Admin = await this.adminService.findOne(email); + if (!admin) throw new UnauthorizedException('Invalid email'); + + const resetCode: string = randomInt(100000, 999999).toString(); + + const expirationDate: Date = new Date(); + expirationDate.setMinutes(expirationDate.getMinutes() + 10); + + const existUserCode: AdminResetCode[] = await this.adminResetCodeRepository.findBy({ + admin: admin, + code: resetCode, + }); + + if (existUserCode.length > 0) { + await this.adminResetCodeRepository.delete(existUserCode[0].id); + } + await this.adminResetCodeRepository.save({ admin, code: resetCode, expirationDate }); + return resetCode; + } + + async resetPassword(adminResetPasswordDto: AdminResetPasswordDto): Promise { + const admin: Admin = await this.adminService.findOne(adminResetPasswordDto.email); + if (!admin) throw new UnauthorizedException('Invalid email'); + + const adminResetCode: AdminResetCode[] = await this.adminResetCodeRepository.findBy({ + admin: admin, + code: adminResetPasswordDto.resetCode, + }); + + if (adminResetCode.length === 0) throw new UnauthorizedException('Invalid code'); + if (adminResetCode[0].expirationDate < new Date()) throw new UnauthorizedException('Code expired'); + + const hashedPwd = await this.encryptionService.hashPassword(adminResetPasswordDto.newPassword); + await this.adminService.updatePassword(adminResetPasswordDto.email, hashedPwd); + await this.adminResetCodeRepository.delete(adminResetCode[0].id); + } } diff --git a/backend/src/auth/admin/authAdmincontroller.ts b/backend/src/auth/admin/authAdmincontroller.ts index 20ebd5a71370fb66eb3a1828b14c7072f0972785..2accf32dff13921b64992b75313acbae4b517e50 100644 --- a/backend/src/auth/admin/authAdmincontroller.ts +++ b/backend/src/auth/admin/authAdmincontroller.ts @@ -9,11 +9,17 @@ import { ADMIN_ROLE, ADMIN_ROLES, SUPERADMIN_ROLES } from 'src/shared/enum/admin import { AuthAdminGuard } from './authAdmin.guard'; import { UpdatePwdDto } from '../user/dto/update-pwd.dto'; import { CustomAdminRequest } from './interface/customAdminReq'; +import { AdminRequestCodeBody, AdminResetPasswordBody } from './dto/admin-reset-password.dto'; +import { GetResetCode } from './dto/get-reset-code.dto'; +import { EmailService } from 'src/email/email.service'; @Controller() @ApiTags('Create admin account and sign in as admin') export class AuthAdminController { - constructor(private readonly authAdminService: AuthAdminService) {} + constructor( + private readonly authAdminService: AuthAdminService, + private readonly mailService: EmailService, + ) {} @ApiBody({ type: CreateAdminDto }) @ApiCreatedResponse({ @@ -29,13 +35,14 @@ export class AuthAdminController { @Roles(SUPERADMIN_ROLES) @Post('admin/signup') @ApiBearerAuth('jwt') + @ApiUnauthorizedResponse() async signUp(@Body() createAdminDto: CreateAdminDto) { try { createAdminDto.role = ADMIN_ROLE.ADMIN; const accessToken = await this.authAdminService.signUp(createAdminDto); return { token: accessToken }; } catch (e) { - return e; + throw e; } } @ApiBody({ type: LoginAdminDto }) @@ -61,4 +68,21 @@ export class AuthAdminController { const email = req.admin.email; return this.authAdminService.changePassword(email, updatePwdDto); } + + @ApiBody({ type: AdminResetPasswordBody }) + @Post('admin/reset-password') + async resetPassword(@Body() resetPasswordDto: AdminResetPasswordBody) { + return this.authAdminService.resetPassword(resetPasswordDto); + } + + @ApiBody({ type: AdminRequestCodeBody }) + @Post('admin/get-reset-code') + async getResetCode(@Body() resetPasswordInfo: GetResetCode) { + try { + const code = await this.authAdminService.getResetPasswordCode(resetPasswordInfo.email); + await this.mailService.sendResetPasswordEmail(resetPasswordInfo.email, code); + } catch (e) { + throw e; + } + } } diff --git a/backend/src/auth/admin/dto/admin-reset-password.dto.ts b/backend/src/auth/admin/dto/admin-reset-password.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..37e830c3b54df6a3aa1b168f3bbaca8eaf98d874 --- /dev/null +++ b/backend/src/auth/admin/dto/admin-reset-password.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export interface AdminResetPasswordDto { + email: string; + resetCode: string; + newPassword: string; +} + +export class AdminResetPasswordBody { + @ApiProperty() + email: string; + @ApiProperty() + resetCode: string; + @ApiProperty() + newPassword: string; +} + +export class AdminRequestCodeBody { + @ApiProperty() + email: string; +} diff --git a/backend/src/auth/admin/dto/get-reset-code.dto.ts b/backend/src/auth/admin/dto/get-reset-code.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..756126a57e242b3ef09fb476d37c627e35b95420 --- /dev/null +++ b/backend/src/auth/admin/dto/get-reset-code.dto.ts @@ -0,0 +1,3 @@ +export interface GetResetCode { + email: string; +} diff --git a/backend/src/auth/admin/entitites/admin-reset-code.entity.ts b/backend/src/auth/admin/entitites/admin-reset-code.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f8593b3c7b60c38ae43cc7d8034f3db1033d343 --- /dev/null +++ b/backend/src/auth/admin/entitites/admin-reset-code.entity.ts @@ -0,0 +1,17 @@ +import { Admin } from 'src/admin/entities/admin.entity'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class AdminResetCode { + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(() => Admin, (admin) => admin.resetCodes) + admin: Admin; + + @Column() + code: string; + + @Column() + expirationDate: Date; +} diff --git a/backend/src/category/category.module.ts b/backend/src/category/category.module.ts index 403cc8879a3822cb9dfde37558c35ae21196fb7a..8ed00071a7ee680a5eeef7c73632c50f45a198d8 100644 --- a/backend/src/category/category.module.ts +++ b/backend/src/category/category.module.ts @@ -9,11 +9,12 @@ import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { AdminService } from 'src/admin/admin.service'; import { Admin } from 'src/admin/entities/admin.entity'; import { Town } from 'src/town/entities/town.entity'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ controllers: [CategoryController], providers: [CategoryService, AuthAdminService, JwtService, EncryptionService, AdminService], - imports: [TypeOrmModule.forFeature([Category, Admin, Town])], + imports: [TypeOrmModule.forFeature([Category, Admin, Town, AdminResetCode])], exports: [CategoryService], }) export class CategoryModule {} diff --git a/backend/src/database-seeder/database-seeder.module.ts b/backend/src/database-seeder/database-seeder.module.ts index f9c5fb36ef36e39196bac9b44cb5e71255b6ef13..0624ae92733677dfb24ab2c065c7f87853be61ae 100644 --- a/backend/src/database-seeder/database-seeder.module.ts +++ b/backend/src/database-seeder/database-seeder.module.ts @@ -1,7 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DatabaseSeederService } from './database-seeder.service'; - import { State } from 'src/state/entities/state.entity'; import { StateService } from 'src/state/state.service'; import { Town } from 'src/town/entities/town.entity'; @@ -21,6 +20,7 @@ import { PlaceTraduction } from 'src/place/entities/place-traduction.entity'; import { PointOfInterestTraduction } from 'src/pointOfInterest/entities/PointOfInterestTraduction.entity'; import { CategoryService } from 'src/category/category.service'; import { Category } from 'src/category/entities/category.entity'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ providers: [ @@ -48,6 +48,7 @@ import { Category } from 'src/category/entities/category.entity'; PlaceTraduction, PointOfInterestTraduction, Category, + AdminResetCode, ]), ], }) diff --git a/backend/src/database-seeder/database-seeder.service.ts b/backend/src/database-seeder/database-seeder.service.ts index 522177fc5ff96cfa311b59b79c02f880492d0e39..2d6e0d961cb571da27ba97058b7c0c0abf525759 100644 --- a/backend/src/database-seeder/database-seeder.service.ts +++ b/backend/src/database-seeder/database-seeder.service.ts @@ -119,7 +119,6 @@ export class DatabaseSeederService implements OnModuleInit { async onModuleInit() { await this.insertStates(); - await this.insertTowns(); await this.insertSuperAdmin(); await this.insertCategories(); } diff --git a/backend/src/place/place.module.ts b/backend/src/place/place.module.ts index dd524b38ef68f75822090abc0baf306984eeee58..31f722214a648554fff4ed8f703ab3f75afb2449 100644 --- a/backend/src/place/place.module.ts +++ b/backend/src/place/place.module.ts @@ -15,6 +15,7 @@ import { AdminService } from 'src/admin/admin.service'; import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { Admin } from 'src/admin/entities/admin.entity'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ controllers: [PlaceController], @@ -29,7 +30,16 @@ import { Admin } from 'src/admin/entities/admin.entity'; TownService, ], imports: [ - TypeOrmModule.forFeature([Place, AvailableDate, PlaceTraduction, Town, TownTraduction, State, Admin]), + TypeOrmModule.forFeature([ + Place, + AvailableDate, + PlaceTraduction, + Town, + TownTraduction, + State, + Admin, + AdminResetCode, + ]), TypeOrmModule, ], }) diff --git a/backend/src/pointOfInterest/PointOfInterest.module.ts b/backend/src/pointOfInterest/PointOfInterest.module.ts index aa78034b1c9aae1bd06baaf714ec5c86dc5db0eb..3f5b0101aa0dc4c56dd6c8017806779a52a7d9ad 100644 --- a/backend/src/pointOfInterest/PointOfInterest.module.ts +++ b/backend/src/pointOfInterest/PointOfInterest.module.ts @@ -14,6 +14,7 @@ import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { AdminService } from 'src/admin/admin.service'; import { Admin } from 'src/admin/entities/admin.entity'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ controllers: [PointOfInterestController], @@ -27,6 +28,7 @@ import { Admin } from 'src/admin/entities/admin.entity'; PlaceTraduction, Town, Admin, + AdminResetCode, ]), ], }) diff --git a/backend/src/town/town.module.ts b/backend/src/town/town.module.ts index 401bac47b4a71849a97ce3ae00c633cb95c19c3e..a15fec6573a782426042a7f8eae4c8a47f556665 100644 --- a/backend/src/town/town.module.ts +++ b/backend/src/town/town.module.ts @@ -11,11 +11,12 @@ import { AdminService } from 'src/admin/admin.service'; import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { Admin } from 'src/admin/entities/admin.entity'; +import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; @Module({ controllers: [TownController], providers: [TownService, StateService, TypeOrmModule, AuthAdminService, AdminService, JwtService, EncryptionService], - imports: [TypeOrmModule.forFeature([Town, State, TownTraduction, Admin]), TypeOrmModule], + imports: [TypeOrmModule.forFeature([Town, State, TownTraduction, Admin, AdminResetCode]), TypeOrmModule], exports: [TownService], }) export class TownModule {} diff --git a/web/package-lock.json b/web/package-lock.json index cfe258b941830817d6518c6cd3beccbf6bf541dc..32123da503259c7105c1e7e65dd183bf6e896a9f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -15,17 +15,20 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", + "@types/mapbox-gl": "^3.4.0", "@types/node": "^16.18.87", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "@vis.gl/react-google-maps": "^1.1.0", "axios": "^1.6.8", "buffer": "^6.0.3", + "mapbox-gl": "^3.7.0", "react": "^18.2.0", "react-data-table-component": "^7.6.2", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.51.2", + "react-map-gl": "^7.1.7", "react-pro-sidebar": "^1.1.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", @@ -3447,6 +3450,68 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz", + "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==" + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "19.3.3", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^3.0.0", + "minimist": "^1.2.8", + "rw": "^1.3.3", + "sort-object": "^3.0.3" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -4339,6 +4404,19 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/google.maps": { "version": "3.55.11", "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.55.11.tgz", @@ -4410,6 +4488,29 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "node_modules/@types/mapbox-gl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.4.0.tgz", + "integrity": "sha512-tbn++Mm94H1kE7W6FF0oVC9rMXHVzDDNUbS7KfBMRF8NV/8csFi+67ytKcZJ4LsrpsJ+8MC6Os6ZinEDCsrunw==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -4433,6 +4534,11 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" + }, "node_modules/@types/prettier": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", @@ -4545,6 +4651,14 @@ "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==", "peer": true }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5201,6 +5315,14 @@ "dequal": "^2.0.3" } }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -5402,6 +5524,14 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -6085,6 +6215,23 @@ "node": ">= 0.8" } }, + "node_modules/bytewise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", + "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", + "dependencies": { + "bytewise-core": "^1.2.2", + "typewise": "^1.0.3" + } + }, + "node_modules/bytewise-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", + "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", + "dependencies": { + "typewise-core": "^1.2" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -6207,6 +6354,11 @@ "node": ">=10" } }, + "node_modules/cheap-ruler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-4.0.0.tgz", + "integrity": "sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==" + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -6839,6 +6991,11 @@ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, "node_modules/cssdb": { "version": "7.11.1", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.1.tgz", @@ -7376,6 +7533,11 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "node_modules/earcut": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -8467,6 +8629,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9033,6 +9206,11 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9099,6 +9277,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -9231,6 +9422,11 @@ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -9902,6 +10098,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10038,6 +10242,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -10206,6 +10421,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -12409,6 +12632,11 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json-stringify-pretty-compact": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -12475,6 +12703,11 @@ "node": ">=4.0" } }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -12703,6 +12936,41 @@ "tmpl": "1.0.5" } }, + "node_modules/mapbox-gl": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.7.0.tgz", + "integrity": "sha512-dCbVyH1uGobwv6f4QKRv2Z2wuVT/RmspsudK3sTxGRFxZi6Pd2P9axdbVyZpmGddCAREy44pHhvzvO0qgpdKAg==", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^3.0.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "^3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "cheap-ruler": "^4.0.0", + "csscolorparser": "~1.0.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "serialize-to-js": "^3.1.2", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -12939,6 +13207,11 @@ "multicast-dns": "cli.js" } }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -13471,6 +13744,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -14841,6 +15126,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14938,6 +15228,11 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15023,6 +15318,11 @@ } ] }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -15299,6 +15599,29 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-map-gl": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.7.tgz", + "integrity": "sha512-mwjc0obkBJOXCcoXQr3VoLqmqwo9vS4bXfbGsdxXzEgVCv/PM0v+1QggL7W0d/ccIy+VCjbXNlGij+PENz6VNg==", + "dependencies": { + "@maplibre/maplibre-gl-style-spec": "^19.2.1", + "@types/mapbox-gl": ">=1.0.0" + }, + "peerDependencies": { + "mapbox-gl": ">=1.13.0", + "maplibre-gl": ">=1.13.0", + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + }, + "peerDependenciesMeta": { + "mapbox-gl": { + "optional": true + }, + "maplibre-gl": { + "optional": true + } + } + }, "node_modules/react-pro-sidebar": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/react-pro-sidebar/-/react-pro-sidebar-1.1.0.tgz", @@ -15673,6 +15996,14 @@ "node": ">=8" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve-url-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", @@ -15864,6 +16195,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-array-concat": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", @@ -16100,6 +16436,14 @@ "randombytes": "^2.1.0" } }, + "node_modules/serialize-to-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz", + "integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -16214,6 +16558,20 @@ "node": ">= 0.4" } }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -16297,6 +16655,38 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sort-asc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", + "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz", + "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-object": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz", + "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", + "dependencies": { + "bytewise": "^1.1.0", + "get-value": "^2.0.2", + "is-extendable": "^0.1.1", + "sort-asc": "^0.2.0", + "sort-desc": "^0.2.0", + "union-value": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -16389,6 +16779,40 @@ "wbuf": "^1.7.3" } }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -16931,6 +17355,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "dependencies": { + "kdbush": "^4.0.2" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -17276,6 +17708,11 @@ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -17541,6 +17978,19 @@ "node": ">=4.2.0" } }, + "node_modules/typewise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", + "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", + "dependencies": { + "typewise-core": "^1.2.0" + } + }, + "node_modules/typewise-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -17596,6 +18046,20 @@ "node": ">=4" } }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -17749,6 +18213,16 @@ "node": ">= 0.8" } }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/web/package.json b/web/package.json index d6ec7c7b90bca281a0926ca69ddfbf0e0cf31336..11e4fac2eea231da818bd0aae8b3df50e5797641 100644 --- a/web/package.json +++ b/web/package.json @@ -10,17 +10,20 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", + "@types/mapbox-gl": "^3.4.0", "@types/node": "^16.18.87", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "@vis.gl/react-google-maps": "^1.1.0", "axios": "^1.6.8", "buffer": "^6.0.3", + "mapbox-gl": "^3.7.0", "react": "^18.2.0", "react-data-table-component": "^7.6.2", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.51.2", + "react-map-gl": "^7.1.7", "react-pro-sidebar": "^1.1.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", diff --git a/web/src/App.tsx b/web/src/App.tsx index 043cee412f94a3ecfe87c9388c834f89e1a0c1bc..0c54f7ff25cb420f58ed174bc56b125909e14ef9 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,9 +1,9 @@ -import { RouterProvider } from 'react-router-dom'; -import './App.css'; -import { AuthContextProvider } from './context/auth_context'; -import { MessageContextProvider } from './context/message_context'; -import { router } from './router/router'; -import { ToastContainer } from 'react-toastify'; +import { RouterProvider } from "react-router-dom"; +import "./App.css"; +import { AuthContextProvider } from "./context/auth_context"; +import { MessageContextProvider } from "./context/message_context"; +import { router } from "./router/router"; +import { ToastContainer } from "react-toastify"; function App() { @@ -11,17 +11,17 @@ function App() {
- + + position="bottom-right" + autoClose={1000} + hideProgressBar={true} + closeOnClick + rtl={false} + pauseOnFocusLoss + />
); } diff --git a/web/src/components/admin_panel_places/admin_panel_place_register/admin_panel_place_register.tsx b/web/src/components/admin_panel_places/admin_panel_place_register/admin_panel_place_register.tsx index 9a2e93ccf5e6504b728bd85ffd5ced78cdf2b8ca..41a064917c6008a843c71e40e8b164f291b4f847 100644 --- a/web/src/components/admin_panel_places/admin_panel_place_register/admin_panel_place_register.tsx +++ b/web/src/components/admin_panel_places/admin_panel_place_register/admin_panel_place_register.tsx @@ -1,18 +1,24 @@ import { faWindowClose } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import "./assets/css/styles.css"; -import { MapComponent, Position } from "../../map/map"; +import { MapGoogleComponent, Position } from "../../map/map_google"; import { usePlace } from "../../../hooks/usePlace"; import { languaguesList } from "../../../constants/languages"; import { LoadingScreen } from "../../loading_screen/loading_screen"; import { MultipleImagesDropzone } from "../../multiple_images_dropzone/multiple_images_dropzone"; -import { AvailableDays, availableDaysList, EmptyPlace, Place } from "../../../infraestructure/entities/place"; +import { + AvailableDays, + availableDaysList, + EmptyPlace, + Place, +} from "../../../infraestructure/entities/place"; import { Category } from "../../../infraestructure/entities/category"; -import { Geocoding } from "../../geocoding/geocoding"; +import { GeocodingGoogle } from "../../geocoding/geocoding_google"; import { APIProvider } from "@vis.gl/react-google-maps"; -import { REACT_APP_GOOGLE_API_KEY } from "../../../constants/api_keys"; import { LoadingSpinner } from "../../loading_spinner/loading_spinner"; +import { MapBoxComponent } from "../../map/map_mapbox"; +import { GeocodingMapbox } from "../../geocoding/geocoding_mapbox"; interface props { setWindowVisibility: (visibility: boolean) => void; @@ -23,7 +29,14 @@ interface props { form?: Place; } -export const AdminPanelPlaceRegister = ({setWindowVisibility, idTown, categoriesList, forceRenderList, isRegister, form}: props) => { +export const AdminPanelPlaceRegister = ({ + setWindowVisibility, + idTown, + categoriesList, + forceRenderList, + isRegister, + form, +}: props) => { const { register, handleSubmit, @@ -43,264 +56,350 @@ export const AdminPanelPlaceRegister = ({setWindowVisibility, idTown, categories onSubmitRegister, onSubmitUpdate, clearErrors, - getValues + getValues, } = usePlace(forceRenderList, setWindowVisibility); - const [clickedCategories, setClickedCategories] = useState(new Array(categoriesList.length).fill(false)); + const [clickedCategories, setClickedCategories] = useState( + new Array(categoriesList.length).fill(false) + ); const [isLoading, setIsLoading] = useState(false); const [actualPlace, setActualPlace] = useState(null); - const [openHourInput, setOpenHourInput] = useState(''); - const [closeHourInput, setCloseHourInput] = useState(''); + const [openHourInput, setOpenHourInput] = useState(""); + const [closeHourInput, setCloseHourInput] = useState(""); //Maps const [position, setPosition] = useState(null); - const [ isSearching , setIsSearching] = useState(false); + const [isSearching, setIsSearching] = useState(false); const onClickCategory = (idCategory: number) => { const index = categoriesId.indexOf(idCategory); - const indexList = categoriesList.findIndex(category => category.idCategory === idCategory); - const clickedCategoriesBackup = clickedCategories.map((clickedCategory, index)=> { - if(index === indexList){ - return !clickedCategory; - }else{ - return clickedCategory; + const indexList = categoriesList.findIndex( + (category) => category.idCategory === idCategory + ); + const clickedCategoriesBackup = clickedCategories.map( + (clickedCategory, index) => { + if (index === indexList) { + return !clickedCategory; + } else { + return clickedCategory; + } } - }) - if(index > -1){ - setCategoriesId( - categoriesId.filter(cat => cat !== idCategory) - ); - }else{ - setCategoriesId( - [...categoriesId, - idCategory - ] - ) + ); + if (index > -1) { + setCategoriesId(categoriesId.filter((cat) => cat !== idCategory)); + } else { + setCategoriesId([...categoriesId, idCategory]); } setClickedCategories(clickedCategoriesBackup); - } + }; useEffect(() => { setIsLoading(true); const fetchData = async () => { - setValue('idTown', idTown); + setValue("idTown", idTown); if (!isRegister && form) { const placeGetted = await getPlaceById(form.idPlace || 0); - if(placeGetted){ + if (placeGetted) { setActualPlace(placeGetted); - setValue('idPlace', placeGetted.idPlace); - setValue('name', placeGetted.name); - setValue('descriptions', placeGetted.descriptions); - setDescriptions(placeGetted.descriptions || ['', '']); - setValue('openAt', placeGetted.openAt); - setOpenHourInput(placeGetted.openAt < 10 ? `0${placeGetted.openAt}:00` : `${placeGetted.openAt}:00`); - setCloseHourInput(placeGetted.closeAt < 10 ? `0${placeGetted.closeAt}:00` : `${placeGetted.closeAt}:00`); - setValue('closeAt', placeGetted.closeAt); - setValue('available', placeGetted.available); - setAvailableDays(placeGetted.available); - setPosition({latitude: Number(placeGetted.latitude), longitude: Number(placeGetted.longitude)}); - setValue('address', placeGetted.address); - const clickedCategoriesBackup : boolean[] = []; + setValue("idPlace", placeGetted.idPlace); + setValue("name", placeGetted.name); + setValue("descriptions", placeGetted.descriptions); + setDescriptions(placeGetted.descriptions || ["", ""]); + setValue("openAt", placeGetted.openAt); + setOpenHourInput( + placeGetted.openAt < 10 + ? `0${placeGetted.openAt}:00` + : `${placeGetted.openAt}:00` + ); + setCloseHourInput( + placeGetted.closeAt < 10 + ? `0${placeGetted.closeAt}:00` + : `${placeGetted.closeAt}:00` + ); + setValue("closeAt", placeGetted.closeAt); + setValue("available", placeGetted.available); + setAvailableDays(placeGetted.available); + setPosition({ + latitude: Number(placeGetted.latitude), + longitude: Number(placeGetted.longitude), + }); + setValue("address", placeGetted.address); + const clickedCategoriesBackup: boolean[] = []; categoriesList.forEach((category) => { - if(placeGetted.categoriesId.indexOf(category.idCategory) > -1){ + if (placeGetted.categoriesId.indexOf(category.idCategory) > -1) { clickedCategoriesBackup.push(true); - }else{ + } else { clickedCategoriesBackup.push(false); } - }); + }); setClickedCategories(clickedCategoriesBackup); setCategoriesId(placeGetted.categoriesId); } } else { - setValue('available', availableDays, { shouldValidate: true }); + setValue("available", availableDays, { shouldValidate: true }); } }; fetchData(); setIsLoading(false); - },[]); + }, []); return (
Registra el lugar - setWindowVisibility(false)}/> + setWindowVisibility(false)} + />
- {isLoading || (!isRegister && actualPlace===null) - ? - - : -
-
-
-
- Nombre del lugar + {isLoading || (!isRegister && actualPlace === null) ? ( + + ) : ( + +
+
+
Nombre del lugar
+ +

{errors.name?.message}

- -

{errors.name?.message}

-
-
-
- Descripción del lugar - + setLanguageDescriptionIndexSelected( + Number(e.target.value) + ) + } + > {languaguesList.map((language, index) => { return ( - + ); })} - -
- { - languaguesList.map((language, index) => { - if(index===languageDescriptionIndexSelected){ - return ( -