diff --git a/admin/src/app/sugerencias/page.tsx b/admin/src/app/sugerencias/page.tsx
new file mode 100644
index 0000000..7ea71ea
--- /dev/null
+++ b/admin/src/app/sugerencias/page.tsx
@@ -0,0 +1,96 @@
+'use client';
+
+import { useState } from 'react';
+import { api } from '@/lib/api';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Textarea } from '@/components/ui/textarea';
+import { MessageSquarePlus, CheckCircle2 } from 'lucide-react';
+
+export default function SugerenciasPage() {
+ const [name, setName] = useState('');
+ const [message, setMessage] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [sent, setSent] = useState(false);
+ const [error, setError] = useState('');
+
+ async function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+ if (!message.trim()) return;
+ setLoading(true);
+ setError('');
+ try {
+ await api.post('/suggestions', { name: name.trim() || undefined, message: message.trim() });
+ setSent(true);
+ } catch {
+ setError('No se pudo enviar la sugerencia. Por favor intenta de nuevo.');
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ return (
+
+
+
+
+ Sugerencias
+
+ Ayúdanos a mejorar ProsApp con tus comentarios y sugerencias
+
+
+
+ {sent ? (
+
+
+
¡Gracias por tu sugerencia!
+
+ Tu mensaje fue recibido. Lo tendremos en cuenta para mejorar la app.
+
+
+
+ ) : (
+
+ )}
+
+
+
+ );
+}
diff --git a/admin/src/components/auth-guard.tsx b/admin/src/components/auth-guard.tsx
index e313638..d362960 100644
--- a/admin/src/components/auth-guard.tsx
+++ b/admin/src/components/auth-guard.tsx
@@ -9,7 +9,7 @@ export default function AuthGuard({ children }: { children: ReactNode }) {
const router = useRouter();
const pathname = usePathname();
- const isLoginPage = pathname === '/login';
+ const isLoginPage = pathname === '/login' || pathname === '/sugerencias';
useEffect(() => {
if (!isLoginPage && !isLoading && !user) router.replace('/login');
diff --git a/admin/src/components/sidebar.tsx b/admin/src/components/sidebar.tsx
index d2230b5..04ee767 100644
--- a/admin/src/components/sidebar.tsx
+++ b/admin/src/components/sidebar.tsx
@@ -39,7 +39,7 @@ export default function Sidebar({ children }: { children: React.ReactNode }) {
const { user, logout } = useAuth();
const [collapsed, setCollapsed] = useState(false);
- if (path === '/login') return <>{children}>;
+ if (path === '/login' || path === '/sugerencias') return <>{children}>;
return (
diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma
index a9262d8..1bacc83 100644
--- a/backend/prisma/schema.prisma
+++ b/backend/prisma/schema.prisma
@@ -185,6 +185,13 @@ model settings {
updated_at DateTime @default(now()) @db.Timestamptz(6)
}
+model suggestions {
+ id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
+ name String? @db.VarChar(255)
+ message String
+ created_at DateTime @default(now()) @db.Timestamptz(6)
+}
+
model specializations {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
professional_id String @db.Uuid
diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts
index 6bdee9b..1a34e33 100644
--- a/backend/src/app.module.ts
+++ b/backend/src/app.module.ts
@@ -13,6 +13,7 @@ import { ProfessionsModule } from './professions/professions.module';
import { StorageModule } from './storage/storage.module';
import { NotificationsModule } from './notifications/notifications.module';
import { SmsModule } from './sms/sms.module';
+import { SuggestionsModule } from './suggestions/suggestions.module';
@Module({
imports: [
@@ -30,6 +31,7 @@ import { SmsModule } from './sms/sms.module';
StorageModule,
NotificationsModule,
SmsModule,
+ SuggestionsModule,
],
})
export class AppModule {}
diff --git a/backend/src/suggestions/dto/suggestion.dto.ts b/backend/src/suggestions/dto/suggestion.dto.ts
new file mode 100644
index 0000000..5d97834
--- /dev/null
+++ b/backend/src/suggestions/dto/suggestion.dto.ts
@@ -0,0 +1,11 @@
+import { IsString, IsOptional, MinLength } from 'class-validator';
+
+export class CreateSuggestionDto {
+ @IsString()
+ @IsOptional()
+ name?: string;
+
+ @IsString()
+ @MinLength(1)
+ message: string;
+}
diff --git a/backend/src/suggestions/suggestions.controller.ts b/backend/src/suggestions/suggestions.controller.ts
new file mode 100644
index 0000000..3dae938
--- /dev/null
+++ b/backend/src/suggestions/suggestions.controller.ts
@@ -0,0 +1,23 @@
+import { Controller, Get, Post, Body, UseGuards, Query } from '@nestjs/common';
+import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
+import { SuggestionsService } from './suggestions.service';
+import { CreateSuggestionDto } from './dto/suggestion.dto';
+import { JwtAuthGuard } from '../auth/jwt-auth.guard';
+
+@ApiTags('Suggestions')
+@Controller('suggestions')
+export class SuggestionsController {
+ constructor(private suggestions: SuggestionsService) {}
+
+ @Post()
+ create(@Body() dto: CreateSuggestionDto) {
+ return this.suggestions.create(dto);
+ }
+
+ @Get()
+ @UseGuards(JwtAuthGuard)
+ @ApiBearerAuth()
+ findAll(@Query('page') page = '1', @Query('limit') limit = '50') {
+ return this.suggestions.findAll(+page, +limit);
+ }
+}
diff --git a/backend/src/suggestions/suggestions.module.ts b/backend/src/suggestions/suggestions.module.ts
new file mode 100644
index 0000000..339667e
--- /dev/null
+++ b/backend/src/suggestions/suggestions.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { SuggestionsService } from './suggestions.service';
+import { SuggestionsController } from './suggestions.controller';
+
+@Module({
+ providers: [SuggestionsService],
+ controllers: [SuggestionsController],
+})
+export class SuggestionsModule {}
diff --git a/backend/src/suggestions/suggestions.service.ts b/backend/src/suggestions/suggestions.service.ts
new file mode 100644
index 0000000..b436f1f
--- /dev/null
+++ b/backend/src/suggestions/suggestions.service.ts
@@ -0,0 +1,26 @@
+import { Injectable } from '@nestjs/common';
+import { PrismaService } from '../prisma/prisma.service';
+
+@Injectable()
+export class SuggestionsService {
+ constructor(private prisma: PrismaService) {}
+
+ create(data: { name?: string; message: string }) {
+ return this.prisma.suggestions.create({ data });
+ }
+
+ findAll(page = 1, limit = 50) {
+ const skip = (page - 1) * limit;
+ return Promise.all([
+ this.prisma.suggestions.findMany({
+ skip,
+ take: limit,
+ orderBy: { created_at: 'desc' },
+ }),
+ this.prisma.suggestions.count(),
+ ]).then(([data, total]) => ({
+ data,
+ meta: { total, page, limit, totalPages: Math.ceil(total / limit) || 1 },
+ }));
+ }
+}