adsdasdasd

main
Elmer Yujra Condori 2 months ago
parent b62acb8d62
commit 5b16207e51

@ -57,8 +57,10 @@ class PreguntaController extends Controller
} }
} }
public function getPregunta($id)
{
public function getPregunta($id)
{
$pregunta = Pregunta::find($id); $pregunta = Pregunta::find($id);
if (!$pregunta) { if (!$pregunta) {
@ -68,30 +70,29 @@ class PreguntaController extends Controller
], 404); ], 404);
} }
$pregunta->imagenes = collect($pregunta->imagenes ?? [])->map(fn($path) => $path ? url(Storage::url($path)) : null);
$pregunta->imagenes_explicacion = collect($pregunta->imagenes_explicacion ?? [])->map(fn($path) => $path ? url(Storage::url($path)) : null);
return response()->json([ return response()->json([
'success' => true, 'success' => true,
'data' => $pregunta 'data' => $pregunta
]); ]);
} }
public function agregarPreguntaCurso(Request $request) public function agregarPreguntaCurso(Request $request)
{ {
try { try {
$user = auth()->user(); $user = auth()->user();
if (!$user->hasRole('administrador')) {
if (!$user->hasRole('Admin')) { return response()->json(['success' => false, 'message' => 'No autorizado'], 403);
return response()->json([
'success' => false,
'message' => 'No autorizado'
], 403);
} }
// Validación (igual que antes)
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
'curso_id' => 'required|exists:cursos,id', 'curso_id' => 'required|exists:cursos,id',
'enunciado' => 'required|string', 'enunciado' => 'required|string',
'enunciado_adicional' => 'nullable|string', 'enunciado_adicional' => 'nullable|string',
'opciones' => 'required|array|min:2', 'opciones' => 'required',
'opciones.*' => 'required|string',
'respuesta_correcta' => 'required|string', 'respuesta_correcta' => 'required|string',
'explicacion' => 'nullable|string', 'explicacion' => 'nullable|string',
'nivel_dificultad' => 'required|in:facil,medio,dificil', 'nivel_dificultad' => 'required|in:facil,medio,dificil',
@ -100,96 +101,81 @@ class PreguntaController extends Controller
'imagenes.*' => 'image|mimes:jpg,jpeg,png,gif,webp|max:2048', 'imagenes.*' => 'image|mimes:jpg,jpeg,png,gif,webp|max:2048',
'imagenes_explicacion' => 'nullable|array', 'imagenes_explicacion' => 'nullable|array',
'imagenes_explicacion.*' => 'image|mimes:jpg,jpeg,png,gif,webp|max:2048', 'imagenes_explicacion.*' => 'image|mimes:jpg,jpeg,png,gif,webp|max:2048',
], [
'opciones.required' => 'Debe agregar al menos 2 opciones',
'opciones.min' => 'Debe agregar al menos 2 opciones',
'respuesta_correcta.required' => 'Debe seleccionar una respuesta correcta',
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
'success' => false,
'errors' => $validator->errors()
], 422);
} }
// Validar que la respuesta correcta esté en las opciones // Opciones
if (!in_array($request->respuesta_correcta, $request->opciones)) { $opciones = is_string($request->opciones) ? json_decode($request->opciones, true) : $request->opciones;
return response()->json([ $opcionesValidas = array_map('trim', $opciones);
'success' => false,
'errors' => ['respuesta_correcta' => ['La respuesta correcta debe estar entre las opciones']] // Validar respuesta correcta
], 422); if (!in_array($request->respuesta_correcta, $opcionesValidas)) {
return response()->json(['success' => false, 'errors' => ['respuesta_correcta' => ['La respuesta correcta debe estar entre las opciones']]] ,422);
} }
// Procesar imágenes del enunciado // Procesar imágenes del enunciado y devolver URLs completas
$imagenesPaths = []; $imagenesUrls = [];
if ($request->hasFile('imagenes')) { if ($request->hasFile('imagenes')) {
foreach ($request->file('imagenes') as $imagen) { foreach ($request->file('imagenes') as $imagen) {
$path = $imagen->store('preguntas/enunciados', 'public'); $path = $imagen->store('preguntas/enunciados', 'public');
$imagenesPaths[] = $path; $imagenesUrls[] = url(Storage::url($path)); // URL completa
} }
} }
// Procesar imágenes de la explicación // Procesar imágenes de la explicación
$imagenesExplicacionPaths = []; $imagenesExplicacionUrls = [];
if ($request->hasFile('imagenes_explicacion')) { if ($request->hasFile('imagenes_explicacion')) {
foreach ($request->file('imagenes_explicacion') as $imagen) { foreach ($request->file('imagenes_explicacion') as $imagen) {
$path = $imagen->store('preguntas/explicaciones', 'public'); $path = $imagen->store('preguntas/explicaciones', 'public');
$imagenesExplicacionPaths[] = $path; $imagenesExplicacionUrls[] = url(Storage::url($path)); // URL completa
} }
} }
// Crear pregunta
$pregunta = Pregunta::create([ $pregunta = Pregunta::create([
'curso_id' => $request->curso_id, 'curso_id' => $request->curso_id,
'enunciado' => $request->enunciado, 'enunciado' => $request->enunciado,
'enunciado_adicional' => $request->enunciado_adicional, 'enunciado_adicional' => $request->enunciado_adicional,
'opciones' => $request->opciones, 'opciones' => $opcionesValidas,
'respuesta_correcta' => $request->respuesta_correcta, 'respuesta_correcta' => $request->respuesta_correcta,
'explicacion' => $request->explicacion, 'explicacion' => $request->explicacion,
'nivel_dificultad' => $request->nivel_dificultad, 'nivel_dificultad' => $request->nivel_dificultad,
'activo' => $request->boolean('activo'), 'activo' => $request->boolean('activo'),
'imagenes' => $imagenesPaths, 'imagenes' => $imagenesUrls,
'imagenes_explicacion' => $imagenesExplicacionPaths, 'imagenes_explicacion' => $imagenesExplicacionUrls,
]); ]);
Log::info('Pregunta creada', [ return response()->json(['success' => true, 'message' => 'Pregunta creada correctamente', 'data' => $pregunta], 201);
'pregunta_id' => $pregunta->id,
'curso_id' => $request->curso_id,
'user_id' => $user->id
]);
return response()->json([
'success' => true,
'message' => 'Pregunta creada correctamente',
'data' => $pregunta
], 201);
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error('Error creando pregunta', ['error' => $e->getMessage()]); Log::error('Error creando pregunta', ['error' => $e->getMessage()]);
return response()->json(['success' => false, 'message' => 'Error al crear la pregunta'], 500);
return response()->json([
'success' => false,
'message' => 'Error al crear la pregunta'
], 500);
} }
}
public function actualizarPregunta(Request $request, $id)
{
try {
$user = auth()->user();
if (!$user->hasRole('administrador')) {
return response()->json(['success' => false, 'message' => 'No autorizado'], 403);
} }
public function actualizarPregunta(Request $request, $id)
{
$pregunta = Pregunta::find($id); $pregunta = Pregunta::find($id);
if (!$pregunta) { if (!$pregunta) {
return response()->json([ return response()->json(['success' => false, 'message' => 'Pregunta no encontrada'], 404);
'success' => false,
'message' => 'Pregunta no encontrada'
], 404);
} }
// Validación (igual que antes)
$validator = Validator::make($request->all(), [ $validator = Validator::make($request->all(), [
'curso_id' => 'required|exists:cursos,id',
'enunciado' => 'required|string', 'enunciado' => 'required|string',
'enunciado_adicional' => 'nullable|string', 'enunciado_adicional' => 'nullable|string',
'opciones' => 'required|array|min:2', 'opciones' => 'required',
'opciones.*' => 'required|string',
'respuesta_correcta' => 'required|string', 'respuesta_correcta' => 'required|string',
'explicacion' => 'nullable|string', 'explicacion' => 'nullable|string',
'nivel_dificultad' => 'required|in:facil,medio,dificil', 'nivel_dificultad' => 'required|in:facil,medio,dificil',
@ -201,51 +187,40 @@ class PreguntaController extends Controller
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
'success' => false,
'errors' => $validator->errors()
], 422);
} }
// Validar que la respuesta correcta esté en las opciones // Opciones
if (!in_array($request->respuesta_correcta, $request->opciones)) { $opciones = is_string($request->opciones) ? json_decode($request->opciones, true) : $request->opciones;
return response()->json([ $opcionesValidas = array_map('trim', $opciones);
'success' => false,
'errors' => ['respuesta_correcta' => ['La respuesta correcta debe estar entre las opciones']] if (!in_array($request->respuesta_correcta, $opcionesValidas)) {
], 422); return response()->json(['success' => false, 'errors' => ['respuesta_correcta' => ['La respuesta correcta debe estar entre las opciones']]], 422);
} }
// Procesar nuevas imágenes del enunciado // Imágenes del enunciado
$imagenesActuales = $pregunta->imagenes ?? []; $imagenesActuales = $request->imagenes_existentes ?? $pregunta->imagenes ?? [];
if ($request->hasFile('imagenes')) { if ($request->hasFile('imagenes')) {
foreach ($request->file('imagenes') as $imagen) { foreach ($request->file('imagenes') as $imagen) {
$path = $imagen->store('preguntas/enunciados', 'public'); $path = $imagen->store('preguntas/enunciados', 'public');
$imagenesActuales[] = $path; $imagenesActuales[] = url(Storage::url($path));
} }
} }
// Procesar nuevas imágenes de la explicación // Imágenes de la explicación
$imagenesExplicacionActuales = $pregunta->imagenes_explicacion ?? []; $imagenesExplicacionActuales = $request->imagenes_explicacion_existentes ?? $pregunta->imagenes_explicacion ?? [];
if ($request->hasFile('imagenes_explicacion')) { if ($request->hasFile('imagenes_explicacion')) {
foreach ($request->file('imagenes_explicacion') as $imagen) { foreach ($request->file('imagenes_explicacion') as $imagen) {
$path = $imagen->store('preguntas/explicaciones', 'public'); $path = $imagen->store('preguntas/explicaciones', 'public');
$imagenesExplicacionActuales[] = $path; $imagenesExplicacionActuales[] = url(Storage::url($path));
} }
} }
// Si se enviaron imágenes existentes en edición
if ($request->has('imagenes_existentes')) {
$imagenesActuales = $request->imagenes_existentes;
}
if ($request->has('imagenes_explicacion_existentes')) {
$imagenesExplicacionActuales = $request->imagenes_explicacion_existentes;
}
$pregunta->update([ $pregunta->update([
'curso_id' => $request->curso_id,
'enunciado' => $request->enunciado, 'enunciado' => $request->enunciado,
'enunciado_adicional' => $request->enunciado_adicional, 'enunciado_adicional' => $request->enunciado_adicional,
'opciones' => $request->opciones, 'opciones' => $opcionesValidas,
'respuesta_correcta' => $request->respuesta_correcta, 'respuesta_correcta' => $request->respuesta_correcta,
'explicacion' => $request->explicacion, 'explicacion' => $request->explicacion,
'nivel_dificultad' => $request->nivel_dificultad, 'nivel_dificultad' => $request->nivel_dificultad,
@ -254,12 +229,13 @@ class PreguntaController extends Controller
'imagenes_explicacion' => $imagenesExplicacionActuales, 'imagenes_explicacion' => $imagenesExplicacionActuales,
]); ]);
return response()->json([ return response()->json(['success' => true, 'message' => 'Pregunta actualizada correctamente', 'data' => $pregunta]);
'success' => true,
'message' => 'Pregunta actualizada correctamente', } catch (\Exception $e) {
'data' => $pregunta Log::error('Error actualizando pregunta', ['error' => $e->getMessage()]);
]); return response()->json(['success' => false, 'message' => 'Error al actualizar la pregunta'], 500);
} }
}
public function eliminarPregunta($id) public function eliminarPregunta($id)
{ {

@ -5,27 +5,54 @@ namespace Database\Seeders;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Permission;
use Illuminate\Support\Facades\Schema; use Spatie\Permission\PermissionRegistrar;
class RoleSeeder extends Seeder class RoleSeeder extends Seeder
{ {
public function run(): void public function run(): void
{ {
// Evita problemas si se vuelve a ejecutar // 🔥 Limpiar cache de permisos para evitar conflictos
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); app()[PermissionRegistrar::class]->forgetCachedPermissions();
// Roles base /* ================= PERMISOS ================= */
$permissions = [
// Ejemplo: CRUD de preguntas
'ver-preguntas',
'crear-preguntas',
'editar-preguntas',
'eliminar-preguntas',
// Ejemplo: CRUD de cursos
'ver-cursos',
'crear-cursos',
'editar-cursos',
'eliminar-cursos',
];
foreach ($permissions as $permission) {
Permission::firstOrCreate([
'name' => $permission,
'guard_name' => 'web',
]);
}
/* ================= ROLES ================= */
$roles = [ $roles = [
'usuario', 'usuario' => [], // rol básico sin permisos
'administrador', 'administrador' => $permissions, // asigna todos los permisos al administrador
'superadmin', 'superadmin' => $permissions, // opcionalmente igual que administrador
]; ];
foreach ($roles as $role) { foreach ($roles as $roleName => $rolePermissions) {
Role::firstOrCreate([ $role = Role::firstOrCreate([
'name' => $role, 'name' => $roleName,
'guard_name' => 'web', 'guard_name' => 'web',
]); ]);
// Asignar permisos si los hay
if (!empty($rolePermissions)) {
$role->syncPermissions($rolePermissions);
}
} }
} }
} }

@ -1110,6 +1110,7 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -1119,6 +1120,7 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
}, },
@ -1224,6 +1226,7 @@
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -1233,7 +1236,6 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
}, },
@ -1246,6 +1248,7 @@
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"dependencies": { "dependencies": {
"string-width": "^4.2.0", "string-width": "^4.2.0",
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
@ -1257,6 +1260,7 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"color-name": "~1.1.4" "color-name": "~1.1.4"
}, },
@ -1268,7 +1272,8 @@
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
@ -1331,6 +1336,7 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -1348,7 +1354,8 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/dom-align": { "node_modules/dom-align": {
"version": "1.12.4", "version": "1.12.4",
@ -1380,7 +1387,8 @@
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/entities": { "node_modules/entities": {
"version": "7.0.1", "version": "7.0.1",
@ -1510,6 +1518,7 @@
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"locate-path": "^5.0.0", "locate-path": "^5.0.0",
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
@ -1583,6 +1592,7 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": "6.* || 8.* || >= 10.*" "node": "6.* || 8.* || >= 10.*"
} }
@ -1686,6 +1696,7 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -1722,6 +1733,7 @@
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"p-locate": "^4.1.0" "p-locate": "^4.1.0"
}, },
@ -1827,6 +1839,7 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"p-try": "^2.0.0" "p-try": "^2.0.0"
}, },
@ -1842,6 +1855,7 @@
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"p-limit": "^2.2.0" "p-limit": "^2.2.0"
}, },
@ -1854,6 +1868,7 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -1863,6 +1878,7 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -1885,7 +1901,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -1919,6 +1934,7 @@
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=10.13.0" "node": ">=10.13.0"
} }
@ -1980,6 +1996,7 @@
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -1988,7 +2005,8 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC" "license": "ISC",
"peer": true
}, },
"node_modules/resize-observer-polyfill": { "node_modules/resize-observer-polyfill": {
"version": "1.5.1", "version": "1.5.1",
@ -2060,7 +2078,8 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC" "license": "ISC",
"peer": true
}, },
"node_modules/shallow-equal": { "node_modules/shallow-equal": {
"version": "1.2.1", "version": "1.2.1",
@ -2091,6 +2110,7 @@
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0", "is-fullwidth-code-point": "^3.0.0",
@ -2105,6 +2125,7 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
}, },
@ -2168,7 +2189,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.27.0", "esbuild": "^0.27.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@ -2243,7 +2263,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz",
"integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==", "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.27", "@vue/compiler-dom": "3.5.27",
"@vue/compiler-sfc": "3.5.27", "@vue/compiler-sfc": "3.5.27",
@ -2335,13 +2354,15 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC" "license": "ISC",
"peer": true
}, },
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ansi-styles": "^4.0.0", "ansi-styles": "^4.0.0",
"string-width": "^4.1.0", "string-width": "^4.1.0",
@ -2355,13 +2376,15 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC" "license": "ISC",
"peer": true
}, },
"node_modules/yargs": { "node_modules/yargs": {
"version": "15.4.1", "version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cliui": "^6.0.0", "cliui": "^6.0.0",
"decamelize": "^1.2.0", "decamelize": "^1.2.0",
@ -2384,6 +2407,7 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"dependencies": { "dependencies": {
"camelcase": "^5.0.0", "camelcase": "^5.0.0",
"decamelize": "^1.2.0" "decamelize": "^1.2.0"

@ -14,6 +14,11 @@ const routes = [
component: Login, component: Login,
meta: { guest: true } meta: { guest: true }
}, },
{
path: '/unauthorized',
name: 'Unauthorized',
component: () => import('../views/403.vue')
},
{ {
path: '/usuario/dashboard', path: '/usuario/dashboard',
name: 'dashboard', name: 'dashboard',
@ -51,14 +56,14 @@ const routes = [
path: '/admin/dashboard/cursos/:id/preguntas', path: '/admin/dashboard/cursos/:id/preguntas',
name: 'CursoPreguntas', name: 'CursoPreguntas',
component: () => import('../views/administrador/cursos/PreguntasCursoView.vue'), component: () => import('../views/administrador/cursos/PreguntasCursoView.vue'),
meta: { requiresAuth: true } meta: { requiresAuth: true, role: 'administrador' }
}, },
{ {
path: '/admin/dashboard/procesos', path: '/admin/dashboard/procesos',
name: 'Procesos', name: 'Procesos',
component: () => import('../views/administrador/Procesos/ProcesosList.vue'), component: () => import('../views/administrador/Procesos/ProcesosList.vue'),
meta: { requiresAuth: true } meta: { requiresAuth: true, role: 'administrador' }
} }
] ]

@ -56,61 +56,57 @@ export const usePreguntaStore = defineStore('pregunta', {
/* =============================== /* ===============================
CREAR PREGUNTA (CON IMÁGENES) CREAR PREGUNTA (CON IMÁGENES)
=============================== */ =============================== */
async crearPregunta(data) { async crearPregunta(formData) {
this.loading = true this.loading = true
this.errors = null this.errors = null
try { try {
const formData = new FormData() const response = await api.post('/admin/preguntas', formData, {
headers: {
// Campos simples 'Content-Type': 'multipart/form-data'
formData.append('curso_id', data.curso_id)
formData.append('enunciado', data.enunciado)
formData.append('nivel_dificultad', data.nivel_dificultad)
if (data.enunciado_adicional)
formData.append('enunciado_adicional', data.enunciado_adicional)
if (data.respuesta_correcta)
formData.append('respuesta_correcta', data.respuesta_correcta)
if (data.explicacion)
formData.append('explicacion', data.explicacion)
if (data.opciones)
formData.append('opciones', JSON.stringify(data.opciones))
// Imágenes del enunciado
if (data.imagenes?.length) {
data.imagenes.forEach(img => {
formData.append('imagenes[]', img)
})
} }
// Imágenes de la explicación
if (data.imagenes_explicacion?.length) {
data.imagenes_explicacion.forEach(img => {
formData.append('imagenes_explicacion[]', img)
}) })
if (response.data.success) {
return response.data
} else {
throw new Error(response.data.message || 'Error al crear pregunta')
}
} catch (error) {
if (error.response?.status === 422) {
this.errors = error.response.data.errors
}
throw error
} finally {
this.loading = false
} }
},
const res = await api.post('/admin/preguntas', formData, { async actualizarPregunta(id, formData) {
headers: { 'Content-Type': 'multipart/form-data' }, this.loading = true
this.errors = null
try {
const response = await api.post(`/admin/preguntas/${id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}) })
this.preguntas.unshift(res.data.data) if (response.data.success) {
return res.data.data return response.data
} else {
throw new Error(response.data.message || 'Error al actualizar pregunta')
}
} catch (error) { } catch (error) {
this.errors = if (error.response?.status === 422) {
error.response?.status === 422 this.errors = error.response.data.errors
? error.response.data.errors }
: error.response?.data || error.message
throw error throw error
} finally { } finally {
this.loading = false this.loading = false
} }
}, },
/* =============================== /* ===============================
ACTUALIZAR PREGUNTA (SUMA IMÁGENES) ACTUALIZAR PREGUNTA (SUMA IMÁGENES)
=============================== */ =============================== */

@ -25,64 +25,61 @@
class="login-form" class="login-form"
> >
<!-- Nombre (solo registro) --> <!-- Nombre (solo registro) -->
<a-form-item <!-- Nombre (solo registro) -->
v-if="isRegister" <a-form-item v-if="isRegister" label="Nombre completo" name="name">
label="Nombre completo"
name="name"
>
<a-input <a-input
v-model:value="formState.name" v-model:value="formState.name"
placeholder="Ingrese su nombre" placeholder="Ingrese su nombre"
size="large" size="large"
autocomplete="name"
> >
<template #prefix> <template #prefix>
<UserOutlined /> <UserOutlined />
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<!-- Email --> <!-- Email -->
<a-form-item label="Correo electrónico" name="email"> <a-form-item label="Correo electrónico" name="email">
<a-input <a-input
v-model:value="formState.email" v-model:value="formState.email"
placeholder="ejemplo@correo.com" placeholder="ejemplo@correo.com"
size="large" size="large"
autocomplete="email"
> >
<template #prefix> <template #prefix>
<MailOutlined /> <MailOutlined />
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<!-- Contraseña --> <!-- Contraseña -->
<a-form-item label="Contraseña" name="password"> <a-form-item label="Contraseña" name="password">
<a-input-password <a-input-password
v-model:value="formState.password" v-model:value="formState.password"
placeholder="Ingrese su contraseña" placeholder="Ingrese su contraseña"
size="large" size="large"
autocomplete="current-password"
> >
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<!-- Confirmar contraseña (solo registro) --> <!-- Confirmar contraseña (solo registro) -->
<a-form-item <a-form-item v-if="isRegister" label="Confirmar contraseña" name="password_confirmation">
v-if="isRegister"
label="Confirmar contraseña"
name="password_confirmation"
>
<a-input-password <a-input-password
v-model:value="formState.password_confirmation" v-model:value="formState.password_confirmation"
placeholder="Confirme su contraseña" placeholder="Confirme su contraseña"
size="large" size="large"
autocomplete="new-password"
> >
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<!-- Recordarme (solo login) --> <!-- Recordarme (solo login) -->
<div v-if="!isRegister" class="remember-forgot"> <div v-if="!isRegister" class="remember-forgot">

@ -492,9 +492,9 @@
<img <img
v-for="(imagen, index) in preguntaSeleccionada.imagenes" v-for="(imagen, index) in preguntaSeleccionada.imagenes"
:key="index" :key="index"
:src="getImageUrl(imagen)" :src="imagen"
:alt="'Imagen ' + (index+1)" :alt="'Imagen ' + (index+1)"
@click="verImagen(getImageUrl(imagen))" @click="verImagen(imagen)"
class="clickable-image" class="clickable-image"
/> />
</div> </div>
@ -1002,9 +1002,11 @@ const submitPreguntaForm = async () => {
formData.append('explicacion', formPreguntaState.explicacion) formData.append('explicacion', formPreguntaState.explicacion)
} }
// Agregar opciones como JSON // Agregar opciones como array (no JSON stringificado)
const opcionesValidas = formPreguntaState.opciones.filter(op => op && op.trim() !== '') const opcionesValidas = formPreguntaState.opciones.filter(op => op && op.trim() !== '')
formData.append('opciones', JSON.stringify(opcionesValidas)) opcionesValidas.forEach((opcion, index) => {
formData.append(`opciones[${index}]`, opcion)
})
if (formPreguntaState.respuesta_correcta) { if (formPreguntaState.respuesta_correcta) {
formData.append('respuesta_correcta', formPreguntaState.respuesta_correcta) formData.append('respuesta_correcta', formPreguntaState.respuesta_correcta)
@ -1025,9 +1027,17 @@ const submitPreguntaForm = async () => {
}) })
if (isEditingPregunta.value) { if (isEditingPregunta.value) {
// Para edición, también enviar imágenes existentes que no fueron eliminadas // Para edición, también enviar imágenes existentes
if (formPreguntaState.imagenes_existentes?.length) {
formData.append('imagenes_existentes', JSON.stringify(formPreguntaState.imagenes_existentes)) formData.append('imagenes_existentes', JSON.stringify(formPreguntaState.imagenes_existentes))
}
if (formPreguntaState.imagenes_explicacion_existentes?.length) {
formData.append('imagenes_explicacion_existentes', JSON.stringify(formPreguntaState.imagenes_explicacion_existentes)) formData.append('imagenes_explicacion_existentes', JSON.stringify(formPreguntaState.imagenes_explicacion_existentes))
}
// IMPORTANTE: Enviar curso_id también en actualización
formData.append('curso_id', formPreguntaState.curso_id)
await preguntaStore.actualizarPregunta(formPreguntaState.id, formData) await preguntaStore.actualizarPregunta(formPreguntaState.id, formData)
message.success('Pregunta actualizada correctamente') message.success('Pregunta actualizada correctamente')
@ -1043,8 +1053,16 @@ const submitPreguntaForm = async () => {
} catch (error) { } catch (error) {
console.error('Error al guardar pregunta:', error) console.error('Error al guardar pregunta:', error)
// Mostrar errores específicos del backend si existen
if (error.response && error.response.data.errors) {
const errors = error.response.data.errors
Object.values(errors).forEach(errorList => {
errorList.forEach(err => message.error(err))
})
} else {
message.error('Error al guardar la pregunta') message.error('Error al guardar la pregunta')
} }
}
} }
const togglePreguntaStatus = async (pregunta) => { const togglePreguntaStatus = async (pregunta) => {

Loading…
Cancel
Save