feat: add editable ordered trail pages
All checks were successful
CI / Validate frontend and API (push) Successful in 36s
CI / Build and publish Docker images (push) Successful in 20s

This commit is contained in:
Cauê Faleiros
2026-09-08 15:53:20 -03:00
parent aec9c001d3
commit f5aa826b32
7 changed files with 185 additions and 3 deletions

View File

@@ -0,0 +1,20 @@
-- Course categories were previously presented as trilhas but only stored a
-- label. Turn every existing label into a real ordered trilha without moving
-- or deleting any course.
insert into learning_paths (title, description, status, published_at)
select category.name, '', 'draft', null
from course_categories category
where not exists (
select 1 from learning_paths path where lower(path.title) = lower(category.name)
);
insert into learning_path_courses (learning_path_id, course_id, position)
select path.id, course.id, ranked.position
from (
select id, category, row_number() over (partition by category order by published_at nulls last, title, id)::integer as position
from courses
where status <> 'archived'
) ranked
join courses course on course.id = ranked.id
join learning_paths path on lower(path.title) = lower(ranked.category)
on conflict (learning_path_id, course_id) do nothing;

View File

@@ -271,7 +271,18 @@ export const manageCourseRoutes: FastifyPluginAsync = async (app) => {
const { courseId } = paramsSchema.parse(request.params);
const accessError = await assertCanManageCourse(courseId, request.user);
if (accessError) return reply.code(accessError.statusCode).send({ error: accessError.error });
await pool.query(`update courses set status = 'archived', published_at = null where id = $1`, [courseId]);
const client = await pool.connect();
try {
await client.query('begin');
await client.query(`update courses set status = 'archived', published_at = null where id = $1`, [courseId]);
await client.query('delete from learning_path_courses where course_id = $1', [courseId]);
await client.query('commit');
} catch (error) {
await client.query('rollback');
throw error;
} finally {
client.release();
}
await recordAudit({ actorId: request.user.id, action: 'course.archived', subjectType: 'course', subjectId: courseId, ipAddress: request.ip });
return reply.code(204).send();
});