feat: add city/state/country to complex, new settings page with sidebar, and Biome linting
- Added city, state, and country optional fields to Complex model - Updated onboarding to include optional location fields - Created new settings page with sidebar navigation (Datos del Complejo, Canchas) - Replaced ESLint with Biome for frontend and backend linting - Added parallel dev script with concurrently - Migrated register-routes to use direct app.route() pattern
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import type { CreateSportInput } from '@repo/api-contract'
|
||||
import { Prisma } from '@/generated/prisma/client'
|
||||
import type { AppContext } from '@/types/hono'
|
||||
import { createSport } from '@/modules/sport/services/sport.service'
|
||||
import { Prisma } from '@/generated/prisma/client';
|
||||
import { createSport } from '@/modules/sport/services/sport.service';
|
||||
import type { AppContext } from '@/types/hono';
|
||||
import type { CreateSportInput } from '@repo/api-contract';
|
||||
|
||||
export async function createSportHandler(c: AppContext) {
|
||||
const payload = c.req.valid('json' as never) as CreateSportInput
|
||||
const payload = c.req.valid('json' as never) as CreateSportInput;
|
||||
|
||||
try {
|
||||
const sport = await createSport(payload)
|
||||
return c.json(sport, 201)
|
||||
const sport = await createSport(payload);
|
||||
return c.json(sport, 201);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
return c.json({ message: 'No se pudo crear el deporte.' }, 409)
|
||||
return c.json({ message: 'No se pudo crear el deporte.' }, 409);
|
||||
}
|
||||
throw error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AppContext } from '@/types/hono'
|
||||
import { listSports } from '@/modules/sport/services/sport.service'
|
||||
import { listSports } from '@/modules/sport/services/sport.service';
|
||||
import type { AppContext } from '@/types/hono';
|
||||
|
||||
export async function listSportsHandler(c: AppContext) {
|
||||
const sports = await listSports()
|
||||
return c.json(sports)
|
||||
const sports = await listSports();
|
||||
return c.json(sports);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import type { UpdateSportInput } from '@repo/api-contract'
|
||||
import { Prisma } from '@/generated/prisma/client'
|
||||
import type { AppContext } from '@/types/hono'
|
||||
import { getSportById, updateSport } from '@/modules/sport/services/sport.service'
|
||||
import { Prisma } from '@/generated/prisma/client';
|
||||
import { getSportById, updateSport } from '@/modules/sport/services/sport.service';
|
||||
import type { AppContext } from '@/types/hono';
|
||||
import type { UpdateSportInput } from '@repo/api-contract';
|
||||
|
||||
type SportIdParams = { id: string }
|
||||
type SportIdParams = { id: string };
|
||||
|
||||
export async function updateSportHandler(c: AppContext) {
|
||||
const { id } = c.req.valid('param' as never) as SportIdParams
|
||||
const payload = c.req.valid('json' as never) as UpdateSportInput
|
||||
const { id } = c.req.valid('param' as never) as SportIdParams;
|
||||
const payload = c.req.valid('json' as never) as UpdateSportInput;
|
||||
|
||||
const existing = await getSportById(id)
|
||||
const existing = await getSportById(id);
|
||||
|
||||
if (!existing) {
|
||||
return c.json({ message: 'Deporte no encontrado.' }, 404)
|
||||
return c.json({ message: 'Deporte no encontrado.' }, 404);
|
||||
}
|
||||
|
||||
try {
|
||||
const sport = await updateSport(id, payload)
|
||||
return c.json(sport)
|
||||
const sport = await updateSport(id, payload);
|
||||
return c.json(sport);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
return c.json({ message: 'No se pudo actualizar el deporte.' }, 409)
|
||||
return c.json({ message: 'No se pudo actualizar el deporte.' }, 409);
|
||||
}
|
||||
throw error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { v7 as uuidv7 } from 'uuid'
|
||||
import { Prisma } from '@/generated/prisma/client'
|
||||
import { db } from '@/lib/prisma'
|
||||
import { Prisma } from '@/generated/prisma/client';
|
||||
import { db } from '@/lib/prisma';
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
export type CreateSportInput = {
|
||||
name: string
|
||||
}
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type UpdateSportInput = {
|
||||
name?: string
|
||||
isActive?: boolean
|
||||
}
|
||||
name?: string;
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
function slugify(value: string): string {
|
||||
return value
|
||||
@@ -19,18 +19,15 @@ function slugify(value: string): string {
|
||||
.trim()
|
||||
.replace(/[^a-z0-9\s-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/-+/g, '-');
|
||||
}
|
||||
|
||||
async function buildUniqueSlug(
|
||||
source: string,
|
||||
excludeSportId?: string,
|
||||
): Promise<string> {
|
||||
const base = slugify(source)
|
||||
const fallback = base.length > 0 ? base : `sport-${uuidv7().slice(0, 8)}`
|
||||
async function buildUniqueSlug(source: string, excludeSportId?: string): Promise<string> {
|
||||
const base = slugify(source);
|
||||
const fallback = base.length > 0 ? base : `sport-${uuidv7().slice(0, 8)}`;
|
||||
|
||||
let candidate = fallback
|
||||
let index = 1
|
||||
let candidate = fallback;
|
||||
let index = 1;
|
||||
|
||||
while (true) {
|
||||
const existing = await db.sport.findFirst({
|
||||
@@ -45,23 +42,23 @@ async function buildUniqueSlug(
|
||||
: {}),
|
||||
},
|
||||
select: { id: true },
|
||||
})
|
||||
});
|
||||
|
||||
if (!existing) return candidate
|
||||
if (!existing) return candidate;
|
||||
|
||||
index += 1
|
||||
candidate = `${fallback}-${index}`
|
||||
index += 1;
|
||||
candidate = `${fallback}-${index}`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function listSports() {
|
||||
return db.sport.findMany({
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export async function createSport(input: CreateSportInput) {
|
||||
const slug = await buildUniqueSlug(input.name)
|
||||
const slug = await buildUniqueSlug(input.name);
|
||||
|
||||
return db.sport.create({
|
||||
data: {
|
||||
@@ -70,27 +67,27 @@ export async function createSport(input: CreateSportInput) {
|
||||
slug,
|
||||
isActive: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSportById(id: string) {
|
||||
return db.sport.findUnique({ where: { id } })
|
||||
return db.sport.findUnique({ where: { id } });
|
||||
}
|
||||
|
||||
export async function updateSport(id: string, input: UpdateSportInput) {
|
||||
const data: Prisma.SportUncheckedUpdateInput = {}
|
||||
const data: Prisma.SportUncheckedUpdateInput = {};
|
||||
|
||||
if (input.name) {
|
||||
data.name = input.name
|
||||
data.slug = await buildUniqueSlug(input.name, id)
|
||||
data.name = input.name;
|
||||
data.slug = await buildUniqueSlug(input.name, id);
|
||||
}
|
||||
|
||||
if (input.isActive !== undefined) {
|
||||
data.isActive = input.isActive
|
||||
data.isActive = input.isActive;
|
||||
}
|
||||
|
||||
return db.sport.update({
|
||||
where: { id },
|
||||
data,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,30 +1,25 @@
|
||||
import { zValidator } from '@hono/zod-validator'
|
||||
import { createSportSchema, updateSportSchema } from '@repo/api-contract'
|
||||
import { Hono } from 'hono'
|
||||
import { z } from 'zod'
|
||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
||||
import { requireSuperAdmin } from '@/middlewares/require-super-admin.middleware'
|
||||
import { createSportHandler } from '@/modules/sport/handlers/create-sport.handler'
|
||||
import { listSportsHandler } from '@/modules/sport/handlers/list-sports.handler'
|
||||
import { updateSportHandler } from '@/modules/sport/handlers/update-sport.handler'
|
||||
import type { AppEnv } from '@/types/hono'
|
||||
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||
import { requireSuperAdmin } from '@/middlewares/require-super-admin.middleware';
|
||||
import { createSportHandler } from '@/modules/sport/handlers/create-sport.handler';
|
||||
import { listSportsHandler } from '@/modules/sport/handlers/list-sports.handler';
|
||||
import { updateSportHandler } from '@/modules/sport/handlers/update-sport.handler';
|
||||
import type { AppEnv } from '@/types/hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { createSportSchema, updateSportSchema } from '@repo/api-contract';
|
||||
import { Hono } from 'hono';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const sportRoutes = new Hono<AppEnv>()
|
||||
const sportIdParamsSchema = z.object({ id: z.uuid() })
|
||||
export const sportRoutes = new Hono<AppEnv>();
|
||||
const sportIdParamsSchema = z.object({ id: z.uuid() });
|
||||
|
||||
sportRoutes.use('*', requireAuth)
|
||||
sportRoutes.use('*', requireAuth);
|
||||
|
||||
sportRoutes.get('/', listSportsHandler)
|
||||
sportRoutes.post(
|
||||
'/',
|
||||
requireSuperAdmin,
|
||||
zValidator('json', createSportSchema),
|
||||
createSportHandler,
|
||||
)
|
||||
sportRoutes.get('/', listSportsHandler);
|
||||
sportRoutes.post('/', requireSuperAdmin, zValidator('json', createSportSchema), createSportHandler);
|
||||
sportRoutes.patch(
|
||||
'/:id',
|
||||
requireSuperAdmin,
|
||||
zValidator('param', sportIdParamsSchema),
|
||||
zValidator('json', updateSportSchema),
|
||||
updateSportHandler,
|
||||
)
|
||||
updateSportHandler
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user