Add Feedback domain module: public submission flow, admin CRUD, reporting (#7)
Jenkins Production Deployment
Jenkins Production Deployment
Co-authored-by: Patrick Müller <mail@pmueller.me> Reviewed-on: #7 Co-authored-by: Patrick Mueller <patrick@mueller-patrick.tech> Co-committed-by: Patrick Mueller <patrick@mueller-patrick.tech>
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import {NachklangFeedbackDB} from '../Feedback.db';
|
||||
import {QuestionType} from '../feedback.interface';
|
||||
import {AdminQuestion} from './admin.interface';
|
||||
|
||||
const mapRow = (row: any): AdminQuestion => ({
|
||||
questionId: row.question_id,
|
||||
label: row.label,
|
||||
helpText: row.help_text,
|
||||
questionType: row.question_type,
|
||||
isArchived: !!row.is_archived
|
||||
});
|
||||
|
||||
export const listQuestions = async (includeArchived: boolean): Promise<AdminQuestion[]> => {
|
||||
let conn = await NachklangFeedbackDB.getConnection();
|
||||
try {
|
||||
const query = includeArchived
|
||||
? 'SELECT * FROM questions ORDER BY created_at DESC'
|
||||
: 'SELECT * FROM questions WHERE is_archived = 0 ORDER BY created_at DESC';
|
||||
const rows = await conn.query(query);
|
||||
return rows.map(mapRow);
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
|
||||
export const createQuestion = async (label: string, helpText: string | null, questionType: QuestionType): Promise<number> => {
|
||||
let conn = await NachklangFeedbackDB.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
const res = await conn.query(
|
||||
'INSERT INTO questions (label, help_text, question_type) VALUES (?,?,?) RETURNING question_id',
|
||||
[label, helpText, questionType]
|
||||
);
|
||||
await conn.commit();
|
||||
return res[0].question_id;
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits label/help text only. question_type is immutable after creation -
|
||||
* changing it would invalidate existing answers' question_type_snapshot
|
||||
* semantics. The admin UI offers "archive and create new" instead.
|
||||
*/
|
||||
export const updateQuestion = async (questionId: number, label: string, helpText: string | null): Promise<boolean> => {
|
||||
let conn = await NachklangFeedbackDB.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
const res = await conn.query('UPDATE questions SET label = ?, help_text = ? WHERE question_id = ?', [label, helpText, questionId]);
|
||||
await conn.commit();
|
||||
return res.affectedRows > 0;
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
|
||||
export type RemoveQuestionResult = 'ARCHIVED' | 'DELETED' | 'NOT_FOUND';
|
||||
|
||||
/**
|
||||
* Archives (soft delete) a question. Hard-deletes it instead if it has
|
||||
* never been assigned to any event, so an admin's typo doesn't have to
|
||||
* live forever in the library.
|
||||
*/
|
||||
export const removeQuestion = async (questionId: number): Promise<RemoveQuestionResult> => {
|
||||
let conn = await NachklangFeedbackDB.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
|
||||
const existsRows = await conn.query('SELECT 1 FROM questions WHERE question_id = ?', [questionId]);
|
||||
if (existsRows.length === 0) {
|
||||
await conn.rollback();
|
||||
return 'NOT_FOUND';
|
||||
}
|
||||
|
||||
const usageRows = await conn.query('SELECT 1 FROM event_questions WHERE question_id = ? LIMIT 1', [questionId]);
|
||||
if (usageRows.length === 0) {
|
||||
await conn.query('DELETE FROM questions WHERE question_id = ?', [questionId]);
|
||||
await conn.commit();
|
||||
return 'DELETED';
|
||||
}
|
||||
|
||||
await conn.query('UPDATE questions SET is_archived = 1 WHERE question_id = ?', [questionId]);
|
||||
await conn.commit();
|
||||
return 'ARCHIVED';
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user