feat: Refactor governance models and views
- Moved governance models (`Vote`, `VoteType`, `VotingResults`) from `models/governance.rs` to `controllers/governance.rs` for better organization and to avoid circular dependencies. This improves maintainability and reduces complexity. - Updated governance views to use the new model locations. - Added a limit to the number of recent activities displayed on the dashboard for performance optimization.
This commit is contained in:
parent
9802d51acc
commit
7e95391a9c
@ -2,7 +2,7 @@ use crate::db::governance::{
|
|||||||
self, create_activity, get_all_activities, get_proposal_by_id, get_proposals,
|
self, create_activity, get_all_activities, get_proposal_by_id, get_proposals,
|
||||||
get_recent_activities,
|
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 crate::utils::render_template;
|
||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::{HttpResponse, Responder, Result, web};
|
use actix_web::{HttpResponse, Responder, Result, web};
|
||||||
@ -15,6 +15,71 @@ use tera::Tera;
|
|||||||
|
|
||||||
use chrono::prelude::*;
|
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
|
/// Controller for handling governance-related routes
|
||||||
pub struct GovernanceController;
|
pub struct GovernanceController;
|
||||||
|
|
||||||
@ -153,9 +218,9 @@ impl GovernanceController {
|
|||||||
let stats = Self::calculate_statistics_from_database(&proposals_for_stats);
|
let stats = Self::calculate_statistics_from_database(&proposals_for_stats);
|
||||||
ctx.insert("stats", &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() {
|
let recent_activity = match Self::get_recent_governance_activities() {
|
||||||
Ok(activities) => activities,
|
Ok(activities) => activities.into_iter().take(4).collect::<Vec<_>>(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to load recent activities: {}", e);
|
eprintln!("Failed to load recent activities: {}", e);
|
||||||
Vec::new()
|
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 contract;
|
||||||
pub mod defi;
|
pub mod defi;
|
||||||
pub mod flow;
|
pub mod flow;
|
||||||
pub mod governance;
|
|
||||||
pub mod marketplace;
|
pub mod marketplace;
|
||||||
pub mod ticket;
|
pub mod ticket;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
@ -87,15 +87,16 @@
|
|||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h5 class="mb-3">Cast Your Vote</h5>
|
<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">
|
<div class="mb-3">
|
||||||
<input type="text" class="form-control" placeholder="Optional comment on your vote"
|
<input type="text" class="form-control" name="comment"
|
||||||
aria-label="Vote comment">
|
placeholder="Optional comment on your vote" aria-label="Vote comment">
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between">
|
<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_type" 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_type" 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="Abstain"
|
||||||
|
class="btn btn-secondary">Abstain</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user