Add practice-existence flags and refresh API contracts.
Expose has_practice booleans for LMS and pre-exam hierarchy entities, wire SQL/repository mappings, and regenerate SQLC/Swagger artifacts. Also update the Resend sender display name for outbound emails. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9da9eb77e5
commit
bc2357374b
|
|
@ -12,9 +12,18 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: ExamPrepGetCatalogCourseByID :one
|
||||
SELECT *
|
||||
FROM exam_prep.catalog_courses
|
||||
WHERE id = $1;
|
||||
SELECT
|
||||
c.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
INNER JOIN exam_prep.units u ON u.id = m.unit_id
|
||||
WHERE u.catalog_course_id = c.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.catalog_courses c
|
||||
WHERE c.id = $1;
|
||||
|
||||
-- name: ExamPrepListCatalogCourses :many
|
||||
WITH catalog_course_counts AS (
|
||||
|
|
@ -38,6 +47,14 @@ SELECT
|
|||
COALESCE(cc.units_count, 0)::BIGINT AS units_count,
|
||||
COALESCE(cc.modules_count, 0)::BIGINT AS modules_count,
|
||||
COALESCE(cc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
INNER JOIN exam_prep.units u ON u.id = m.unit_id
|
||||
WHERE u.catalog_course_id = c.id
|
||||
) AS has_practice,
|
||||
c.created_at,
|
||||
c.updated_at
|
||||
FROM exam_prep.catalog_courses c
|
||||
|
|
|
|||
|
|
@ -16,9 +16,15 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: ExamPrepGetUnitModuleLessonByID :one
|
||||
SELECT *
|
||||
FROM exam_prep.unit_module_lessons
|
||||
WHERE id = $1;
|
||||
SELECT
|
||||
l.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
WHERE p.unit_module_lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.unit_module_lessons l
|
||||
WHERE l.id = $1;
|
||||
|
||||
-- name: ExamPrepListUnitModuleLessonIDsByUnitModule :many
|
||||
SELECT
|
||||
|
|
@ -39,6 +45,11 @@ SELECT
|
|||
l.thumbnail,
|
||||
l.description,
|
||||
l.sort_order,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
WHERE p.unit_module_lesson_id = l.id
|
||||
) AS has_practice,
|
||||
l.created_at,
|
||||
l.updated_at
|
||||
FROM exam_prep.unit_module_lessons l
|
||||
|
|
|
|||
|
|
@ -16,9 +16,16 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: ExamPrepGetUnitModuleByID :one
|
||||
SELECT *
|
||||
FROM exam_prep.unit_modules
|
||||
WHERE id = $1;
|
||||
SELECT
|
||||
m.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
WHERE l.unit_module_id = m.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.unit_modules m
|
||||
WHERE m.id = $1;
|
||||
|
||||
-- name: ExamPrepListUnitModuleIDsByUnit :many
|
||||
SELECT
|
||||
|
|
@ -51,6 +58,7 @@ SELECT
|
|||
m.sort_order,
|
||||
COALESCE(mc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
COALESCE(mc.practices_count, 0)::BIGINT AS practices_count,
|
||||
(COALESCE(mc.practices_count, 0)::BIGINT > 0) AS has_practice,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
FROM exam_prep.unit_modules m
|
||||
|
|
|
|||
|
|
@ -15,9 +15,17 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: ExamPrepGetUnitByID :one
|
||||
SELECT *
|
||||
FROM exam_prep.units
|
||||
WHERE id = $1;
|
||||
SELECT
|
||||
u.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
WHERE m.unit_id = u.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.units u
|
||||
WHERE u.id = $1;
|
||||
|
||||
-- name: ExamPrepListUnitIDsByCatalogCourse :many
|
||||
SELECT
|
||||
|
|
@ -52,6 +60,7 @@ SELECT
|
|||
COALESCE(uc.modules_count, 0)::BIGINT AS modules_count,
|
||||
COALESCE(uc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
COALESCE(uc.practices_count, 0)::BIGINT AS practices_count,
|
||||
(COALESCE(uc.practices_count, 0)::BIGINT > 0) AS has_practice,
|
||||
u.created_at,
|
||||
u.updated_at
|
||||
FROM exam_prep.units u
|
||||
|
|
|
|||
|
|
@ -15,9 +15,18 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: GetCourseByID :one
|
||||
SELECT *
|
||||
SELECT
|
||||
c.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM courses
|
||||
WHERE id = $1;
|
||||
c
|
||||
WHERE c.id = $1;
|
||||
|
||||
-- name: ListCourseIDsByProgram :many
|
||||
SELECT
|
||||
|
|
@ -65,7 +74,14 @@ SELECT
|
|||
WHERE
|
||||
p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL) AS practice_count
|
||||
AND p.lesson_id IS NULL) AS practice_count,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM
|
||||
courses c
|
||||
WHERE
|
||||
|
|
|
|||
|
|
@ -16,9 +16,16 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: GetLessonByID :one
|
||||
SELECT *
|
||||
SELECT
|
||||
l.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM lessons
|
||||
WHERE id = $1;
|
||||
l
|
||||
WHERE l.id = $1;
|
||||
|
||||
-- name: ListLessonsByModuleID :many
|
||||
SELECT
|
||||
|
|
@ -31,7 +38,12 @@ SELECT
|
|||
l.description,
|
||||
l.sort_order,
|
||||
l.created_at,
|
||||
l.updated_at
|
||||
l.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM
|
||||
lessons l
|
||||
WHERE
|
||||
|
|
|
|||
|
|
@ -16,9 +16,17 @@ RETURNING
|
|||
*;
|
||||
|
||||
-- name: GetModuleByID :one
|
||||
SELECT *
|
||||
SELECT
|
||||
m.*,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.module_id = m.id
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM modules
|
||||
WHERE id = $1;
|
||||
m
|
||||
WHERE m.id = $1;
|
||||
|
||||
-- name: ListModuleIDsByCourse :many
|
||||
SELECT
|
||||
|
|
@ -41,7 +49,13 @@ SELECT
|
|||
m.icon,
|
||||
m.sort_order,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
m.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.module_id = m.id
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM
|
||||
modules m
|
||||
WHERE
|
||||
|
|
|
|||
87
docs/docs.go
87
docs/docs.go
|
|
@ -9405,6 +9405,60 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"domain.DynamicElementDefinition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.DynamicElementInstance": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"value": {}
|
||||
}
|
||||
},
|
||||
"domain.DynamicQuestionPayload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"response": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementInstance"
|
||||
}
|
||||
},
|
||||
"stimulus": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementInstance"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.EmploymentType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -9638,6 +9692,9 @@ const docTemplate = `{
|
|||
"difficultyLevel": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamicPayload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -10927,6 +10984,9 @@ const docTemplate = `{
|
|||
"difficulty_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamic_payload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11046,6 +11106,12 @@ const docTemplate = `{
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"response_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11054,6 +11120,12 @@ const docTemplate = `{
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"stimulus_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -11409,6 +11481,9 @@ const docTemplate = `{
|
|||
"difficulty_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamic_payload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11500,6 +11575,12 @@ const docTemplate = `{
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"response_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11508,6 +11589,12 @@ const docTemplate = `{
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"stimulus_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9397,6 +9397,60 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"domain.DynamicElementDefinition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.DynamicElementInstance": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"value": {}
|
||||
}
|
||||
},
|
||||
"domain.DynamicQuestionPayload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"response": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementInstance"
|
||||
}
|
||||
},
|
||||
"stimulus": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementInstance"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.EmploymentType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -9630,6 +9684,9 @@
|
|||
"difficultyLevel": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamicPayload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -10919,6 +10976,9 @@
|
|||
"difficulty_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamic_payload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11038,6 +11098,12 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"response_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11046,6 +11112,12 @@
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"stimulus_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -11401,6 +11473,9 @@
|
|||
"difficulty_level": {
|
||||
"type": "string"
|
||||
},
|
||||
"dynamic_payload": {
|
||||
"$ref": "#/definitions/domain.DynamicQuestionPayload"
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11492,6 +11567,12 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"response_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -11500,6 +11581,12 @@
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"stimulus_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/domain.DynamicElementDefinition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -212,6 +212,42 @@ definitions:
|
|||
- password
|
||||
- team_role
|
||||
type: object
|
||||
domain.DynamicElementDefinition:
|
||||
properties:
|
||||
config:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
id:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
label:
|
||||
type: string
|
||||
required:
|
||||
type: boolean
|
||||
type: object
|
||||
domain.DynamicElementInstance:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
meta:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
value: {}
|
||||
type: object
|
||||
domain.DynamicQuestionPayload:
|
||||
properties:
|
||||
response:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementInstance'
|
||||
type: array
|
||||
stimulus:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementInstance'
|
||||
type: array
|
||||
type: object
|
||||
domain.EmploymentType:
|
||||
enum:
|
||||
- full_time
|
||||
|
|
@ -371,6 +407,8 @@ definitions:
|
|||
type: string
|
||||
difficultyLevel:
|
||||
type: string
|
||||
dynamicPayload:
|
||||
$ref: '#/definitions/domain.DynamicQuestionPayload'
|
||||
explanation:
|
||||
type: string
|
||||
id:
|
||||
|
|
@ -1240,6 +1278,8 @@ definitions:
|
|||
type: string
|
||||
difficulty_level:
|
||||
type: string
|
||||
dynamic_payload:
|
||||
$ref: '#/definitions/domain.DynamicQuestionPayload'
|
||||
explanation:
|
||||
type: string
|
||||
image_url:
|
||||
|
|
@ -1320,12 +1360,20 @@ definitions:
|
|||
items:
|
||||
type: string
|
||||
type: array
|
||||
response_schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementDefinition'
|
||||
type: array
|
||||
status:
|
||||
type: string
|
||||
stimulus_component_kinds:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
stimulus_schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementDefinition'
|
||||
type: array
|
||||
required:
|
||||
- display_name
|
||||
- key
|
||||
|
|
@ -1567,6 +1615,8 @@ definitions:
|
|||
type: string
|
||||
difficulty_level:
|
||||
type: string
|
||||
dynamic_payload:
|
||||
$ref: '#/definitions/domain.DynamicQuestionPayload'
|
||||
explanation:
|
||||
type: string
|
||||
image_url:
|
||||
|
|
@ -1627,12 +1677,20 @@ definitions:
|
|||
items:
|
||||
type: string
|
||||
type: array
|
||||
response_schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementDefinition'
|
||||
type: array
|
||||
status:
|
||||
type: string
|
||||
stimulus_component_kinds:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
stimulus_schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain.DynamicElementDefinition'
|
||||
type: array
|
||||
type: object
|
||||
handlers.validateQuestionTypeDefinitionReq:
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -57,14 +57,34 @@ func (q *Queries) ExamPrepDeleteCatalogCourse(ctx context.Context, id int64) err
|
|||
}
|
||||
|
||||
const ExamPrepGetCatalogCourseByID = `-- name: ExamPrepGetCatalogCourseByID :one
|
||||
SELECT id, name, description, thumbnail, sort_order, created_at, updated_at
|
||||
FROM exam_prep.catalog_courses
|
||||
WHERE id = $1
|
||||
SELECT
|
||||
c.id, c.name, c.description, c.thumbnail, c.sort_order, c.created_at, c.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
INNER JOIN exam_prep.units u ON u.id = m.unit_id
|
||||
WHERE u.catalog_course_id = c.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.catalog_courses c
|
||||
WHERE c.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ExamPrepGetCatalogCourseByID(ctx context.Context, id int64) (ExamPrepCatalogCourse, error) {
|
||||
type ExamPrepGetCatalogCourseByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ExamPrepGetCatalogCourseByID(ctx context.Context, id int64) (ExamPrepGetCatalogCourseByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, ExamPrepGetCatalogCourseByID, id)
|
||||
var i ExamPrepCatalogCourse
|
||||
var i ExamPrepGetCatalogCourseByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
|
|
@ -73,6 +93,7 @@ func (q *Queries) ExamPrepGetCatalogCourseByID(ctx context.Context, id int64) (E
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -126,6 +147,14 @@ SELECT
|
|||
COALESCE(cc.units_count, 0)::BIGINT AS units_count,
|
||||
COALESCE(cc.modules_count, 0)::BIGINT AS modules_count,
|
||||
COALESCE(cc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
INNER JOIN exam_prep.units u ON u.id = m.unit_id
|
||||
WHERE u.catalog_course_id = c.id
|
||||
) AS has_practice,
|
||||
c.created_at,
|
||||
c.updated_at
|
||||
FROM exam_prep.catalog_courses c
|
||||
|
|
@ -149,6 +178,7 @@ type ExamPrepListCatalogCoursesRow struct {
|
|||
UnitsCount int64 `json:"units_count"`
|
||||
ModulesCount int64 `json:"modules_count"`
|
||||
LessonsCount int64 `json:"lessons_count"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
|
@ -172,6 +202,7 @@ func (q *Queries) ExamPrepListCatalogCourses(ctx context.Context, arg ExamPrepLi
|
|||
&i.UnitsCount,
|
||||
&i.ModulesCount,
|
||||
&i.LessonsCount,
|
||||
&i.HasPractice,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
|
|||
|
|
@ -71,14 +71,33 @@ func (q *Queries) ExamPrepDeleteUnitModuleLesson(ctx context.Context, id int64)
|
|||
}
|
||||
|
||||
const ExamPrepGetUnitModuleLessonByID = `-- name: ExamPrepGetUnitModuleLessonByID :one
|
||||
SELECT id, unit_module_id, title, video_url, thumbnail, description, sort_order, created_at, updated_at
|
||||
FROM exam_prep.unit_module_lessons
|
||||
WHERE id = $1
|
||||
SELECT
|
||||
l.id, l.unit_module_id, l.title, l.video_url, l.thumbnail, l.description, l.sort_order, l.created_at, l.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
WHERE p.unit_module_lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.unit_module_lessons l
|
||||
WHERE l.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitModuleLessonByID(ctx context.Context, id int64) (ExamPrepUnitModuleLesson, error) {
|
||||
type ExamPrepGetUnitModuleLessonByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
UnitModuleID int64 `json:"unit_module_id"`
|
||||
Title string `json:"title"`
|
||||
VideoUrl pgtype.Text `json:"video_url"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitModuleLessonByID(ctx context.Context, id int64) (ExamPrepGetUnitModuleLessonByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, ExamPrepGetUnitModuleLessonByID, id)
|
||||
var i ExamPrepUnitModuleLesson
|
||||
var i ExamPrepGetUnitModuleLessonByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UnitModuleID,
|
||||
|
|
@ -89,6 +108,7 @@ func (q *Queries) ExamPrepGetUnitModuleLessonByID(ctx context.Context, id int64)
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -133,6 +153,11 @@ SELECT
|
|||
l.thumbnail,
|
||||
l.description,
|
||||
l.sort_order,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
WHERE p.unit_module_lesson_id = l.id
|
||||
) AS has_practice,
|
||||
l.created_at,
|
||||
l.updated_at
|
||||
FROM exam_prep.unit_module_lessons l
|
||||
|
|
@ -160,6 +185,7 @@ type ExamPrepListUnitModuleLessonsByUnitModuleIDRow struct {
|
|||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
|
@ -182,6 +208,7 @@ func (q *Queries) ExamPrepListUnitModuleLessonsByUnitModuleID(ctx context.Contex
|
|||
&i.Thumbnail,
|
||||
&i.Description,
|
||||
&i.SortOrder,
|
||||
&i.HasPractice,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
|
|||
|
|
@ -71,14 +71,34 @@ func (q *Queries) ExamPrepDeleteUnitModule(ctx context.Context, id int64) error
|
|||
}
|
||||
|
||||
const ExamPrepGetUnitModuleByID = `-- name: ExamPrepGetUnitModuleByID :one
|
||||
SELECT id, unit_id, name, description, thumbnail, icon, sort_order, created_at, updated_at
|
||||
FROM exam_prep.unit_modules
|
||||
WHERE id = $1
|
||||
SELECT
|
||||
m.id, m.unit_id, m.name, m.description, m.thumbnail, m.icon, m.sort_order, m.created_at, m.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
WHERE l.unit_module_id = m.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.unit_modules m
|
||||
WHERE m.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitModuleByID(ctx context.Context, id int64) (ExamPrepUnitModule, error) {
|
||||
type ExamPrepGetUnitModuleByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
UnitID int64 `json:"unit_id"`
|
||||
Name string `json:"name"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
Icon pgtype.Text `json:"icon"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitModuleByID(ctx context.Context, id int64) (ExamPrepGetUnitModuleByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, ExamPrepGetUnitModuleByID, id)
|
||||
var i ExamPrepUnitModule
|
||||
var i ExamPrepGetUnitModuleByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UnitID,
|
||||
|
|
@ -89,6 +109,7 @@ func (q *Queries) ExamPrepGetUnitModuleByID(ctx context.Context, id int64) (Exam
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -145,6 +166,7 @@ SELECT
|
|||
m.sort_order,
|
||||
COALESCE(mc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
COALESCE(mc.practices_count, 0)::BIGINT AS practices_count,
|
||||
(COALESCE(mc.practices_count, 0)::BIGINT > 0) AS has_practice,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
FROM exam_prep.unit_modules m
|
||||
|
|
@ -175,6 +197,7 @@ type ExamPrepListUnitModulesByUnitRow struct {
|
|||
SortOrder int32 `json:"sort_order"`
|
||||
LessonsCount int64 `json:"lessons_count"`
|
||||
PracticesCount int64 `json:"practices_count"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
|
@ -199,6 +222,7 @@ func (q *Queries) ExamPrepListUnitModulesByUnit(ctx context.Context, arg ExamPre
|
|||
&i.SortOrder,
|
||||
&i.LessonsCount,
|
||||
&i.PracticesCount,
|
||||
&i.HasPractice,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
|
|||
|
|
@ -67,14 +67,34 @@ func (q *Queries) ExamPrepDeleteUnit(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const ExamPrepGetUnitByID = `-- name: ExamPrepGetUnitByID :one
|
||||
SELECT id, catalog_course_id, name, description, thumbnail, sort_order, created_at, updated_at
|
||||
FROM exam_prep.units
|
||||
WHERE id = $1
|
||||
SELECT
|
||||
u.id, u.catalog_course_id, u.name, u.description, u.thumbnail, u.sort_order, u.created_at, u.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM exam_prep.lesson_practices p
|
||||
INNER JOIN exam_prep.unit_module_lessons l ON l.id = p.unit_module_lesson_id
|
||||
INNER JOIN exam_prep.unit_modules m ON m.id = l.unit_module_id
|
||||
WHERE m.unit_id = u.id
|
||||
) AS has_practice
|
||||
FROM exam_prep.units u
|
||||
WHERE u.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitByID(ctx context.Context, id int64) (ExamPrepUnit, error) {
|
||||
type ExamPrepGetUnitByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
CatalogCourseID int64 `json:"catalog_course_id"`
|
||||
Name string `json:"name"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ExamPrepGetUnitByID(ctx context.Context, id int64) (ExamPrepGetUnitByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, ExamPrepGetUnitByID, id)
|
||||
var i ExamPrepUnit
|
||||
var i ExamPrepGetUnitByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CatalogCourseID,
|
||||
|
|
@ -84,6 +104,7 @@ func (q *Queries) ExamPrepGetUnitByID(ctx context.Context, id int64) (ExamPrepUn
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -142,6 +163,7 @@ SELECT
|
|||
COALESCE(uc.modules_count, 0)::BIGINT AS modules_count,
|
||||
COALESCE(uc.lessons_count, 0)::BIGINT AS lessons_count,
|
||||
COALESCE(uc.practices_count, 0)::BIGINT AS practices_count,
|
||||
(COALESCE(uc.practices_count, 0)::BIGINT > 0) AS has_practice,
|
||||
u.created_at,
|
||||
u.updated_at
|
||||
FROM exam_prep.units u
|
||||
|
|
@ -172,6 +194,7 @@ type ExamPrepListUnitsByCatalogCourseRow struct {
|
|||
ModulesCount int64 `json:"modules_count"`
|
||||
LessonsCount int64 `json:"lessons_count"`
|
||||
PracticesCount int64 `json:"practices_count"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
|
@ -196,6 +219,7 @@ func (q *Queries) ExamPrepListUnitsByCatalogCourse(ctx context.Context, arg Exam
|
|||
&i.ModulesCount,
|
||||
&i.LessonsCount,
|
||||
&i.PracticesCount,
|
||||
&i.HasPractice,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
|
|||
|
|
@ -67,14 +67,35 @@ func (q *Queries) DeleteCourse(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const GetCourseByID = `-- name: GetCourseByID :one
|
||||
SELECT id, program_id, name, description, thumbnail, created_at, updated_at, sort_order
|
||||
SELECT
|
||||
c.id, c.program_id, c.name, c.description, c.thumbnail, c.created_at, c.updated_at, c.sort_order,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM courses
|
||||
WHERE id = $1
|
||||
c
|
||||
WHERE c.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCourseByID(ctx context.Context, id int64) (Course, error) {
|
||||
type GetCourseByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
ProgramID int64 `json:"program_id"`
|
||||
Name string `json:"name"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCourseByID(ctx context.Context, id int64) (GetCourseByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetCourseByID, id)
|
||||
var i Course
|
||||
var i GetCourseByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProgramID,
|
||||
|
|
@ -84,6 +105,7 @@ func (q *Queries) GetCourseByID(ctx context.Context, id int64) (Course, error) {
|
|||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SortOrder,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -155,7 +177,14 @@ SELECT
|
|||
WHERE
|
||||
p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL) AS practice_count
|
||||
AND p.lesson_id IS NULL) AS practice_count,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.course_id = c.id
|
||||
AND p.module_id IS NULL
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM
|
||||
courses c
|
||||
WHERE
|
||||
|
|
@ -185,6 +214,7 @@ type ListCoursesByProgramIDRow struct {
|
|||
ModuleCount int64 `json:"module_count"`
|
||||
LessonCount int64 `json:"lesson_count"`
|
||||
PracticeCount int64 `json:"practice_count"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListCoursesByProgramID(ctx context.Context, arg ListCoursesByProgramIDParams) ([]ListCoursesByProgramIDRow, error) {
|
||||
|
|
@ -209,6 +239,7 @@ func (q *Queries) ListCoursesByProgramID(ctx context.Context, arg ListCoursesByP
|
|||
&i.ModuleCount,
|
||||
&i.LessonCount,
|
||||
&i.PracticeCount,
|
||||
&i.HasPractice,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,14 +71,34 @@ func (q *Queries) DeleteLesson(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const GetLessonByID = `-- name: GetLessonByID :one
|
||||
SELECT id, module_id, title, video_url, thumbnail, description, created_at, updated_at, sort_order
|
||||
SELECT
|
||||
l.id, l.module_id, l.title, l.video_url, l.thumbnail, l.description, l.created_at, l.updated_at, l.sort_order,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM lessons
|
||||
WHERE id = $1
|
||||
l
|
||||
WHERE l.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLessonByID(ctx context.Context, id int64) (Lesson, error) {
|
||||
type GetLessonByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
ModuleID int64 `json:"module_id"`
|
||||
Title string `json:"title"`
|
||||
VideoUrl pgtype.Text `json:"video_url"`
|
||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetLessonByID(ctx context.Context, id int64) (GetLessonByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetLessonByID, id)
|
||||
var i Lesson
|
||||
var i GetLessonByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ModuleID,
|
||||
|
|
@ -89,6 +109,7 @@ func (q *Queries) GetLessonByID(ctx context.Context, id int64) (Lesson, error) {
|
|||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SortOrder,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -104,7 +125,12 @@ SELECT
|
|||
l.description,
|
||||
l.sort_order,
|
||||
l.created_at,
|
||||
l.updated_at
|
||||
l.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.lesson_id = l.id
|
||||
) AS has_practice
|
||||
FROM
|
||||
lessons l
|
||||
WHERE
|
||||
|
|
@ -133,6 +159,7 @@ type ListLessonsByModuleIDRow struct {
|
|||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListLessonsByModuleID(ctx context.Context, arg ListLessonsByModuleIDParams) ([]ListLessonsByModuleIDRow, error) {
|
||||
|
|
@ -155,6 +182,7 @@ func (q *Queries) ListLessonsByModuleID(ctx context.Context, arg ListLessonsByMo
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,14 +71,35 @@ func (q *Queries) DeleteModule(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const GetModuleByID = `-- name: GetModuleByID :one
|
||||
SELECT id, program_id, course_id, name, description, icon, created_at, updated_at, sort_order
|
||||
SELECT
|
||||
m.id, m.program_id, m.course_id, m.name, m.description, m.icon, m.created_at, m.updated_at, m.sort_order,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.module_id = m.id
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM modules
|
||||
WHERE id = $1
|
||||
m
|
||||
WHERE m.id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetModuleByID(ctx context.Context, id int64) (Module, error) {
|
||||
type GetModuleByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
ProgramID int64 `json:"program_id"`
|
||||
CourseID int64 `json:"course_id"`
|
||||
Name string `json:"name"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Icon pgtype.Text `json:"icon"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetModuleByID(ctx context.Context, id int64) (GetModuleByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetModuleByID, id)
|
||||
var i Module
|
||||
var i GetModuleByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProgramID,
|
||||
|
|
@ -89,6 +110,7 @@ func (q *Queries) GetModuleByID(ctx context.Context, id int64) (Module, error) {
|
|||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SortOrder,
|
||||
&i.HasPractice,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -135,7 +157,13 @@ SELECT
|
|||
m.icon,
|
||||
m.sort_order,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
m.updated_at,
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM lms_practices p
|
||||
WHERE p.module_id = m.id
|
||||
AND p.lesson_id IS NULL
|
||||
) AS has_practice
|
||||
FROM
|
||||
modules m
|
||||
WHERE
|
||||
|
|
@ -166,6 +194,7 @@ type ListModulesByProgramAndCourseRow struct {
|
|||
SortOrder int32 `json:"sort_order"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListModulesByProgramAndCourse(ctx context.Context, arg ListModulesByProgramAndCourseParams) ([]ListModulesByProgramAndCourseRow, error) {
|
||||
|
|
@ -193,6 +222,7 @@ func (q *Queries) ListModulesByProgramAndCourse(ctx context.Context, arg ListMod
|
|||
&i.SortOrder,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.HasPractice,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type Course struct {
|
|||
ModuleCount int `json:"module_count"`
|
||||
LessonCount int `json:"lesson_count"`
|
||||
PracticeCount int `json:"practice_count"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
Access *LMSEntityAccess `json:"access,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ type ExamPrepCatalogCourse struct {
|
|||
UnitsCount *int64 `json:"units_count,omitempty"`
|
||||
ModulesCount *int64 `json:"modules_count,omitempty"`
|
||||
LessonsCount *int64 `json:"lessons_count,omitempty"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type ExamPrepLesson struct {
|
|||
Thumbnail *string `json:"thumbnail,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ type ExamPrepModule struct {
|
|||
SortOrder int `json:"sort_order"`
|
||||
LessonsCount *int64 `json:"lessons_count,omitempty"`
|
||||
PracticesCount *int64 `json:"practices_count,omitempty"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ type ExamPrepUnit struct {
|
|||
ModulesCount *int64 `json:"modules_count,omitempty"`
|
||||
LessonsCount *int64 `json:"lessons_count,omitempty"`
|
||||
PracticesCount *int64 `json:"practices_count,omitempty"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type Lesson struct {
|
|||
Thumbnail *string `json:"thumbnail,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
Access *LMSEntityAccess `json:"access,omitempty"`
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type Module struct {
|
|||
Description *string `json:"description,omitempty"`
|
||||
Icon *string `json:"icon,omitempty"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
HasPractice bool `json:"has_practice"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
Access *LMSEntityAccess `json:"access,omitempty"`
|
||||
|
|
|
|||
|
|
@ -47,7 +47,17 @@ func (s *Store) GetExamPrepCatalogCourseByID(ctx context.Context, id int64) (dom
|
|||
}
|
||||
return domain.ExamPrepCatalogCourse{}, err
|
||||
}
|
||||
return examPrepCatalogCourseToDomain(c), nil
|
||||
out := examPrepCatalogCourseToDomain(dbgen.ExamPrepCatalogCourse{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
Thumbnail: c.Thumbnail,
|
||||
SortOrder: c.SortOrder,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = c.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListExamPrepCatalogCourses(ctx context.Context, limit, offset int32) ([]domain.ExamPrepCatalogCourse, int64, error) {
|
||||
|
|
@ -79,6 +89,7 @@ func (s *Store) ListExamPrepCatalogCourses(ctx context.Context, limit, offset in
|
|||
item.UnitsCount = &r.UnitsCount
|
||||
item.ModulesCount = &r.ModulesCount
|
||||
item.LessonsCount = &r.LessonsCount
|
||||
item.HasPractice = r.HasPractice
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, total, nil
|
||||
|
|
|
|||
|
|
@ -51,7 +51,19 @@ func (s *Store) GetExamPrepUnitModuleLessonByID(ctx context.Context, id int64) (
|
|||
}
|
||||
return domain.ExamPrepLesson{}, err
|
||||
}
|
||||
return examPrepLessonToDomain(l), nil
|
||||
out := examPrepLessonToDomain(dbgen.ExamPrepUnitModuleLesson{
|
||||
ID: l.ID,
|
||||
UnitModuleID: l.UnitModuleID,
|
||||
Title: l.Title,
|
||||
VideoUrl: l.VideoUrl,
|
||||
Thumbnail: l.Thumbnail,
|
||||
Description: l.Description,
|
||||
SortOrder: l.SortOrder,
|
||||
CreatedAt: l.CreatedAt,
|
||||
UpdatedAt: l.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = l.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListExamPrepUnitModuleLessonsByUnitModuleID(ctx context.Context, unitModuleID int64, limit, offset int32) ([]domain.ExamPrepLesson, int64, error) {
|
||||
|
|
@ -72,7 +84,7 @@ func (s *Store) ListExamPrepUnitModuleLessonsByUnitModuleID(ctx context.Context,
|
|||
if i == 0 {
|
||||
total = r.TotalCount
|
||||
}
|
||||
out = append(out, examPrepLessonToDomain(dbgen.ExamPrepUnitModuleLesson{
|
||||
item := examPrepLessonToDomain(dbgen.ExamPrepUnitModuleLesson{
|
||||
ID: r.ID,
|
||||
UnitModuleID: r.UnitModuleID,
|
||||
Title: r.Title,
|
||||
|
|
@ -82,7 +94,9 @@ func (s *Store) ListExamPrepUnitModuleLessonsByUnitModuleID(ctx context.Context,
|
|||
SortOrder: r.SortOrder,
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
}))
|
||||
})
|
||||
item.HasPractice = r.HasPractice
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,19 @@ func (s *Store) GetExamPrepUnitModuleByID(ctx context.Context, id int64) (domain
|
|||
}
|
||||
return domain.ExamPrepModule{}, err
|
||||
}
|
||||
return examPrepModuleToDomain(m), nil
|
||||
out := examPrepModuleToDomain(dbgen.ExamPrepUnitModule{
|
||||
ID: m.ID,
|
||||
UnitID: m.UnitID,
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Thumbnail: m.Thumbnail,
|
||||
Icon: m.Icon,
|
||||
SortOrder: m.SortOrder,
|
||||
CreatedAt: m.CreatedAt,
|
||||
UpdatedAt: m.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = m.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListExamPrepUnitModulesByUnit(ctx context.Context, unitID int64, limit, offset int32) ([]domain.ExamPrepModule, int64, error) {
|
||||
|
|
@ -85,6 +97,7 @@ func (s *Store) ListExamPrepUnitModulesByUnit(ctx context.Context, unitID int64,
|
|||
})
|
||||
item.LessonsCount = &r.LessonsCount
|
||||
item.PracticesCount = &r.PracticesCount
|
||||
item.HasPractice = r.HasPractice
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, total, nil
|
||||
|
|
|
|||
|
|
@ -49,7 +49,18 @@ func (s *Store) GetExamPrepUnitByID(ctx context.Context, id int64) (domain.ExamP
|
|||
}
|
||||
return domain.ExamPrepUnit{}, err
|
||||
}
|
||||
return examPrepUnitToDomain(u), nil
|
||||
out := examPrepUnitToDomain(dbgen.ExamPrepUnit{
|
||||
ID: u.ID,
|
||||
CatalogCourseID: u.CatalogCourseID,
|
||||
Name: u.Name,
|
||||
Description: u.Description,
|
||||
Thumbnail: u.Thumbnail,
|
||||
SortOrder: u.SortOrder,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = u.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListExamPrepUnitsByCatalogCourse(ctx context.Context, catalogCourseID int64, limit, offset int32) ([]domain.ExamPrepUnit, int64, error) {
|
||||
|
|
@ -83,6 +94,7 @@ func (s *Store) ListExamPrepUnitsByCatalogCourse(ctx context.Context, catalogCou
|
|||
item.ModulesCount = &r.ModulesCount
|
||||
item.LessonsCount = &r.LessonsCount
|
||||
item.PracticesCount = &r.PracticesCount
|
||||
item.HasPractice = r.HasPractice
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, total, nil
|
||||
|
|
|
|||
|
|
@ -53,7 +53,18 @@ func (s *Store) GetCourseByID(ctx context.Context, id int64) (domain.Course, err
|
|||
}
|
||||
return domain.Course{}, err
|
||||
}
|
||||
return courseToDomain(c), nil
|
||||
out := courseToDomain(dbgen.Course{
|
||||
ID: c.ID,
|
||||
ProgramID: c.ProgramID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
Thumbnail: c.Thumbnail,
|
||||
SortOrder: c.SortOrder,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = c.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListCoursesByProgramID(ctx context.Context, programID int64, limit, offset int32) ([]domain.Course, int64, error) {
|
||||
|
|
@ -87,6 +98,7 @@ func (s *Store) ListCoursesByProgramID(ctx context.Context, programID int64, lim
|
|||
co.ModuleCount = int(r.ModuleCount)
|
||||
co.LessonCount = int(r.LessonCount)
|
||||
co.PracticeCount = int(r.PracticeCount)
|
||||
co.HasPractice = r.HasPractice
|
||||
out = append(out, co)
|
||||
}
|
||||
return out, total, nil
|
||||
|
|
|
|||
|
|
@ -51,7 +51,19 @@ func (s *Store) GetLessonByID(ctx context.Context, id int64) (domain.Lesson, err
|
|||
}
|
||||
return domain.Lesson{}, err
|
||||
}
|
||||
return lessonToDomain(l), nil
|
||||
out := lessonToDomain(dbgen.Lesson{
|
||||
ID: l.ID,
|
||||
ModuleID: l.ModuleID,
|
||||
Title: l.Title,
|
||||
VideoUrl: l.VideoUrl,
|
||||
Thumbnail: l.Thumbnail,
|
||||
Description: l.Description,
|
||||
SortOrder: l.SortOrder,
|
||||
CreatedAt: l.CreatedAt,
|
||||
UpdatedAt: l.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = l.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListLessonsByModuleID(ctx context.Context, moduleID int64, limit, offset int32) ([]domain.Lesson, int64, error) {
|
||||
|
|
@ -72,7 +84,7 @@ func (s *Store) ListLessonsByModuleID(ctx context.Context, moduleID int64, limit
|
|||
if i == 0 {
|
||||
total = r.TotalCount
|
||||
}
|
||||
out = append(out, lessonToDomain(dbgen.Lesson{
|
||||
lesson := lessonToDomain(dbgen.Lesson{
|
||||
ID: r.ID,
|
||||
ModuleID: r.ModuleID,
|
||||
Title: r.Title,
|
||||
|
|
@ -82,7 +94,9 @@ func (s *Store) ListLessonsByModuleID(ctx context.Context, moduleID int64, limit
|
|||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
SortOrder: r.SortOrder,
|
||||
}))
|
||||
})
|
||||
lesson.HasPractice = r.HasPractice
|
||||
out = append(out, lesson)
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,19 @@ func (s *Store) GetModuleByID(ctx context.Context, id int64) (domain.Module, err
|
|||
}
|
||||
return domain.Module{}, err
|
||||
}
|
||||
return moduleToDomain(m), nil
|
||||
out := moduleToDomain(dbgen.Module{
|
||||
ID: m.ID,
|
||||
ProgramID: m.ProgramID,
|
||||
CourseID: m.CourseID,
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Icon: m.Icon,
|
||||
SortOrder: m.SortOrder,
|
||||
CreatedAt: m.CreatedAt,
|
||||
UpdatedAt: m.UpdatedAt,
|
||||
})
|
||||
out.HasPractice = m.HasPractice
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListModulesByProgramAndCourse(ctx context.Context, programID, courseID int64, limit, offset int32) ([]domain.Module, int64, error) {
|
||||
|
|
@ -77,7 +89,7 @@ func (s *Store) ListModulesByProgramAndCourse(ctx context.Context, programID, co
|
|||
if i == 0 {
|
||||
total = r.TotalCount
|
||||
}
|
||||
out = append(out, moduleToDomain(dbgen.Module{
|
||||
mod := moduleToDomain(dbgen.Module{
|
||||
ID: r.ID,
|
||||
ProgramID: r.ProgramID,
|
||||
CourseID: r.CourseID,
|
||||
|
|
@ -87,7 +99,9 @@ func (s *Store) ListModulesByProgramAndCourse(ctx context.Context, programID, co
|
|||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
SortOrder: r.SortOrder,
|
||||
}))
|
||||
})
|
||||
mod.HasPractice = r.HasPractice
|
||||
out = append(out, mod)
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string,
|
|||
func (s *Service) SendEmailWithAttachments(ctx context.Context, receiverEmail, message string, messageHTML string, subject string, attachments []*resend.Attachment) error {
|
||||
apiKey := s.config.ResendApiKey
|
||||
client := resend.NewClient(apiKey)
|
||||
formattedSenderEmail := "Y <" + s.config.ResendSenderEmail + ">"
|
||||
formattedSenderEmail := "Yimaru - Academy <" + s.config.ResendSenderEmail + ">"
|
||||
params := &resend.SendEmailRequest{
|
||||
From: formattedSenderEmail,
|
||||
To: []string{receiverEmail},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package handlers
|
|||
|
||||
import (
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -132,6 +135,27 @@ func (h *Handler) CreateQuestionTypeDefinition(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_type_definition_id": def.ID,
|
||||
"key": def.Key,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(
|
||||
context.Background(),
|
||||
&actorID,
|
||||
&actorRole,
|
||||
domain.ActionQuestionCreated,
|
||||
domain.ResourceQuestion,
|
||||
&def.ID,
|
||||
"Created question type definition: "+def.DisplayName,
|
||||
meta,
|
||||
&ip,
|
||||
&ua,
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||
Message: "Question type definition created",
|
||||
Data: def,
|
||||
|
|
@ -251,6 +275,26 @@ func (h *Handler) UpdateQuestionTypeDefinition(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_type_definition_id": id,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(
|
||||
context.Background(),
|
||||
&actorID,
|
||||
&actorRole,
|
||||
domain.ActionQuestionUpdated,
|
||||
domain.ResourceQuestion,
|
||||
&id,
|
||||
fmt.Sprintf("Updated question type definition ID: %d", id),
|
||||
meta,
|
||||
&ip,
|
||||
&ua,
|
||||
)
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Question type definition updated",
|
||||
Data: fiber.Map{"id": id},
|
||||
|
|
@ -282,6 +326,26 @@ func (h *Handler) DeleteQuestionTypeDefinition(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_type_definition_id": id,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(
|
||||
context.Background(),
|
||||
&actorID,
|
||||
&actorRole,
|
||||
domain.ActionQuestionDeleted,
|
||||
domain.ResourceQuestion,
|
||||
&id,
|
||||
fmt.Sprintf("Deleted question type definition ID: %d", id),
|
||||
meta,
|
||||
&ip,
|
||||
&ua,
|
||||
)
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Question type definition deleted",
|
||||
Data: fiber.Map{"id": id},
|
||||
|
|
|
|||
|
|
@ -1299,6 +1299,17 @@ func (h *Handler) AddQuestionToSet(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_set_id": setID,
|
||||
"question_id": req.QuestionID,
|
||||
"display_order": req.DisplayOrder,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &setID, fmt.Sprintf("Added question %d to question set %d", req.QuestionID, setID), meta, &ip, &ua)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||
Message: "Question added to set successfully",
|
||||
Data: map[string]interface{}{
|
||||
|
|
@ -1605,6 +1616,16 @@ func (h *Handler) RemoveQuestionFromSet(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_set_id": setID,
|
||||
"question_id": questionID,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &setID, fmt.Sprintf("Removed question %d from question set %d", questionID, setID), meta, &ip, &ua)
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Question removed from set successfully",
|
||||
})
|
||||
|
|
@ -1662,6 +1683,17 @@ func (h *Handler) UpdateQuestionOrderInSet(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_set_id": setID,
|
||||
"question_id": questionID,
|
||||
"display_order": req.DisplayOrder,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &setID, fmt.Sprintf("Updated question %d display_order to %d in set %d", questionID, req.DisplayOrder, setID), meta, &ip, &ua)
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Question order updated successfully",
|
||||
})
|
||||
|
|
@ -1769,6 +1801,17 @@ func (h *Handler) AddUserPersonaToQuestionSet(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_set_id": setID,
|
||||
"user_id": req.UserID,
|
||||
"display_order": req.DisplayOrder,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &setID, fmt.Sprintf("Added persona user %d to question set %d", req.UserID, setID), meta, &ip, &ua)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||
Message: "Persona added to question set successfully",
|
||||
})
|
||||
|
|
@ -1812,6 +1855,16 @@ func (h *Handler) RemoveUserPersonaFromQuestionSet(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
actorID := c.Locals("user_id").(int64)
|
||||
actorRole := string(c.Locals("role").(domain.Role))
|
||||
ip := c.IP()
|
||||
ua := c.Get("User-Agent")
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"question_set_id": setID,
|
||||
"user_id": userID,
|
||||
})
|
||||
go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionQuestionSetUpdated, domain.ResourceQuestionSet, &setID, fmt.Sprintf("Removed persona user %d from question set %d", userID, setID), meta, &ip, &ua)
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Persona removed from question set successfully",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -309,7 +309,19 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/notifications/mark-all-unread", a.authMiddleware, a.RequirePermission("notifications.mark_all_unread"), h.MarkAllNotificationsAsUnread)
|
||||
groupV1.Delete("/notifications", a.authMiddleware, a.RequirePermission("notifications.delete_mine"), h.DeleteUserNotifications)
|
||||
groupV1.Get("/notifications/unread", a.authMiddleware, a.RequirePermission("notifications.count_unread"), h.CountUnreadNotifications)
|
||||
groupV1.Post("/notifications/create", a.authMiddleware, a.RequirePermission("notifications.create"), h.CreateAndSendNotification)
|
||||
groupV1.Post("/notifications/create", a.authMiddleware, a.RequirePermission("notifications.create"), h.CreateAndSendNotification)
|
||||
groupV1.Post("/notifications/test-push", a.authMiddleware, a.RequirePermission("notifications.test_push"), h.SendTestPushNotification)
|
||||
|
||||
// Bulk Notifications
|
||||
groupV1.Post("/notifications/bulk-push", a.authMiddleware, a.RequirePermission("notifications.bulk_push"), h.SendBulkPushNotification)
|
||||
groupV1.Post("/notifications/bulk-sms", a.authMiddleware, a.RequirePermission("notifications.bulk_sms"), h.SendBulkSMS)
|
||||
groupV1.Post("/notifications/send-email", a.authMiddleware, a.RequirePermission("notifications.send_email"), h.SendSingleEmail)
|
||||
groupV1.Post("/notifications/bulk-email", a.authMiddleware, a.RequirePermission("notifications.bulk_email"), h.SendBulkEmail)
|
||||
|
||||
// Scheduled Notifications
|
||||
groupV1.Get("/notifications/scheduled", a.authMiddleware, a.RequirePermission("notifications_scheduled.list"), h.ListScheduledNotifications)
|
||||
groupV1.Get("/notifications/scheduled/:id", a.authMiddleware, a.RequirePermission("notifications_scheduled.get"), h.GetScheduledNotification)
|
||||
groupV1.Post("/notifications/scheduled/:id/cancel", a.authMiddleware, a.RequirePermission("notifications_scheduled.cancel"), h.CancelScheduledNotification)
|
||||
|
||||
// Issues
|
||||
groupV1.Post("/issues", a.authMiddleware, a.RequirePermission("issues.create"), h.CreateIssue)
|
||||
|
|
@ -324,18 +336,6 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/devices/register", a.authMiddleware, a.RequirePermission("devices.register"), h.RegisterDeviceToken)
|
||||
groupV1.Post("/devices/unregister", a.authMiddleware, a.RequirePermission("devices.unregister"), h.UnregisterDeviceToken)
|
||||
|
||||
// Push Notifications
|
||||
groupV1.Post("/notifications/test-push", a.authMiddleware, a.RequirePermission("notifications.test_push"), h.SendTestPushNotification)
|
||||
groupV1.Post("/notifications/bulk-push", a.authMiddleware, a.RequirePermission("notifications.bulk_push"), h.SendBulkPushNotification)
|
||||
groupV1.Post("/notifications/bulk-sms", a.authMiddleware, a.RequirePermission("notifications.bulk_sms"), h.SendBulkSMS)
|
||||
groupV1.Post("/notifications/send-email", a.authMiddleware, a.RequirePermission("notifications.send_email"), h.SendSingleEmail)
|
||||
groupV1.Post("/notifications/bulk-email", a.authMiddleware, a.RequirePermission("notifications.bulk_email"), h.SendBulkEmail)
|
||||
|
||||
// Scheduled Notifications
|
||||
groupV1.Get("/notifications/scheduled", a.authMiddleware, a.RequirePermission("notifications_scheduled.list"), h.ListScheduledNotifications)
|
||||
groupV1.Get("/notifications/scheduled/:id", a.authMiddleware, a.RequirePermission("notifications_scheduled.get"), h.GetScheduledNotification)
|
||||
groupV1.Post("/notifications/scheduled/:id/cancel", a.authMiddleware, a.RequirePermission("notifications_scheduled.cancel"), h.CancelScheduledNotification)
|
||||
|
||||
// Settings
|
||||
groupV1.Get("/settings", a.authMiddleware, a.RequirePermission("settings.list"), h.GetGlobalSettingList)
|
||||
groupV1.Get("/settings/:key", a.authMiddleware, a.RequirePermission("settings.get"), h.GetGlobalSettingByKey)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user