feat: Improve user experience after voting on proposals
- Redirect users to the proposal detail page with a success message after a successful vote, improving feedback. - Automatically remove the success message from the URL after a short time to avoid URL clutter and maintain a clean browsing experience. - Add a success alert message on the proposal detail page to provide immediate visual confirmation of a successful vote. - Improve the visual presentation of the votes list on the proposal detail page by adding top margin for better spacing.
This commit is contained in:
parent
52fbc77e3e
commit
3d8aca19cc
@ -196,9 +196,13 @@ impl GovernanceController {
|
|||||||
/// Handles the proposal detail page route
|
/// Handles the proposal detail page route
|
||||||
pub async fn proposal_detail(
|
pub async fn proposal_detail(
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
req: actix_web::HttpRequest,
|
||||||
tmpl: web::Data<Tera>,
|
tmpl: web::Data<Tera>,
|
||||||
session: Session,
|
session: Session,
|
||||||
) -> Result<impl Responder> {
|
) -> Result<impl Responder> {
|
||||||
|
// Extract query parameters from the request
|
||||||
|
let query_str = req.query_string();
|
||||||
|
let vote_success = query_str.contains("vote_success=true");
|
||||||
let proposal_id = path.into_inner();
|
let proposal_id = path.into_inner();
|
||||||
let mut ctx = tera::Context::new();
|
let mut ctx = tera::Context::new();
|
||||||
ctx.insert("active_page", "governance");
|
ctx.insert("active_page", "governance");
|
||||||
@ -220,6 +224,11 @@ impl GovernanceController {
|
|||||||
// Calculate voting results directly from the proposal
|
// Calculate voting results directly from the proposal
|
||||||
let results = Self::calculate_voting_results_from_proposal(&proposal);
|
let results = Self::calculate_voting_results_from_proposal(&proposal);
|
||||||
ctx.insert("results", &results);
|
ctx.insert("results", &results);
|
||||||
|
|
||||||
|
// Check if vote_success parameter is present and add success message
|
||||||
|
if vote_success {
|
||||||
|
ctx.insert("success", "Your vote has been successfully recorded!");
|
||||||
|
}
|
||||||
|
|
||||||
render_template(&tmpl, "governance/proposal_detail.html", &ctx)
|
render_template(&tmpl, "governance/proposal_detail.html", &ctx)
|
||||||
} else {
|
} else {
|
||||||
@ -392,18 +401,10 @@ impl GovernanceController {
|
|||||||
form.comment.as_ref().map(|s| s.to_string()), // Pass the comment from the form
|
form.comment.as_ref().map(|s| s.to_string()), // Pass the comment from the form
|
||||||
) {
|
) {
|
||||||
Ok(updated_proposal) => {
|
Ok(updated_proposal) => {
|
||||||
ctx.insert("proposal", &updated_proposal);
|
// Redirect to the proposal detail page with a success message
|
||||||
ctx.insert("success", "Your vote has been recorded!");
|
return Ok(HttpResponse::Found()
|
||||||
|
.append_header(("Location", format!("/governance/proposals/{}?vote_success=true", proposal_id)))
|
||||||
// Extract votes directly from the updated proposal
|
.finish());
|
||||||
let votes = Self::extract_votes_from_proposal(&updated_proposal);
|
|
||||||
ctx.insert("votes", &votes);
|
|
||||||
|
|
||||||
// Calculate voting results directly from the updated proposal
|
|
||||||
let results = Self::calculate_voting_results_from_proposal(&updated_proposal);
|
|
||||||
ctx.insert("results", &results);
|
|
||||||
|
|
||||||
render_template(&tmpl, "governance/proposal_detail.html", &ctx)
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ctx.insert("error", &format!("Failed to submit vote: {}", e));
|
ctx.insert("error", &format!("Failed to submit vote: {}", e));
|
||||||
|
@ -240,7 +240,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Votes List -->
|
<!-- Votes List -->
|
||||||
<div class="row">
|
<div class="row mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header bg-light d-flex justify-content-between align-items-center flex-wrap">
|
<div class="card-header bg-light d-flex justify-content-between align-items-center flex-wrap">
|
||||||
@ -338,6 +338,23 @@
|
|||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// Remove query parameters from URL without refreshing the page
|
||||||
|
if (window.location.search.includes('vote_success=true')) {
|
||||||
|
const newUrl = window.location.pathname;
|
||||||
|
window.history.replaceState({}, document.title, newUrl);
|
||||||
|
|
||||||
|
// Auto-hide the success alert after 5 seconds
|
||||||
|
const successAlert = document.querySelector('.alert-success');
|
||||||
|
if (successAlert) {
|
||||||
|
setTimeout(function() {
|
||||||
|
successAlert.classList.remove('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
successAlert.remove();
|
||||||
|
}, 500);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Vote filtering using data-filter attributes
|
// Vote filtering using data-filter attributes
|
||||||
const filterButtons = document.querySelectorAll('[data-filter]');
|
const filterButtons = document.querySelectorAll('[data-filter]');
|
||||||
const voteRows = document.querySelectorAll('.vote-row');
|
const voteRows = document.querySelectorAll('.vote-row');
|
||||||
|
Loading…
Reference in New Issue
Block a user