refactor: moving to my own template, using fomantic instead of uikit. removing uikit, and some other removed stuff

This commit is contained in:
2024-01-15 21:55:28 +01:00
parent b5f9be7e5a
commit 8b42c94aa6
29 changed files with 564 additions and 37519 deletions

View File

@@ -1,5 +1,7 @@
pub use config::AdminModelConfig;
pub use dto::AdminApp;
pub use dto::AdminModel;
pub use repository::{AdminRepository, RepositoryList};
mod auth {
@@ -12,6 +14,14 @@ mod auth {
struct AdminActionLog {}
}
mod config {
// user uses this configuration object to register another model.
pub struct AdminModelConfig {
pub name: String,
pub app_key: String,
}
}
mod dto {
use serde::{Deserialize, Serialize};
@@ -26,12 +36,6 @@ mod dto {
pub add_url: Option<String>,
}
impl AdminModel {
pub fn object_name(&self) -> &'static str {
"hi there"
}
}
#[derive(Deserialize, Serialize)]
pub struct AdminApp {
pub key: String,
@@ -40,10 +44,59 @@ mod dto {
pub app_url: String,
pub models: Vec<AdminModel>,
}
}
impl AdminApp {
pub fn app_label(&self) -> &'static str {
"gogo"
pub mod repository {
use serde::{Serialize, Serializer};
use serde_json::Value;
use std::vec::IntoIter;
pub enum RepositoryList {
Empty,
List {
values: Vec<Value>,
},
Page {
values: Vec<Value>,
offset: usize,
total: usize,
},
Stream {
values: Vec<Value>,
next_index: Option<String>,
},
}
impl IntoIterator for RepositoryList {
type Item = Value;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
match self {
RepositoryList::Empty => vec![].into_iter(),
RepositoryList::List { values } => values.into_iter(),
RepositoryList::Page { values, .. } => values.into_iter(),
RepositoryList::Stream { values, .. } => values.into_iter(),
}
}
}
impl Serialize for RepositoryList {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
RepositoryList::Empty => serializer.serialize_unit(),
RepositoryList::List { values }
| RepositoryList::Page { values, .. }
| RepositoryList::Stream { values, .. } => values.serialize(serializer),
}
}
}
pub trait AdminRepository: Send + Sync {
fn get_item(&self, id: usize) -> Option<Value>;
fn get_list(&self) -> RepositoryList;
}
}

View File

@@ -1,6 +1,7 @@
// implementation of static repository
use super::state::{config::AdminModelConfig, AdminRegistry, AdminRepository};
use super::domain::{AdminModelConfig, AdminRepository, RepositoryList};
use super::state::AdminRegistry;
use serde_json::{json, Value};
struct MyStaticRepository {}
@@ -13,11 +14,13 @@ impl AdminRepository for MyStaticRepository {
}))
}
fn get_list(&self) -> Value {
json!([
{"name": "Strange", "age": 150 },
{"name": "Adam", "age": 12}
])
fn get_list(&self) -> RepositoryList {
RepositoryList::List {
values: vec![
json!({"name": "Strange", "age": 150 }),
json!({"name": "Adam", "age": 12}),
],
}
}
}

View File

