added inittal assessment feature
This commit is contained in:
parent
915185c317
commit
2c907a34db
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
9
.idea/Yimaru Backend.iml
Normal file
9
.idea/Yimaru Backend.iml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Yimaru Backend.iml" filepath="$PROJECT_DIR$/.idea/Yimaru Backend.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"Yimaru-Backend/internal/logger/mongoLogger"
|
"Yimaru-Backend/internal/logger/mongoLogger"
|
||||||
"Yimaru-Backend/internal/repository"
|
"Yimaru-Backend/internal/repository"
|
||||||
"Yimaru-Backend/internal/services/arifpay"
|
"Yimaru-Backend/internal/services/arifpay"
|
||||||
|
"Yimaru-Backend/internal/services/assessment"
|
||||||
"Yimaru-Backend/internal/services/authentication"
|
"Yimaru-Backend/internal/services/authentication"
|
||||||
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
||||||
"Yimaru-Backend/internal/services/messenger"
|
"Yimaru-Backend/internal/services/messenger"
|
||||||
|
|
@ -323,6 +324,13 @@ func main() {
|
||||||
// transferStore := repository.NewTransferStore(store)
|
// transferStore := repository.NewTransferStore(store)
|
||||||
// walletStore := wallet.WalletStore(store)
|
// walletStore := wallet.WalletStore(store)
|
||||||
|
|
||||||
|
assessmentSvc := assessment.NewService(
|
||||||
|
repository.NewUserStore(store),
|
||||||
|
repository.NewInitialAssessmentStore(store),
|
||||||
|
notificationSvc,
|
||||||
|
cfg,
|
||||||
|
)
|
||||||
|
|
||||||
arifpaySvc := arifpay.NewArifpayService(cfg, *transactionSvc, &http.Client{
|
arifpaySvc := arifpay.NewArifpayService(cfg, *transactionSvc, &http.Client{
|
||||||
Timeout: 30 * time.Second})
|
Timeout: 30 * time.Second})
|
||||||
|
|
||||||
|
|
@ -333,6 +341,7 @@ func main() {
|
||||||
|
|
||||||
// Initialize and start HTTP server
|
// Initialize and start HTTP server
|
||||||
app := httpserver.NewApp(
|
app := httpserver.NewApp(
|
||||||
|
assessmentSvc,
|
||||||
arifpaySvc,
|
arifpaySvc,
|
||||||
issueReportingSvc,
|
issueReportingSvc,
|
||||||
cfg.Port,
|
cfg.Port,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,40 @@
|
||||||
|
CREATE TABLE assessment_questions (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
question_type VARCHAR(50) NOT NULL, -- MULTIPLE_CHOICE, TRUE_FALSE, SHORT_ANSWER
|
||||||
|
difficulty_level VARCHAR(50) NOT NULL, -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE assessment_question_options (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
question_id BIGINT NOT NULL REFERENCES assessment_questions(id) ON DELETE CASCADE,
|
||||||
|
option_text TEXT NOT NULL,
|
||||||
|
is_correct BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE assessment_attempts (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
total_questions INT NOT NULL,
|
||||||
|
correct_answers INT NOT NULL,
|
||||||
|
score_percentage NUMERIC(5,2) NOT NULL,
|
||||||
|
knowledge_level VARCHAR(50) NOT NULL, -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||||
|
completed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE assessment_answers (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
attempt_id BIGINT NOT NULL REFERENCES assessment_attempts(id) ON DELETE CASCADE,
|
||||||
|
question_id BIGINT NOT NULL REFERENCES assessment_questions(id),
|
||||||
|
selected_option_id BIGINT REFERENCES assessment_question_options(id),
|
||||||
|
short_answer TEXT,
|
||||||
|
is_correct BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
first_name VARCHAR(255) NOT NULL,
|
first_name VARCHAR(255) NOT NULL,
|
||||||
|
|
@ -12,6 +49,16 @@ CREATE TABLE IF NOT EXISTS users (
|
||||||
education_level VARCHAR(100),
|
education_level VARCHAR(100),
|
||||||
country VARCHAR(100),
|
country VARCHAR(100),
|
||||||
region VARCHAR(100),
|
region VARCHAR(100),
|
||||||
|
|
||||||
|
knowledge_level VARCHAR(50), -- BEGINNER, INTERMEDIATE, ADVANCED
|
||||||
|
nick_name VARCHAR(100),
|
||||||
|
occupation VARCHAR(150),
|
||||||
|
learning_goal TEXT,
|
||||||
|
language_goal TEXT,
|
||||||
|
language_challange TEXT,
|
||||||
|
favoutite_topic TEXT,
|
||||||
|
|
||||||
|
initial_assessment_completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
status VARCHAR(50) NOT NULL, -- PENDING, ACTIVE, SUSPENDED, DEACTIVATED
|
status VARCHAR(50) NOT NULL, -- PENDING, ACTIVE, SUSPENDED, DEACTIVATED
|
||||||
|
|
@ -26,7 +73,6 @@ CREATE TABLE IF NOT EXISTS users (
|
||||||
CHECK (email IS NOT NULL OR phone_number IS NOT NULL)
|
CHECK (email IS NOT NULL OR phone_number IS NOT NULL)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE refresh_tokens (
|
CREATE TABLE refresh_tokens (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
|
|
||||||
93
db/query/initial_assessment.sql
Normal file
93
db/query/initial_assessment.sql
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
-- name: CreateAssessmentAttempt :one
|
||||||
|
INSERT INTO assessment_attempts (
|
||||||
|
user_id,
|
||||||
|
total_questions,
|
||||||
|
correct_answers,
|
||||||
|
score_percentage,
|
||||||
|
knowledge_level
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, -- user_id
|
||||||
|
$2, -- total_questions
|
||||||
|
$3, -- correct_answers
|
||||||
|
$4, -- score_percentage
|
||||||
|
$5 -- knowledge_level
|
||||||
|
)
|
||||||
|
RETURNING
|
||||||
|
id,
|
||||||
|
user_id,
|
||||||
|
total_questions,
|
||||||
|
correct_answers,
|
||||||
|
score_percentage,
|
||||||
|
knowledge_level,
|
||||||
|
completed_at;
|
||||||
|
|
||||||
|
-- name: CreateAssessmentAnswer :exec
|
||||||
|
INSERT INTO assessment_answers (
|
||||||
|
attempt_id,
|
||||||
|
question_id,
|
||||||
|
selected_option_id,
|
||||||
|
short_answer,
|
||||||
|
is_correct
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, -- attempt_id
|
||||||
|
$2, -- question_id
|
||||||
|
$3, -- selected_option_id
|
||||||
|
$4, -- short_answer
|
||||||
|
$5 -- is_correct
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: GetAssessmentOptionByID :one
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
question_id,
|
||||||
|
option_text,
|
||||||
|
is_correct
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE id = $1
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetCorrectOptionForQuestion :one
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE question_id = $1
|
||||||
|
AND is_correct = TRUE
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetLatestAssessmentAttempt :one
|
||||||
|
SELECT *
|
||||||
|
FROM assessment_attempts
|
||||||
|
WHERE user_id = $1
|
||||||
|
ORDER BY completed_at DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: CreateAssessmentQuestion :one
|
||||||
|
INSERT INTO assessment_questions (
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
question_type,
|
||||||
|
difficulty_level
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: CreateAssessmentQuestionOption :exec
|
||||||
|
INSERT INTO assessment_question_options (
|
||||||
|
question_id,
|
||||||
|
option_text,
|
||||||
|
is_correct
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3);
|
||||||
|
|
||||||
|
-- name: GetActiveAssessmentQuestions :many
|
||||||
|
SELECT *
|
||||||
|
FROM assessment_questions
|
||||||
|
WHERE is_active = TRUE
|
||||||
|
ORDER BY difficulty_level, id;
|
||||||
|
|
||||||
|
-- name: GetQuestionOptions :many
|
||||||
|
SELECT *
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE question_id = $1;
|
||||||
|
|
@ -25,30 +25,50 @@ INSERT INTO users (
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
profile_completed,
|
profile_completed,
|
||||||
|
profile_picture_url,
|
||||||
preferred_language,
|
preferred_language,
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
$1, -- first_name
|
$1, -- first_name
|
||||||
$2, -- last_name
|
$2, -- last_name
|
||||||
$3, -- user_name
|
$3, -- user_name
|
||||||
$4, -- email
|
$4, -- email
|
||||||
$5, -- phone_number
|
$5, -- phone_number
|
||||||
$6, -- role
|
$6, -- role
|
||||||
$7, -- password (BYTEA)
|
$7, -- password
|
||||||
$8, -- age
|
$8, -- age
|
||||||
$9, -- education_level
|
$9, -- education_level
|
||||||
$10, -- country
|
$10, -- country
|
||||||
$11, -- region
|
$11, -- region
|
||||||
$12, -- email_verified
|
|
||||||
$13, -- phone_verified
|
$12, -- nick_name
|
||||||
$14, -- status (PENDING | ACTIVE)
|
$13, -- occupation
|
||||||
$15, -- profile_completed
|
$14, -- learning_goal
|
||||||
$16, -- preferred_language
|
$15, -- language_goal
|
||||||
|
$16, -- language_challange
|
||||||
|
$17, -- favoutite_topic
|
||||||
|
|
||||||
|
$18, -- initial_assessment_completed
|
||||||
|
$19, -- email_verified
|
||||||
|
$20, -- phone_verified
|
||||||
|
$21, -- status
|
||||||
|
$22, -- profile_completed
|
||||||
|
$23, -- profile_picture_url
|
||||||
|
$24, -- preferred_language
|
||||||
CURRENT_TIMESTAMP
|
CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
RETURNING
|
RETURNING
|
||||||
|
|
@ -63,10 +83,20 @@ RETURNING
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
profile_completed,
|
profile_completed,
|
||||||
|
profile_picture_url,
|
||||||
preferred_language,
|
preferred_language,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at;
|
updated_at;
|
||||||
|
|
@ -90,6 +120,17 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
|
profile_picture_url,
|
||||||
|
preferred_language,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -137,6 +178,17 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
|
profile_picture_url,
|
||||||
|
preferred_language,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -158,11 +210,30 @@ WHERE (
|
||||||
-- name: UpdateUser :exec
|
-- name: UpdateUser :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET
|
SET
|
||||||
first_name = $1,
|
first_name = $1,
|
||||||
last_name = $2,
|
last_name = $2,
|
||||||
status = $3,
|
user_name = $3,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
age = $4,
|
||||||
WHERE id = $4;
|
education_level = $5,
|
||||||
|
country = $6,
|
||||||
|
region = $7,
|
||||||
|
|
||||||
|
nick_name = $8,
|
||||||
|
occupation = $9,
|
||||||
|
learning_goal = $10,
|
||||||
|
language_goal = $11,
|
||||||
|
language_challange = $12,
|
||||||
|
favoutite_topic = $13,
|
||||||
|
|
||||||
|
initial_assessment_completed = $14,
|
||||||
|
email_verified = $15,
|
||||||
|
phone_verified = $16,
|
||||||
|
status = $17,
|
||||||
|
profile_completed = $18,
|
||||||
|
profile_picture_url = $19,
|
||||||
|
preferred_language = $20,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = $21;
|
||||||
|
|
||||||
-- name: DeleteUser :exec
|
-- name: DeleteUser :exec
|
||||||
DELETE FROM users
|
DELETE FROM users
|
||||||
|
|
@ -171,14 +242,10 @@ WHERE id = $1;
|
||||||
-- name: CheckPhoneEmailExist :one
|
-- name: CheckPhoneEmailExist :one
|
||||||
SELECT
|
SELECT
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1 FROM users u1 WHERE u1.phone_number = $1
|
||||||
FROM users u1
|
|
||||||
WHERE u1.phone_number = $1
|
|
||||||
) AS phone_exists,
|
) AS phone_exists,
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1 FROM users u2 WHERE u2.email = $2
|
||||||
FROM users u2
|
|
||||||
WHERE u2.email = $2
|
|
||||||
) AS email_exists;
|
) AS email_exists;
|
||||||
|
|
||||||
-- name: GetUserByUserName :one
|
-- name: GetUserByUserName :one
|
||||||
|
|
@ -195,6 +262,14 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -222,6 +297,14 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -233,7 +316,7 @@ SELECT
|
||||||
updated_at
|
updated_at
|
||||||
FROM users
|
FROM users
|
||||||
WHERE (email = $1 AND $1 IS NOT NULL)
|
WHERE (email = $1 AND $1 IS NOT NULL)
|
||||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: UpdatePassword :exec
|
-- name: UpdatePassword :exec
|
||||||
|
|
@ -241,7 +324,7 @@ UPDATE users
|
||||||
SET
|
SET
|
||||||
password = $1,
|
password = $1,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE email = $2 OR phone_number = $3;
|
WHERE user_name = $2;
|
||||||
|
|
||||||
-- name: UpdateUserStatus :exec
|
-- name: UpdateUserStatus :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
|
|
@ -249,3 +332,10 @@ SET
|
||||||
status = $1,
|
status = $1,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $2;
|
WHERE id = $2;
|
||||||
|
|
||||||
|
-- name: UpdateUserKnowledgeLevel :exec
|
||||||
|
UPDATE users
|
||||||
|
SET
|
||||||
|
knowledge_level = $1,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = $2;
|
||||||
|
|
|
||||||
690
docs/docs.go
690
docs/docs.go
|
|
@ -229,9 +229,109 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/assessment/questions": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns all active questions used for initial knowledge assessment",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Get active initial assessment questions",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"description": "Creates a new question for the initial knowledge assessment",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Create assessment question",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Assessment question payload",
|
||||||
|
"name": "question",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "Created",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/auth/logout": {
|
"/api/v1/auth/logout": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Logout customer",
|
"description": "Logout user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -241,10 +341,10 @@ const docTemplate = `{
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Logout customer",
|
"summary": "Logout user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Logout customer",
|
"description": "Logout user",
|
||||||
"name": "logout",
|
"name": "logout",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
|
|
@ -309,7 +409,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
"$ref": "#/definitions/handlers.loginUserRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -393,6 +493,52 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/sendSMS": {
|
||||||
|
"post": {
|
||||||
|
"description": "Sends an SMS message to a single phone number using AfroMessage",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Send single SMS via AfroMessage",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Send SMS request",
|
||||||
|
"name": "sendSMS",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handlers.SendSingleAfroSMSReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/super-login": {
|
"/api/v1/super-login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login super-admin",
|
"description": "Login super-admin",
|
||||||
|
|
@ -823,6 +969,52 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/user/verify-otp": {
|
||||||
|
"post": {
|
||||||
|
"description": "Verify OTP for registration or other actions",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Verify OTP",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Verify OTP",
|
||||||
|
"name": "verifyOtp",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/user/{user_name}/is-unique": {
|
"/api/v1/user/{user_name}/is-unique": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns whether the specified user_name is available (unique)",
|
"description": "Returns whether the specified user_name is available (unique)",
|
||||||
|
|
@ -869,7 +1061,7 @@ const docTemplate = `{
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/admin-login": {
|
"/api/v1/{tenant_slug}/admin-login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Login user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -879,7 +1071,7 @@ const docTemplate = `{
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Login customer",
|
"summary": "Login user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Login admin",
|
"description": "Login admin",
|
||||||
|
|
@ -919,9 +1111,114 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/customer-login": {
|
"/api/v1/{tenant_slug}/assessment/submit": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Evaluates user responses, calculates knowledge level, updates user profile, and sends notification",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Submit initial knowledge assessment",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "User ID",
|
||||||
|
"name": "user_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Assessment responses",
|
||||||
|
"name": "payload",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.SubmitAssessmentReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/{tenant_slug}/otp/resend": {
|
||||||
|
"post": {
|
||||||
|
"description": "Resend OTP if the previous one is expired",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"otp"
|
||||||
|
],
|
||||||
|
"summary": "Resend OTP",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Resend OTP",
|
||||||
|
"name": "resendOtp",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ResendOtpReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/{tenant_slug}/user-login": {
|
||||||
|
"post": {
|
||||||
|
"description": "Login user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -931,15 +1228,15 @@ const docTemplate = `{
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Login customer",
|
"summary": "Login user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Login customer",
|
"description": "Login user",
|
||||||
"name": "login",
|
"name": "login",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
"$ref": "#/definitions/handlers.loginUserReq"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -947,7 +1244,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
"$ref": "#/definitions/handlers.loginUserRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1057,14 +1354,9 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/user/customer-profile": {
|
"/api/v1/{tenant_slug}/user/knowledge-level": {
|
||||||
"get": {
|
"put": {
|
||||||
"security": [
|
"description": "Updates the knowledge level of the specified user after initial assessment",
|
||||||
{
|
|
||||||
"Bearer": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get user profile",
|
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -1074,24 +1366,48 @@ const docTemplate = `{
|
||||||
"tags": [
|
"tags": [
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
"summary": "Get user profile",
|
"summary": "Update user's knowledge level",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "User ID",
|
||||||
|
"name": "user_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Knowledge level",
|
||||||
|
"name": "knowledge_level",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateKnowledgeLevelReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CustomerProfileRes"
|
"$ref": "#/definitions/domain.Response"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"500": {
|
"500": {
|
||||||
"description": "Internal Server Error",
|
"description": "Internal Server Error",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1281,9 +1597,14 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/user/verify-otp": {
|
"/api/v1/{tenant_slug}/user/user-profile": {
|
||||||
"post": {
|
"get": {
|
||||||
"description": "Verify OTP for registration or other actions",
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Get user profile",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -1293,23 +1614,12 @@ const docTemplate = `{
|
||||||
"tags": [
|
"tags": [
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
"summary": "Verify OTP",
|
"summary": "Get user profile",
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "Verify OTP",
|
|
||||||
"name": "verifyOtp",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.UserProfileResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1379,6 +1689,48 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"domain.AssessmentOption": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"isCorrect": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"optionText": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AssessmentQuestion": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"difficultyLevel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentOption"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questionType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.ErrorResponse": {
|
"domain.ErrorResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1437,17 +1789,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.OtpFor": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"reset",
|
|
||||||
"register"
|
|
||||||
],
|
|
||||||
"x-enum-varnames": [
|
|
||||||
"OtpReset",
|
|
||||||
"OtpRegister"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"domain.OtpMedium": {
|
"domain.OtpMedium": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
@ -1485,34 +1826,46 @@ const docTemplate = `{
|
||||||
"country": {
|
"country": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"educationLevel": {
|
"education_level": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"firstName": {
|
"favoutite_topic": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"lastName": {
|
"first_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"organizationID": {
|
"language_challange": {
|
||||||
"$ref": "#/definitions/domain.ValidInt64"
|
|
||||||
},
|
|
||||||
"otp": {
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"otpMedium": {
|
"language_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"learning_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nick_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"otp_medium": {
|
||||||
"$ref": "#/definitions/domain.OtpMedium"
|
"$ref": "#/definitions/domain.OtpMedium"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"phoneNumber": {
|
"phone_number": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"preferredLanguage": {
|
"preferred_language": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
|
|
@ -1521,7 +1874,18 @@ const docTemplate = `{
|
||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"userName": {
|
"user_name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.ResendOtpReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"user_name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"user_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1559,6 +1923,52 @@ const docTemplate = `{
|
||||||
"RoleSupport"
|
"RoleSupport"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.SubmitAssessmentReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"answers"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"answers": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UserAnswer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UpdateKnowledgeLevelReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"knowledge_level": {
|
||||||
|
"description": "BEGINNER, INTERMEDIATE, ADVANCED",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UserAnswer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"isCorrect": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"questionID": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"selectedOptionID": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"shortAnswer": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.UserProfileResponse": {
|
"domain.UserProfileResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1580,20 +1990,39 @@ const docTemplate = `{
|
||||||
"email_verified": {
|
"email_verified": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"favoutite_topic": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"first_name": {
|
"first_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"initial_assessment_completed": {
|
||||||
|
"description": "Profile fields",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"language_challange": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"language_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"last_login": {
|
"last_login": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"last_name": {
|
"last_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"organization_id": {
|
"learning_goal": {
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nick_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
@ -1642,48 +2071,17 @@ const docTemplate = `{
|
||||||
"UserStatusDeactivated"
|
"UserStatusDeactivated"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"domain.ValidInt64": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"valid": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.VerifyOtpReq": {
|
"domain.VerifyOtpReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"otp",
|
"otp",
|
||||||
"otp_for",
|
"user_name"
|
||||||
"otp_medium"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"description": "Required if medium is email",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"otp": {
|
"otp": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"otp_for": {
|
"user_name": {
|
||||||
"$ref": "#/definitions/domain.OtpFor"
|
|
||||||
},
|
|
||||||
"otp_medium": {
|
|
||||||
"enum": [
|
|
||||||
"email",
|
|
||||||
"sms"
|
|
||||||
],
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/domain.OtpMedium"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"phone_number": {
|
|
||||||
"description": "Required if medium is SMS",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1803,10 +2201,6 @@ const docTemplate = `{
|
||||||
"handlers.CreateAdminReq": {
|
"handlers.CreateAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.doe@example.com"
|
"example": "john.doe@example.com"
|
||||||
|
|
@ -1829,53 +2223,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.CustomerProfileRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email_verified": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"first_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"last_login": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"last_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phone_number": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phone_verified": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"referral_code": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"$ref": "#/definitions/domain.Role"
|
|
||||||
},
|
|
||||||
"suspended": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"suspended_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.LoginAdminRes": {
|
"handlers.LoginAdminRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1920,13 +2267,10 @@ const docTemplate = `{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"otp",
|
"otp",
|
||||||
"password"
|
"password",
|
||||||
|
"user_name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "john.doe@example.com"
|
|
||||||
},
|
|
||||||
"otp": {
|
"otp": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "123456"
|
"example": "123456"
|
||||||
|
|
@ -1936,9 +2280,9 @@ const docTemplate = `{
|
||||||
"minLength": 8,
|
"minLength": 8,
|
||||||
"example": "newpassword123"
|
"example": "newpassword123"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"user_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "johndoe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1953,27 +2297,41 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handlers.SendSingleAfroSMSReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"message",
|
||||||
|
"recipient"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Hello world"
|
||||||
|
},
|
||||||
|
"recipient": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "+251912345678"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handlers.loginAdminReq": {
|
"handlers.loginAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password"
|
"password",
|
||||||
|
"user_name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "john.doe@example.com"
|
|
||||||
},
|
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "password123"
|
"example": "password123"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"user_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "adminuser"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.loginCustomerReq": {
|
"handlers.loginUserReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password",
|
"password",
|
||||||
|
|
@ -1990,7 +2348,7 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.loginCustomerRes": {
|
"handlers.loginUserRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"access_token": {
|
"access_token": {
|
||||||
|
|
@ -2036,10 +2394,6 @@ const docTemplate = `{
|
||||||
"handlers.updateAdminReq": {
|
"handlers.updateAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"first_name": {
|
"first_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John"
|
"example": "John"
|
||||||
|
|
|
||||||
|
|
@ -221,9 +221,109 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/assessment/questions": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns all active questions used for initial knowledge assessment",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Get active initial assessment questions",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"description": "Creates a new question for the initial knowledge assessment",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Create assessment question",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Assessment question payload",
|
||||||
|
"name": "question",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "Created",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentQuestion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/auth/logout": {
|
"/api/v1/auth/logout": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Logout customer",
|
"description": "Logout user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -233,10 +333,10 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Logout customer",
|
"summary": "Logout user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Logout customer",
|
"description": "Logout user",
|
||||||
"name": "logout",
|
"name": "logout",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
|
|
@ -301,7 +401,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
"$ref": "#/definitions/handlers.loginUserRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -385,6 +485,52 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/sendSMS": {
|
||||||
|
"post": {
|
||||||
|
"description": "Sends an SMS message to a single phone number using AfroMessage",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Send single SMS via AfroMessage",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Send SMS request",
|
||||||
|
"name": "sendSMS",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handlers.SendSingleAfroSMSReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/super-login": {
|
"/api/v1/super-login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login super-admin",
|
"description": "Login super-admin",
|
||||||
|
|
@ -815,6 +961,52 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/user/verify-otp": {
|
||||||
|
"post": {
|
||||||
|
"description": "Verify OTP for registration or other actions",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Verify OTP",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Verify OTP",
|
||||||
|
"name": "verifyOtp",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.VerifyOtpReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/user/{user_name}/is-unique": {
|
"/api/v1/user/{user_name}/is-unique": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns whether the specified user_name is available (unique)",
|
"description": "Returns whether the specified user_name is available (unique)",
|
||||||
|
|
@ -861,7 +1053,7 @@
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/admin-login": {
|
"/api/v1/{tenant_slug}/admin-login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Login user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -871,7 +1063,7 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Login customer",
|
"summary": "Login user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Login admin",
|
"description": "Login admin",
|
||||||
|
|
@ -911,9 +1103,114 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/customer-login": {
|
"/api/v1/{tenant_slug}/assessment/submit": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Evaluates user responses, calculates knowledge level, updates user profile, and sends notification",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"assessment"
|
||||||
|
],
|
||||||
|
"summary": "Submit initial knowledge assessment",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "User ID",
|
||||||
|
"name": "user_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Assessment responses",
|
||||||
|
"name": "payload",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.SubmitAssessmentReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/{tenant_slug}/otp/resend": {
|
||||||
|
"post": {
|
||||||
|
"description": "Resend OTP if the previous one is expired",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"otp"
|
||||||
|
],
|
||||||
|
"summary": "Resend OTP",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Resend OTP",
|
||||||
|
"name": "resendOtp",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ResendOtpReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/{tenant_slug}/user-login": {
|
||||||
|
"post": {
|
||||||
|
"description": "Login user",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -923,15 +1220,15 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"auth"
|
"auth"
|
||||||
],
|
],
|
||||||
"summary": "Login customer",
|
"summary": "Login user",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"description": "Login customer",
|
"description": "Login user",
|
||||||
"name": "login",
|
"name": "login",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
"$ref": "#/definitions/handlers.loginUserReq"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -939,7 +1236,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
"$ref": "#/definitions/handlers.loginUserRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1049,14 +1346,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/user/customer-profile": {
|
"/api/v1/{tenant_slug}/user/knowledge-level": {
|
||||||
"get": {
|
"put": {
|
||||||
"security": [
|
"description": "Updates the knowledge level of the specified user after initial assessment",
|
||||||
{
|
|
||||||
"Bearer": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get user profile",
|
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -1066,24 +1358,48 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
"summary": "Get user profile",
|
"summary": "Update user's knowledge level",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "User ID",
|
||||||
|
"name": "user_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Knowledge level",
|
||||||
|
"name": "knowledge_level",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateKnowledgeLevelReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CustomerProfileRes"
|
"$ref": "#/definitions/domain.Response"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"500": {
|
"500": {
|
||||||
"description": "Internal Server Error",
|
"description": "Internal Server Error",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1273,9 +1589,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/{tenant_slug}/user/verify-otp": {
|
"/api/v1/{tenant_slug}/user/user-profile": {
|
||||||
"post": {
|
"get": {
|
||||||
"description": "Verify OTP for registration or other actions",
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Get user profile",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
|
@ -1285,23 +1606,12 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
"summary": "Verify OTP",
|
"summary": "Get user profile",
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "Verify OTP",
|
|
||||||
"name": "verifyOtp",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/domain.VerifyOtpReq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
"$ref": "#/definitions/domain.UserProfileResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1371,6 +1681,48 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"domain.AssessmentOption": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"isCorrect": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"optionText": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AssessmentQuestion": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"difficultyLevel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AssessmentOption"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questionType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.ErrorResponse": {
|
"domain.ErrorResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1429,17 +1781,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.OtpFor": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"reset",
|
|
||||||
"register"
|
|
||||||
],
|
|
||||||
"x-enum-varnames": [
|
|
||||||
"OtpReset",
|
|
||||||
"OtpRegister"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"domain.OtpMedium": {
|
"domain.OtpMedium": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
@ -1477,34 +1818,46 @@
|
||||||
"country": {
|
"country": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"educationLevel": {
|
"education_level": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"firstName": {
|
"favoutite_topic": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"lastName": {
|
"first_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"organizationID": {
|
"language_challange": {
|
||||||
"$ref": "#/definitions/domain.ValidInt64"
|
|
||||||
},
|
|
||||||
"otp": {
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"otpMedium": {
|
"language_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"learning_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nick_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"otp_medium": {
|
||||||
"$ref": "#/definitions/domain.OtpMedium"
|
"$ref": "#/definitions/domain.OtpMedium"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"phoneNumber": {
|
"phone_number": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"preferredLanguage": {
|
"preferred_language": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"region": {
|
"region": {
|
||||||
|
|
@ -1513,7 +1866,18 @@
|
||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"userName": {
|
"user_name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.ResendOtpReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"user_name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"user_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1551,6 +1915,52 @@
|
||||||
"RoleSupport"
|
"RoleSupport"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.SubmitAssessmentReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"answers"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"answers": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UserAnswer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UpdateKnowledgeLevelReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"knowledge_level": {
|
||||||
|
"description": "BEGINNER, INTERMEDIATE, ADVANCED",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UserAnswer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"isCorrect": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"questionID": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"selectedOptionID": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"shortAnswer": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.UserProfileResponse": {
|
"domain.UserProfileResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1572,20 +1982,39 @@
|
||||||
"email_verified": {
|
"email_verified": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"favoutite_topic": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"first_name": {
|
"first_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"initial_assessment_completed": {
|
||||||
|
"description": "Profile fields",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"language_challange": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"language_goal": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"last_login": {
|
"last_login": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"last_name": {
|
"last_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"organization_id": {
|
"learning_goal": {
|
||||||
"type": "integer"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nick_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
@ -1634,48 +2063,17 @@
|
||||||
"UserStatusDeactivated"
|
"UserStatusDeactivated"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"domain.ValidInt64": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"valid": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.VerifyOtpReq": {
|
"domain.VerifyOtpReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"otp",
|
"otp",
|
||||||
"otp_for",
|
"user_name"
|
||||||
"otp_medium"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"description": "Required if medium is email",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"otp": {
|
"otp": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"otp_for": {
|
"user_name": {
|
||||||
"$ref": "#/definitions/domain.OtpFor"
|
|
||||||
},
|
|
||||||
"otp_medium": {
|
|
||||||
"enum": [
|
|
||||||
"email",
|
|
||||||
"sms"
|
|
||||||
],
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/domain.OtpMedium"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"phone_number": {
|
|
||||||
"description": "Required if medium is SMS",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1795,10 +2193,6 @@
|
||||||
"handlers.CreateAdminReq": {
|
"handlers.CreateAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.doe@example.com"
|
"example": "john.doe@example.com"
|
||||||
|
|
@ -1821,53 +2215,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.CustomerProfileRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email_verified": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"first_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"last_login": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"last_name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phone_number": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phone_verified": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"referral_code": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"$ref": "#/definitions/domain.Role"
|
|
||||||
},
|
|
||||||
"suspended": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"suspended_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.LoginAdminRes": {
|
"handlers.LoginAdminRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -1912,13 +2259,10 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"otp",
|
"otp",
|
||||||
"password"
|
"password",
|
||||||
|
"user_name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "john.doe@example.com"
|
|
||||||
},
|
|
||||||
"otp": {
|
"otp": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "123456"
|
"example": "123456"
|
||||||
|
|
@ -1928,9 +2272,9 @@
|
||||||
"minLength": 8,
|
"minLength": 8,
|
||||||
"example": "newpassword123"
|
"example": "newpassword123"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"user_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "johndoe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1945,27 +2289,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handlers.SendSingleAfroSMSReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"message",
|
||||||
|
"recipient"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Hello world"
|
||||||
|
},
|
||||||
|
"recipient": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "+251912345678"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handlers.loginAdminReq": {
|
"handlers.loginAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password"
|
"password",
|
||||||
|
"user_name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "john.doe@example.com"
|
|
||||||
},
|
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "password123"
|
"example": "password123"
|
||||||
},
|
},
|
||||||
"phone_number": {
|
"user_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "adminuser"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.loginCustomerReq": {
|
"handlers.loginUserReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"password",
|
"password",
|
||||||
|
|
@ -1982,7 +2340,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.loginCustomerRes": {
|
"handlers.loginUserRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"access_token": {
|
"access_token": {
|
||||||
|
|
@ -2028,10 +2386,6 @@
|
||||||
"handlers.updateAdminReq": {
|
"handlers.updateAdminReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"first_name": {
|
"first_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John"
|
"example": "John"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,32 @@
|
||||||
definitions:
|
definitions:
|
||||||
|
domain.AssessmentOption:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
isCorrect:
|
||||||
|
type: boolean
|
||||||
|
optionText:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
domain.AssessmentQuestion:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
difficultyLevel:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
options:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AssessmentOption'
|
||||||
|
type: array
|
||||||
|
questionType:
|
||||||
|
type: string
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.ErrorResponse:
|
domain.ErrorResponse:
|
||||||
properties:
|
properties:
|
||||||
error:
|
error:
|
||||||
|
|
@ -37,14 +65,6 @@ definitions:
|
||||||
pagination:
|
pagination:
|
||||||
$ref: '#/definitions/domain.Pagination'
|
$ref: '#/definitions/domain.Pagination'
|
||||||
type: object
|
type: object
|
||||||
domain.OtpFor:
|
|
||||||
enum:
|
|
||||||
- reset
|
|
||||||
- register
|
|
||||||
type: string
|
|
||||||
x-enum-varnames:
|
|
||||||
- OtpReset
|
|
||||||
- OtpRegister
|
|
||||||
domain.OtpMedium:
|
domain.OtpMedium:
|
||||||
enum:
|
enum:
|
||||||
- email
|
- email
|
||||||
|
|
@ -70,33 +90,48 @@ definitions:
|
||||||
type: integer
|
type: integer
|
||||||
country:
|
country:
|
||||||
type: string
|
type: string
|
||||||
educationLevel:
|
education_level:
|
||||||
type: string
|
type: string
|
||||||
email:
|
email:
|
||||||
type: string
|
type: string
|
||||||
firstName:
|
favoutite_topic:
|
||||||
type: string
|
type: string
|
||||||
lastName:
|
first_name:
|
||||||
type: string
|
type: string
|
||||||
organizationID:
|
language_challange:
|
||||||
$ref: '#/definitions/domain.ValidInt64'
|
|
||||||
otp:
|
|
||||||
type: string
|
type: string
|
||||||
otpMedium:
|
language_goal:
|
||||||
|
type: string
|
||||||
|
last_name:
|
||||||
|
type: string
|
||||||
|
learning_goal:
|
||||||
|
type: string
|
||||||
|
nick_name:
|
||||||
|
type: string
|
||||||
|
occupation:
|
||||||
|
type: string
|
||||||
|
otp_medium:
|
||||||
$ref: '#/definitions/domain.OtpMedium'
|
$ref: '#/definitions/domain.OtpMedium'
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
phoneNumber:
|
phone_number:
|
||||||
type: string
|
type: string
|
||||||
preferredLanguage:
|
preferred_language:
|
||||||
type: string
|
type: string
|
||||||
region:
|
region:
|
||||||
type: string
|
type: string
|
||||||
role:
|
role:
|
||||||
type: string
|
type: string
|
||||||
userName:
|
user_name:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
domain.ResendOtpReq:
|
||||||
|
properties:
|
||||||
|
user_name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- user_name
|
||||||
|
type: object
|
||||||
domain.Response:
|
domain.Response:
|
||||||
properties:
|
properties:
|
||||||
data: {}
|
data: {}
|
||||||
|
|
@ -122,6 +157,37 @@ definitions:
|
||||||
- RoleStudent
|
- RoleStudent
|
||||||
- RoleInstructor
|
- RoleInstructor
|
||||||
- RoleSupport
|
- RoleSupport
|
||||||
|
domain.SubmitAssessmentReq:
|
||||||
|
properties:
|
||||||
|
answers:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.UserAnswer'
|
||||||
|
minItems: 1
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- answers
|
||||||
|
type: object
|
||||||
|
domain.UpdateKnowledgeLevelReq:
|
||||||
|
properties:
|
||||||
|
knowledge_level:
|
||||||
|
description: BEGINNER, INTERMEDIATE, ADVANCED
|
||||||
|
type: string
|
||||||
|
user_id:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.UserAnswer:
|
||||||
|
properties:
|
||||||
|
isCorrect:
|
||||||
|
type: boolean
|
||||||
|
questionID:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
selectedOptionID:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
shortAnswer:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.UserProfileResponse:
|
domain.UserProfileResponse:
|
||||||
properties:
|
properties:
|
||||||
age:
|
age:
|
||||||
|
|
@ -136,16 +202,29 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
email_verified:
|
email_verified:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
favoutite_topic:
|
||||||
|
type: string
|
||||||
first_name:
|
first_name:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
initial_assessment_completed:
|
||||||
|
description: Profile fields
|
||||||
|
type: boolean
|
||||||
|
language_challange:
|
||||||
|
type: string
|
||||||
|
language_goal:
|
||||||
|
type: string
|
||||||
last_login:
|
last_login:
|
||||||
type: string
|
type: string
|
||||||
last_name:
|
last_name:
|
||||||
type: string
|
type: string
|
||||||
organization_id:
|
learning_goal:
|
||||||
type: integer
|
type: string
|
||||||
|
nick_name:
|
||||||
|
type: string
|
||||||
|
occupation:
|
||||||
|
type: string
|
||||||
phone_number:
|
phone_number:
|
||||||
type: string
|
type: string
|
||||||
phone_verified:
|
phone_verified:
|
||||||
|
|
@ -179,35 +258,15 @@ definitions:
|
||||||
- UserStatusActive
|
- UserStatusActive
|
||||||
- UserStatusSuspended
|
- UserStatusSuspended
|
||||||
- UserStatusDeactivated
|
- UserStatusDeactivated
|
||||||
domain.ValidInt64:
|
|
||||||
properties:
|
|
||||||
valid:
|
|
||||||
type: boolean
|
|
||||||
value:
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
domain.VerifyOtpReq:
|
domain.VerifyOtpReq:
|
||||||
properties:
|
properties:
|
||||||
email:
|
|
||||||
description: Required if medium is email
|
|
||||||
type: string
|
|
||||||
otp:
|
otp:
|
||||||
type: string
|
type: string
|
||||||
otp_for:
|
user_name:
|
||||||
$ref: '#/definitions/domain.OtpFor'
|
|
||||||
otp_medium:
|
|
||||||
allOf:
|
|
||||||
- $ref: '#/definitions/domain.OtpMedium'
|
|
||||||
enum:
|
|
||||||
- email
|
|
||||||
- sms
|
|
||||||
phone_number:
|
|
||||||
description: Required if medium is SMS
|
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- otp
|
- otp
|
||||||
- otp_for
|
- user_name
|
||||||
- otp_medium
|
|
||||||
type: object
|
type: object
|
||||||
handlers.AdminProfileRes:
|
handlers.AdminProfileRes:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -285,9 +344,6 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
handlers.CreateAdminReq:
|
handlers.CreateAdminReq:
|
||||||
properties:
|
properties:
|
||||||
company_id:
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
email:
|
email:
|
||||||
example: john.doe@example.com
|
example: john.doe@example.com
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -304,37 +360,6 @@ definitions:
|
||||||
example: "1234567890"
|
example: "1234567890"
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handlers.CustomerProfileRes:
|
|
||||||
properties:
|
|
||||||
created_at:
|
|
||||||
type: string
|
|
||||||
email:
|
|
||||||
type: string
|
|
||||||
email_verified:
|
|
||||||
type: boolean
|
|
||||||
first_name:
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
last_login:
|
|
||||||
type: string
|
|
||||||
last_name:
|
|
||||||
type: string
|
|
||||||
phone_number:
|
|
||||||
type: string
|
|
||||||
phone_verified:
|
|
||||||
type: boolean
|
|
||||||
referral_code:
|
|
||||||
type: string
|
|
||||||
role:
|
|
||||||
$ref: '#/definitions/domain.Role'
|
|
||||||
suspended:
|
|
||||||
type: boolean
|
|
||||||
suspended_at:
|
|
||||||
type: string
|
|
||||||
updated_at:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
handlers.LoginAdminRes:
|
handlers.LoginAdminRes:
|
||||||
properties:
|
properties:
|
||||||
access_token:
|
access_token:
|
||||||
|
|
@ -364,9 +389,6 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
handlers.ResetPasswordReq:
|
handlers.ResetPasswordReq:
|
||||||
properties:
|
properties:
|
||||||
email:
|
|
||||||
example: john.doe@example.com
|
|
||||||
type: string
|
|
||||||
otp:
|
otp:
|
||||||
example: "123456"
|
example: "123456"
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -374,12 +396,13 @@ definitions:
|
||||||
example: newpassword123
|
example: newpassword123
|
||||||
minLength: 8
|
minLength: 8
|
||||||
type: string
|
type: string
|
||||||
phone_number:
|
user_name:
|
||||||
example: "1234567890"
|
example: johndoe
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- otp
|
- otp
|
||||||
- password
|
- password
|
||||||
|
- user_name
|
||||||
type: object
|
type: object
|
||||||
handlers.SearchUserByNameOrPhoneReq:
|
handlers.SearchUserByNameOrPhoneReq:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -388,21 +411,31 @@ definitions:
|
||||||
role:
|
role:
|
||||||
$ref: '#/definitions/domain.Role'
|
$ref: '#/definitions/domain.Role'
|
||||||
type: object
|
type: object
|
||||||
|
handlers.SendSingleAfroSMSReq:
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
example: Hello world
|
||||||
|
type: string
|
||||||
|
recipient:
|
||||||
|
example: "+251912345678"
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- message
|
||||||
|
- recipient
|
||||||
|
type: object
|
||||||
handlers.loginAdminReq:
|
handlers.loginAdminReq:
|
||||||
properties:
|
properties:
|
||||||
email:
|
|
||||||
example: john.doe@example.com
|
|
||||||
type: string
|
|
||||||
password:
|
password:
|
||||||
example: password123
|
example: password123
|
||||||
type: string
|
type: string
|
||||||
phone_number:
|
user_name:
|
||||||
example: "1234567890"
|
example: adminuser
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- password
|
- password
|
||||||
|
- user_name
|
||||||
type: object
|
type: object
|
||||||
handlers.loginCustomerReq:
|
handlers.loginUserReq:
|
||||||
properties:
|
properties:
|
||||||
password:
|
password:
|
||||||
example: password123
|
example: password123
|
||||||
|
|
@ -414,7 +447,7 @@ definitions:
|
||||||
- password
|
- password
|
||||||
- user_name
|
- user_name
|
||||||
type: object
|
type: object
|
||||||
handlers.loginCustomerRes:
|
handlers.loginUserRes:
|
||||||
properties:
|
properties:
|
||||||
access_token:
|
access_token:
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -445,9 +478,6 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
handlers.updateAdminReq:
|
handlers.updateAdminReq:
|
||||||
properties:
|
properties:
|
||||||
company_id:
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
first_name:
|
first_name:
|
||||||
example: John
|
example: John
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -498,7 +528,7 @@ paths:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Login customer
|
description: Login user
|
||||||
parameters:
|
parameters:
|
||||||
- description: Login admin
|
- description: Login admin
|
||||||
in: body
|
in: body
|
||||||
|
|
@ -525,28 +555,98 @@ paths:
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/response.APIResponse'
|
||||||
summary: Login customer
|
summary: Login user
|
||||||
tags:
|
tags:
|
||||||
- auth
|
- auth
|
||||||
/api/v1/{tenant_slug}/customer-login:
|
/api/v1/{tenant_slug}/assessment/submit:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Login customer
|
description: Evaluates user responses, calculates knowledge level, updates user
|
||||||
|
profile, and sends notification
|
||||||
parameters:
|
parameters:
|
||||||
- description: Login customer
|
- description: User ID
|
||||||
|
in: path
|
||||||
|
name: user_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Assessment responses
|
||||||
in: body
|
in: body
|
||||||
name: login
|
name: payload
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.loginCustomerReq'
|
$ref: '#/definitions/domain.SubmitAssessmentReq'
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
$ref: '#/definitions/domain.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Submit initial knowledge assessment
|
||||||
|
tags:
|
||||||
|
- assessment
|
||||||
|
/api/v1/{tenant_slug}/otp/resend:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Resend OTP if the previous one is expired
|
||||||
|
parameters:
|
||||||
|
- description: Resend OTP
|
||||||
|
in: body
|
||||||
|
name: resendOtp
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ResendOtpReq'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Resend OTP
|
||||||
|
tags:
|
||||||
|
- otp
|
||||||
|
/api/v1/{tenant_slug}/user-login:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Login user
|
||||||
|
parameters:
|
||||||
|
- description: Login user
|
||||||
|
in: body
|
||||||
|
name: login
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handlers.loginUserReq'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handlers.loginUserRes'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -559,7 +659,7 @@ paths:
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/response.APIResponse'
|
||||||
summary: Login customer
|
summary: Login user
|
||||||
tags:
|
tags:
|
||||||
- auth
|
- auth
|
||||||
/api/v1/{tenant_slug}/user/{user_name}/is-pending:
|
/api/v1/{tenant_slug}/user/{user_name}/is-pending:
|
||||||
|
|
@ -650,29 +750,44 @@ paths:
|
||||||
summary: Check if phone number or email exist
|
summary: Check if phone number or email exist
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
/api/v1/{tenant_slug}/user/customer-profile:
|
/api/v1/{tenant_slug}/user/knowledge-level:
|
||||||
get:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Get user profile
|
description: Updates the knowledge level of the specified user after initial
|
||||||
|
assessment
|
||||||
|
parameters:
|
||||||
|
- description: User ID
|
||||||
|
in: path
|
||||||
|
name: user_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Knowledge level
|
||||||
|
in: body
|
||||||
|
name: knowledge_level
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.UpdateKnowledgeLevelReq'
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.CustomerProfileRes'
|
$ref: '#/definitions/domain.Response'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
"500":
|
"500":
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
security:
|
summary: Update user's knowledge level
|
||||||
- Bearer: []
|
|
||||||
summary: Get user profile
|
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
/api/v1/{tenant_slug}/user/register:
|
/api/v1/{tenant_slug}/user/register:
|
||||||
|
|
@ -795,25 +910,18 @@ paths:
|
||||||
summary: Send reset code
|
summary: Send reset code
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
/api/v1/{tenant_slug}/user/verify-otp:
|
/api/v1/{tenant_slug}/user/user-profile:
|
||||||
post:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Verify OTP for registration or other actions
|
description: Get user profile
|
||||||
parameters:
|
|
||||||
- description: Verify OTP
|
|
||||||
in: body
|
|
||||||
name: verifyOtp
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.VerifyOtpReq'
|
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/domain.UserProfileResponse'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -822,7 +930,9 @@ paths:
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/response.APIResponse'
|
||||||
summary: Verify OTP
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get user profile
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
/api/v1/admin:
|
/api/v1/admin:
|
||||||
|
|
@ -960,13 +1070,73 @@ paths:
|
||||||
summary: Update Admin
|
summary: Update Admin
|
||||||
tags:
|
tags:
|
||||||
- admin
|
- admin
|
||||||
|
/api/v1/assessment/questions:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns all active questions used for initial knowledge assessment
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Get active initial assessment questions
|
||||||
|
tags:
|
||||||
|
- assessment
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Creates a new question for the initial knowledge assessment
|
||||||
|
parameters:
|
||||||
|
- description: Assessment question payload
|
||||||
|
in: body
|
||||||
|
name: question
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
description: Created
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/domain.AssessmentQuestion'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Create assessment question
|
||||||
|
tags:
|
||||||
|
- assessment
|
||||||
/api/v1/auth/logout:
|
/api/v1/auth/logout:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Logout customer
|
description: Logout user
|
||||||
parameters:
|
parameters:
|
||||||
- description: Logout customer
|
- description: Logout user
|
||||||
in: body
|
in: body
|
||||||
name: logout
|
name: logout
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -991,7 +1161,7 @@ paths:
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/response.APIResponse'
|
$ref: '#/definitions/response.APIResponse'
|
||||||
summary: Logout customer
|
summary: Logout user
|
||||||
tags:
|
tags:
|
||||||
- auth
|
- auth
|
||||||
/api/v1/auth/refresh:
|
/api/v1/auth/refresh:
|
||||||
|
|
@ -1012,7 +1182,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
$ref: '#/definitions/handlers.loginUserRes'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -1070,6 +1240,36 @@ paths:
|
||||||
summary: Retrieve application logs with filtering and pagination
|
summary: Retrieve application logs with filtering and pagination
|
||||||
tags:
|
tags:
|
||||||
- Logs
|
- Logs
|
||||||
|
/api/v1/sendSMS:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Sends an SMS message to a single phone number using AfroMessage
|
||||||
|
parameters:
|
||||||
|
- description: Send SMS request
|
||||||
|
in: body
|
||||||
|
name: sendSMS
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handlers.SendSingleAfroSMSReq'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Send single SMS via AfroMessage
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
/api/v1/super-login:
|
/api/v1/super-login:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -1381,6 +1581,36 @@ paths:
|
||||||
summary: Get user by id
|
summary: Get user by id
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
/api/v1/user/verify-otp:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Verify OTP for registration or other actions
|
||||||
|
parameters:
|
||||||
|
- description: Verify OTP
|
||||||
|
in: body
|
||||||
|
name: verifyOtp
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.VerifyOtpReq'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Verify OTP
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: auth.sql
|
// source: auth.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
||||||
|
|
|
||||||
290
gen/db/initial_assessment.sql.go
Normal file
290
gen/db/initial_assessment.sql.go
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: initial_assessment.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CreateAssessmentAnswer = `-- name: CreateAssessmentAnswer :exec
|
||||||
|
INSERT INTO assessment_answers (
|
||||||
|
attempt_id,
|
||||||
|
question_id,
|
||||||
|
selected_option_id,
|
||||||
|
short_answer,
|
||||||
|
is_correct
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, -- attempt_id
|
||||||
|
$2, -- question_id
|
||||||
|
$3, -- selected_option_id
|
||||||
|
$4, -- short_answer
|
||||||
|
$5 -- is_correct
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateAssessmentAnswerParams struct {
|
||||||
|
AttemptID int64 `json:"attempt_id"`
|
||||||
|
QuestionID int64 `json:"question_id"`
|
||||||
|
SelectedOptionID pgtype.Int8 `json:"selected_option_id"`
|
||||||
|
ShortAnswer pgtype.Text `json:"short_answer"`
|
||||||
|
IsCorrect bool `json:"is_correct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAssessmentAnswer(ctx context.Context, arg CreateAssessmentAnswerParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, CreateAssessmentAnswer,
|
||||||
|
arg.AttemptID,
|
||||||
|
arg.QuestionID,
|
||||||
|
arg.SelectedOptionID,
|
||||||
|
arg.ShortAnswer,
|
||||||
|
arg.IsCorrect,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateAssessmentAttempt = `-- name: CreateAssessmentAttempt :one
|
||||||
|
INSERT INTO assessment_attempts (
|
||||||
|
user_id,
|
||||||
|
total_questions,
|
||||||
|
correct_answers,
|
||||||
|
score_percentage,
|
||||||
|
knowledge_level
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, -- user_id
|
||||||
|
$2, -- total_questions
|
||||||
|
$3, -- correct_answers
|
||||||
|
$4, -- score_percentage
|
||||||
|
$5 -- knowledge_level
|
||||||
|
)
|
||||||
|
RETURNING
|
||||||
|
id,
|
||||||
|
user_id,
|
||||||
|
total_questions,
|
||||||
|
correct_answers,
|
||||||
|
score_percentage,
|
||||||
|
knowledge_level,
|
||||||
|
completed_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateAssessmentAttemptParams struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
TotalQuestions int32 `json:"total_questions"`
|
||||||
|
CorrectAnswers int32 `json:"correct_answers"`
|
||||||
|
ScorePercentage pgtype.Numeric `json:"score_percentage"`
|
||||||
|
KnowledgeLevel string `json:"knowledge_level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAssessmentAttempt(ctx context.Context, arg CreateAssessmentAttemptParams) (AssessmentAttempt, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateAssessmentAttempt,
|
||||||
|
arg.UserID,
|
||||||
|
arg.TotalQuestions,
|
||||||
|
arg.CorrectAnswers,
|
||||||
|
arg.ScorePercentage,
|
||||||
|
arg.KnowledgeLevel,
|
||||||
|
)
|
||||||
|
var i AssessmentAttempt
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.TotalQuestions,
|
||||||
|
&i.CorrectAnswers,
|
||||||
|
&i.ScorePercentage,
|
||||||
|
&i.KnowledgeLevel,
|
||||||
|
&i.CompletedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateAssessmentQuestion = `-- name: CreateAssessmentQuestion :one
|
||||||
|
INSERT INTO assessment_questions (
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
question_type,
|
||||||
|
difficulty_level
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
RETURNING id, title, description, question_type, difficulty_level, is_active, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateAssessmentQuestionParams struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description pgtype.Text `json:"description"`
|
||||||
|
QuestionType string `json:"question_type"`
|
||||||
|
DifficultyLevel string `json:"difficulty_level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAssessmentQuestion(ctx context.Context, arg CreateAssessmentQuestionParams) (AssessmentQuestion, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateAssessmentQuestion,
|
||||||
|
arg.Title,
|
||||||
|
arg.Description,
|
||||||
|
arg.QuestionType,
|
||||||
|
arg.DifficultyLevel,
|
||||||
|
)
|
||||||
|
var i AssessmentQuestion
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Description,
|
||||||
|
&i.QuestionType,
|
||||||
|
&i.DifficultyLevel,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateAssessmentQuestionOption = `-- name: CreateAssessmentQuestionOption :exec
|
||||||
|
INSERT INTO assessment_question_options (
|
||||||
|
question_id,
|
||||||
|
option_text,
|
||||||
|
is_correct
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateAssessmentQuestionOptionParams struct {
|
||||||
|
QuestionID int64 `json:"question_id"`
|
||||||
|
OptionText string `json:"option_text"`
|
||||||
|
IsCorrect bool `json:"is_correct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAssessmentQuestionOption(ctx context.Context, arg CreateAssessmentQuestionOptionParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, CreateAssessmentQuestionOption, arg.QuestionID, arg.OptionText, arg.IsCorrect)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetActiveAssessmentQuestions = `-- name: GetActiveAssessmentQuestions :many
|
||||||
|
SELECT id, title, description, question_type, difficulty_level, is_active, created_at, updated_at
|
||||||
|
FROM assessment_questions
|
||||||
|
WHERE is_active = TRUE
|
||||||
|
ORDER BY difficulty_level, id
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetActiveAssessmentQuestions(ctx context.Context) ([]AssessmentQuestion, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetActiveAssessmentQuestions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []AssessmentQuestion
|
||||||
|
for rows.Next() {
|
||||||
|
var i AssessmentQuestion
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Description,
|
||||||
|
&i.QuestionType,
|
||||||
|
&i.DifficultyLevel,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetAssessmentOptionByID = `-- name: GetAssessmentOptionByID :one
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
question_id,
|
||||||
|
option_text,
|
||||||
|
is_correct
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE id = $1
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetAssessmentOptionByID(ctx context.Context, id int64) (AssessmentQuestionOption, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetAssessmentOptionByID, id)
|
||||||
|
var i AssessmentQuestionOption
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.QuestionID,
|
||||||
|
&i.OptionText,
|
||||||
|
&i.IsCorrect,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetCorrectOptionForQuestion = `-- name: GetCorrectOptionForQuestion :one
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE question_id = $1
|
||||||
|
AND is_correct = TRUE
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetCorrectOptionForQuestion(ctx context.Context, questionID int64) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetCorrectOptionForQuestion, questionID)
|
||||||
|
var id int64
|
||||||
|
err := row.Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetLatestAssessmentAttempt = `-- name: GetLatestAssessmentAttempt :one
|
||||||
|
SELECT id, user_id, total_questions, correct_answers, score_percentage, knowledge_level, completed_at
|
||||||
|
FROM assessment_attempts
|
||||||
|
WHERE user_id = $1
|
||||||
|
ORDER BY completed_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetLatestAssessmentAttempt(ctx context.Context, userID int64) (AssessmentAttempt, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetLatestAssessmentAttempt, userID)
|
||||||
|
var i AssessmentAttempt
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.TotalQuestions,
|
||||||
|
&i.CorrectAnswers,
|
||||||
|
&i.ScorePercentage,
|
||||||
|
&i.KnowledgeLevel,
|
||||||
|
&i.CompletedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetQuestionOptions = `-- name: GetQuestionOptions :many
|
||||||
|
SELECT id, question_id, option_text, is_correct
|
||||||
|
FROM assessment_question_options
|
||||||
|
WHERE question_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetQuestionOptions(ctx context.Context, questionID int64) ([]AssessmentQuestionOption, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetQuestionOptions, questionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []AssessmentQuestionOption
|
||||||
|
for rows.Next() {
|
||||||
|
var i AssessmentQuestionOption
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.QuestionID,
|
||||||
|
&i.OptionText,
|
||||||
|
&i.IsCorrect,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: issue_reporting.sql
|
// source: issue_reporting.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
|
||||||
107
gen/db/models.go
107
gen/db/models.go
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
||||||
|
|
@ -18,6 +18,43 @@ type Assessment struct {
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AssessmentAnswer struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
AttemptID int64 `json:"attempt_id"`
|
||||||
|
QuestionID int64 `json:"question_id"`
|
||||||
|
SelectedOptionID pgtype.Int8 `json:"selected_option_id"`
|
||||||
|
ShortAnswer pgtype.Text `json:"short_answer"`
|
||||||
|
IsCorrect bool `json:"is_correct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentAttempt struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
TotalQuestions int32 `json:"total_questions"`
|
||||||
|
CorrectAnswers int32 `json:"correct_answers"`
|
||||||
|
ScorePercentage pgtype.Numeric `json:"score_percentage"`
|
||||||
|
KnowledgeLevel string `json:"knowledge_level"`
|
||||||
|
CompletedAt pgtype.Timestamptz `json:"completed_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentQuestion struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description pgtype.Text `json:"description"`
|
||||||
|
QuestionType string `json:"question_type"`
|
||||||
|
DifficultyLevel string `json:"difficulty_level"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentQuestionOption struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
QuestionID int64 `json:"question_id"`
|
||||||
|
OptionText string `json:"option_text"`
|
||||||
|
IsCorrect bool `json:"is_correct"`
|
||||||
|
}
|
||||||
|
|
||||||
type AssessmentSubmission struct {
|
type AssessmentSubmission struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
AssessmentID int64 `json:"assessment_id"`
|
AssessmentID int64 `json:"assessment_id"`
|
||||||
|
|
@ -29,15 +66,15 @@ type AssessmentSubmission struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Course struct {
|
type Course struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
InstructorID int64 `json:"instructor_id"`
|
InstructorID int64 `json:"instructor_id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description pgtype.Text `json:"description"`
|
Description pgtype.Text `json:"description"`
|
||||||
Level pgtype.Text `json:"level"`
|
Level pgtype.Text `json:"level"`
|
||||||
Language pgtype.Text `json:"language"`
|
Language pgtype.Text `json:"language"`
|
||||||
IsPublished bool `json:"is_published"`
|
IsPublished bool `json:"is_published"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CourseModule struct {
|
type CourseModule struct {
|
||||||
|
|
@ -132,25 +169,33 @@ type ReportedIssue struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Email pgtype.Text `json:"email"`
|
Email pgtype.Text `json:"email"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Password []byte `json:"password"`
|
Password []byte `json:"password"`
|
||||||
Age pgtype.Int4 `json:"age"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
KnowledgeLevel pgtype.Text `json:"knowledge_level"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
Status string `json:"status"`
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
LastLogin pgtype.Timestamptz `json:"last_login"`
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
LastLogin pgtype.Timestamptz `json:"last_login"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: notification.sql
|
// source: notification.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: otp.sql
|
// source: otp.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: settings.sql
|
// source: settings.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.29.0
|
// sqlc v1.30.0
|
||||||
// source: user.sql
|
// source: user.sql
|
||||||
|
|
||||||
package dbgen
|
package dbgen
|
||||||
|
|
@ -14,14 +14,10 @@ import (
|
||||||
const CheckPhoneEmailExist = `-- name: CheckPhoneEmailExist :one
|
const CheckPhoneEmailExist = `-- name: CheckPhoneEmailExist :one
|
||||||
SELECT
|
SELECT
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1 FROM users u1 WHERE u1.phone_number = $1
|
||||||
FROM users u1
|
|
||||||
WHERE u1.phone_number = $1
|
|
||||||
) AS phone_exists,
|
) AS phone_exists,
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1 FROM users u2 WHERE u2.email = $2
|
||||||
FROM users u2
|
|
||||||
WHERE u2.email = $2
|
|
||||||
) AS email_exists
|
) AS email_exists
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -55,30 +51,50 @@ INSERT INTO users (
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
profile_completed,
|
profile_completed,
|
||||||
|
profile_picture_url,
|
||||||
preferred_language,
|
preferred_language,
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
$1, -- first_name
|
$1, -- first_name
|
||||||
$2, -- last_name
|
$2, -- last_name
|
||||||
$3, -- user_name
|
$3, -- user_name
|
||||||
$4, -- email
|
$4, -- email
|
||||||
$5, -- phone_number
|
$5, -- phone_number
|
||||||
$6, -- role
|
$6, -- role
|
||||||
$7, -- password (BYTEA)
|
$7, -- password
|
||||||
$8, -- age
|
$8, -- age
|
||||||
$9, -- education_level
|
$9, -- education_level
|
||||||
$10, -- country
|
$10, -- country
|
||||||
$11, -- region
|
$11, -- region
|
||||||
$12, -- email_verified
|
|
||||||
$13, -- phone_verified
|
$12, -- nick_name
|
||||||
$14, -- status (PENDING | ACTIVE)
|
$13, -- occupation
|
||||||
$15, -- profile_completed
|
$14, -- learning_goal
|
||||||
$16, -- preferred_language
|
$15, -- language_goal
|
||||||
|
$16, -- language_challange
|
||||||
|
$17, -- favoutite_topic
|
||||||
|
|
||||||
|
$18, -- initial_assessment_completed
|
||||||
|
$19, -- email_verified
|
||||||
|
$20, -- phone_verified
|
||||||
|
$21, -- status
|
||||||
|
$22, -- profile_completed
|
||||||
|
$23, -- profile_picture_url
|
||||||
|
$24, -- preferred_language
|
||||||
CURRENT_TIMESTAMP
|
CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
RETURNING
|
RETURNING
|
||||||
|
|
@ -93,53 +109,79 @@ RETURNING
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
profile_completed,
|
profile_completed,
|
||||||
|
profile_picture_url,
|
||||||
preferred_language,
|
preferred_language,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateUserParams struct {
|
type CreateUserParams struct {
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Email pgtype.Text `json:"email"`
|
Email pgtype.Text `json:"email"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Password []byte `json:"password"`
|
Password []byte `json:"password"`
|
||||||
Age pgtype.Int4 `json:"age"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
Status string `json:"status"`
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateUserRow struct {
|
type CreateUserRow struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Email pgtype.Text `json:"email"`
|
Email pgtype.Text `json:"email"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Age pgtype.Int4 `json:"age"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
Status string `json:"status"`
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
|
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
|
||||||
|
|
@ -155,10 +197,18 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
|
||||||
arg.EducationLevel,
|
arg.EducationLevel,
|
||||||
arg.Country,
|
arg.Country,
|
||||||
arg.Region,
|
arg.Region,
|
||||||
|
arg.NickName,
|
||||||
|
arg.Occupation,
|
||||||
|
arg.LearningGoal,
|
||||||
|
arg.LanguageGoal,
|
||||||
|
arg.LanguageChallange,
|
||||||
|
arg.FavoutiteTopic,
|
||||||
|
arg.InitialAssessmentCompleted,
|
||||||
arg.EmailVerified,
|
arg.EmailVerified,
|
||||||
arg.PhoneVerified,
|
arg.PhoneVerified,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
arg.ProfileCompleted,
|
arg.ProfileCompleted,
|
||||||
|
arg.ProfilePictureUrl,
|
||||||
arg.PreferredLanguage,
|
arg.PreferredLanguage,
|
||||||
)
|
)
|
||||||
var i CreateUserRow
|
var i CreateUserRow
|
||||||
|
|
@ -174,10 +224,18 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
|
&i.InitialAssessmentCompleted,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.ProfileCompleted,
|
&i.ProfileCompleted,
|
||||||
|
&i.ProfilePictureUrl,
|
||||||
&i.PreferredLanguage,
|
&i.PreferredLanguage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
|
@ -209,6 +267,17 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
|
profile_picture_url,
|
||||||
|
preferred_language,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -249,25 +318,34 @@ type GetAllUsersParams struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetAllUsersRow struct {
|
type GetAllUsersRow struct {
|
||||||
TotalCount int64 `json:"total_count"`
|
TotalCount int64 `json:"total_count"`
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Email pgtype.Text `json:"email"`
|
Email pgtype.Text `json:"email"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Age pgtype.Int4 `json:"age"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
Status string `json:"status"`
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
PreferredLanguage_2 pgtype.Text `json:"preferred_language_2"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]GetAllUsersRow, error) {
|
func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]GetAllUsersRow, error) {
|
||||||
|
|
@ -299,11 +377,20 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
|
&i.InitialAssessmentCompleted,
|
||||||
|
&i.ProfilePictureUrl,
|
||||||
|
&i.PreferredLanguage,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.ProfileCompleted,
|
&i.ProfileCompleted,
|
||||||
&i.PreferredLanguage,
|
&i.PreferredLanguage_2,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
@ -344,6 +431,14 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -355,7 +450,7 @@ SELECT
|
||||||
updated_at
|
updated_at
|
||||||
FROM users
|
FROM users
|
||||||
WHERE (email = $1 AND $1 IS NOT NULL)
|
WHERE (email = $1 AND $1 IS NOT NULL)
|
||||||
OR (phone_number = $2 AND $2 IS NOT NULL)
|
OR (phone_number = $2 AND $2 IS NOT NULL)
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -377,6 +472,12 @@ type GetUserByEmailPhoneRow struct {
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
|
@ -404,6 +505,12 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
|
|
@ -418,7 +525,7 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetUserByID = `-- name: GetUserByID :one
|
const GetUserByID = `-- name: GetUserByID :one
|
||||||
SELECT id, first_name, last_name, user_name, email, phone_number, role, password, age, education_level, country, region, email_verified, phone_verified, status, last_login, profile_completed, profile_picture_url, preferred_language, created_at, updated_at
|
SELECT id, first_name, last_name, user_name, email, phone_number, role, password, age, education_level, country, region, knowledge_level, nick_name, occupation, learning_goal, language_goal, language_challange, favoutite_topic, initial_assessment_completed, email_verified, phone_verified, status, last_login, profile_completed, profile_picture_url, preferred_language, created_at, updated_at
|
||||||
FROM users
|
FROM users
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -439,6 +546,14 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.KnowledgeLevel,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
|
&i.InitialAssessmentCompleted,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
|
|
@ -466,6 +581,14 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -493,6 +616,12 @@ type GetUserByUserNameRow struct {
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
|
@ -520,6 +649,12 @@ func (q *Queries) GetUserByUserName(ctx context.Context, userName string) (GetUs
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
|
|
@ -575,6 +710,17 @@ SELECT
|
||||||
education_level,
|
education_level,
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
|
|
||||||
|
nick_name,
|
||||||
|
occupation,
|
||||||
|
learning_goal,
|
||||||
|
language_goal,
|
||||||
|
language_challange,
|
||||||
|
favoutite_topic,
|
||||||
|
|
||||||
|
initial_assessment_completed,
|
||||||
|
profile_picture_url,
|
||||||
|
preferred_language,
|
||||||
email_verified,
|
email_verified,
|
||||||
phone_verified,
|
phone_verified,
|
||||||
status,
|
status,
|
||||||
|
|
@ -600,23 +746,32 @@ type SearchUserByNameOrPhoneParams struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchUserByNameOrPhoneRow struct {
|
type SearchUserByNameOrPhoneRow struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
Email pgtype.Text `json:"email"`
|
Email pgtype.Text `json:"email"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Age pgtype.Int4 `json:"age"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
EducationLevel pgtype.Text `json:"education_level"`
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
Country pgtype.Text `json:"country"`
|
Country pgtype.Text `json:"country"`
|
||||||
Region pgtype.Text `json:"region"`
|
Region pgtype.Text `json:"region"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
Status string `json:"status"`
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) {
|
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) {
|
||||||
|
|
@ -640,6 +795,15 @@ func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByN
|
||||||
&i.EducationLevel,
|
&i.EducationLevel,
|
||||||
&i.Country,
|
&i.Country,
|
||||||
&i.Region,
|
&i.Region,
|
||||||
|
&i.NickName,
|
||||||
|
&i.Occupation,
|
||||||
|
&i.LearningGoal,
|
||||||
|
&i.LanguageGoal,
|
||||||
|
&i.LanguageChallange,
|
||||||
|
&i.FavoutiteTopic,
|
||||||
|
&i.InitialAssessmentCompleted,
|
||||||
|
&i.ProfilePictureUrl,
|
||||||
|
&i.PreferredLanguage,
|
||||||
&i.EmailVerified,
|
&i.EmailVerified,
|
||||||
&i.PhoneVerified,
|
&i.PhoneVerified,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
|
|
@ -662,47 +826,117 @@ UPDATE users
|
||||||
SET
|
SET
|
||||||
password = $1,
|
password = $1,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE email = $2 OR phone_number = $3
|
WHERE user_name = $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdatePasswordParams struct {
|
type UpdatePasswordParams struct {
|
||||||
Password []byte `json:"password"`
|
Password []byte `json:"password"`
|
||||||
Email pgtype.Text `json:"email"`
|
UserName string `json:"user_name"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
||||||
_, err := q.db.Exec(ctx, UpdatePassword, arg.Password, arg.Email, arg.PhoneNumber)
|
_, err := q.db.Exec(ctx, UpdatePassword, arg.Password, arg.UserName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const UpdateUser = `-- name: UpdateUser :exec
|
const UpdateUser = `-- name: UpdateUser :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET
|
SET
|
||||||
first_name = $1,
|
first_name = $1,
|
||||||
last_name = $2,
|
last_name = $2,
|
||||||
status = $3,
|
user_name = $3,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
age = $4,
|
||||||
WHERE id = $4
|
education_level = $5,
|
||||||
|
country = $6,
|
||||||
|
region = $7,
|
||||||
|
|
||||||
|
nick_name = $8,
|
||||||
|
occupation = $9,
|
||||||
|
learning_goal = $10,
|
||||||
|
language_goal = $11,
|
||||||
|
language_challange = $12,
|
||||||
|
favoutite_topic = $13,
|
||||||
|
|
||||||
|
initial_assessment_completed = $14,
|
||||||
|
email_verified = $15,
|
||||||
|
phone_verified = $16,
|
||||||
|
status = $17,
|
||||||
|
profile_completed = $18,
|
||||||
|
profile_picture_url = $19,
|
||||||
|
preferred_language = $20,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = $21
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateUserParams struct {
|
type UpdateUserParams struct {
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
Status string `json:"status"`
|
UserName string `json:"user_name"`
|
||||||
ID int64 `json:"id"`
|
Age pgtype.Int4 `json:"age"`
|
||||||
|
EducationLevel pgtype.Text `json:"education_level"`
|
||||||
|
Country pgtype.Text `json:"country"`
|
||||||
|
Region pgtype.Text `json:"region"`
|
||||||
|
NickName pgtype.Text `json:"nick_name"`
|
||||||
|
Occupation pgtype.Text `json:"occupation"`
|
||||||
|
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||||
|
LanguageGoal pgtype.Text `json:"language_goal"`
|
||||||
|
LanguageChallange pgtype.Text `json:"language_challange"`
|
||||||
|
FavoutiteTopic pgtype.Text `json:"favoutite_topic"`
|
||||||
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
|
ProfilePictureUrl pgtype.Text `json:"profile_picture_url"`
|
||||||
|
PreferredLanguage pgtype.Text `json:"preferred_language"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||||
_, err := q.db.Exec(ctx, UpdateUser,
|
_, err := q.db.Exec(ctx, UpdateUser,
|
||||||
arg.FirstName,
|
arg.FirstName,
|
||||||
arg.LastName,
|
arg.LastName,
|
||||||
|
arg.UserName,
|
||||||
|
arg.Age,
|
||||||
|
arg.EducationLevel,
|
||||||
|
arg.Country,
|
||||||
|
arg.Region,
|
||||||
|
arg.NickName,
|
||||||
|
arg.Occupation,
|
||||||
|
arg.LearningGoal,
|
||||||
|
arg.LanguageGoal,
|
||||||
|
arg.LanguageChallange,
|
||||||
|
arg.FavoutiteTopic,
|
||||||
|
arg.InitialAssessmentCompleted,
|
||||||
|
arg.EmailVerified,
|
||||||
|
arg.PhoneVerified,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
|
arg.ProfileCompleted,
|
||||||
|
arg.ProfilePictureUrl,
|
||||||
|
arg.PreferredLanguage,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UpdateUserKnowledgeLevel = `-- name: UpdateUserKnowledgeLevel :exec
|
||||||
|
UPDATE users
|
||||||
|
SET
|
||||||
|
knowledge_level = $1,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateUserKnowledgeLevelParams struct {
|
||||||
|
KnowledgeLevel pgtype.Text `json:"knowledge_level"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateUserKnowledgeLevel(ctx context.Context, arg UpdateUserKnowledgeLevelParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, UpdateUserKnowledgeLevel, arg.KnowledgeLevel, arg.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateUserStatus = `-- name: UpdateUserStatus :exec
|
const UpdateUserStatus = `-- name: UpdateUserStatus :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET
|
SET
|
||||||
|
|
|
||||||
47
internal/domain/initial_assessment.go
Normal file
47
internal/domain/initial_assessment.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type QuestionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
QuestionTypeMultipleChoice QuestionType = "multiple_choice"
|
||||||
|
QuestionTypeTrueFalse QuestionType = "true_false"
|
||||||
|
QuestionTypeShortAnswer QuestionType = "short_answer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubmitAssessmentReq struct {
|
||||||
|
Answers []UserAnswer `json:"answers" validate:"required,min=1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentQuestion struct {
|
||||||
|
ID int64
|
||||||
|
Title string
|
||||||
|
Description string
|
||||||
|
QuestionType string
|
||||||
|
DifficultyLevel string
|
||||||
|
Options []AssessmentOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentOption struct {
|
||||||
|
ID int64
|
||||||
|
OptionText string
|
||||||
|
IsCorrect bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAnswer struct {
|
||||||
|
QuestionID int64
|
||||||
|
SelectedOptionID int64
|
||||||
|
ShortAnswer string
|
||||||
|
IsCorrect bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssessmentAttempt struct {
|
||||||
|
ID int64
|
||||||
|
UserID int64
|
||||||
|
TotalQuestions int
|
||||||
|
CorrectAnswers int
|
||||||
|
ScorePercentage float64
|
||||||
|
KnowledgeLevel string
|
||||||
|
CompletedAt time.Time
|
||||||
|
}
|
||||||
|
|
@ -14,27 +14,7 @@ type NotificationDeliveryStatus string
|
||||||
type DeliveryChannel string
|
type DeliveryChannel string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NotificationTypeWalletUpdated NotificationType = "wallet_updated"
|
NOTIFICATION_TYPE_KNOWLEDGE_LEVEL_UPDATE NotificationType = "knowledge_level_update"
|
||||||
NotificationTypeDepositResult NotificationType = "deposit_result"
|
|
||||||
NotificationTypeDepositVerification NotificationType = "deposit_verification"
|
|
||||||
NotificationTypeCashOutSuccess NotificationType = "cash_out_success"
|
|
||||||
NotificationTypeDepositSuccess NotificationType = "deposit_success"
|
|
||||||
NotificationTypeWithdrawSuccess NotificationType = "withdraw_success"
|
|
||||||
NotificationTypeBetPlaced NotificationType = "bet_placed"
|
|
||||||
NotificationTypeDailyReport NotificationType = "daily_report"
|
|
||||||
NotificationTypeReportRequest NotificationType = "report_request"
|
|
||||||
NotificationTypeHighLossOnBet NotificationType = "high_loss_on_bet"
|
|
||||||
NotificationTypeBetOverload NotificationType = "bet_overload"
|
|
||||||
NotificationTypeSignUpWelcome NotificationType = "signup_welcome"
|
|
||||||
NotificationTypeOTPSent NotificationType = "otp_sent"
|
|
||||||
NOTIFICATION_TYPE_WALLET NotificationType = "wallet_threshold"
|
|
||||||
NOTIFICATION_TYPE_TRANSFER_FAIL NotificationType = "transfer_failed"
|
|
||||||
NOTIFICATION_TYPE_TRANSFER_SUCCESS NotificationType = "transfer_success"
|
|
||||||
NOTIFICATION_TYPE_ADMIN_ALERT NotificationType = "admin_alert"
|
|
||||||
NOTIFICATION_TYPE_BET_RESULT NotificationType = "bet_result"
|
|
||||||
NOTIFICATION_TYPE_TRANSFER_REJECTED NotificationType = "transfer_rejected"
|
|
||||||
NOTIFICATION_TYPE_APPROVAL_REQUIRED NotificationType = "approval_required"
|
|
||||||
NOTIFICATION_TYPE_BONUS_AWARDED NotificationType = "bonus_awarded"
|
|
||||||
|
|
||||||
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
|
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
|
||||||
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
|
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ const (
|
||||||
UserStatusDeactivated UserStatus = "DEACTIVATED"
|
UserStatusDeactivated UserStatus = "DEACTIVATED"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UpdateKnowledgeLevelReq struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
KnowledgeLevel string `json:"knowledge_level"` // BEGINNER, INTERMEDIATE, ADVANCED
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int64
|
ID int64
|
||||||
FirstName string
|
FirstName string
|
||||||
|
|
@ -40,6 +45,15 @@ type User struct {
|
||||||
Country string
|
Country string
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Profile fields
|
||||||
|
initial_assessment_completed bool
|
||||||
|
NickName string
|
||||||
|
Occupation string
|
||||||
|
LearningGoal string
|
||||||
|
LanguageGoal string
|
||||||
|
LanguageChallange string
|
||||||
|
FavoutiteTopic string
|
||||||
|
|
||||||
EmailVerified bool
|
EmailVerified bool
|
||||||
PhoneVerified bool
|
PhoneVerified bool
|
||||||
Status UserStatus
|
Status UserStatus
|
||||||
|
|
@ -54,26 +68,39 @@ type User struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserProfileResponse struct {
|
type UserProfileResponse struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
UserName string `json:"user_name,omitempty"`
|
UserName string `json:"user_name,omitempty"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
PhoneNumber string `json:"phone_number,omitempty"`
|
PhoneNumber string `json:"phone_number,omitempty"`
|
||||||
Role Role `json:"role"`
|
Role Role `json:"role"`
|
||||||
Age int `json:"age,omitempty"`
|
|
||||||
EducationLevel string `json:"education_level,omitempty"`
|
Age int `json:"age,omitempty"`
|
||||||
Country string `json:"country,omitempty"`
|
EducationLevel string `json:"education_level,omitempty"`
|
||||||
Region string `json:"region,omitempty"`
|
Country string `json:"country,omitempty"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
Region string `json:"region,omitempty"`
|
||||||
PhoneVerified bool `json:"phone_verified"`
|
|
||||||
Status UserStatus `json:"status"`
|
// Profile fields
|
||||||
|
InitialAssessmentCompleted bool `json:"initial_assessment_completed,omitempty"`
|
||||||
|
NickName string `json:"nick_name,omitempty"`
|
||||||
|
Occupation string `json:"occupation,omitempty"`
|
||||||
|
LearningGoal string `json:"learning_goal,omitempty"`
|
||||||
|
LanguageGoal string `json:"language_goal,omitempty"`
|
||||||
|
LanguageChallange string `json:"language_challange,omitempty"`
|
||||||
|
FavoutiteTopic string `json:"favoutite_topic,omitempty"`
|
||||||
|
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
PhoneVerified bool `json:"phone_verified"`
|
||||||
|
Status UserStatus `json:"status"`
|
||||||
|
|
||||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||||
ProfileCompleted bool `json:"profile_completed"`
|
ProfileCompleted bool `json:"profile_completed"`
|
||||||
ProfilePictureURL string `json:"profile_picture_url,omitempty"`
|
ProfilePictureURL string `json:"profile_picture_url,omitempty"`
|
||||||
PreferredLanguage string `json:"preferred_language,omitempty"`
|
PreferredLanguage string `json:"preferred_language,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserFilter struct {
|
type UserFilter struct {
|
||||||
|
|
@ -87,21 +114,28 @@ type UserFilter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterUserReq struct {
|
type RegisterUserReq struct {
|
||||||
FirstName string
|
FirstName string `json:"first_name"`
|
||||||
LastName string
|
LastName string `json:"last_name"`
|
||||||
UserName string
|
UserName string `json:"user_name"`
|
||||||
Email string
|
Email string `json:"email"`
|
||||||
PhoneNumber string
|
PhoneNumber string `json:"phone_number"`
|
||||||
Password string
|
Password string `json:"password"`
|
||||||
Role string
|
Role string `json:"role"`
|
||||||
|
|
||||||
OtpMedium OtpMedium
|
OtpMedium OtpMedium `json:"otp_medium"`
|
||||||
|
|
||||||
Age int
|
NickName string `json:"nick_name,omitempty"`
|
||||||
EducationLevel string
|
Occupation string `json:"occupation,omitempty"`
|
||||||
Country string
|
LearningGoal string `json:"learning_goal,omitempty"`
|
||||||
Region string
|
LanguageGoal string `json:"language_goal,omitempty"`
|
||||||
PreferredLanguage string
|
LanguageChallange string `json:"language_challange,omitempty"`
|
||||||
|
FavoutiteTopic string `json:"favoutite_topic,omitempty"`
|
||||||
|
|
||||||
|
Age int `json:"age,omitempty"`
|
||||||
|
EducationLevel string `json:"education_level,omitempty"`
|
||||||
|
Country string `json:"country,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
PreferredLanguage string `json:"preferred_language,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateUserReq struct {
|
type CreateUserReq struct {
|
||||||
|
|
@ -115,10 +149,19 @@ type CreateUserReq struct {
|
||||||
|
|
||||||
Status UserStatus
|
Status UserStatus
|
||||||
|
|
||||||
Age int
|
Age int
|
||||||
EducationLevel string
|
EducationLevel string
|
||||||
Country string
|
Country string
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Profile fields
|
||||||
|
NickName string
|
||||||
|
Occupation string
|
||||||
|
LearningGoal string
|
||||||
|
LanguageGoal string
|
||||||
|
LanguageChallange string
|
||||||
|
FavoutiteTopic string
|
||||||
|
|
||||||
PreferredLanguage string
|
PreferredLanguage string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +170,6 @@ type ResetPasswordReq struct {
|
||||||
Password string
|
Password string
|
||||||
OtpCode string
|
OtpCode string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserReq struct {
|
type UpdateUserReq struct {
|
||||||
UserID int64
|
UserID int64
|
||||||
|
|
||||||
|
|
@ -142,6 +184,14 @@ type UpdateUserReq struct {
|
||||||
Country ValidString
|
Country ValidString
|
||||||
Region ValidString
|
Region ValidString
|
||||||
|
|
||||||
|
// Profile fields
|
||||||
|
NickName ValidString
|
||||||
|
Occupation ValidString
|
||||||
|
LearningGoal ValidString
|
||||||
|
LanguageGoal ValidString
|
||||||
|
LanguageChallange ValidString
|
||||||
|
FavoutiteTopic ValidString
|
||||||
|
|
||||||
ProfileCompleted ValidBool
|
ProfileCompleted ValidBool
|
||||||
ProfilePictureURL ValidString
|
ProfilePictureURL ValidString
|
||||||
PreferredLanguage ValidString
|
PreferredLanguage ValidString
|
||||||
|
|
|
||||||
16
internal/ports/initial_assessment.go
Normal file
16
internal/ports/initial_assessment.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package ports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Yimaru-Backend/internal/domain"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InitialAssessmentStore interface {
|
||||||
|
CreateAssessmentQuestion(
|
||||||
|
ctx context.Context,
|
||||||
|
q domain.AssessmentQuestion,
|
||||||
|
) (domain.AssessmentQuestion, error)
|
||||||
|
GetActiveAssessmentQuestions(ctx context.Context) ([]domain.AssessmentQuestion, error)
|
||||||
|
SaveAssessmentAttempt(ctx context.Context, userID int64, answers []domain.UserAnswer) (domain.AssessmentAttempt, error)
|
||||||
|
GetOptionByID(ctx context.Context, optionID int64) (domain.AssessmentOption, error)
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,20 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
dbgen "Yimaru-Backend/gen/db"
|
||||||
"Yimaru-Backend/internal/domain"
|
"Yimaru-Backend/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserStore interface {
|
type UserStore interface {
|
||||||
|
GetCorrectOptionForQuestion(
|
||||||
|
ctx context.Context,
|
||||||
|
questionID int64,
|
||||||
|
) (int64, error)
|
||||||
|
GetLatestAssessmentAttempt(
|
||||||
|
ctx context.Context,
|
||||||
|
userID int64,
|
||||||
|
) (*dbgen.AssessmentAttempt, error)
|
||||||
|
UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error
|
||||||
IsUserNameUnique(ctx context.Context, userName string) (bool, error)
|
IsUserNameUnique(ctx context.Context, userName string) (bool, error)
|
||||||
IsUserPending(ctx context.Context, UserName string) (bool, error)
|
IsUserPending(ctx context.Context, UserName string) (bool, error)
|
||||||
GetUserByUserName(
|
GetUserByUserName(
|
||||||
|
|
@ -48,24 +58,7 @@ type UserStore interface {
|
||||||
email string,
|
email string,
|
||||||
phone string,
|
phone string,
|
||||||
) (domain.User, error)
|
) (domain.User, error)
|
||||||
UpdatePassword(ctx context.Context, password, email, phone string, updatedAt time.Time) error
|
UpdatePassword(ctx context.Context, password, userName string) error
|
||||||
// GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error)
|
|
||||||
// GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error)
|
|
||||||
// UpdateUserSuspend(ctx context.Context, id int64, status bool) error
|
|
||||||
|
|
||||||
// UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
|
|
||||||
// UpdateUserSuspend(ctx context.Context, id int64, status bool) error
|
|
||||||
// DeleteUser(ctx context.Context, id int64) error
|
|
||||||
// CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error)
|
|
||||||
// GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error)
|
|
||||||
// GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error)
|
|
||||||
// SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
|
|
||||||
// UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error
|
|
||||||
// GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error)
|
|
||||||
|
|
||||||
// GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
|
||||||
// GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error)
|
|
||||||
// GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
|
||||||
}
|
}
|
||||||
type SmsGateway interface {
|
type SmsGateway interface {
|
||||||
SendSMSOTP(ctx context.Context, phoneNumber, otp string) error
|
SendSMSOTP(ctx context.Context, phoneNumber, otp string) error
|
||||||
|
|
|
||||||
175
internal/repository/initial_assessment.go
Normal file
175
internal/repository/initial_assessment.go
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
dbgen "Yimaru-Backend/gen/db"
|
||||||
|
"Yimaru-Backend/internal/domain"
|
||||||
|
"Yimaru-Backend/internal/ports"
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewInitialAssessmentStore(s *Store) ports.InitialAssessmentStore { return s }
|
||||||
|
|
||||||
|
func (r *Store) GetCorrectOptionForQuestion(
|
||||||
|
ctx context.Context,
|
||||||
|
questionID int64,
|
||||||
|
) (int64, error) {
|
||||||
|
|
||||||
|
optId, err := r.queries.GetCorrectOptionForQuestion(ctx, questionID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return optId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Store) GetLatestAssessmentAttempt(
|
||||||
|
ctx context.Context,
|
||||||
|
userID int64,
|
||||||
|
) (*dbgen.AssessmentAttempt, error) {
|
||||||
|
|
||||||
|
attempt, err := r.queries.GetLatestAssessmentAttempt(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &attempt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateAssessmentQuestion(
|
||||||
|
ctx context.Context,
|
||||||
|
q domain.AssessmentQuestion,
|
||||||
|
) (domain.AssessmentQuestion, error) {
|
||||||
|
|
||||||
|
row, err := s.queries.CreateAssessmentQuestion(ctx, dbgen.CreateAssessmentQuestionParams{
|
||||||
|
Title: q.Title,
|
||||||
|
Description: pgtype.Text{String: q.Description, Valid: q.Description != ""},
|
||||||
|
QuestionType: q.QuestionType,
|
||||||
|
DifficultyLevel: q.DifficultyLevel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentQuestion{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range q.Options {
|
||||||
|
if err := s.queries.CreateAssessmentQuestionOption(ctx,
|
||||||
|
dbgen.CreateAssessmentQuestionOptionParams{
|
||||||
|
QuestionID: row.ID,
|
||||||
|
OptionText: opt.OptionText,
|
||||||
|
IsCorrect: opt.IsCorrect,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
return domain.AssessmentQuestion{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q.ID = row.ID
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetActiveAssessmentQuestions(ctx context.Context) ([]domain.AssessmentQuestion, error) {
|
||||||
|
questionsRows, err := s.queries.GetActiveAssessmentQuestions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
questions := make([]domain.AssessmentQuestion, 0, len(questionsRows))
|
||||||
|
for _, q := range questionsRows {
|
||||||
|
optionsRows, err := s.queries.GetQuestionOptions(ctx, q.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
options := make([]domain.AssessmentOption, 0, len(optionsRows))
|
||||||
|
for _, o := range optionsRows {
|
||||||
|
options = append(options, domain.AssessmentOption{
|
||||||
|
ID: o.ID,
|
||||||
|
OptionText: o.OptionText,
|
||||||
|
IsCorrect: o.IsCorrect,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
questions = append(questions, domain.AssessmentQuestion{
|
||||||
|
ID: q.ID,
|
||||||
|
Title: q.Title,
|
||||||
|
Description: q.Description.String,
|
||||||
|
QuestionType: q.QuestionType,
|
||||||
|
DifficultyLevel: q.DifficultyLevel,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAssessmentAttempt saves the attempt summary and answers
|
||||||
|
func (s *Store) SaveAssessmentAttempt(ctx context.Context, userID int64, answers []domain.UserAnswer) (domain.AssessmentAttempt, error) {
|
||||||
|
total := len(answers)
|
||||||
|
correct := 0
|
||||||
|
|
||||||
|
for _, ans := range answers {
|
||||||
|
if ans.IsCorrect {
|
||||||
|
correct++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
score := float64(correct) / float64(total) * 100
|
||||||
|
knowledgeLevel := "BEGINNER"
|
||||||
|
switch {
|
||||||
|
case score >= 80:
|
||||||
|
knowledgeLevel = "ADVANCED"
|
||||||
|
case score >= 50:
|
||||||
|
knowledgeLevel = "INTERMEDIATE"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save attempt
|
||||||
|
attemptRow, err := s.queries.CreateAssessmentAttempt(ctx, dbgen.CreateAssessmentAttemptParams{
|
||||||
|
UserID: userID,
|
||||||
|
TotalQuestions: int32(total),
|
||||||
|
CorrectAnswers: int32(correct),
|
||||||
|
ScorePercentage: pgtype.Numeric{Int: big.NewInt(int64(score * 100)), Valid: true},
|
||||||
|
KnowledgeLevel: knowledgeLevel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save answers
|
||||||
|
for _, ans := range answers {
|
||||||
|
err := s.queries.CreateAssessmentAnswer(ctx, dbgen.CreateAssessmentAnswerParams{
|
||||||
|
AttemptID: attemptRow.ID,
|
||||||
|
QuestionID: ans.QuestionID,
|
||||||
|
SelectedOptionID: pgtype.Int8{Int64: ans.SelectedOptionID, Valid: true},
|
||||||
|
ShortAnswer: pgtype.Text{String: ans.ShortAnswer, Valid: true},
|
||||||
|
IsCorrect: ans.IsCorrect,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.AssessmentAttempt{
|
||||||
|
ID: attemptRow.ID,
|
||||||
|
UserID: userID,
|
||||||
|
TotalQuestions: total,
|
||||||
|
CorrectAnswers: correct,
|
||||||
|
ScorePercentage: score,
|
||||||
|
KnowledgeLevel: knowledgeLevel,
|
||||||
|
CompletedAt: attemptRow.CompletedAt.Time,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOptionByID fetches a single option to validate correctness
|
||||||
|
func (s *Store) GetOptionByID(ctx context.Context, optionID int64) (domain.AssessmentOption, error) {
|
||||||
|
o, err := s.queries.GetAssessmentOptionByID(ctx, optionID)
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentOption{}, err
|
||||||
|
}
|
||||||
|
return domain.AssessmentOption{
|
||||||
|
ID: o.ID,
|
||||||
|
OptionText: o.OptionText,
|
||||||
|
IsCorrect: o.IsCorrect,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,13 @@ import (
|
||||||
|
|
||||||
func NewUserStore(s *Store) ports.UserStore { return s }
|
func NewUserStore(s *Store) ports.UserStore { return s }
|
||||||
|
|
||||||
|
func (s *Store) UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error {
|
||||||
|
return s.queries.UpdateUserKnowledgeLevel(ctx, dbgen.UpdateUserKnowledgeLevelParams{
|
||||||
|
ID: userID,
|
||||||
|
KnowledgeLevel: pgtype.Text{String: knowledgeLevel, Valid: true},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) IsUserPending(ctx context.Context, UserName string) (bool, error) {
|
func (s *Store) IsUserPending(ctx context.Context, UserName string) (bool, error) {
|
||||||
isPending, err := s.queries.IsUserPending(ctx, UserName)
|
isPending, err := s.queries.IsUserPending(ctx, UserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -56,17 +63,44 @@ func (s *Store) CreateUserWithoutOtp(
|
||||||
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
||||||
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
||||||
|
|
||||||
|
NickName: pgtype.Text{
|
||||||
|
String: user.NickName,
|
||||||
|
Valid: user.NickName != "",
|
||||||
|
},
|
||||||
|
Occupation: pgtype.Text{
|
||||||
|
String: user.Occupation,
|
||||||
|
Valid: user.Occupation != "",
|
||||||
|
},
|
||||||
|
LearningGoal: pgtype.Text{
|
||||||
|
String: user.LearningGoal,
|
||||||
|
Valid: user.LearningGoal != "",
|
||||||
|
},
|
||||||
|
LanguageGoal: pgtype.Text{
|
||||||
|
String: user.LanguageGoal,
|
||||||
|
Valid: user.LanguageGoal != "",
|
||||||
|
},
|
||||||
|
LanguageChallange: pgtype.Text{
|
||||||
|
String: user.LanguageChallange,
|
||||||
|
Valid: user.LanguageChallange != "",
|
||||||
|
},
|
||||||
|
FavoutiteTopic: pgtype.Text{
|
||||||
|
String: user.FavoutiteTopic,
|
||||||
|
Valid: user.FavoutiteTopic != "",
|
||||||
|
},
|
||||||
|
|
||||||
EmailVerified: user.EmailVerified,
|
EmailVerified: user.EmailVerified,
|
||||||
PhoneVerified: user.PhoneVerified,
|
PhoneVerified: user.PhoneVerified,
|
||||||
|
|
||||||
|
ProfilePictureUrl: pgtype.Text{
|
||||||
|
String: user.ProfilePictureURL,
|
||||||
|
Valid: user.ProfilePictureURL != "",
|
||||||
|
},
|
||||||
Status: string(user.Status),
|
Status: string(user.Status),
|
||||||
ProfileCompleted: user.ProfileCompleted,
|
ProfileCompleted: user.ProfileCompleted,
|
||||||
PreferredLanguage: pgtype.Text{
|
PreferredLanguage: pgtype.Text{
|
||||||
String: user.PreferredLanguage,
|
String: user.PreferredLanguage,
|
||||||
Valid: user.PreferredLanguage != "",
|
Valid: user.PreferredLanguage != "",
|
||||||
},
|
},
|
||||||
|
|
||||||
// OrganizationID: user.OrganizationID.ToPG(),
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.User{}, err
|
return domain.User{}, err
|
||||||
|
|
@ -77,31 +111,7 @@ func (s *Store) CreateUserWithoutOtp(
|
||||||
updatedAt = &userRes.UpdatedAt.Time
|
updatedAt = &userRes.UpdatedAt.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain.User{
|
return mapCreateUserResult(userRes, user.Password, updatedAt), nil
|
||||||
ID: userRes.ID,
|
|
||||||
FirstName: userRes.FirstName,
|
|
||||||
LastName: userRes.LastName,
|
|
||||||
UserName: userRes.UserName,
|
|
||||||
Email: userRes.Email.String,
|
|
||||||
PhoneNumber: userRes.PhoneNumber.String,
|
|
||||||
Role: domain.Role(userRes.Role),
|
|
||||||
Password: user.Password,
|
|
||||||
|
|
||||||
Age: int(userRes.Age.Int32),
|
|
||||||
EducationLevel: userRes.EducationLevel.String,
|
|
||||||
Country: userRes.Country.String,
|
|
||||||
Region: userRes.Region.String,
|
|
||||||
|
|
||||||
EmailVerified: userRes.EmailVerified,
|
|
||||||
PhoneVerified: userRes.PhoneVerified,
|
|
||||||
Status: domain.UserStatus(userRes.Status),
|
|
||||||
|
|
||||||
ProfileCompleted: userRes.ProfileCompleted,
|
|
||||||
PreferredLanguage: userRes.PreferredLanguage.String,
|
|
||||||
|
|
||||||
CreatedAt: userRes.CreatedAt.Time,
|
|
||||||
UpdatedAt: updatedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser inserts a new user into the database
|
// CreateUser inserts a new user into the database
|
||||||
|
|
@ -111,7 +121,6 @@ func (s *Store) CreateUser(
|
||||||
usedOtpId int64,
|
usedOtpId int64,
|
||||||
) (domain.User, error) {
|
) (domain.User, error) {
|
||||||
|
|
||||||
// Optional: mark OTP as used
|
|
||||||
if usedOtpId > 0 {
|
if usedOtpId > 0 {
|
||||||
if err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{
|
if err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{
|
||||||
ID: usedOtpId,
|
ID: usedOtpId,
|
||||||
|
|
@ -137,17 +146,26 @@ func (s *Store) CreateUser(
|
||||||
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
|
||||||
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
|
||||||
|
|
||||||
|
NickName: pgtype.Text{String: user.NickName, Valid: user.NickName != ""},
|
||||||
|
Occupation: pgtype.Text{String: user.Occupation, Valid: user.Occupation != ""},
|
||||||
|
LearningGoal: pgtype.Text{String: user.LearningGoal, Valid: user.LearningGoal != ""},
|
||||||
|
LanguageGoal: pgtype.Text{String: user.LanguageGoal, Valid: user.LanguageGoal != ""},
|
||||||
|
LanguageChallange: pgtype.Text{String: user.LanguageChallange, Valid: user.LanguageChallange != ""},
|
||||||
|
FavoutiteTopic: pgtype.Text{String: user.FavoutiteTopic, Valid: user.FavoutiteTopic != ""},
|
||||||
|
|
||||||
EmailVerified: user.EmailVerified,
|
EmailVerified: user.EmailVerified,
|
||||||
PhoneVerified: user.PhoneVerified,
|
PhoneVerified: user.PhoneVerified,
|
||||||
|
|
||||||
|
ProfilePictureUrl: pgtype.Text{
|
||||||
|
String: user.ProfilePictureURL,
|
||||||
|
Valid: user.ProfilePictureURL != "",
|
||||||
|
},
|
||||||
Status: string(user.Status),
|
Status: string(user.Status),
|
||||||
ProfileCompleted: user.ProfileCompleted,
|
ProfileCompleted: user.ProfileCompleted,
|
||||||
PreferredLanguage: pgtype.Text{
|
PreferredLanguage: pgtype.Text{
|
||||||
String: user.PreferredLanguage,
|
String: user.PreferredLanguage,
|
||||||
Valid: user.PreferredLanguage != "",
|
Valid: user.PreferredLanguage != "",
|
||||||
},
|
},
|
||||||
|
|
||||||
// OrganizationID: user.OrganizationID.ToPG(),
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.User{}, err
|
return domain.User{}, err
|
||||||
|
|
@ -158,31 +176,7 @@ func (s *Store) CreateUser(
|
||||||
updatedAt = &userRes.UpdatedAt.Time
|
updatedAt = &userRes.UpdatedAt.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain.User{
|
return mapCreateUserResult(userRes, user.Password, updatedAt), nil
|
||||||
ID: userRes.ID,
|
|
||||||
FirstName: userRes.FirstName,
|
|
||||||
LastName: userRes.LastName,
|
|
||||||
UserName: userRes.UserName,
|
|
||||||
Email: userRes.Email.String,
|
|
||||||
PhoneNumber: userRes.PhoneNumber.String,
|
|
||||||
Role: domain.Role(userRes.Role),
|
|
||||||
Password: user.Password,
|
|
||||||
|
|
||||||
Age: int(userRes.Age.Int32),
|
|
||||||
EducationLevel: userRes.EducationLevel.String,
|
|
||||||
Country: userRes.Country.String,
|
|
||||||
Region: userRes.Region.String,
|
|
||||||
|
|
||||||
EmailVerified: userRes.EmailVerified,
|
|
||||||
PhoneVerified: userRes.PhoneVerified,
|
|
||||||
Status: domain.UserStatus(userRes.Status),
|
|
||||||
|
|
||||||
ProfileCompleted: userRes.ProfileCompleted,
|
|
||||||
PreferredLanguage: userRes.PreferredLanguage.String,
|
|
||||||
|
|
||||||
CreatedAt: userRes.CreatedAt.Time,
|
|
||||||
UpdatedAt: updatedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByID retrieves a user by ID
|
// GetUserByID retrieves a user by ID
|
||||||
|
|
@ -223,6 +217,13 @@ func (s *Store) GetUserByID(
|
||||||
Country: u.Country.String,
|
Country: u.Country.String,
|
||||||
Region: u.Region.String,
|
Region: u.Region.String,
|
||||||
|
|
||||||
|
NickName: u.NickName.String,
|
||||||
|
Occupation: u.Occupation.String,
|
||||||
|
LearningGoal: u.LearningGoal.String,
|
||||||
|
LanguageGoal: u.LanguageGoal.String,
|
||||||
|
LanguageChallange: u.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
EmailVerified: u.EmailVerified,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PhoneVerified: u.PhoneVerified,
|
||||||
Status: domain.UserStatus(u.Status),
|
Status: domain.UserStatus(u.Status),
|
||||||
|
|
@ -232,10 +233,6 @@ func (s *Store) GetUserByID(
|
||||||
ProfilePictureURL: u.ProfilePictureUrl.String,
|
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||||
PreferredLanguage: u.PreferredLanguage.String,
|
PreferredLanguage: u.PreferredLanguage.String,
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
|
||||||
// Value: u.OrganizationID.Int64,
|
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
CreatedAt: u.CreatedAt.Time,
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -259,10 +256,6 @@ func (s *Store) GetAllUsers(
|
||||||
params.Role = *role
|
params.Role = *role
|
||||||
}
|
}
|
||||||
|
|
||||||
// if organizationID != nil {
|
|
||||||
// params.OrganizationID = pgtype.Int8{Int64: *organizationID, Valid: true}
|
|
||||||
// }
|
|
||||||
|
|
||||||
if query != nil {
|
if query != nil {
|
||||||
params.Query = pgtype.Text{String: *query, Valid: true}
|
params.Query = pgtype.Text{String: *query, Valid: true}
|
||||||
}
|
}
|
||||||
|
|
@ -308,17 +301,21 @@ func (s *Store) GetAllUsers(
|
||||||
Country: u.Country.String,
|
Country: u.Country.String,
|
||||||
Region: u.Region.String,
|
Region: u.Region.String,
|
||||||
|
|
||||||
|
NickName: u.NickName.String,
|
||||||
|
Occupation: u.Occupation.String,
|
||||||
|
LearningGoal: u.LearningGoal.String,
|
||||||
|
LanguageGoal: u.LanguageGoal.String,
|
||||||
|
LanguageChallange: u.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
EmailVerified: u.EmailVerified,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PhoneVerified: u.PhoneVerified,
|
||||||
Status: domain.UserStatus(u.Status),
|
Status: domain.UserStatus(u.Status),
|
||||||
|
|
||||||
|
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||||
ProfileCompleted: u.ProfileCompleted,
|
ProfileCompleted: u.ProfileCompleted,
|
||||||
PreferredLanguage: u.PreferredLanguage.String,
|
PreferredLanguage: u.PreferredLanguage.String,
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
|
||||||
// Value: u.OrganizationID.Int64,
|
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
CreatedAt: u.CreatedAt.Time,
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
})
|
})
|
||||||
|
|
@ -350,13 +347,6 @@ func (s *Store) SearchUserByNameOrPhone(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// if organizationID != nil {
|
|
||||||
// params.OrganizationID = pgtype.Int8{
|
|
||||||
// Int64: *organizationID,
|
|
||||||
// Valid: true,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if role != nil {
|
if role != nil {
|
||||||
params.Role = pgtype.Text{
|
params.Role = pgtype.Text{
|
||||||
String: *role,
|
String: *role,
|
||||||
|
|
@ -369,7 +359,12 @@ func (s *Store) SearchUserByNameOrPhone(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(rows) == 0 {
|
||||||
|
return []domain.User{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
users := make([]domain.User, 0, len(rows))
|
users := make([]domain.User, 0, len(rows))
|
||||||
|
|
||||||
for _, u := range rows {
|
for _, u := range rows {
|
||||||
|
|
||||||
var updatedAt *time.Time
|
var updatedAt *time.Time
|
||||||
|
|
@ -391,16 +386,21 @@ func (s *Store) SearchUserByNameOrPhone(
|
||||||
Country: u.Country.String,
|
Country: u.Country.String,
|
||||||
Region: u.Region.String,
|
Region: u.Region.String,
|
||||||
|
|
||||||
|
NickName: u.NickName.String,
|
||||||
|
Occupation: u.Occupation.String,
|
||||||
|
LearningGoal: u.LearningGoal.String,
|
||||||
|
LanguageGoal: u.LanguageGoal.String,
|
||||||
|
LanguageChallange: u.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
EmailVerified: u.EmailVerified,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PhoneVerified: u.PhoneVerified,
|
||||||
Status: domain.UserStatus(u.Status),
|
Status: domain.UserStatus(u.Status),
|
||||||
|
|
||||||
ProfileCompleted: u.ProfileCompleted,
|
ProfileCompleted: u.ProfileCompleted,
|
||||||
|
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||||
|
PreferredLanguage: u.PreferredLanguage.String,
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
|
||||||
// Value: u.OrganizationID.Int64,
|
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
CreatedAt: u.CreatedAt.Time,
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
})
|
})
|
||||||
|
|
@ -410,32 +410,73 @@ func (s *Store) SearchUserByNameOrPhone(
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser updates basic user info
|
// UpdateUser updates basic user info
|
||||||
func (s *Store) UpdateUser(ctx context.Context, user domain.User) error {
|
func (s *Store) UpdateUser(
|
||||||
|
ctx context.Context,
|
||||||
|
user domain.User,
|
||||||
|
) error {
|
||||||
|
|
||||||
return s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
|
return s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
|
||||||
ID: user.ID,
|
|
||||||
FirstName: user.FirstName,
|
FirstName: user.FirstName,
|
||||||
LastName: user.LastName,
|
LastName: user.LastName,
|
||||||
Status: string(user.Status),
|
UserName: user.UserName,
|
||||||
|
|
||||||
|
Age: pgtype.Int4{
|
||||||
|
Int32: int32(user.Age),
|
||||||
|
Valid: user.Age > 0,
|
||||||
|
},
|
||||||
|
EducationLevel: pgtype.Text{
|
||||||
|
String: user.EducationLevel,
|
||||||
|
Valid: user.EducationLevel != "",
|
||||||
|
},
|
||||||
|
Country: pgtype.Text{
|
||||||
|
String: user.Country,
|
||||||
|
Valid: user.Country != "",
|
||||||
|
},
|
||||||
|
Region: pgtype.Text{
|
||||||
|
String: user.Region,
|
||||||
|
Valid: user.Region != "",
|
||||||
|
},
|
||||||
|
|
||||||
|
NickName: pgtype.Text{
|
||||||
|
String: user.NickName,
|
||||||
|
Valid: user.NickName != "",
|
||||||
|
},
|
||||||
|
Occupation: pgtype.Text{
|
||||||
|
String: user.Occupation,
|
||||||
|
Valid: user.Occupation != "",
|
||||||
|
},
|
||||||
|
LearningGoal: pgtype.Text{
|
||||||
|
String: user.LearningGoal,
|
||||||
|
Valid: user.LearningGoal != "",
|
||||||
|
},
|
||||||
|
LanguageGoal: pgtype.Text{
|
||||||
|
String: user.LanguageGoal,
|
||||||
|
Valid: user.LanguageGoal != "",
|
||||||
|
},
|
||||||
|
LanguageChallange: pgtype.Text{
|
||||||
|
String: user.LanguageChallange,
|
||||||
|
Valid: user.LanguageChallange != "",
|
||||||
|
},
|
||||||
|
FavoutiteTopic: pgtype.Text{
|
||||||
|
String: user.FavoutiteTopic,
|
||||||
|
Valid: user.FavoutiteTopic != "",
|
||||||
|
},
|
||||||
|
|
||||||
|
Status: string(user.Status),
|
||||||
|
ProfileCompleted: user.ProfileCompleted,
|
||||||
|
ProfilePictureUrl: pgtype.Text{
|
||||||
|
String: user.ProfilePictureURL,
|
||||||
|
Valid: user.ProfilePictureURL != "",
|
||||||
|
},
|
||||||
|
PreferredLanguage: pgtype.Text{
|
||||||
|
String: user.PreferredLanguage,
|
||||||
|
Valid: user.PreferredLanguage != "",
|
||||||
|
},
|
||||||
|
|
||||||
|
ID: user.ID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserOrganization updates a user's organization
|
|
||||||
// func (s *Store) UpdateUserOrganization(ctx context.Context, userID, organizationID int64) error {
|
|
||||||
// return s.queries.UpdateUserOrganization(ctx, dbgen.UpdateUserOrganizationParams{
|
|
||||||
// OrganizationID: pgtype.Int8{Int64: organizationID, Valid: true},
|
|
||||||
// ID: userID,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// SuspendUser suspends a user
|
|
||||||
// func (s *Store) SuspendUser(ctx context.Context, userID int64, suspended bool, suspendedAt time.Time) error {
|
|
||||||
// return s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
|
|
||||||
// Suspended: suspended,
|
|
||||||
// SuspendedAt: pgtype.Timestamptz{Time: suspendedAt, Valid: true},
|
|
||||||
// ID: userID,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// DeleteUser removes a user
|
// DeleteUser removes a user
|
||||||
func (s *Store) DeleteUser(ctx context.Context, userID int64) error {
|
func (s *Store) DeleteUser(ctx context.Context, userID int64) error {
|
||||||
return s.queries.DeleteUser(ctx, userID)
|
return s.queries.DeleteUser(ctx, userID)
|
||||||
|
|
@ -461,7 +502,7 @@ func (s *Store) GetUserByUserName(
|
||||||
|
|
||||||
u, err := s.queries.GetUserByUserName(ctx, userName)
|
u, err := s.queries.GetUserByUserName(ctx, userName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
return domain.User{}, authentication.ErrUserNotFound
|
return domain.User{}, authentication.ErrUserNotFound
|
||||||
}
|
}
|
||||||
return domain.User{}, err
|
return domain.User{}, err
|
||||||
|
|
@ -492,18 +533,22 @@ func (s *Store) GetUserByUserName(
|
||||||
Country: u.Country.String,
|
Country: u.Country.String,
|
||||||
Region: u.Region.String,
|
Region: u.Region.String,
|
||||||
|
|
||||||
|
NickName: u.NickName.String,
|
||||||
|
Occupation: u.Occupation.String,
|
||||||
|
LearningGoal: u.LearningGoal.String,
|
||||||
|
LanguageGoal: u.LanguageGoal.String,
|
||||||
|
LanguageChallange: u.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
EmailVerified: u.EmailVerified,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PhoneVerified: u.PhoneVerified,
|
||||||
Status: domain.UserStatus(u.Status),
|
Status: domain.UserStatus(u.Status),
|
||||||
|
|
||||||
LastLogin: lastLogin,
|
LastLogin: lastLogin,
|
||||||
ProfileCompleted: u.ProfileCompleted,
|
ProfileCompleted: u.ProfileCompleted,
|
||||||
|
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||||
PreferredLanguage: u.PreferredLanguage.String,
|
PreferredLanguage: u.PreferredLanguage.String,
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
|
||||||
// Value: u.OrganizationID.Int64,
|
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
CreatedAt: u.CreatedAt.Time,
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -558,88 +603,72 @@ func (s *Store) GetUserByEmailPhone(
|
||||||
Country: u.Country.String,
|
Country: u.Country.String,
|
||||||
Region: u.Region.String,
|
Region: u.Region.String,
|
||||||
|
|
||||||
|
NickName: u.NickName.String,
|
||||||
|
Occupation: u.Occupation.String,
|
||||||
|
LearningGoal: u.LearningGoal.String,
|
||||||
|
LanguageGoal: u.LanguageGoal.String,
|
||||||
|
LanguageChallange: u.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: u.FavoutiteTopic.String,
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
EmailVerified: u.EmailVerified,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PhoneVerified: u.PhoneVerified,
|
||||||
Status: domain.UserStatus(u.Status),
|
Status: domain.UserStatus(u.Status),
|
||||||
|
|
||||||
|
ProfilePictureURL: u.ProfilePictureUrl.String,
|
||||||
LastLogin: lastLogin,
|
LastLogin: lastLogin,
|
||||||
ProfileCompleted: u.ProfileCompleted,
|
ProfileCompleted: u.ProfileCompleted,
|
||||||
PreferredLanguage: u.PreferredLanguage.String,
|
PreferredLanguage: u.PreferredLanguage.String,
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
|
||||||
// Value: u.OrganizationID.Int64,
|
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
CreatedAt: u.CreatedAt.Time,
|
||||||
UpdatedAt: updatedAt,
|
UpdatedAt: updatedAt,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword updates a user's password
|
// UpdatePassword updates a user's password
|
||||||
func (s *Store) UpdatePassword(ctx context.Context, password, email, phone string, updatedAt time.Time) error {
|
func (s *Store) UpdatePassword(ctx context.Context, password, userName string) error {
|
||||||
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||||
Password: []byte(password),
|
Password: []byte(password),
|
||||||
Email: pgtype.Text{String: email},
|
UserName: userName,
|
||||||
PhoneNumber: pgtype.Text{String: phone},
|
|
||||||
// OrganizationID: pgtype.Int8{Int64: organizationID},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnerByOrganizationID retrieves the owner user of an organization
|
|
||||||
// func (s *Store) GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error) {
|
|
||||||
// userRes, err := s.queries.GetOwnerByOrganizationID(ctx, organizationID)
|
|
||||||
// if err != nil {
|
|
||||||
// return domain.User{}, err
|
|
||||||
// }
|
|
||||||
// return mapUser(userRes), nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (s *Store) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
|
|
||||||
// err := s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
|
|
||||||
// ID: id,
|
|
||||||
// Suspended: status,
|
|
||||||
// SuspendedAt: pgtype.Timestamptz{
|
|
||||||
// Time: time.Now(),
|
|
||||||
// Valid: true,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// mapUser converts dbgen.User to domain.User
|
// mapUser converts dbgen.User to domain.User
|
||||||
func MapUser(u dbgen.User) domain.User {
|
func mapCreateUserResult(
|
||||||
|
userRes dbgen.CreateUserRow,
|
||||||
|
password []byte,
|
||||||
|
updatedAt *time.Time,
|
||||||
|
) domain.User {
|
||||||
|
|
||||||
return domain.User{
|
return domain.User{
|
||||||
ID: u.ID,
|
ID: userRes.ID,
|
||||||
FirstName: u.FirstName,
|
FirstName: userRes.FirstName,
|
||||||
LastName: u.LastName,
|
LastName: userRes.LastName,
|
||||||
|
UserName: userRes.UserName,
|
||||||
|
Email: userRes.Email.String,
|
||||||
|
PhoneNumber: userRes.PhoneNumber.String,
|
||||||
|
Role: domain.Role(userRes.Role),
|
||||||
|
Password: password,
|
||||||
|
|
||||||
UserName: u.UserName,
|
Age: int(userRes.Age.Int32),
|
||||||
Email: u.Email.String,
|
EducationLevel: userRes.EducationLevel.String,
|
||||||
PhoneNumber: u.PhoneNumber.String,
|
Country: userRes.Country.String,
|
||||||
|
Region: userRes.Region.String,
|
||||||
|
|
||||||
Role: domain.Role(u.Role),
|
NickName: userRes.NickName.String,
|
||||||
|
Occupation: userRes.Occupation.String,
|
||||||
|
LearningGoal: userRes.LearningGoal.String,
|
||||||
|
LanguageGoal: userRes.LanguageGoal.String,
|
||||||
|
LanguageChallange: userRes.LanguageChallange.String,
|
||||||
|
FavoutiteTopic: userRes.FavoutiteTopic.String,
|
||||||
|
|
||||||
Age: int(u.Age.Int32),
|
EmailVerified: userRes.EmailVerified,
|
||||||
EducationLevel: u.EducationLevel.String,
|
PhoneVerified: userRes.PhoneVerified,
|
||||||
Country: u.Country.String,
|
Status: domain.UserStatus(userRes.Status),
|
||||||
Region: u.Region.String,
|
|
||||||
|
|
||||||
EmailVerified: u.EmailVerified,
|
ProfileCompleted: userRes.ProfileCompleted,
|
||||||
PhoneVerified: u.PhoneVerified,
|
PreferredLanguage: userRes.PreferredLanguage.String,
|
||||||
Status: domain.UserStatus(u.Status),
|
|
||||||
LastLogin: &u.LastLogin.Time,
|
|
||||||
ProfileCompleted: u.ProfileCompleted,
|
|
||||||
PreferredLanguage: u.PreferredLanguage.String,
|
|
||||||
|
|
||||||
// OrganizationID: domain.ValidInt64{
|
CreatedAt: userRes.CreatedAt.Time,
|
||||||
// Value: u.OrganizationID.Int64,
|
UpdatedAt: updatedAt,
|
||||||
// Valid: u.OrganizationID.Valid,
|
|
||||||
// },
|
|
||||||
|
|
||||||
CreatedAt: u.CreatedAt.Time,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
181
internal/services/assessment/initial_assessment.go
Normal file
181
internal/services/assessment/initial_assessment.go
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
package assessment
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Yimaru-Backend/internal/domain"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) GetActiveAssessmentQuestions(
|
||||||
|
ctx context.Context,
|
||||||
|
) ([]domain.AssessmentQuestion, error) {
|
||||||
|
|
||||||
|
questions, err := s.initialAssessmentStore.GetActiveAssessmentQuestions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT:
|
||||||
|
// Do NOT expose correct answers to the client
|
||||||
|
for i := range questions {
|
||||||
|
for j := range questions[i].Options {
|
||||||
|
questions[i].Options[j].IsCorrect = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateAssessmentQuestion(
|
||||||
|
ctx context.Context,
|
||||||
|
q domain.AssessmentQuestion,
|
||||||
|
) (domain.AssessmentQuestion, error) {
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if q.Title == "" {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("question title is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.QuestionType == "" {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("question type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.DifficultyLevel == "" {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("difficulty level is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple choice / true-false must have options
|
||||||
|
if q.QuestionType != string(domain.QuestionTypeShortAnswer) {
|
||||||
|
if len(q.Options) < 2 {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("at least two options are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCorrect := false
|
||||||
|
for _, opt := range q.Options {
|
||||||
|
if opt.OptionText == "" {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("option text cannot be empty")
|
||||||
|
}
|
||||||
|
if opt.IsCorrect {
|
||||||
|
hasCorrect = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasCorrect {
|
||||||
|
return domain.AssessmentQuestion{}, errors.New("at least one correct option is required")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist via repository
|
||||||
|
return s.initialAssessmentStore.CreateAssessmentQuestion(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SubmitAssessment(
|
||||||
|
ctx context.Context,
|
||||||
|
userID int64,
|
||||||
|
responses []domain.UserAnswer,
|
||||||
|
) (domain.AssessmentAttempt, error) {
|
||||||
|
|
||||||
|
if userID <= 0 {
|
||||||
|
return domain.AssessmentAttempt{}, errors.New("invalid user id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(responses) == 0 {
|
||||||
|
return domain.AssessmentAttempt{}, errors.New("no responses submitted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Validate and evaluate answers
|
||||||
|
for i, ans := range responses {
|
||||||
|
if ans.QuestionID == 0 {
|
||||||
|
return domain.AssessmentAttempt{}, errors.New("invalid question id")
|
||||||
|
}
|
||||||
|
|
||||||
|
isCorrect, err := s.validateAnswer(ctx, ans)
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
responses[i].IsCorrect = isCorrect
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Persist assessment attempt + answers
|
||||||
|
attempt, err := s.initialAssessmentStore.SaveAssessmentAttempt(
|
||||||
|
ctx,
|
||||||
|
userID,
|
||||||
|
responses,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Update user's knowledge level
|
||||||
|
if err := s.userStore.UpdateUserKnowledgeLevel(
|
||||||
|
ctx,
|
||||||
|
userID,
|
||||||
|
attempt.KnowledgeLevel,
|
||||||
|
); err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Send in-app notification
|
||||||
|
notification := &domain.Notification{
|
||||||
|
|
||||||
|
RecipientID: userID,
|
||||||
|
Level: domain.NotificationLevelInfo,
|
||||||
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
IsRead: false,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusSent,
|
||||||
|
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
Payload: domain.NotificationPayload{
|
||||||
|
Headline: "Knowledge Assessment Completed",
|
||||||
|
Message: "Your knowledge assessment is complete. Your knowledge level is " + attempt.KnowledgeLevel + ".",
|
||||||
|
Tags: []string{"assessment", "knowledge-level"},
|
||||||
|
},
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Type: domain.NOTIFICATION_TYPE_KNOWLEDGE_LEVEL_UPDATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
||||||
|
return domain.AssessmentAttempt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return attempt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) validateAnswer(
|
||||||
|
ctx context.Context,
|
||||||
|
answer domain.UserAnswer,
|
||||||
|
) (bool, error) {
|
||||||
|
|
||||||
|
// Multiple choice / True-False
|
||||||
|
if answer.SelectedOptionID != 0 {
|
||||||
|
option, err := s.initialAssessmentStore.GetOptionByID(
|
||||||
|
ctx,
|
||||||
|
answer.SelectedOptionID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return option.IsCorrect, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short answer (future-proofing)
|
||||||
|
if answer.ShortAnswer != "" {
|
||||||
|
// Placeholder: subjective/manual evaluation
|
||||||
|
// For now, mark incorrect
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.New("invalid answer submission")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateKnowledgeLevel(score float64) string {
|
||||||
|
switch {
|
||||||
|
case score >= 80:
|
||||||
|
return "ADVANCED"
|
||||||
|
case score >= 50:
|
||||||
|
return "INTERMEDIATE"
|
||||||
|
default:
|
||||||
|
return "BEGINNER"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
internal/services/assessment/service.go
Normal file
31
internal/services/assessment/service.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package assessment
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Yimaru-Backend/internal/config"
|
||||||
|
"Yimaru-Backend/internal/ports"
|
||||||
|
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
userStore ports.UserStore
|
||||||
|
initialAssessmentStore ports.InitialAssessmentStore
|
||||||
|
notificationSvc *notificationservice.Service
|
||||||
|
// messengerSvc *messenger.Service
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(
|
||||||
|
userStore ports.UserStore,
|
||||||
|
initialAssessmentStore ports.InitialAssessmentStore,
|
||||||
|
notificationSvc *notificationservice.Service,
|
||||||
|
// messengerSvc *messenger.Service,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *Service {
|
||||||
|
return &Service{
|
||||||
|
userStore: userStore,
|
||||||
|
initialAssessmentStore: initialAssessmentStore,
|
||||||
|
notificationSvc: notificationSvc,
|
||||||
|
// messengerSvc: messengerSvc,
|
||||||
|
config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *Service) UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error {
|
||||||
|
return s.userStore.UpdateUserKnowledgeLevel(ctx, userID, knowledgeLevel)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) CreateUser(
|
func (s *Service) CreateUser(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req domain.CreateUserReq,
|
req domain.CreateUserReq,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserStore interface {
|
type UserStore interface {
|
||||||
|
UpdateUserKnowledgeLevel(ctx context.Context, userID int64, knowledgeLevel string) error
|
||||||
IsUserPending(ctx context.Context, userName string) (bool, error)
|
IsUserPending(ctx context.Context, userName string) (bool, error)
|
||||||
GetUserByUserName(
|
GetUserByUserName(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
// user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
if otp.Used {
|
if otp.Used {
|
||||||
return domain.ErrOtpAlreadyUsed
|
return domain.ErrOtpAlreadyUsed
|
||||||
|
|
@ -48,7 +48,7 @@ func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswo
|
||||||
return domain.ErrInvalidOtp
|
return domain.ErrInvalidOtp
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.userStore.UpdatePassword(ctx, resetReq.Password, user.Email, user.PhoneNumber, time.Now())
|
err = s.userStore.UpdatePassword(ctx, resetReq.Password, resetReq.UserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package httpserver
|
||||||
import (
|
import (
|
||||||
"Yimaru-Backend/internal/config"
|
"Yimaru-Backend/internal/config"
|
||||||
"Yimaru-Backend/internal/services/arifpay"
|
"Yimaru-Backend/internal/services/arifpay"
|
||||||
|
"Yimaru-Backend/internal/services/assessment"
|
||||||
"Yimaru-Backend/internal/services/authentication"
|
"Yimaru-Backend/internal/services/authentication"
|
||||||
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
|
||||||
notificationservice "Yimaru-Backend/internal/services/notification"
|
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||||
|
|
@ -24,25 +25,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
arifpaySvc *arifpay.ArifpayService
|
assessmentSvc *assessment.Service
|
||||||
|
arifpaySvc *arifpay.ArifpayService
|
||||||
issueReportingSvc *issuereporting.Service
|
issueReportingSvc *issuereporting.Service
|
||||||
fiber *fiber.App
|
fiber *fiber.App
|
||||||
recommendationSvc recommendation.RecommendationService
|
recommendationSvc recommendation.RecommendationService
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
NotidicationStore *notificationservice.Service
|
NotidicationStore *notificationservice.Service
|
||||||
port int
|
port int
|
||||||
settingSvc *settings.Service
|
settingSvc *settings.Service
|
||||||
authSvc *authentication.Service
|
authSvc *authentication.Service
|
||||||
userSvc *user.Service
|
userSvc *user.Service
|
||||||
transactionSvc *transaction.Service
|
transactionSvc *transaction.Service
|
||||||
validator *customvalidator.CustomValidator
|
validator *customvalidator.CustomValidator
|
||||||
JwtConfig jwtutil.JwtConfig
|
JwtConfig jwtutil.JwtConfig
|
||||||
Logger *slog.Logger
|
Logger *slog.Logger
|
||||||
mongoLoggerSvc *zap.Logger
|
mongoLoggerSvc *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(
|
func NewApp(
|
||||||
|
assessmentSvc *assessment.Service,
|
||||||
arifpaySvc *arifpay.ArifpayService,
|
arifpaySvc *arifpay.ArifpayService,
|
||||||
issueReportingSvc *issuereporting.Service,
|
issueReportingSvc *issuereporting.Service,
|
||||||
port int, validator *customvalidator.CustomValidator,
|
port int, validator *customvalidator.CustomValidator,
|
||||||
|
|
@ -74,22 +77,23 @@ func NewApp(
|
||||||
app.Static("/static", "./static")
|
app.Static("/static", "./static")
|
||||||
|
|
||||||
s := &App{
|
s := &App{
|
||||||
arifpaySvc: arifpaySvc,
|
assessmentSvc: assessmentSvc,
|
||||||
|
arifpaySvc: arifpaySvc,
|
||||||
// issueReportingSvc: issueReportingSvc,
|
// issueReportingSvc: issueReportingSvc,
|
||||||
fiber: app,
|
fiber: app,
|
||||||
port: port,
|
port: port,
|
||||||
settingSvc: settingSvc,
|
settingSvc: settingSvc,
|
||||||
authSvc: authSvc,
|
authSvc: authSvc,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
JwtConfig: JwtConfig,
|
JwtConfig: JwtConfig,
|
||||||
userSvc: userSvc,
|
userSvc: userSvc,
|
||||||
transactionSvc: transactionSvc,
|
transactionSvc: transactionSvc,
|
||||||
NotidicationStore: notidicationStore,
|
NotidicationStore: notidicationStore,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
recommendationSvc: recommendationSvc,
|
recommendationSvc: recommendationSvc,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
mongoLoggerSvc: mongoLoggerSvc,
|
mongoLoggerSvc: mongoLoggerSvc,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.initAppRoutes()
|
s.initAppRoutes()
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"Yimaru-Backend/internal/config"
|
"Yimaru-Backend/internal/config"
|
||||||
"Yimaru-Backend/internal/services/arifpay"
|
"Yimaru-Backend/internal/services/arifpay"
|
||||||
|
"Yimaru-Backend/internal/services/assessment"
|
||||||
"Yimaru-Backend/internal/services/authentication"
|
"Yimaru-Backend/internal/services/authentication"
|
||||||
notificationservice "Yimaru-Backend/internal/services/notification"
|
notificationservice "Yimaru-Backend/internal/services/notification"
|
||||||
"Yimaru-Backend/internal/services/recommendation"
|
"Yimaru-Backend/internal/services/recommendation"
|
||||||
|
|
||||||
// referralservice "Yimaru-Backend/internal/services/referal"
|
// referralservice "Yimaru-Backend/internal/services/referal"
|
||||||
|
|
||||||
"Yimaru-Backend/internal/services/settings"
|
"Yimaru-Backend/internal/services/settings"
|
||||||
|
|
@ -20,6 +22,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
assessmentSvc *assessment.Service
|
||||||
arifpaySvc *arifpay.ArifpayService
|
arifpaySvc *arifpay.ArifpayService
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
settingSvc *settings.Service
|
settingSvc *settings.Service
|
||||||
|
|
@ -35,6 +38,7 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
|
assessmentSvc *assessment.Service,
|
||||||
arifpaySvc *arifpay.ArifpayService,
|
arifpaySvc *arifpay.ArifpayService,
|
||||||
logger *slog.Logger,
|
logger *slog.Logger,
|
||||||
settingSvc *settings.Service,
|
settingSvc *settings.Service,
|
||||||
|
|
@ -49,6 +53,7 @@ func New(
|
||||||
mongoLoggerSvc *zap.Logger,
|
mongoLoggerSvc *zap.Logger,
|
||||||
) *Handler {
|
) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
|
assessmentSvc: assessmentSvc,
|
||||||
arifpaySvc: arifpaySvc,
|
arifpaySvc: arifpaySvc,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
settingSvc: settingSvc,
|
settingSvc: settingSvc,
|
||||||
|
|
|
||||||
142
internal/web_server/handlers/initial_assessment.go
Normal file
142
internal/web_server/handlers/initial_assessment.go
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Yimaru-Backend/internal/domain"
|
||||||
|
"Yimaru-Backend/internal/services/authentication"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateAssessmentQuestion godoc
|
||||||
|
// @Summary Create assessment question
|
||||||
|
// @Description Creates a new question for the initial knowledge assessment
|
||||||
|
// @Tags assessment
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param question body domain.AssessmentQuestion true "Assessment question payload"
|
||||||
|
// @Success 201 {object} domain.Response{data=domain.AssessmentQuestion}
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/assessment/questions [post]
|
||||||
|
func (h *Handler) CreateAssessmentQuestion(c *fiber.Ctx) error {
|
||||||
|
var req domain.AssessmentQuestion
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid request body",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
question, err := h.assessmentSvc.CreateAssessmentQuestion(c.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to create assessment question",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||||
|
Message: "Assessment question created successfully",
|
||||||
|
Data: question,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActiveAssessmentQuestions godoc
|
||||||
|
// @Summary Get active initial assessment questions
|
||||||
|
// @Description Returns all active questions used for initial knowledge assessment
|
||||||
|
// @Tags assessment
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} domain.Response{data=[]domain.AssessmentQuestion}
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/assessment/questions [get]
|
||||||
|
func (h *Handler) GetActiveAssessmentQuestions(c *fiber.Ctx) error {
|
||||||
|
questions, err := h.assessmentSvc.GetActiveAssessmentQuestions(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to fetch assessment questions",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: "Assessment questions fetched successfully",
|
||||||
|
Data: questions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitAssessment godoc
|
||||||
|
// @Summary Submit initial knowledge assessment
|
||||||
|
// @Description Evaluates user responses, calculates knowledge level, updates user profile, and sends notification
|
||||||
|
// @Tags assessment
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param user_id path int true "User ID"
|
||||||
|
// @Param payload body domain.SubmitAssessmentReq true "Assessment responses"
|
||||||
|
// @Success 200 {object} domain.Response
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 404 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/{tenant_slug}/assessment/submit [post]
|
||||||
|
func (h *Handler) SubmitAssessment(c *fiber.Ctx) error {
|
||||||
|
|
||||||
|
// User ID (from auth context or path, depending on your setup)
|
||||||
|
userIDStr, ok := c.Locals("user_id").(string)
|
||||||
|
if !ok || userIDStr == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid user context",
|
||||||
|
Error: "User ID not found in request context",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||||
|
if err != nil || userID <= 0 {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid user ID",
|
||||||
|
Error: "User ID must be a positive integer",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse request body
|
||||||
|
var req domain.SubmitAssessmentReq
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid request body",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Answers) == 0 {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "No answers submitted",
|
||||||
|
Error: "Assessment answers cannot be empty",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit assessment
|
||||||
|
attempt, err := h.assessmentSvc.SubmitAssessment(
|
||||||
|
c.Context(),
|
||||||
|
userID,
|
||||||
|
req.Answers,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||||
|
Message: "User not found",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to submit assessment",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: "Assessment submitted successfully",
|
||||||
|
Data: attempt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,56 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateUserKnowledgeLevel godoc
|
||||||
|
// @Summary Update user's knowledge level
|
||||||
|
// @Description Updates the knowledge level of the specified user after initial assessment
|
||||||
|
// @Tags user
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param user_id path int true "User ID"
|
||||||
|
// @Param knowledge_level body domain.UpdateKnowledgeLevelReq true "Knowledge level"
|
||||||
|
// @Success 200 {object} domain.Response
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 404 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/{tenant_slug}/user/knowledge-level [put]
|
||||||
|
func (h *Handler) UpdateUserKnowledgeLevel(c *fiber.Ctx) error {
|
||||||
|
userIDStr := c.Locals("user_id").(string)
|
||||||
|
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||||
|
if err != nil || userID <= 0 {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid user ID",
|
||||||
|
Error: "User ID must be a positive integer",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var req domain.UpdateKnowledgeLevelReq
|
||||||
|
if err := c.BodyParser(&req); err != nil || req.KnowledgeLevel == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid request body",
|
||||||
|
Error: "Knowledge level is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.userSvc.UpdateUserKnowledgeLevel(c.Context(), userID, req.KnowledgeLevel)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, authentication.ErrUserNotFound) {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||||
|
Message: "User not found",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to update user knowledge level",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: "User knowledge level updated successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ResendOtp godoc
|
// ResendOtp godoc
|
||||||
// @Summary Resend OTP
|
// @Summary Resend OTP
|
||||||
// @Description Resend OTP if the previous one is expired
|
// @Description Resend OTP if the previous one is expired
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
func (a *App) initAppRoutes() {
|
func (a *App) initAppRoutes() {
|
||||||
h := handlers.New(
|
h := handlers.New(
|
||||||
|
a.assessmentSvc,
|
||||||
a.arifpaySvc,
|
a.arifpaySvc,
|
||||||
a.logger,
|
a.logger,
|
||||||
a.settingSvc,
|
a.settingSvc,
|
||||||
|
|
@ -79,6 +80,11 @@ func (a *App) initAppRoutes() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//assessment Routes
|
||||||
|
groupV1.Post("/assessment/questions", h.CreateAssessmentQuestion)
|
||||||
|
groupV1.Get("/assessment/questions", h.GetActiveAssessmentQuestions)
|
||||||
|
tenant.Post("/assessment/submit", a.authMiddleware, h.SubmitAssessment)
|
||||||
|
|
||||||
// Auth Routes
|
// Auth Routes
|
||||||
tenant.Post("/auth/customer-login", h.LoginUser)
|
tenant.Post("/auth/customer-login", h.LoginUser)
|
||||||
tenant.Post("/auth/admin-login", h.LoginAdmin)
|
tenant.Post("/auth/admin-login", h.LoginAdmin)
|
||||||
|
|
@ -122,6 +128,7 @@ func (a *App) initAppRoutes() {
|
||||||
// groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler
|
// groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler
|
||||||
|
|
||||||
// User Routes
|
// User Routes
|
||||||
|
tenant.Put("/user/knowledge-level", h.UpdateUserKnowledgeLevel)
|
||||||
groupV1.Get("/user/:user_name/is-unique", h.CheckUserNameUnique)
|
groupV1.Get("/user/:user_name/is-unique", h.CheckUserNameUnique)
|
||||||
groupV1.Get("/user/:user_name/is-pending", h.CheckUserPending)
|
groupV1.Get("/user/:user_name/is-pending", h.CheckUserPending)
|
||||||
groupV1.Post("/user/resetPassword", h.ResetPassword)
|
groupV1.Post("/user/resetPassword", h.ResetPassword)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user