This commit is contained in:
despiegk 2025-04-22 10:39:29 +04:00
parent 15b05cb599
commit 34594b95fa
10 changed files with 208 additions and 299 deletions

View File

@ -2,6 +2,7 @@ use actix_web::{web, HttpResponse, Responder, Result, http::header, cookie::Cook
use actix_session::Session;
use tera::Tera;
use crate::models::user::{User, LoginCredentials, RegistrationData, UserRole};
use crate::utils::render_template;
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
use serde::{Deserialize, Serialize};
use chrono::{Utc, Duration};
@ -91,13 +92,7 @@ impl AuthController {
let mut ctx = tera::Context::new();
ctx.insert("active_page", "login");
let rendered = tmpl.render("auth/login.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "auth/login.html", &ctx)
}
/// Handles user login
@ -146,13 +141,7 @@ impl AuthController {
let mut ctx = tera::Context::new();
ctx.insert("active_page", "register");
let rendered = tmpl.render("auth/register.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "auth/register.html", &ctx)
}
/// Handles user registration

View File

@ -6,7 +6,7 @@ use tera::Tera;
use serde_json::Value;
use crate::models::{CalendarEvent, CalendarViewMode};
use crate::utils::RedisCalendarService;
use crate::utils::{RedisCalendarService, render_template};
/// Controller for handling calendar-related routes
pub struct CalendarController;
@ -215,13 +215,7 @@ impl CalendarController {
},
}
let rendered = tmpl.render("calendar/index.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "calendar/index.html", &ctx)
}
/// Handles the new event page route
@ -234,13 +228,7 @@ impl CalendarController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("calendar/new_event.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "calendar/new_event.html", &ctx)
}
/// Handles the create event route
@ -298,13 +286,9 @@ impl CalendarController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("calendar/new_event.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
let result = render_template(&tmpl, "calendar/new_event.html", &ctx)?;
Ok(HttpResponse::InternalServerError().content_type("text/html").body(rendered))
Ok(HttpResponse::InternalServerError().content_type("text/html").body(result.into_body()))
}
}
}

View File

@ -4,6 +4,7 @@ use chrono::{Utc, Duration};
use serde::Deserialize;
use crate::models::contract::{Contract, ContractStatus, ContractType, ContractStatistics, SignerStatus};
use crate::utils::render_template;
#[derive(Debug, Deserialize)]
pub struct ContractForm {
@ -62,15 +63,7 @@ impl ContractController {
context.insert("draft_contracts", &draft_contracts);
let rendered = tmpl.render("contracts/index.html", &context)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", context);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contracts/index.html", &context)
}
// Display the list of all contracts
@ -89,15 +82,7 @@ impl ContractController {
context.insert("contracts", &contracts_data);
context.insert("filter", &"all");
let rendered = tmpl.render("contracts/contracts.html", &context)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", context);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contracts/contracts.html", &context)
}
// Display the list of user's contracts
@ -115,15 +100,7 @@ impl ContractController {
context.insert("contracts", &contracts_data);
let rendered = tmpl.render("contracts/my_contracts.html", &context)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", context);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contracts/my_contracts.html", &context)
}
// Display a specific contract
@ -146,15 +123,7 @@ impl ContractController {
context.insert("contract", &contract_json);
context.insert("user_has_signed", &false); // Mock data
let rendered = tmpl.render("contracts/contract_detail.html", &context)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", context);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contracts/contract_detail.html", &context)
},
None => {
Ok(HttpResponse::NotFound().finish())
@ -180,26 +149,18 @@ impl ContractController {
context.insert("contract_types", &contract_types);
let rendered = tmpl.render("contracts/create_contract.html", &context)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", context);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contracts/create_contract.html", &context)
}
// Process the create contract form
pub async fn create(
tmpl: web::Data<Tera>,
form: web::Form<ContractForm>,
_tmpl: web::Data<Tera>,
_form: web::Form<ContractForm>,
) -> Result<HttpResponse> {
// In a real application, we would save the contract to the database
// For now, we'll just redirect to the contracts list
Ok(HttpResponse::Found().header("Location", "/contracts").finish())
Ok(HttpResponse::Found().append_header(("Location", "/contracts")).finish())
}
// Helper method to convert Contract to a JSON object for templates

View File

