feat: full locations CRUD + legal policies in settings
Backend: - CRUD completo para countries, regions y cities - GET /settings/policy/:key — página HTML pública para políticas - GET/PATCH /settings/policies/:key — admin endpoints protegidos Admin: - /locations: árbol interactivo País → Región → Ciudad con add/edit/delete - /settings: editor de Política de Privacidad y Términos con enlace público copiable - Sidebar: Ciudades → Ubicaciones Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
5c740420e9
commit
39c4301d1b
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { LocationsService } from './locations.service';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
@@ -8,25 +8,70 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
export class LocationsController {
|
||||
constructor(private locations: LocationsService) {}
|
||||
|
||||
// Public reads
|
||||
@Get('countries')
|
||||
getCountries() {
|
||||
return this.locations.getCountries();
|
||||
}
|
||||
getCountries() { return this.locations.getCountries(); }
|
||||
|
||||
@Get('countries/:countryId/regions')
|
||||
getRegions(@Param('countryId') id: string) {
|
||||
return this.locations.getRegions(id);
|
||||
}
|
||||
getRegions(@Param('countryId') id: string) { return this.locations.getRegions(id); }
|
||||
|
||||
@Get('regions/:regionId/cities')
|
||||
getCities(@Param('regionId') id: string) {
|
||||
return this.locations.getCities(id);
|
||||
getCities(@Param('regionId') id: string) { return this.locations.getCities(id); }
|
||||
|
||||
// Countries CRUD
|
||||
@Post('countries')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
createCountry(@Body() body: { name: string }) {
|
||||
return this.locations.createCountry(body.name);
|
||||
}
|
||||
|
||||
@Patch('countries/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
updateCountry(@Param('id') id: string, @Body() body: { name: string }) {
|
||||
return this.locations.updateCountry(id, body.name);
|
||||
}
|
||||
|
||||
@Delete('countries/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
deleteCountry(@Param('id') id: string) {
|
||||
return this.locations.deleteCountry(id);
|
||||
}
|
||||
|
||||
// Regions CRUD
|
||||
@Post('regions')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
createRegion(@Body() body: { country_id: string; name: string }) {
|
||||
return this.locations.createRegion(body.country_id, body.name);
|
||||
}
|
||||
|
||||
@Patch('regions/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
updateRegion(@Param('id') id: string, @Body() body: { name: string }) {
|
||||
return this.locations.updateRegion(id, body.name);
|
||||
}
|
||||
|
||||
@Delete('regions/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
deleteRegion(@Param('id') id: string) {
|
||||
return this.locations.deleteRegion(id);
|
||||
}
|
||||
|
||||
// Cities CRUD
|
||||
@Post('cities')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
createCity(@Body() body: { region_id: string; name: string; latitude?: number; longitude?: number }) {
|
||||
return this.locations.createCity(body);
|
||||
}
|
||||
|
||||
@Patch('cities/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
updateCity(@Param('id') id: string, @Body() body: { name?: string; latitude?: number; longitude?: number }) {
|
||||
return this.locations.updateCity(id, body);
|
||||
}
|
||||
|
||||
@Delete('cities/:id')
|
||||
@UseGuards(JwtAuthGuard) @ApiBearerAuth()
|
||||
deleteCity(@Param('id') id: string) {
|
||||
return this.locations.deleteCity(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,28 +6,82 @@ export class LocationsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
getCountries() {
|
||||
return this.prisma.countries.findMany({ include: { regions: { include: { cities: true } } } });
|
||||
return this.prisma.countries.findMany({
|
||||
include: { regions: { include: { cities: true }, orderBy: { name: 'asc' } } },
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
getRegions(countryId: string) {
|
||||
return this.prisma.regions.findMany({ where: { country_id: countryId }, include: { cities: true } });
|
||||
return this.prisma.regions.findMany({
|
||||
where: { country_id: countryId },
|
||||
include: { cities: { orderBy: { name: 'asc' } } },
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
getCities(regionId: string) {
|
||||
return this.prisma.cities.findMany({ where: { region_id: regionId } });
|
||||
return this.prisma.cities.findMany({ where: { region_id: regionId }, orderBy: { name: 'asc' } });
|
||||
}
|
||||
|
||||
// Countries
|
||||
async createCountry(name: string) {
|
||||
const existing = await this.prisma.countries.findUnique({ where: { name } });
|
||||
if (existing) throw new ConflictException('Ya existe este país');
|
||||
return this.prisma.countries.create({ data: { name } });
|
||||
}
|
||||
|
||||
async updateCountry(id: string, name: string) {
|
||||
const country = await this.prisma.countries.findUnique({ where: { id } });
|
||||
if (!country) throw new NotFoundException('País no encontrado');
|
||||
return this.prisma.countries.update({ where: { id }, data: { name } });
|
||||
}
|
||||
|
||||
async deleteCountry(id: string) {
|
||||
const country = await this.prisma.countries.findUnique({ where: { id } });
|
||||
if (!country) throw new NotFoundException('País no encontrado');
|
||||
return this.prisma.countries.delete({ where: { id } });
|
||||
}
|
||||
|
||||
// Regions
|
||||
async createRegion(countryId: string, name: string) {
|
||||
const country = await this.prisma.countries.findUnique({ where: { id: countryId } });
|
||||
if (!country) throw new NotFoundException('País no encontrado');
|
||||
const existing = await this.prisma.regions.findFirst({ where: { country_id: countryId, name } });
|
||||
if (existing) throw new ConflictException('Ya existe esta región en el país');
|
||||
return this.prisma.regions.create({ data: { country_id: countryId, name } });
|
||||
}
|
||||
|
||||
async updateRegion(id: string, name: string) {
|
||||
const region = await this.prisma.regions.findUnique({ where: { id } });
|
||||
if (!region) throw new NotFoundException('Región no encontrada');
|
||||
return this.prisma.regions.update({ where: { id }, data: { name } });
|
||||
}
|
||||
|
||||
async deleteRegion(id: string) {
|
||||
const region = await this.prisma.regions.findUnique({ where: { id } });
|
||||
if (!region) throw new NotFoundException('Región no encontrada');
|
||||
return this.prisma.regions.delete({ where: { id } });
|
||||
}
|
||||
|
||||
// Cities
|
||||
async createCity(data: { region_id: string; name: string; latitude?: number; longitude?: number }) {
|
||||
const region = await this.prisma.regions.findUnique({ where: { id: data.region_id } });
|
||||
if (!region) throw new NotFoundException('Región no encontrada');
|
||||
|
||||
const existing = await this.prisma.cities.findFirst({
|
||||
where: { region_id: data.region_id, name: data.name },
|
||||
});
|
||||
const existing = await this.prisma.cities.findFirst({ where: { region_id: data.region_id, name: data.name } });
|
||||
if (existing) throw new ConflictException('Ya existe esta ciudad en la región');
|
||||
return this.prisma.cities.create({ data });
|
||||
}
|
||||
|
||||
return this.prisma.cities.create({
|
||||
data: { region_id: data.region_id, name: data.name, latitude: data.latitude, longitude: data.longitude },
|
||||
});
|
||||
async updateCity(id: string, data: { name?: string; latitude?: number; longitude?: number }) {
|
||||
const city = await this.prisma.cities.findUnique({ where: { id } });
|
||||
if (!city) throw new NotFoundException('Ciudad no encontrada');
|
||||
return this.prisma.cities.update({ where: { id }, data });
|
||||
}
|
||||
|
||||
async deleteCity(id: string) {
|
||||
const city = await this.prisma.cities.findUnique({ where: { id } });
|
||||
if (!city) throw new NotFoundException('Ciudad no encontrada');
|
||||
return this.prisma.cities.delete({ where: { id } });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user