Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions backend/database-seeding/src/seeder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,72 @@ pub async fn seed_database(dev_email: String, mut seeder: Seeder) {
)
.await.expect("Failed seeding Question 3");

let question_id_4 = Question::create(
campaign_id,
"Where did you hear about DevSoc from?".to_string(),
Some("This is a general question for all roles".to_string()),
true,
None,
true,
QuestionData::MultiChoice(
MultiOptionData {
options: vec![
MultiOptionQuestionOption {
id: 0,
text: "Email".to_string(),
display_order: 1,
},
MultiOptionQuestionOption {
id: 0,
text: "Word of Mouth".to_string(),
display_order: 2,
},
MultiOptionQuestionOption {
id: 0,
text: "Social Media".to_string(),
display_order: 3,
},
]
}
),
&mut seeder.app_state.snowflake_generator,
&mut tx,
)
.await.expect("Failed seeding Question 4");

let question_id_5 = Question::create(
campaign_id,
"Rank these programming languages?".to_string(),
Some("This is a general question for all technical roles".to_string()),
false,
Some(vec![role_id_1, role_id_2]),
true,
QuestionData::Ranking(
MultiOptionData {
options: vec![
MultiOptionQuestionOption {
id: 0,
text: "Rust".to_string(),
display_order: 1,
},
MultiOptionQuestionOption {
id: 0,
text: "JavaScript".to_string(),
display_order: 2,
},
MultiOptionQuestionOption {
id: 0,
text: "Python".to_string(),
display_order: 3,
},
]
}
),
&mut seeder.app_state.snowflake_generator,
&mut tx,
)
.await.expect("Failed seeding Question 4");

let application_id_1 = Application::create(
campaign_id,
2,
Expand Down
49 changes: 32 additions & 17 deletions backend/server/src/models/question.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,26 @@ impl Question {
.await?;

if !common {
for role in roles.unwrap_or_default() {
sqlx::query!(
if let Some(roles) = roles {
if roles.len() == 0 {
return Err(ChaosError::BadRequestWithMessage("Question must either be common or assigned to at least one role".to_string()));
}

for role in roles {
sqlx::query!(
"
INSERT INTO question_roles (question_id, role_id) VALUES ($1, $2)
",
id,
role
)
.execute(transaction.deref_mut())
.await?;
.execute(transaction.deref_mut())
.await?;
}
} else {
return Err(ChaosError::BadRequestWithMessage("Question must either be common or assigned to at least one role".to_string()));
}

}

Ok(id)
Expand All @@ -159,7 +168,7 @@ impl Question {
q.title,
q.description,
q.common,
COALESCE(array_remove(array_agg(qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
COALESCE(array_remove(array_agg(DISTINCT qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
q.required,
q.question_type AS "question_type: QuestionType",
q.created_at,
Expand Down Expand Up @@ -220,7 +229,7 @@ impl Question {
q.title,
q.description,
q.common,
COALESCE(array_remove(array_agg(qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
COALESCE(array_remove(array_agg(DISTINCT qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
q.required,
q.question_type AS "question_type: QuestionType",
q.created_at,
Expand Down Expand Up @@ -288,19 +297,22 @@ impl Question {
q.title,
q.description,
q.common,
COALESCE(array_remove(array_agg(qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
COALESCE(
(SELECT array_agg(qr.role_id) FROM question_roles qr WHERE qr.question_id = q.id),
'{}'
) AS "roles!: Vec<i64>",
q.required,
q.question_type AS "question_type: QuestionType",
q.created_at,
q.updated_at,
to_jsonb(
array_agg(
jsonb_build_object(
'id', mod.id,
'display_order', mod.display_order,
'text', mod.text
) ORDER BY mod.display_order
) FILTER (WHERE mod.id IS NOT NULL)
(
SELECT to_jsonb(array_agg(jsonb_build_object(
'id', mod.id,
'display_order', mod.display_order,
'text', mod.text
) ORDER BY mod.display_order))
FROM multi_option_question_options mod
WHERE mod.question_id = q.id
) AS "multi_option_data: Json<Vec<MultiOptionQuestionOption>>"
FROM
questions q
Expand All @@ -309,7 +321,9 @@ impl Question {
LEFT JOIN
multi_option_question_options mod ON q.id = mod.question_id
AND q.question_type IN ('MultiChoice', 'MultiSelect', 'DropDown', 'Ranking')
WHERE q.campaign_id = $1 AND q.common = false AND qr.role_id = $2
WHERE q.campaign_id = $1 AND q.common = false AND EXISTS (
SELECT 1 FROM question_roles qr_check WHERE qr_check.question_id = q.id AND qr_check.role_id = $2
)
GROUP BY
q.id
"#,
Expand Down Expand Up @@ -356,7 +370,7 @@ impl Question {
q.title,
q.description,
q.common,
COALESCE(array_remove(array_agg(qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
COALESCE(array_remove(array_agg(DISTINCT qr.role_id), NULL), '{}') AS "roles!: Vec<i64>",
q.required,
q.question_type AS "question_type: QuestionType",
q.created_at,
Expand Down Expand Up @@ -540,6 +554,7 @@ pub struct MultiOptionData {
#[derive(Deserialize, Serialize)]
pub struct MultiOptionQuestionOption {
#[serde(serialize_with = "crate::models::serde_string::serialize")]
#[serde(deserialize_with = "crate::models::serde_string::deserialize")]
pub id: i64,
pub display_order: i32,
pub text: String,
Expand Down
9 changes: 7 additions & 2 deletions backend/server/src/models/serde_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ pub fn deserialize<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse::<i64>().map_err(Error::custom)
match serde_json::Value::deserialize(deserializer)? {
serde_json::Value::String(s) => s.parse::<i64>().map_err(Error::custom),
serde_json::Value::Number(n) => n
.as_i64()
.ok_or_else(|| Error::custom("number out of range for i64")),
_ => Err(Error::custom("expected string or number")),
}
}

pub fn deserialize_vec<'de, D>(deserializer: D) -> Result<Vec<i64>, D::Error>
Expand Down
25 changes: 25 additions & 0 deletions frontend-nextjs/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions frontend-nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"start": "next start"
},
"dependencies": {
"@hello-pangea/dnd": "^18.0.1",
"@lexical/react": "^0.39.0",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-collapsible": "^1.1.12",
Expand All @@ -21,10 +22,12 @@
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@sapphire/snowflake": "^3.5.5",
"@tanstack/react-query": "^5.90.13",
"@tanstack/react-table": "^8.21.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"lexical": "^0.39.0",
"lucide-react": "^0.562.0",
Expand Down
Loading
Loading