@@ -1,16 +1,9 @@
use crate::admin::domain::{AdminApp, AdminModel};
use axum::Router;
use serde_json::Value;
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
use std::collections::HashMap;
use std::sync::Arc;
pub type AdminState = Arc<AdminRegistry>;
pub trait AdminRepository: Send + Sync {
fn get_item(&self, id: usize) -> Option<Value>;
fn get_list(&self) -> Value;
}
// main registry.
pub struct AdminRegistry {
base_path: String,
@@ -41,7 +34,7 @@ impl AdminRegistry {
AdminApp {
name: key.to_owned(),
key: node.name.to_owned(),
app_url: key.to_owned(),
app_url: format!("/{}/app/{}", self.base_path, key.to_owned()),
models: my_models,
}
}
@@ -67,7 +60,7 @@ impl AdminRegistry {
key: internal_model.model_key.clone(),
name: internal_model.name.clone(),
admin_url: format!(
"{}/app/{}/model/{}",
"/{}/app/{}/model/{}",
self.base_path, internal_model.app_key, internal_model.model_key
),
view_only: false,
@@ -91,7 +84,7 @@ impl AdminRegistry {
Some(self.model_from_internal(internal_model))
}
fn register_model_config(&mut self, model: config::AdminModelConfig) -> Result<String, String> {
fn register_model_config(&mut self, model: AdminModelConfig) -> Result<String, String> {
let local_config = internal::AdminModel::from(model);
if local_config.model_key.is_empty() {
return Err("No model name".to_owned());
@@ -107,7 +100,7 @@ impl AdminRegistry {
pub fn register_model<R: AdminRepository + 'static>(
&mut self,
model: config::AdminModelConfig,
model: AdminModelConfig,
repository: R,
) -> Result<(), String> {
let model_key = self.register_model_config(model)?;
@@ -124,15 +117,10 @@ impl AdminRegistry {
}
}
pub mod config {
// user uses this configuration object to register another model.
pub struct AdminModelConfig {
pub name: String,
pub app_key: String,
}
}
mod internal {
// how the registry saves data internally.
use super::super::domain::AdminModelConfig;
#[derive(Clone)]
pub struct AdminApp {
pub key: String,
@@ -146,8 +134,8 @@ mod internal {
pub name: String,
}
impl From<super::config::AdminModelConfig> for AdminModel {
fn from(value: super::config::AdminModelConfig) -> Self {
impl From<AdminModelConfig> for AdminModel {
fn from(value: AdminModelConfig) -> Self {
AdminModel {
app_key: value.app_key,
model_key: slug::slugify(value.name.clone()),

View File

@@ -10,6 +10,8 @@ use crate::service::templates;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::domain::RepositoryList;
#[derive(Deserialize)]
pub struct Question {
question: String,
@@ -26,11 +28,12 @@ pub struct AdminRequest {
pub path: String,
}
#[derive(Deserialize, Serialize)]
#[derive(Serialize)]
pub struct AdminContext {
pub language_code: Option<String>,
pub language_bidi: Option<bool>,
pub user: Option<String>, // Todo: user type
pub admin_url: String,
pub site_url: Option<String>,
pub docsroot: Option<String>,
pub messages: Vec<String>, // Todo: message type
@@ -40,7 +43,7 @@ pub struct AdminContext {
pub request: AdminRequest,
pub available_apps: Vec<AdminApp>,
pub item_list: Option<Value>,
pub item_list: RepositoryList,
pub item: Option<Value>,
}
@@ -50,6 +53,7 @@ impl Default for AdminContext {
language_code: Some("en-us".to_string()), // Default language code
language_bidi: Some(false), // Default language bidi
user: None, //UserType::default(), // Assuming UserType has a Default impl
admin_url: "/admin".to_owned(),
site_url: None,
docsroot: None,
messages: Vec::new(), // Empty vector for messages
@@ -60,7 +64,7 @@ impl Default for AdminContext {
request: AdminRequest {
path: "".to_owned(),
},
item_list: None,
item_list: RepositoryList::Empty,
item: None,
}
}
@@ -88,7 +92,7 @@ pub async fn list_app(
templates: State<templates::Templates>,
Path(app_key): Path<String>,
) -> impl IntoResponse {
templates.render_html("admin/app_list.html", ())
templates.render_html("admin/app_list.jinja", ())
}
// List Items renders the entire list item page.
@@ -101,15 +105,17 @@ pub async fn list_item_collection(
let context = if let Ok(repo) = registry.get_repository(&model_key) {
// we should consider using Vec<Value> instead in get_list.
AdminContext {
item_list: Some(repo.get_list()),
available_apps: registry.get_apps(),
item_list: repo.get_list(),
..Default::default()
}
} else {
AdminContext {
available_apps: registry.get_apps(),
..Default::default()
}
};
templates.render_html("admin/items/item_list.html", context)
templates.render_html("admin/items/item_list.jinja", context)
}
// Items Action is a POST to an item list. By default these are actions, that work on a list of items as input.
@@ -125,7 +131,7 @@ pub async fn item_details(
templates: State<templates::Templates>,
Path((app_key, model_key, id)): Path<(String, String, String)>,
) -> impl IntoResponse {
templates.render_html("admin/items/item_detail.html", ())
templates.render_html("admin/items/item_detail.jinja", ())
}
// Item Action allows running an action on one single dataset.

View File

@@ -12,7 +12,7 @@ use axum::{
use dotenvy::dotenv;
use log::info;
use std::env;
use std::net::{Ipv4Addr, SocketAddr};
use std::net::SocketAddr;
use std::sync::Arc;
async fn home(templates: State<templates::Templates>) -> impl IntoResponse {
@@ -60,7 +60,7 @@ async fn main() {
.unwrap_or("3000".to_string())
.parse()
.expect("Port expected in APP_PORT");
// the listen_addr is the address we bind to.
// the listen_addr is the address we bind to. This might be multiple domains, like 0.0.0.0
let listen_addr = SocketAddr::from((app_host, app_port));
// the server addr is a concrete address the user can connect to.
let server_addr = if app_host.is_unspecified() {

View File

@@ -16,6 +16,7 @@ impl Templates {
let mut environment = Environment::new();
let template_path = "templates";
environment.set_loader(path_loader(&template_path));
environment.add_filter("none", none);
environment.add_filter("markdown", markdown);
environment.add_filter("yesno", filter_yesno);
environment.add_function("static", tpl_static);
@@ -78,9 +79,17 @@ fn tpl_translate(value: String) -> Value {
}
fn tpl_to_be_implemented(value: String) -> Value {
Value::from_safe_string("".into())
Value::from_safe_string("<!-- To Be Implemented --/>".into())
}
fn tpl_static(value: String) -> Value {
Value::from_safe_string("".into())
Value::from_safe_string(format!("/static/{}", value))
}
pub fn none(value: Value, other: Option<Value>) -> Value {
if value.is_undefined() || value.is_none() {
other.unwrap_or_else(|| Value::from(""))
} else {
value
}
}

View File

@@ -1,14 +1,12 @@
use std::sync::Arc;
use axum::extract::FromRef;
use crate::admin::state::AdminRegistry;
use crate::admin::state::AdminState;
use crate::service::templates;
#[derive(Clone)]
pub struct AppState {
pub templates: templates::Templates,
pub admin: Arc<AdminRegistry>,
pub admin: AdminState,
}
impl FromRef<AppState> for templates::Templates {
@@ -17,8 +15,8 @@ impl FromRef<AppState> for templates::Templates {
}
}
impl FromRef<AppState> for Arc<AdminRegistry> {
fn from_ref(app_state: &AppState) -> Arc<AdminRegistry> {
impl FromRef<AppState> for AdminState {
fn from_ref(app_state: &AppState) -> AdminState {
app_state.admin.clone()
}
}