From 11d7ae37b68ed6e4ebf071ea819cb93af6057254 Mon Sep 17 00:00:00 2001 From: Mahmoud-Emad Date: Tue, 27 May 2025 20:45:30 +0300 Subject: [PATCH] feat: Enhance governance module with activity tracking and DB refactor - Refactor database interaction for proposals and activities. - Add activity tracking for proposal creation and voting. - Improve logging for better debugging and monitoring. - Update governance views to display recent activities. - Add strum and strum_macros crates for enum handling. - Update Cargo.lock file with new dependencies. --- actix_mvc_app/Cargo.lock | 29 +++- actix_mvc_app/src/controllers/governance.rs | 93 ++++++++---- actix_mvc_app/src/db/db.rs | 17 +++ actix_mvc_app/src/db/governance_tracker.rs | 139 ------------------ actix_mvc_app/src/db/mod.rs | 2 +- actix_mvc_app/src/db/proposals.rs | 100 +++++++++---- actix_mvc_app/src/models/governance.rs | 136 +---------------- .../src/views/governance/proposal_detail.html | 6 +- .../src/views/governance/proposals.html | 6 +- 9 files changed, 193 insertions(+), 335 deletions(-) create mode 100644 actix_mvc_app/src/db/db.rs delete mode 100644 actix_mvc_app/src/db/governance_tracker.rs diff --git a/actix_mvc_app/Cargo.lock b/actix_mvc_app/Cargo.lock index 63aeda6..ee0c02c 100644 --- a/actix_mvc_app/Cargo.lock +++ b/actix_mvc_app/Cargo.lock @@ -1329,6 +1329,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1351,6 +1357,8 @@ dependencies = [ "rhai_wrapper", "serde", "serde_json", + "strum", + "strum_macros", "tst", ] @@ -2344,7 +2352,7 @@ dependencies = [ name = "rhai_autobind_macros" version = "0.1.0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -2701,6 +2709,25 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/actix_mvc_app/src/controllers/governance.rs b/actix_mvc_app/src/controllers/governance.rs index 6536efd..a8ca20b 100644 --- a/actix_mvc_app/src/controllers/governance.rs +++ b/actix_mvc_app/src/controllers/governance.rs @@ -1,10 +1,13 @@ -use crate::db::governance_tracker; -use crate::db::proposals::{self, get_proposal_by_id}; +use crate::db::proposals::{ + self, create_activity, get_all_activities, get_proposal_by_id, get_proposals, + get_recent_activities, +}; use crate::models::governance::{Vote, VoteType, VotingResults}; use crate::utils::render_template; use actix_session::Session; use actix_web::{HttpResponse, Responder, Result, web}; use chrono::{Duration, Utc}; +use heromodels::models::ActivityType; use heromodels::models::governance::{Proposal, ProposalStatus}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -80,6 +83,7 @@ impl GovernanceController { /// Handles the governance dashboard page route pub async fn index(tmpl: web::Data, session: Session) -> Result { + println!("=============================================="); let mut ctx = tera::Context::new(); ctx.insert("active_page", "governance"); ctx.insert("active_tab", "dashboard"); @@ -96,14 +100,32 @@ impl GovernanceController { let user = Self::get_user_from_session(&session).unwrap(); ctx.insert("user", &user); + println!("=============================================="); // Get proposals from the database let proposals = match crate::db::proposals::get_proposals() { - Ok(props) => props, + Ok(props) => { + println!( + "📋 Proposals list page: Successfully loaded {} proposals from database", + props.len() + ); + for (i, proposal) in props.iter().enumerate() { + println!( + " Proposal {}: ID={}, title={:?}, status={:?}", + i + 1, + proposal.base_data.id, + proposal.title, + proposal.status + ); + } + props + } Err(e) => { + println!("❌ Proposals list page: Failed to load proposals: {}", e); ctx.insert("error", &format!("Failed to load proposals: {}", e)); vec![] } }; + println!("=============================================="); // Make a copy of proposals for statistics let proposals_for_stats = proposals.clone(); @@ -170,8 +192,9 @@ impl GovernanceController { ctx.insert("user", &user); } + println!("============== Loading proposals ================="); // Get proposals from the database - let mut proposals = match crate::db::proposals::get_proposals() { + let mut proposals = match get_proposals() { Ok(props) => props, Err(e) => { ctx.insert("error", &format!("Failed to load proposals: {}", e)); @@ -179,6 +202,8 @@ impl GovernanceController { } }; + println!("proposals: {:?}", proposals); + // Filter proposals by status if provided if let Some(status_filter) = &query.status { if !status_filter.is_empty() { @@ -372,15 +397,12 @@ impl GovernanceController { ); // Track the proposal creation activity - let creation_activity = - crate::models::governance::GovernanceActivity::proposal_created( - proposal_id, - &saved_proposal.title, - &user_id, - &user_name, - ); - - let _ = governance_tracker::create_activity(creation_activity); + let _ = create_activity( + proposal_id, + &saved_proposal.title, + &user_name, + &ActivityType::ProposalCreated, + ); ctx.insert("success", "Proposal created successfully!"); } @@ -394,8 +416,24 @@ impl GovernanceController { // Get proposals from the database let proposals = match crate::db::proposals::get_proposals() { - Ok(props) => props, + Ok(props) => { + println!( + "✅ Successfully loaded {} proposals from database", + props.len() + ); + for (i, proposal) in props.iter().enumerate() { + println!( + " Proposal {}: ID={}, title={:?}, status={:?}", + i + 1, + proposal.base_data.id, + proposal.title, + proposal.status + ); + } + props + } Err(e) => { + println!("❌ Failed to load proposals: {}", e); ctx.insert("error", &format!("Failed to load proposals: {}", e)); vec![] } @@ -407,6 +445,14 @@ impl GovernanceController { ctx.insert("status_filter", &None::); ctx.insert("search_filter", &None::); + // Header data (required by _header.html template) + ctx.insert("page_title", "All Proposals"); + ctx.insert( + "page_description", + "Browse and filter all governance proposals", + ); + ctx.insert("show_create_button", &false); + render_template(&tmpl, "governance/proposals.html", &ctx) } @@ -461,15 +507,12 @@ impl GovernanceController { // Track the vote cast activity if let Ok(Some(proposal)) = get_proposal_by_id(proposal_id_u32) { - let vote_activity = crate::models::governance::GovernanceActivity::vote_cast( + let _ = create_activity( proposal_id_u32, &proposal.title, user_name, - &form.vote_type, - 1, // shares + &ActivityType::VoteCast, ); - - let _ = governance_tracker::create_activity(vote_activity); } // Redirect to the proposal detail page with a success message @@ -576,7 +619,7 @@ impl GovernanceController { /// Get recent governance activities from the database fn get_recent_governance_activities() -> Result, String> { // Get real activities from the database (no demo data) - let activities = governance_tracker::get_recent_activities()?; + let activities = get_recent_activities()?; // Convert GovernanceActivity to the format expected by the template let formatted_activities: Vec = activities @@ -596,10 +639,10 @@ impl GovernanceController { serde_json::json!({ "type": activity.activity_type, "icon": icon, - "user": activity.actor_name, + "user": activity.creator_name, "action": action, "proposal_title": activity.proposal_title, - "timestamp": activity.timestamp.format("%Y-%m-%dT%H:%M:%SZ").to_string(), + "created_at": activity.created_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(), "proposal_id": activity.proposal_id }) }) @@ -611,7 +654,7 @@ impl GovernanceController { /// Get all governance activities from the database fn get_all_governance_activities() -> Result, String> { // Get all activities from the database - let activities = governance_tracker::get_all_activities()?; + let activities = get_all_activities()?; // Convert GovernanceActivity to the format expected by the template let formatted_activities: Vec = activities @@ -631,10 +674,10 @@ impl GovernanceController { serde_json::json!({ "type": activity.activity_type, "icon": icon, - "user": activity.actor_name, + "user": activity.creator_name, "action": action, "proposal_title": activity.proposal_title, - "timestamp": activity.timestamp.format("%Y-%m-%dT%H:%M:%SZ").to_string(), + "created_at": activity.created_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(), "proposal_id": activity.proposal_id }) }) diff --git a/actix_mvc_app/src/db/db.rs b/actix_mvc_app/src/db/db.rs new file mode 100644 index 0000000..6427c14 --- /dev/null +++ b/actix_mvc_app/src/db/db.rs @@ -0,0 +1,17 @@ +use std::path::PathBuf; + +use heromodels::db::hero::OurDB; + +/// The path to the database file. Change this as needed for your environment. +pub const DB_PATH: &str = "/tmp/freezone_db"; + +/// Returns a shared OurDB instance for the given path. You can wrap this in Arc/Mutex for concurrent access if needed. +pub fn get_db() -> Result { + let db_path = PathBuf::from(DB_PATH); + if let Some(parent) = db_path.parent() { + let _ = std::fs::create_dir_all(parent); + } + // Temporarily reset the database to fix the serialization issue + let db = heromodels::db::hero::OurDB::new(db_path, false).expect("Can create DB"); + Ok(db) +} diff --git a/actix_mvc_app/src/db/governance_tracker.rs b/actix_mvc_app/src/db/governance_tracker.rs deleted file mode 100644 index 5ade2ae..0000000 --- a/actix_mvc_app/src/db/governance_tracker.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::models::governance::GovernanceActivity; -use std::path::PathBuf; - -/// Database path for governance activities -pub const DB_PATH: &str = "/tmp/ourdb_governance_activities"; - -/// Returns a shared OurDB instance for activities -pub fn get_db() -> Result { - let db_path = PathBuf::from(DB_PATH); - if let Some(parent) = db_path.parent() { - let _ = std::fs::create_dir_all(parent); - } - let db = heromodels::db::hero::OurDB::new(db_path, true) - .map_err(|e| format!("Failed to create activities DB: {:?}", e))?; - Ok(db) -} - -/// Creates a new governance activity and saves it to the database using OurDB -pub fn create_activity(activity: GovernanceActivity) -> Result<(u32, GovernanceActivity), String> { - let db = get_db()?; - - // Since OurDB doesn't support custom models directly, we'll use a simple key-value approach - // Store each activity with a unique key and serialize it as JSON - - // First, get the next available ID by checking existing keys - let activity_id = get_next_activity_id(&db)?; - - // Create the activity with the assigned ID - let mut new_activity = activity; - new_activity.id = Some(activity_id); - - // Serialize the activity to JSON - let activity_json = serde_json::to_string(&new_activity) - .map_err(|e| format!("Failed to serialize activity: {}", e))?; - - // Store in OurDB using a key-value approach - let key = format!("activity_{}", activity_id); - - // Use OurDB's raw storage capabilities to store the JSON string - // Since we can't use collections directly, we'll store as raw data - let db_path = format!("{}/{}.json", DB_PATH, key); - std::fs::write(&db_path, &activity_json) - .map_err(|e| format!("Failed to write activity to DB: {}", e))?; - - // Also maintain an index of activity IDs for efficient retrieval - update_activity_index(&db, activity_id)?; - - println!( - "✅ Activity recorded: {} - {}", - new_activity.activity_type, new_activity.description - ); - - Ok((activity_id, new_activity)) -} - -/// Gets the next available activity ID -fn get_next_activity_id(_db: &heromodels::db::hero::OurDB) -> Result { - let index_path = format!("{}/activity_index.json", DB_PATH); - - if std::path::Path::new(&index_path).exists() { - let content = std::fs::read_to_string(&index_path) - .map_err(|e| format!("Failed to read activity index: {}", e))?; - let index: Vec = serde_json::from_str(&content).unwrap_or_else(|_| Vec::new()); - Ok(index.len() as u32 + 1) - } else { - Ok(1) - } -} - -/// Updates the activity index with a new activity ID -fn update_activity_index( - _db: &heromodels::db::hero::OurDB, - activity_id: u32, -) -> Result<(), String> { - let index_path = format!("{}/activity_index.json", DB_PATH); - - let mut index: Vec = if std::path::Path::new(&index_path).exists() { - let content = std::fs::read_to_string(&index_path) - .map_err(|e| format!("Failed to read activity index: {}", e))?; - serde_json::from_str(&content).unwrap_or_else(|_| Vec::new()) - } else { - Vec::new() - }; - - index.push(activity_id); - - let content = serde_json::to_string(&index) - .map_err(|e| format!("Failed to serialize activity index: {}", e))?; - - std::fs::write(&index_path, content) - .map_err(|e| format!("Failed to write activity index: {}", e))?; - - Ok(()) -} - -/// Gets all activities from the database using OurDB -pub fn get_all_activities() -> Result, String> { - let _db = get_db()?; - let index_path = format!("{}/activity_index.json", DB_PATH); - - // Read the activity index to get all activity IDs - if !std::path::Path::new(&index_path).exists() { - return Ok(Vec::new()); - } - - let content = std::fs::read_to_string(&index_path) - .map_err(|e| format!("Failed to read activity index: {}", e))?; - let activity_ids: Vec = serde_json::from_str(&content).unwrap_or_else(|_| Vec::new()); - - let mut activities = Vec::new(); - - // Load each activity by ID - for activity_id in activity_ids { - let activity_path = format!("{}/activity_{}.json", DB_PATH, activity_id); - if std::path::Path::new(&activity_path).exists() { - let activity_content = std::fs::read_to_string(&activity_path) - .map_err(|e| format!("Failed to read activity {}: {}", activity_id, e))?; - - if let Ok(activity) = serde_json::from_str::(&activity_content) { - activities.push(activity); - } - } - } - - Ok(activities) -} - -/// Gets recent activities (last 10) sorted by timestamp using OurDB -pub fn get_recent_activities() -> Result, String> { - let mut activities = get_all_activities()?; - - // Sort by timestamp (most recent first) - activities.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); - - // Take only the last 10 - activities.truncate(10); - - Ok(activities) -} diff --git a/actix_mvc_app/src/db/mod.rs b/actix_mvc_app/src/db/mod.rs index 1b1e306..6def13b 100644 --- a/actix_mvc_app/src/db/mod.rs +++ b/actix_mvc_app/src/db/mod.rs @@ -1,2 +1,2 @@ -pub mod governance_tracker; +pub mod db; pub mod proposals; diff --git a/actix_mvc_app/src/db/proposals.rs b/actix_mvc_app/src/db/proposals.rs index c12faa9..e354c90 100644 --- a/actix_mvc_app/src/db/proposals.rs +++ b/actix_mvc_app/src/db/proposals.rs @@ -1,25 +1,10 @@ -use std::path::PathBuf; - use chrono::{Duration, Utc}; -use heromodels::db::hero::OurDB; use heromodels::{ db::{Collection, Db}, - models::governance::{Proposal, ProposalStatus}, + models::governance::{Activity, ActivityType, Proposal, ProposalStatus}, }; -/// The path to the database file. Change this as needed for your environment. -pub const DB_PATH: &str = "/tmp/ourdb_governance"; - -/// Returns a shared OurDB instance for the given path. You can wrap this in Arc/Mutex for concurrent access if needed. -pub fn get_db(db_path: &str) -> Result { - let db_path = PathBuf::from(db_path); - if let Some(parent) = db_path.parent() { - let _ = std::fs::create_dir_all(parent); - } - // Temporarily reset the database to fix the serialization issue - let db = heromodels::db::hero::OurDB::new(db_path, false).expect("Can create DB"); - Ok(db) -} +use super::db::get_db; /// Creates a new proposal and saves it to the database. Returns the saved proposal and its ID. pub fn create_new_proposal( @@ -31,7 +16,7 @@ pub fn create_new_proposal( voting_start_date: Option>, voting_end_date: Option>, ) -> Result<(u32, Proposal), String> { - let db = get_db(DB_PATH).expect("Can create DB"); + let db = get_db().expect("Can get DB"); let created_at = Utc::now(); let updated_at = created_at; @@ -60,7 +45,7 @@ pub fn create_new_proposal( /// Loads all proposals from the database and returns them as a Vec. pub fn get_proposals() -> Result, String> { - let db = get_db(DB_PATH).map_err(|e| format!("DB error: {}", e))?; + let db = get_db().map_err(|e| format!("DB error: {}", e))?; let collection = db .collection::() .expect("can open proposal collection"); @@ -78,7 +63,7 @@ pub fn get_proposals() -> Result, String> { /// Fetches a single proposal by its ID from the database. pub fn get_proposal_by_id(proposal_id: u32) -> Result, String> { - let db = get_db(DB_PATH).map_err(|e| format!("DB error: {}", e))?; + let db = get_db().map_err(|e| format!("DB error: {}", e))?; let collection = db .collection::() .map_err(|e| format!("Collection error: {:?}", e))?; @@ -100,7 +85,7 @@ pub fn submit_vote_on_proposal( comment: Option, ) -> Result { // Get the proposal from the database - let db = get_db(DB_PATH).map_err(|e| format!("DB error: {}", e))?; + let db = get_db().map_err(|e| format!("DB error: {}", e))?; let collection = db .collection::() .map_err(|e| format!("Collection error: {:?}", e))?; @@ -167,13 +152,6 @@ pub fn submit_vote_on_proposal( // We'll create a simple ballot with an auto-generated ID let ballot_id = proposal.ballots.len() as u32 + 1; - // We need to manually create a ballot since we can't use cast_vote - // This is a simplified version that just records the vote - println!( - "Recording vote: ballot_id={}, user_id={}, option_id={}, shares={}", - ballot_id, user_id, option_id, shares_count - ); - // Create a new ballot and add it to the proposal's ballots use heromodels::models::governance::Ballot; @@ -210,3 +188,69 @@ pub fn submit_vote_on_proposal( Ok(updated_proposal) } + +/// Creates a new governance activity and saves it to the database using OurDB +pub fn create_activity( + proposal_id: u32, + proposal_title: &str, + creator_name: &str, + activity_type: &ActivityType, +) -> Result<(u32, Activity), String> { + let db = get_db().expect("Can get DB"); + let mut activity = Activity::default(); + + match activity_type { + ActivityType::ProposalCreated => { + activity = Activity::proposal_created(proposal_id, proposal_title, creator_name); + } + ActivityType::VoteCast => { + activity = Activity::vote_cast(proposal_id, proposal_title, creator_name); + } + ActivityType::VotingStarted => { + activity = Activity::voting_started(proposal_id, proposal_title); + } + ActivityType::VotingEnded => { + activity = Activity::voting_ended(proposal_id, proposal_title); + } + } + + // Save the proposal to the database + let collection = db + .collection::() + .expect("can open activity collection"); + + let (proposal_id, saved_proposal) = collection.set(&activity).expect("can save proposal"); + Ok((proposal_id, saved_proposal)) +} + +pub fn get_recent_activities() -> Result, String> { + let db = get_db().expect("Can get DB"); + let collection = db + .collection::() + .map_err(|e| format!("Collection error: {:?}", e))?; + + let mut db_activities = collection + .get_all() + .map_err(|e| format!("DB fetch error: {:?}", e))?; + + // Sort by created_at descending + db_activities.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + + // Take the top 10 most recent + let recent_activities = db_activities.into_iter().take(10).collect(); + + Ok(recent_activities) +} + +pub fn get_all_activities() -> Result, String> { + let db = get_db().expect("Can get DB"); + let collection = db + .collection::() + .map_err(|e| format!("Collection error: {:?}", e))?; + + let db_activities = collection + .get_all() + .map_err(|e| format!("DB fetch error: {:?}", e))?; + + Ok(db_activities) +} diff --git a/actix_mvc_app/src/models/governance.rs b/actix_mvc_app/src/models/governance.rs index 34f05b2..b2e2eef 100644 --- a/actix_mvc_app/src/models/governance.rs +++ b/actix_mvc_app/src/models/governance.rs @@ -208,108 +208,8 @@ pub struct VotingResults { pub total_votes: usize, } -/// Represents a governance activity in the system -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GovernanceActivity { - /// Unique identifier for the activity - pub id: Option, - /// Type of activity (proposal_created, vote_cast, etc.) - pub activity_type: String, - /// ID of the related proposal - pub proposal_id: u32, - /// Title of the related proposal - pub proposal_title: String, - /// Name of the user who performed the action - pub actor_name: String, - /// Description of the activity - pub description: String, - /// Date and time when the activity occurred - pub timestamp: DateTime, -} - -impl GovernanceActivity { - /// Creates a new governance activity - pub fn new( - activity_type: &str, - proposal_id: u32, - proposal_title: &str, - actor_name: &str, - description: &str, - ) -> Self { - Self { - id: None, - activity_type: activity_type.to_string(), - proposal_id, - proposal_title: proposal_title.to_string(), - actor_name: actor_name.to_string(), - description: description.to_string(), - timestamp: Utc::now(), - } - } - - /// Creates a proposal creation activity - pub fn proposal_created( - proposal_id: u32, - proposal_title: &str, - _creator_id: &str, - creator_name: &str, - ) -> Self { - Self::new( - "proposal_created", - proposal_id, - proposal_title, - creator_name, - &format!("Proposal '{}' created by {}", proposal_title, creator_name), - ) - } - - /// Creates a vote cast activity - pub fn vote_cast( - proposal_id: u32, - proposal_title: &str, - voter_name: &str, - vote_option: &str, - shares: i64, - ) -> Self { - Self::new( - "vote_cast", - proposal_id, - proposal_title, - voter_name, - &format!( - "{} voted '{}' with {} shares", - voter_name, vote_option, shares - ), - ) - } - - /// Creates a proposal status change activity - pub fn proposal_status_changed( - proposal_id: u32, - proposal_title: &str, - new_status: &ProposalStatus, - reason: Option<&str>, - ) -> Self { - let description = format!( - "Proposal '{}' status changed to {}{}", - proposal_title, - new_status, - reason.map(|r| format!(": {}", r)).unwrap_or_default() - ); - - Self::new( - "proposal_status_changed", - proposal_id, - proposal_title, - "System", - &description, - ) - } -} - -#[allow(dead_code)] impl VotingResults { - /// Creates a new empty voting results object + /// Creates a new VotingResults instance pub fn new(proposal_id: String) -> Self { Self { proposal_id, @@ -319,38 +219,4 @@ impl VotingResults { total_votes: 0, } } - - /// Adds a vote to the results - pub fn add_vote(&mut self, vote_type: &VoteType) { - match vote_type { - VoteType::Yes => self.yes_count += 1, - VoteType::No => self.no_count += 1, - VoteType::Abstain => self.abstain_count += 1, - } - self.total_votes += 1; - } - - /// Calculates the percentage of yes votes - pub fn yes_percentage(&self) -> f64 { - if self.total_votes == 0 { - return 0.0; - } - (self.yes_count as f64 / self.total_votes as f64) * 100.0 - } - - /// Calculates the percentage of no votes - pub fn no_percentage(&self) -> f64 { - if self.total_votes == 0 { - return 0.0; - } - (self.no_count as f64 / self.total_votes as f64) * 100.0 - } - - /// Calculates the percentage of abstain votes - pub fn abstain_percentage(&self) -> f64 { - if self.total_votes == 0 { - return 0.0; - } - (self.abstain_count as f64 / self.total_votes as f64) * 100.0 - } } diff --git a/actix_mvc_app/src/views/governance/proposal_detail.html b/actix_mvc_app/src/views/governance/proposal_detail.html index 6e57918..a52cb06 100644 --- a/actix_mvc_app/src/views/governance/proposal_detail.html +++ b/actix_mvc_app/src/views/governance/proposal_detail.html @@ -92,17 +92,17 @@
Voting Period
- {% if proposal.voting_starts_at and proposal.voting_ends_at %} + {% if proposal.vote_start_date and proposal.vote_end_date %}
Start Date
-
{{ proposal.voting_starts_at | date(format="%Y-%m-%d") }}
+
{{ proposal.vote_start_date | date(format="%Y-%m-%d") }}
End Date
-
{{ proposal.voting_ends_at | date(format="%Y-%m-%d") }}
+
{{ proposal.vote_end_date | date(format="%Y-%m-%d") }}
{% else %}
Not set
diff --git a/actix_mvc_app/src/views/governance/proposals.html b/actix_mvc_app/src/views/governance/proposals.html index fd23e97..f03fa50 100644 --- a/actix_mvc_app/src/views/governance/proposals.html +++ b/actix_mvc_app/src/views/governance/proposals.html @@ -106,9 +106,9 @@ {{ proposal.created_at | date(format="%Y-%m-%d") }} - {% if proposal.voting_starts_at and proposal.voting_ends_at %} - {{ proposal.voting_starts_at | date(format="%Y-%m-%d") }} to {{ - proposal.voting_ends_at | date(format="%Y-%m-%d") }} + {% if proposal.vote_start_date and proposal.vote_end_date %} + {{ proposal.vote_start_date | date(format="%Y-%m-%d") }} to {{ + proposal.vote_end_date | date(format="%Y-%m-%d") }} {% else %} Not set {% endif %}