From 2fd74defab63ebd3bd7959b847f598e0467b4392 Mon Sep 17 00:00:00 2001 From: timurgordon Date: Fri, 16 May 2025 14:07:20 +0300 Subject: [PATCH] update governance ui --- actix_mvc_app/src/controllers/governance.rs | 153 ++++++++++---- actix_mvc_app/src/routes/mod.rs | 4 +- actix_mvc_app/src/views/governance/index.html | 196 +++++++++--------- .../src/views/governance/my_votes.html | 17 +- .../src/views/governance/proposals.html | 22 +- 5 files changed, 238 insertions(+), 154 deletions(-) diff --git a/actix_mvc_app/src/controllers/governance.rs b/actix_mvc_app/src/controllers/governance.rs index eeadfde..b485c00 100644 --- a/actix_mvc_app/src/controllers/governance.rs +++ b/actix_mvc_app/src/controllers/governance.rs @@ -12,9 +12,24 @@ pub struct GovernanceController; impl GovernanceController { /// Helper function to get user from session + /// For testing purposes, this will always return a mock user fn get_user_from_session(session: &Session) -> Option { - session.get::("user").ok().flatten().and_then(|user_json| { + // Try to get user from session first + let session_user = session.get::("user").ok().flatten().and_then(|user_json| { serde_json::from_str(&user_json).ok() + }); + + // If user is not in session, return a mock user for testing + session_user.or_else(|| { + // Create a mock user + let mock_user = serde_json::json!({ + "id": 1, + "username": "test_user", + "email": "test@example.com", + "name": "Test User", + "role": "member" + }); + Some(mock_user) }) } @@ -23,14 +38,32 @@ impl GovernanceController { let mut ctx = tera::Context::new(); ctx.insert("active_page", "governance"); - // Add user to context if available - if let Some(user) = Self::get_user_from_session(&session) { - ctx.insert("user", &user); - } + // Add user to context (will always be available with our mock user) + let user = Self::get_user_from_session(&session).unwrap(); + ctx.insert("user", &user); // Get mock proposals for the dashboard - let proposals = Self::get_mock_proposals(); - ctx.insert("proposals", &proposals); + let mut proposals = Self::get_mock_proposals(); + + // Filter for active proposals only + let active_proposals: Vec = proposals.into_iter() + .filter(|p| p.status == ProposalStatus::Active) + .collect(); + + // Sort active proposals by voting end date (ascending) + let mut sorted_active_proposals = active_proposals.clone(); + sorted_active_proposals.sort_by(|a, b| a.voting_ends_at.cmp(&b.voting_ends_at)); + + ctx.insert("proposals", &sorted_active_proposals); + + // Get the nearest deadline proposal for the voting pane + if let Some(nearest_proposal) = sorted_active_proposals.first() { + ctx.insert("nearest_proposal", nearest_proposal); + } + + // Get recent activity for the timeline + let recent_activity = Self::get_mock_recent_activity(); + ctx.insert("recent_activity", &recent_activity); // Get some statistics let stats = Self::get_mock_statistics(); @@ -106,13 +139,9 @@ impl GovernanceController { ctx.insert("active_page", "governance"); ctx.insert("active_tab", "create"); - // Add user to context if available - if let Some(user) = Self::get_user_from_session(&session) { - ctx.insert("user", &user); - } else { - // Redirect to login if not logged in - return Ok(HttpResponse::Found().append_header(("Location", "/login")).finish()); - } + // Add user to context (will always be available with our mock user) + let user = Self::get_user_from_session(&session).unwrap(); + ctx.insert("user", &user); render_template(&tmpl, "governance/create_proposal.html", &ctx) } @@ -123,18 +152,12 @@ impl GovernanceController { tmpl: web::Data, session: Session ) -> Result { - // Check if user is logged in - if Self::get_user_from_session(&session).is_none() { - return Ok(HttpResponse::Found().append_header(("Location", "/login")).finish()); - } - let mut ctx = tera::Context::new(); ctx.insert("active_page", "governance"); - // Add user to context if available - if let Some(user) = Self::get_user_from_session(&session) { - ctx.insert("user", &user); - } + // Add user to context (will always be available with our mock user) + let user = Self::get_user_from_session(&session).unwrap(); + ctx.insert("user", &user); // In a real application, we would save the proposal to a database // For now, we'll just redirect to the proposals page with a success message @@ -204,19 +227,77 @@ impl GovernanceController { ctx.insert("active_page", "governance"); ctx.insert("active_tab", "my_votes"); - // Add user to context if available - if let Some(user) = Self::get_user_from_session(&session) { - ctx.insert("user", &user); - - // Get mock votes for this user - let votes = Self::get_mock_votes_for_user(1); // Assuming user ID 1 for mock data - ctx.insert("votes", &votes); - - render_template(&tmpl, "governance/my_votes.html", &ctx) - } else { - // Redirect to login if not logged in - Ok(HttpResponse::Found().append_header(("Location", "/login")).finish()) - } + // Add user to context (will always be available with our mock user) + let user = Self::get_user_from_session(&session).unwrap(); + ctx.insert("user", &user); + + // Get mock votes for this user + let votes = Self::get_mock_votes_for_user(1); // Assuming user ID 1 for mock data + ctx.insert("votes", &votes); + + render_template(&tmpl, "governance/my_votes.html", &ctx) + } + + /// Generate mock recent activity data for the dashboard + fn get_mock_recent_activity() -> Vec { + vec![ + serde_json::json!({ + "type": "vote", + "user": "Sarah Johnson", + "proposal_id": "prop-001", + "proposal_title": "Community Garden Initiative", + "action": "voted Yes", + "timestamp": (Utc::now() - Duration::hours(2)).to_rfc3339(), + "icon": "bi-check-circle-fill text-success" + }), + serde_json::json!({ + "type": "comment", + "user": "Michael Chen", + "proposal_id": "prop-003", + "proposal_title": "Weekly Community Calls", + "action": "commented", + "comment": "I think this would greatly improve communication.", + "timestamp": (Utc::now() - Duration::hours(5)).to_rfc3339(), + "icon": "bi-chat-left-text-fill text-primary" + }), + serde_json::json!({ + "type": "vote", + "user": "Robert Callingham", + "proposal_id": "prop-005", + "proposal_title": "Security Audit Implementation", + "action": "voted Yes", + "timestamp": (Utc::now() - Duration::hours(8)).to_rfc3339(), + "icon": "bi-check-circle-fill text-success" + }), + serde_json::json!({ + "type": "proposal", + "user": "Emma Rodriguez", + "proposal_id": "prop-004", + "proposal_title": "Sustainability Roadmap", + "action": "created proposal", + "timestamp": (Utc::now() - Duration::hours(12)).to_rfc3339(), + "icon": "bi-file-earmark-text-fill text-info" + }), + serde_json::json!({ + "type": "vote", + "user": "David Kim", + "proposal_id": "prop-002", + "proposal_title": "Governance Framework Update", + "action": "voted No", + "timestamp": (Utc::now() - Duration::hours(16)).to_rfc3339(), + "icon": "bi-x-circle-fill text-danger" + }), + serde_json::json!({ + "type": "comment", + "user": "Lisa Wang", + "proposal_id": "prop-001", + "proposal_title": "Community Garden Initiative", + "action": "commented", + "comment": "I'd like to volunteer to help coordinate this effort.", + "timestamp": (Utc::now() - Duration::hours(24)).to_rfc3339(), + "icon": "bi-chat-left-text-fill text-primary" + }), + ] } // Mock data generation methods diff --git a/actix_mvc_app/src/routes/mod.rs b/actix_mvc_app/src/routes/mod.rs index 48cd571..b9810f6 100644 --- a/actix_mvc_app/src/routes/mod.rs +++ b/actix_mvc_app/src/routes/mod.rs @@ -65,8 +65,8 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { .route("/governance/proposals", web::get().to(GovernanceController::proposals)) .route("/governance/proposals/{id}", web::get().to(GovernanceController::proposal_detail)) .route("/governance/proposals/{id}/vote", web::post().to(GovernanceController::submit_vote)) - .route("/governance/create-proposal", web::get().to(GovernanceController::create_proposal_form)) - .route("/governance/create-proposal", web::post().to(GovernanceController::submit_proposal)) + .route("/governance/create", web::get().to(GovernanceController::create_proposal_form)) + .route("/governance/create", web::post().to(GovernanceController::submit_proposal)) .route("/governance/my-votes", web::get().to(GovernanceController::my_votes)) // Flow routes diff --git a/actix_mvc_app/src/views/governance/index.html b/actix_mvc_app/src/views/governance/index.html index 97e8fd8..6ca829e 100644 --- a/actix_mvc_app/src/views/governance/index.html +++ b/actix_mvc_app/src/views/governance/index.html @@ -3,51 +3,8 @@ {% block title %}Governance Dashboard{% endblock %} {% block content %} -
-
-

Governance Dashboard

-

Participate in the decision-making process by voting on proposals and creating new ones.

-
-
- - -
-
-
-
-
Total Proposals
-

{{ stats.total_proposals }}

-
-
-
-
-
-
-
Active Proposals
-

{{ stats.active_proposals }}

-
-
-
-
-
-
-
Total Votes
-

{{ stats.total_votes }}

-
-
-
-
-
-
-
Participation Rate
-

{{ stats.participation_rate }}%

-
-
-
-
- -
+
- -
-
-
+ +
+
+
+ +
About Governance
+

The governance system allows token holders to participate in decision-making processes by voting on proposals that affect the platform's future. Create proposals, cast votes, and help shape the direction of our decentralized ecosystem.

+ +
+
+
+ + +
+ +
+ {% if nearest_proposal is defined %} +
-
Active Proposals
- View All +
Urgent: Voting Closes Soon
+
+ Ends: {{ nearest_proposal.voting_ends_at | date(format="%Y-%m-%d") }} + View Full Proposal +
-
- - - - - - - - - - - - {% for proposal in proposals %} - {% if proposal.status == "Active" %} - - - - - - - - {% endif %} - {% endfor %} - -
TitleCreatorStatusVoting EndsActions
{{ proposal.title }}{{ proposal.creator_name }}{{ proposal.status }}{{ proposal.voting_ends_at | date(format="%Y-%m-%d") }} - View -
+

{{ nearest_proposal.title }}

+
Proposed by {{ nearest_proposal.creator_name }}
+ +
+

{{ nearest_proposal.description }}

+ +
+
65% Yes
+
35% No
+
+ +
+ 26 votes cast + Quorum: 75% reached +
+ +
+
Cast Your Vote
+
+
+ +
+
+ + + +
+
+
+
+
+ {% else %} +
+
+ +
No active proposals requiring votes
+

When new proposals are created, they will appear here for voting.

+ Create Proposal +
+
+ {% endif %} +
+ + +
+
+
+
Recent Activity
+
+
+
+ {% for activity in recent_activity %} +
+
+
+ +
+
+
+ {{ activity.user }} + {{ activity.timestamp | date(format="%H:%M") }} +
+

{{ activity.action }} on {{ activity.proposal_title }}

+ {% if activity.type == "comment" and activity.comment is defined %} +

"{{ activity.comment }}"

+ {% endif %} +
+
+
+ {% endfor %} +
+
+
@@ -113,7 +136,7 @@
-
Recent Proposals
+
Active Proposals (Ending Soon)
@@ -133,8 +156,8 @@ View Details
-
@@ -146,17 +169,4 @@
- - -
-
-
-
-

Have an idea to improve our platform?

-

Create a proposal and let the community vote on it.

- Create Proposal -
-
-
-
{% endblock %} diff --git a/actix_mvc_app/src/views/governance/my_votes.html b/actix_mvc_app/src/views/governance/my_votes.html index 11b9120..626d250 100644 --- a/actix_mvc_app/src/views/governance/my_votes.html +++ b/actix_mvc_app/src/views/governance/my_votes.html @@ -3,14 +3,6 @@ {% block title %}My Votes - Governance Dashboard{% endblock %} {% block content %} -
-
-
-

My Votes

-

View all proposals you have voted on.

-
-
-
@@ -52,7 +44,7 @@ - {% for vote, proposal in votes %} + {% for item in votes %}{% set vote = item.0 %}{% set proposal = item.1 %} {{ proposal.title }} @@ -96,7 +88,7 @@
Yes Votes

{% set yes_count = 0 %} - {% for vote, proposal in votes %} + {% for item in votes %}{% set vote = item.0 %}{% set proposal = item.1 %} {% if vote.vote_type == 'Yes' %} {% set yes_count = yes_count + 1 %} {% endif %} @@ -112,7 +104,7 @@

No Votes

{% set no_count = 0 %} - {% for vote, proposal in votes %} + {% for item in votes %}{% set vote = item.0 %}{% set proposal = item.1 %} {% if vote.vote_type == 'No' %} {% set no_count = no_count + 1 %} {% endif %} @@ -128,7 +120,7 @@

Abstain Votes

{% set abstain_count = 0 %} - {% for vote, proposal in votes %} + {% for item in votes %}{% set vote = item.0 %}{% set proposal = item.1 %} {% if vote.vote_type == 'Abstain' %} {% set abstain_count = abstain_count + 1 %} {% endif %} @@ -140,5 +132,4 @@

{% endif %} -
{% endblock %} diff --git a/actix_mvc_app/src/views/governance/proposals.html b/actix_mvc_app/src/views/governance/proposals.html index d3e4e51..36fa089 100644 --- a/actix_mvc_app/src/views/governance/proposals.html +++ b/actix_mvc_app/src/views/governance/proposals.html @@ -3,14 +3,6 @@ {% block title %}Proposals - Governance Dashboard{% endblock %} {% block content %} -
-
-
-

Governance Proposals

-

View and vote on all proposals in the system.

-
-
- {% if success %}
@@ -24,7 +16,7 @@ {% endif %} -
+
+
+
+ +
About Proposals
+

Proposals are formal requests for changes to the platform that require community approval. Each proposal includes a detailed description, implementation plan, and voting period. Browse the list below to see all active and past proposals.

+ +
+
+
@@ -124,5 +127,4 @@
-
{% endblock %}