@ -3,10 +3,10 @@ use actix_session::Session;
use chrono::{Utc, Duration};
use serde::Deserialize;
use tera::Tera;
use std::error::Error; // Add this line
use crate::models::flow::{Flow, FlowStatus, FlowType, FlowStatistics, FlowStep, StepStatus, FlowLog};
use crate::controllers::auth::Claims;
use crate::utils::render_template;
pub struct FlowController;
@ -25,15 +25,7 @@ impl FlowController {
ctx.insert("active_flows", &flows.iter().filter(|f| f.status == FlowStatus::InProgress).collect::<Vec<_>>());
ctx.insert("stuck_flows", &flows.iter().filter(|f| f.status == FlowStatus::Stuck).collect::<Vec<_>>());
let rendered = tmpl.render("flows/index.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "flows/index.html", &ctx)
}
/// Renders the flows list page
@ -46,15 +38,7 @@ impl FlowController {
ctx.insert("user", &user);
ctx.insert("flows", &flows);
let rendered = tmpl.render("flows/flows.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "flows/flows.html", &ctx)
}
/// Renders the flow detail page
@ -76,29 +60,20 @@ impl FlowController {
ctx.insert("user", &user);
ctx.insert("flow", flow);
let rendered = tmpl.render("flows/flow_detail.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "flows/flow_detail.html", &ctx)
} else {
let mut ctx = tera::Context::new();
ctx.insert("active_page", "flows");
ctx.insert("error", "Flow not found");
let rendered = tmpl.render("error.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::NotFound().content_type("text/html").body(rendered))
// For the error page, we'll use a special case to set the status code to 404
match tmpl.render("error.html", &ctx) {
Ok(content) => Ok(HttpResponse::NotFound().content_type("text/html").body(content)),
Err(e) => {
log::error!("Error rendering error template: {}", e);
Err(actix_web::error::ErrorInternalServerError(format!("Error: {}", e)))
}
}
}
}
@ -110,21 +85,13 @@ impl FlowController {
ctx.insert("active_page", "flows");
ctx.insert("user", &user);
let rendered = tmpl.render("flows/create_flow.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "flows/create_flow.html", &ctx)
}
/// Handles the create flow form submission
pub async fn create_flow(
_form: web::Form<FlowForm>,
session: Session
_session: Session
) -> impl Responder {
// In a real application, we would create a new flow here
// For now, just redirect to the flows list
@ -149,15 +116,7 @@ impl FlowController {
ctx.insert("user", &user);
ctx.insert("flows", &my_flows);
let rendered = tmpl.render("flows/my_flows.html", &ctx)
.map_err(|e| {
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "flows/my_flows.html", &ctx)
} else {
Ok(HttpResponse::Found()
.append_header(("Location", "/login"))

View File

@ -5,6 +5,7 @@ use serde_json::Value;
use serde::{Deserialize, Serialize};
use chrono::{Utc, Duration};
use crate::models::governance::{Proposal, Vote, ProposalStatus, VoteType, VotingResults};
use crate::utils::render_template;
/// Controller for handling governance-related routes
pub struct GovernanceController;
@ -35,13 +36,7 @@ impl GovernanceController {
let stats = Self::get_mock_statistics();
ctx.insert("stats", &stats);
let rendered = tmpl.render("governance/index.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/index.html", &ctx)
}
/// Handles the proposal list page route
@ -59,13 +54,7 @@ impl GovernanceController {
let proposals = Self::get_mock_proposals();
ctx.insert("proposals", &proposals);
let rendered = tmpl.render("governance/proposals.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/proposals.html", &ctx)
}
/// Handles the proposal detail page route
@ -96,23 +85,18 @@ impl GovernanceController {
let results = Self::get_mock_voting_results(&proposal_id);
ctx.insert("results", &results);
let rendered = tmpl.render("governance/proposal_detail.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/proposal_detail.html", &ctx)
} else {
// Proposal not found
ctx.insert("error", "Proposal not found");
let rendered = tmpl.render("error.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::NotFound().content_type("text/html").body(rendered))
// For the error page, we'll use a special case to set the status code to 404
match tmpl.render("error.html", &ctx) {
Ok(content) => Ok(HttpResponse::NotFound().content_type("text/html").body(content)),
Err(e) => {
eprintln!("Error rendering error template: {}", e);
Err(actix_web::error::ErrorInternalServerError(format!("Error: {}", e)))
}
}
}
}
@ -130,18 +114,12 @@ impl GovernanceController {
return Ok(HttpResponse::Found().append_header(("Location", "/login")).finish());
}
let rendered = tmpl.render("governance/create_proposal.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/create_proposal.html", &ctx)
}
/// Handles the submission of a new proposal
pub async fn submit_proposal(
form: web::Form<ProposalForm>,
_form: web::Form<ProposalForm>,
tmpl: web::Data<Tera>,
session: Session
) -> Result<impl Responder> {
@ -166,19 +144,13 @@ impl GovernanceController {
let proposals = Self::get_mock_proposals();
ctx.insert("proposals", &proposals);
let rendered = tmpl.render("governance/proposals.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/proposals.html", &ctx)
}
/// Handles the submission of a vote on a proposal
pub async fn submit_vote(
path: web::Path<String>,
form: web::Form<VoteForm>,
_form: web::Form<VoteForm>,
tmpl: web::Data<Tera>,
session: Session
) -> Result<impl Responder> {
@ -211,23 +183,18 @@ impl GovernanceController {
let results = Self::get_mock_voting_results(&proposal_id);
ctx.insert("results", &results);
let rendered = tmpl.render("governance/proposal_detail.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/proposal_detail.html", &ctx)
} else {
// Proposal not found
ctx.insert("error", "Proposal not found");
let rendered = tmpl.render("error.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::NotFound().content_type("text/html").body(rendered))
// For the error page, we'll use a special case to set the status code to 404
match tmpl.render("error.html", &ctx) {
Ok(content) => Ok(HttpResponse::NotFound().content_type("text/html").body(content)),
Err(e) => {
eprintln!("Error rendering error template: {}", e);
Err(actix_web::error::ErrorInternalServerError(format!("Error: {}", e)))
}
}
}
}
@ -245,13 +212,7 @@ impl GovernanceController {
let votes = Self::get_mock_votes_for_user(1); // Assuming user ID 1 for mock data
ctx.insert("votes", &votes);
let rendered = tmpl.render("governance/my_votes.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "governance/my_votes.html", &ctx)
} else {
// Redirect to login if not logged in
Ok(HttpResponse::Found().append_header(("Location", "/login")).finish())

View File

@ -1,8 +1,10 @@
use actix_web::{web, HttpResponse, Responder, Result};
use actix_web::{web, Responder, Result};
use actix_session::Session;
use tera::Tera;
use serde_json::Value;
use crate::utils::render_template;
/// Controller for handling home-related routes
pub struct HomeController;
@ -24,13 +26,7 @@ impl HomeController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("editor.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "editor.html", &ctx)
}
/// Handles the home page route
@ -43,13 +39,7 @@ impl HomeController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("index.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "index.html", &ctx)
}
/// Handles the about page route
@ -62,13 +52,7 @@ impl HomeController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("about.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "about.html", &ctx)
}
/// Handles the contact page route
@ -81,13 +65,7 @@ impl HomeController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("contact.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contact.html", &ctx)
}
/// Handles form submissions from the contact page
@ -112,13 +90,7 @@ impl HomeController {
ctx.insert("user", &user);
}
let rendered = tmpl.render("contact.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "contact.html", &ctx)
}
}

View File

@ -4,6 +4,7 @@ use tera::Tera;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::models::{User, Ticket, TicketComment, TicketStatus, TicketPriority};
use crate::utils::render_template;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
@ -131,13 +132,7 @@ impl TicketController {
]);
// Render the template
let rendered = tmpl.render("tickets/list.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "tickets/list.html", &ctx)
}
/// Shows the form for creating a new ticket
@ -172,13 +167,7 @@ impl TicketController {
]);
// Render the template
let rendered = tmpl.render("tickets/new.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "tickets/new.html", &ctx)
}
/// Creates a new ticket
@ -285,13 +274,7 @@ impl TicketController {
]);
// Render the template
let rendered = tmpl.render("tickets/show.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "tickets/show.html", &ctx)
}
/// Adds a comment to a ticket
@ -443,12 +426,6 @@ impl TicketController {
ctx.insert("my_tickets", &true);
// Render the template
let rendered = tmpl.render("tickets/list.html", &ctx)
.map_err(|e| {
eprintln!("Template rendering error: {}", e);
actix_web::error::ErrorInternalServerError("Template rendering error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(rendered))
render_template(&tmpl, "tickets/list.html", &ctx)
}
}

View File

@ -1,5 +1,6 @@
use actix_web::{error, Error, HttpResponse};
use chrono::{DateTime, Utc};
use tera::{self, Function, Result, Value};
use tera::{self, Context, Function, Tera, Value};
// Export modules
pub mod redis_service;
@ -7,6 +8,22 @@ pub mod redis_service;
// Re-export for easier imports
pub use redis_service::RedisCalendarService;
/// Error type for template rendering
#[derive(Debug)]
pub struct TemplateError {
pub message: String,
pub details: String,
pub location: String,
}
impl std::fmt::Display for TemplateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Template error in {}: {}", self.location, self.message)
}
}
impl std::error::Error for TemplateError {}
/// Registers custom Tera functions
pub fn register_tera_functions(tera: &mut tera::Tera) {
tera.register_function("now", NowFunction);
@ -18,7 +35,7 @@ pub fn register_tera_functions(tera: &mut tera::Tera) {
pub struct NowFunction;
impl Function for NowFunction {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> Result<Value> {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> tera::Result<Value> {
let format = match args.get("format") {
Some(val) => match val.as_str() {
Some(s) => s,
@ -43,7 +60,7 @@ impl Function for NowFunction {
pub struct FormatDateFunction;
impl Function for FormatDateFunction {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> Result<Value> {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> tera::Result<Value> {
let timestamp = match args.get("timestamp") {
Some(val) => match val.as_i64() {
Some(ts) => ts,
@ -96,6 +113,50 @@ pub fn truncate_string(s: &str, max_length: usize) -> String {
}
}
/// Renders a template with error handling
///
/// This function attempts to render a template and handles any errors by rendering
/// the error template with detailed error information.
pub fn render_template(
tmpl: &Tera,
template_name: &str,
ctx: &Context,
) -> Result<HttpResponse, Error> {
match tmpl.render(template_name, ctx) {
Ok(content) => Ok(HttpResponse::Ok().content_type("text/html").body(content)),
Err(e) => {
// Log the error
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
// Create a context for the error template
let mut error_ctx = Context::new();
error_ctx.insert("error", &format!("Template rendering error: {}", e));
error_ctx.insert("error_details", &format!("{:?}", e));
error_ctx.insert("error_location", &template_name);
// Try to render the error template
match tmpl.render("error.html", &error_ctx) {
Ok(error_page) => {
// Return the error page with a 500 status code
Ok(HttpResponse::InternalServerError()
.content_type("text/html")
.body(error_page))
}
Err(render_err) => {
// If we can't render the error template, log it and return a basic error
log::error!("Error rendering error template: {}", render_err);
Err(error::ErrorInternalServerError(format!(
"Template rendering error: {}. Failed to render error page: {}",
e, render_err
)))
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -7,18 +7,44 @@
<div class="col-md-8">
<div class="card border-danger">
<div class="card-header bg-danger text-white">
<h4 class="mb-0"><i class="bi bi-exclamation-triangle-fill me-2"></i>Error</h4>
<h4 class="mb-0"><i class="bi bi-exclamation-triangle-fill me-2"></i>Template Rendering Error</h4>
</div>
<div class="card-body">
<h5 class="card-title">Something went wrong</h5>
<p class="card-text">{{ error }}</p>
<h5 class="card-title">Something went wrong while rendering the template</h5>
<div class="alert alert-danger">
<p class="mb-2"><strong>Error Message:</strong></p>
<pre class="p-3 bg-light border rounded"><code>{{ error | default(value="Unknown error") }}</code></pre>
</div>
{% if error_details is defined and error_details %}
<div class="mt-3">
<p class="mb-2"><strong>Error Details:</strong></p>
<pre class="p-3 bg-light border rounded"><code>{{ error_details }}</code></pre>
</div>
{% endif %}
{% if error_location is defined and error_location %}
<div class="mt-3">
<p class="mb-2"><strong>Error Location:</strong></p>
<p>{{ error_location }}</p>
</div>
{% endif %}
<div class="alert alert-info mt-3">
<p class="mb-0"><i class="bi bi-info-circle me-2"></i>This error is visible only in development mode. In production, a generic error page will be shown.</p>
</div>
<div class="mt-4">
<a href="/" class="btn btn-primary me-2">
<i class="bi bi-house-door me-1"></i>Go to Home
</a>
<a href="javascript:history.back()" class="btn btn-outline-secondary">
<a href="javascript:history.back()" class="btn btn-outline-secondary me-2">
<i class="bi bi-arrow-left me-1"></i>Go Back
</a>
<button onclick="window.location.reload()" class="btn btn-outline-primary">
<i class="bi bi-arrow-clockwise me-1"></i>Reload Page
</button>
</div>
</div>
</div>

View File

@ -1,14 +1,14 @@
{% extends "base.html" %}
{% block title %}All Flows{% endblock %}
{% block title %}Freezone Workflows{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/flows">Flows</a></li>
<li class="breadcrumb-item active" aria-current="page">All Flows</li>
<li class="breadcrumb-item"><a href="/flows">Workflows</a></li>
<li class="breadcrumb-item active" aria-current="page">Freezone Workflows</li>
</ol>
</nav>
</div>
@ -16,11 +16,11 @@
<div class="row mb-4">
<div class="col-md-8">
<h1 class="display-5 mb-0">All Flows</h1>
<h1 class="display-5 mb-0">Freezone Workflow Management</h1>
</div>
<div class="col-md-4 text-md-end">
<a href="/flows/create" class="btn btn-primary">
<i class="bi bi-plus-circle me-1"></i> Create New Flow
<i class="bi bi-plus-circle me-1"></i> Create New Workflow
</a>
</div>
</div>
@ -31,7 +31,7 @@
<div class="card">
<div class="card-body">
<form class="row g-3" action="/flows/list" method="get">
<div class="col-md-4">
<div class="col-md-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="all" selected>All</option>
@ -41,19 +41,34 @@
<option value="cancelled">Cancelled</option>
</select>
</div>
<div class="col-md-4">
<label for="type" class="form-label">Type</label>
<!-- Freezone filter - for UI demonstration only -->
<div class="col-md-3">
<label for="freezone" class="form-label">Freezone</label>
<select class="form-select" id="freezone" name="freezone" disabled>
<option value="all" selected>All Freezones</option>
<option value="dubai_multi_commodities_centre">DMCC</option>
<option value="dubai_international_financial_centre">DIFC</option>
<option value="jebel_ali_free_zone">JAFZA</option>
<option value="dubai_silicon_oasis">DSO</option>
<option value="dubai_internet_city">DIC</option>
<option value="dubai_media_city">DMC</option>
<option value="abu_dhabi_global_market">ADGM</option>
</select>
<div class="form-text">Coming soon</div>
</div>
<div class="col-md-3">
<label for="type" class="form-label">Workflow Type</label>
<select class="form-select" id="type" name="type">
<option value="all" selected>All</option>
<option value="company_registration">Company Registration</option>
<option value="user_onboarding">User Onboarding</option>
<option value="service_activation">Service Activation</option>
<option value="company_registration">Company Incorporation</option>
<option value="user_onboarding">KYC Verification</option>
<option value="service_activation">License Activation</option>
<option value="payment_processing">Payment Processing</option>
</select>
</div>
<div class="col-md-4">
<div class="col-md-3">
<label for="search" class="form-label">Search</label>
<input type="text" class="form-control" id="search" name="search" placeholder="Search flows...">
<input type="text" class="form-control" id="search" name="search" placeholder="Search workflows...">
</div>
<div class="col-12 text-end">
<button type="submit" class="btn btn-primary">
@ -79,14 +94,14 @@
<table class="table table-hover">
<thead>
<tr>
<th>Flow Name</th>
<th>Workflow Name</th>
<th>Type</th>
<th>Status</th>
<th>Owner</th>
<th>Assignee</th>
<th>Progress</th>
<th>Created</th>
<th>Updated</th>
<th>Current Step</th>
<th>Initiated</th>
<th>Last Updated</th>
<th>Current Stage</th>
<th>Actions</th>
</tr>
</thead>
@ -120,21 +135,21 @@
{{ current.name }}
{% else %}
{% if flow.status == 'Completed' %}
All steps completed
<span class="text-success">All stages completed</span>
{% elif flow.status == 'Cancelled' %}
Flow cancelled
<span class="text-secondary">Workflow cancelled</span>
{% else %}
No active step
<span class="text-muted">No active stage</span>
{% endif %}
{% endif %}
</td>
<td>
<div class="btn-group">
<a href="/flows/{{ flow.id }}" class="btn btn-sm btn-primary">
<a href="/flows/{{ flow.id }}" class="btn btn-sm btn-primary" title="View Details">
<i class="bi bi-eye"></i>
</a>
{% if flow.status == 'In Progress' %}
<a href="/flows/{{ flow.id }}#advance" class="btn btn-sm btn-success">
<a href="/flows/{{ flow.id }}#advance" class="btn btn-sm btn-success" title="Advance to Next Stage">
<i class="bi bi-arrow-right"></i>
</a>
{% endif %}
@ -146,7 +161,11 @@
</table>
</div>
{% else %}
<p class="text-center">No flows found matching your criteria.</p>
<div class="text-center py-4">
<i class="bi bi-search display-1 text-muted"></i>
<p class="lead mt-3">No workflows found matching your criteria.</p>
<p class="text-muted">Try adjusting your filters or create a new workflow.</p>
</div>
{% endif %}
</div>
</div>