WIP: development_backend #4
@@ -2,7 +2,7 @@ use crate::db::governance::{
 | 
			
		||||
    self, create_activity, get_all_activities, get_proposal_by_id, get_proposals,
 | 
			
		||||
    get_recent_activities,
 | 
			
		||||
};
 | 
			
		||||
use crate::models::governance::{Vote, VoteType, VotingResults};
 | 
			
		||||
// Note: Now using heromodels directly instead of local governance models
 | 
			
		||||
use crate::utils::render_template;
 | 
			
		||||
use actix_session::Session;
 | 
			
		||||
use actix_web::{HttpResponse, Responder, Result, web};
 | 
			
		||||
@@ -15,6 +15,71 @@ use tera::Tera;
 | 
			
		||||
 | 
			
		||||
use chrono::prelude::*;
 | 
			
		||||
 | 
			
		||||
/// Simple vote type for UI display
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
			
		||||
pub enum VoteType {
 | 
			
		||||
    Yes,
 | 
			
		||||
    No,
 | 
			
		||||
    Abstain,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Simple vote structure for UI display
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Vote {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub proposal_id: String,
 | 
			
		||||
    pub voter_id: i32,
 | 
			
		||||
    pub voter_name: String,
 | 
			
		||||
    pub vote_type: VoteType,
 | 
			
		||||
    pub comment: Option<String>,
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    pub updated_at: DateTime<Utc>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Vote {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        proposal_id: String,
 | 
			
		||||
        voter_id: i32,
 | 
			
		||||
        voter_name: String,
 | 
			
		||||
        vote_type: VoteType,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        Self {
 | 
			
		||||
            id: uuid::Uuid::new_v4().to_string(),
 | 
			
		||||
            proposal_id,
 | 
			
		||||
            voter_id,
 | 
			
		||||
            voter_name,
 | 
			
		||||
            vote_type,
 | 
			
		||||
            comment,
 | 
			
		||||
            created_at: now,
 | 
			
		||||
            updated_at: now,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Simple voting results structure for UI display
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct VotingResults {
 | 
			
		||||
    pub proposal_id: String,
 | 
			
		||||
    pub yes_count: usize,
 | 
			
		||||
    pub no_count: usize,
 | 
			
		||||
    pub abstain_count: usize,
 | 
			
		||||
    pub total_votes: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VotingResults {
 | 
			
		||||
    pub fn new(proposal_id: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            proposal_id,
 | 
			
		||||
            yes_count: 0,
 | 
			
		||||
            no_count: 0,
 | 
			
		||||
            abstain_count: 0,
 | 
			
		||||
            total_votes: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Controller for handling governance-related routes
 | 
			
		||||
pub struct GovernanceController;
 | 
			
		||||
 | 
			
		||||
@@ -153,9 +218,9 @@ impl GovernanceController {
 | 
			
		||||
        let stats = Self::calculate_statistics_from_database(&proposals_for_stats);
 | 
			
		||||
        ctx.insert("stats", &stats);
 | 
			
		||||
 | 
			
		||||
        // Get recent governance activities from our tracker
 | 
			
		||||
        // Get recent governance activities from our tracker (limit to 4 for dashboard)
 | 
			
		||||
        let recent_activity = match Self::get_recent_governance_activities() {
 | 
			
		||||
            Ok(activities) => activities,
 | 
			
		||||
            Ok(activities) => activities.into_iter().take(4).collect::<Vec<_>>(),
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                eprintln!("Failed to load recent activities: {}", e);
 | 
			
		||||
                Vec::new()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,222 +0,0 @@
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
/// Represents the status of a governance proposal
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
			
		||||
pub enum ProposalStatus {
 | 
			
		||||
    /// Proposal is in draft status, not yet open for voting
 | 
			
		||||
    Draft,
 | 
			
		||||
    /// Proposal is active and open for voting
 | 
			
		||||
    Active,
 | 
			
		||||
    /// Proposal has been approved by the community
 | 
			
		||||
    Approved,
 | 
			
		||||
    /// Proposal has been rejected by the community
 | 
			
		||||
    Rejected,
 | 
			
		||||
    /// Proposal has been cancelled by the creator
 | 
			
		||||
    Cancelled,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for ProposalStatus {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            ProposalStatus::Draft => write!(f, "Draft"),
 | 
			
		||||
            ProposalStatus::Active => write!(f, "Active"),
 | 
			
		||||
            ProposalStatus::Approved => write!(f, "Approved"),
 | 
			
		||||
            ProposalStatus::Rejected => write!(f, "Rejected"),
 | 
			
		||||
            ProposalStatus::Cancelled => write!(f, "Cancelled"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a vote on a governance proposal
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
			
		||||
pub enum VoteType {
 | 
			
		||||
    /// Vote in favor of the proposal
 | 
			
		||||
    Yes,
 | 
			
		||||
    /// Vote against the proposal
 | 
			
		||||
    No,
 | 
			
		||||
    /// Abstain from voting on the proposal
 | 
			
		||||
    Abstain,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for VoteType {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            VoteType::Yes => write!(f, "Yes"),
 | 
			
		||||
            VoteType::No => write!(f, "No"),
 | 
			
		||||
            VoteType::Abstain => write!(f, "Abstain"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a governance proposal in the system
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Proposal {
 | 
			
		||||
    /// Unique identifier for the proposal
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    /// User ID of the proposal creator
 | 
			
		||||
    pub creator_id: i32,
 | 
			
		||||
    /// Name of the proposal creator
 | 
			
		||||
    pub creator_name: String,
 | 
			
		||||
    /// Title of the proposal
 | 
			
		||||
    pub title: String,
 | 
			
		||||
    /// Detailed description of the proposal
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    /// Current status of the proposal
 | 
			
		||||
    pub status: ProposalStatus,
 | 
			
		||||
    /// Date and time when the proposal was created
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    /// Date and time when the proposal was last updated
 | 
			
		||||
    pub updated_at: DateTime<Utc>,
 | 
			
		||||
    /// Date and time when voting starts
 | 
			
		||||
    pub voting_starts_at: Option<DateTime<Utc>>,
 | 
			
		||||
    /// Date and time when voting ends
 | 
			
		||||
    pub voting_ends_at: Option<DateTime<Utc>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Proposal {
 | 
			
		||||
    /// Creates a new proposal
 | 
			
		||||
    pub fn new(creator_id: i32, creator_name: String, title: String, description: String) -> Self {
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        Self {
 | 
			
		||||
            id: Uuid::new_v4().to_string(),
 | 
			
		||||
            creator_id,
 | 
			
		||||
            creator_name,
 | 
			
		||||
            title,
 | 
			
		||||
            description,
 | 
			
		||||
            status: ProposalStatus::Draft,
 | 
			
		||||
            created_at: now,
 | 
			
		||||
            updated_at: now,
 | 
			
		||||
            voting_starts_at: None,
 | 
			
		||||
            voting_ends_at: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Updates the proposal status
 | 
			
		||||
    pub fn update_status(&mut self, status: ProposalStatus) {
 | 
			
		||||
        self.status = status;
 | 
			
		||||
        self.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the voting period for the proposal
 | 
			
		||||
    pub fn set_voting_period(&mut self, starts_at: DateTime<Utc>, ends_at: DateTime<Utc>) {
 | 
			
		||||
        self.voting_starts_at = Some(starts_at);
 | 
			
		||||
        self.voting_ends_at = Some(ends_at);
 | 
			
		||||
        self.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Activates the proposal for voting
 | 
			
		||||
    pub fn activate(&mut self) {
 | 
			
		||||
        self.status = ProposalStatus::Active;
 | 
			
		||||
        self.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Cancels the proposal
 | 
			
		||||
    pub fn cancel(&mut self) {
 | 
			
		||||
        self.status = ProposalStatus::Cancelled;
 | 
			
		||||
        self.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a vote cast on a proposal
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Vote {
 | 
			
		||||
    /// Unique identifier for the vote
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    /// ID of the proposal being voted on
 | 
			
		||||
    pub proposal_id: String,
 | 
			
		||||
    /// User ID of the voter
 | 
			
		||||
    pub voter_id: i32,
 | 
			
		||||
    /// Name of the voter
 | 
			
		||||
    pub voter_name: String,
 | 
			
		||||
    /// Type of vote cast
 | 
			
		||||
    pub vote_type: VoteType,
 | 
			
		||||
    /// Optional comment explaining the vote
 | 
			
		||||
    pub comment: Option<String>,
 | 
			
		||||
    /// Date and time when the vote was cast
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    /// Date and time when the vote was last updated
 | 
			
		||||
    pub updated_at: DateTime<Utc>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Vote {
 | 
			
		||||
    /// Creates a new vote
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        proposal_id: String,
 | 
			
		||||
        voter_id: i32,
 | 
			
		||||
        voter_name: String,
 | 
			
		||||
        vote_type: VoteType,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        Self {
 | 
			
		||||
            id: Uuid::new_v4().to_string(),
 | 
			
		||||
            proposal_id,
 | 
			
		||||
            voter_id,
 | 
			
		||||
            voter_name,
 | 
			
		||||
            vote_type,
 | 
			
		||||
            comment,
 | 
			
		||||
            created_at: now,
 | 
			
		||||
            updated_at: now,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Updates the vote type
 | 
			
		||||
    pub fn update_vote(&mut self, vote_type: VoteType, comment: Option<String>) {
 | 
			
		||||
        self.vote_type = vote_type;
 | 
			
		||||
        self.comment = comment;
 | 
			
		||||
        self.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a filter for searching proposals
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct ProposalFilter {
 | 
			
		||||
    /// Filter by proposal status
 | 
			
		||||
    pub status: Option<String>,
 | 
			
		||||
    /// Filter by creator ID
 | 
			
		||||
    pub creator_id: Option<i32>,
 | 
			
		||||
    /// Search term for title and description
 | 
			
		||||
    pub search: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ProposalFilter {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            status: None,
 | 
			
		||||
            creator_id: None,
 | 
			
		||||
            search: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents the voting results for a proposal
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct VotingResults {
 | 
			
		||||
    /// Proposal ID
 | 
			
		||||
    pub proposal_id: String,
 | 
			
		||||
    /// Number of yes votes
 | 
			
		||||
    pub yes_count: usize,
 | 
			
		||||
    /// Number of no votes
 | 
			
		||||
    pub no_count: usize,
 | 
			
		||||
    /// Number of abstain votes
 | 
			
		||||
    pub abstain_count: usize,
 | 
			
		||||
    /// Total number of votes
 | 
			
		||||
    pub total_votes: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VotingResults {
 | 
			
		||||
    /// Creates a new VotingResults instance
 | 
			
		||||
    pub fn new(proposal_id: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            proposal_id,
 | 
			
		||||
            yes_count: 0,
 | 
			
		||||
            no_count: 0,
 | 
			
		||||
            abstain_count: 0,
 | 
			
		||||
            total_votes: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,7 +4,7 @@ pub mod calendar;
 | 
			
		||||
pub mod contract;
 | 
			
		||||
pub mod defi;
 | 
			
		||||
pub mod flow;
 | 
			
		||||
pub mod governance;
 | 
			
		||||
 | 
			
		||||
pub mod marketplace;
 | 
			
		||||
pub mod ticket;
 | 
			
		||||
pub mod user;
 | 
			
		||||
 
 | 
			
		||||
@@ -87,15 +87,16 @@
 | 
			
		||||
 | 
			
		||||
                <div class="mb-4">
 | 
			
		||||
                    <h5 class="mb-3">Cast Your Vote</h5>
 | 
			
		||||
                    <form>
 | 
			
		||||
                    <form action="/governance/proposals/{{ nearest_proposal.base_data.id }}/vote" method="post">
 | 
			
		||||
                        <div class="mb-3">
 | 
			
		||||
                            <input type="text" class="form-control" placeholder="Optional comment on your vote"
 | 
			
		||||
                                aria-label="Vote comment">
 | 
			
		||||
                            <input type="text" class="form-control" name="comment"
 | 
			
		||||
                                placeholder="Optional comment on your vote" aria-label="Vote comment">
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="d-flex justify-content-between">
 | 
			
		||||
                            <button type="submit" name="vote" value="yes" class="btn btn-success">Vote Yes</button>
 | 
			
		||||
                            <button type="submit" name="vote" value="no" class="btn btn-danger">Vote No</button>
 | 
			
		||||
                            <button type="submit" name="vote" value="abstain" class="btn btn-secondary">Abstain</button>
 | 
			
		||||
                            <button type="submit" name="vote_type" value="Yes" class="btn btn-success">Vote Yes</button>
 | 
			
		||||
                            <button type="submit" name="vote_type" value="No" class="btn btn-danger">Vote No</button>
 | 
			
		||||
                            <button type="submit" name="vote_type" value="Abstain"
 | 
			
		||||
                                class="btn btn-secondary">Abstain</button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user