improved htmlerror ergonomics

This commit is contained in:
Per Stark committed 2025-01-21 22:48:11 +01:00
1 parent c953dffe56
commit 1532ada09f
14 files changed
+126 -85

No files matched your search

+1 -1
View File
@@ -1,2 +1,2 @@
/*! tailwindcss v4.0.0-beta.9 | MIT License | https://tailwindcss.com */
@layer theme{:root{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-serif:ui-serif,Georgia,Cambria,"Times New Roman",Times,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(.971 .013 17.38);--color-red-100:oklch(.936 .032 17.717);--color-red-200:oklch(.885 .062 18.334);--color-red-300:oklch(.808 .114 19.571);--color-red-400:oklch(.704 .191 22.216);--color-red-500:oklch(.637 .237 25.331);--color-red-600:oklch(.577 .245 27.325);--color-red-700:oklch(.505 .213 27.518);--color-red-800:oklch(.444 .177 26.899);--color-red-900:oklch(.396 .141 25.723);--color-red-950:oklch(.258 .092 26.042);--color-orange-50:oklch(.98 .016 73.684);--color-orange-100:oklch(.954 .038 75.164);--color-orange-200:oklch(.901 .076 70.697);--color-orange-300:oklch(.837 .128 66.29);--color-orange-400:oklch(.75 .183 55.934);--color-orange-500:oklch(.705 .213 47.604);--color-orange-600:oklch(.646 .222 41.116);--color-orange-700:oklch(.553 .195 38.402);--color-orange-800:oklch(.47 .157 37.304);--color-orange-900:oklch(.408 .123 38.172);--color-orange-950:oklch(.266 .079 36.259);--color-amber-50:oklch(.987 .022 95.277);--color-amber-100:oklch(.962 .059 95.617);--color-amber-200:oklch(.924 .12 95.746);--color-amber-300:oklch(.879 .169 91.605);--color-amber-400:oklch(.828 .189 84.429);--color-amber-500:oklch(.769 .188 70.08);--color-amber-600:oklch(.666 .179 58.318);--color-amber-700:oklch(.555 .163 48.998);--color-amber-800:oklch(.473 .137 46.201);--color-amber-900:oklch(.414 .112 45.904);--color-amber-950:oklch(.279 .077 45.635);--color-yellow-50:oklch(.987 .026 102.212);--color-yellow-100:oklch(.973 .071 103.193);--color-yellow-200:oklch(.945 .129 101.54);--color-yellow-300:oklch(.905 .182 98.111);--color-yellow-400:oklch(.852 .199 91.936);--color-yellow-500:oklch(.795 .184 86.047);--color-yellow-600:oklch(.681 .162 75.834);--color-yellow-700:oklch(.554 .135 66.442);--color-yellow-800:oklch(.476 .114 61.907);--color-yellow-900:oklch(.421 .095 57.708);--color-yellow-950:oklch(.286 .066 53.813);--color-lime-50:oklch(.986 .031 120.757);--color-lime-100:oklch(.967 .067 122.328);--color-lime-200:oklch(.938 .127 124.321);--color-lime-300:oklch(.897 .196 126.665);--color-lime-400:oklch(.841 .238 128.85);--color-lime-500:oklch(.768 .233 130.85);--color-lime-600:oklch(.648 .2 131.684);--color-lime-700:oklch(.532 .157 131.589);--color-lime-800:oklch(.453 .124 130.933);--color-lime-900:oklch(.405 .101 131.063);--color-lime-950:oklch(.274 .072 132.109);--color-green-50:oklch(.982 .018 155.826);--color-green-100:oklch(.962 .044 156.743);--color-green-200:oklch(.925 .084 155.995);--color-green-300:oklch(.871 .15 154.449);--color-green-400:oklch(.792 .209 151.711);--color-green-500:oklch(.723 .219 149.579);--color-green-600:oklch(.627 .194 149.214);--color-green-700:oklch(.527 .154 150.069);--color-green-800:oklch(.448 .119 151.328);--color-green-900:oklch(.393 .095 152.535);--color-green-950:oklch(.266 .065 152.934);--color-emerald-50:oklch(.979 .021 166.113);--color-emerald-100:oklch(.95 .052 163.051);--color-emerald-200:oklch(.905 .093 164.15);--color-emerald-300:oklch(.845 .143 164.978);--color-emerald-400:oklch(.765 .177 163.223);--color-emerald-500:oklch(.696 .17 162.48);--color-emerald-600:oklch(.596 .145 163.225);--color-emerald-700:oklch(.508 .118 165.612);--color-emerald-800:oklch(.432 .095 166.913);--color-emerald-900:oklch(.378 .077 168.94);--color-emerald-950:oklch(.262 .051 172.552);--color-teal-50:oklch(.984 .014 180.72);--color-teal-100:oklch(.953 .051 180.801);--color-teal-200:oklch(.91 .096 180.426);--color-teal-300:oklch(.855 .138 181.071);--color-teal-400:oklch(.777 .152 181.912);--color-teal-500:oklch(.704 .14 182.503);--color-teal-600:oklch(.6 .118 184.704);--color-teal-700:oklch(.511 .096 186.391);--color-teal-800:oklch(.437 .078 188.216);--color-teal-900:oklch(.386 .063 188.416);--color-teal-950:oklch(.277 .046 192.524);--color-cyan-50:oklch(.984 .019 200.873);--color-cyan-100:oklch(.956 .045 203.388);--color-cyan-200:oklch(.917 .08 205.041);--color-cyan-300:oklch(.865 .127 207.078);--color-cyan-400:oklch(.789 .154 211.53);--color-cyan-500:oklch(.715 .143 215.221);--color-cyan-600:oklch(.609 .126 221.723);--color-cyan-700:oklch(.52 .105 223.128);--color-cyan-800:oklch(.45 .085 224.283);--color-cyan-900:oklch(.398 .07 227.392);--color-cyan-950:oklch(.302 .056 229.695);--color-sky-50:oklch(.977 .013 236.62);--color-sky-100:oklch(.951 .026 236.824);--color-sky-200:oklch(.901 .058 230.902);--color-sky-300:oklch(.828 .111 230.318);--color-sky-400:oklch(.746 .16 232.661);--color-sky-500:oklch(.685 .169 237.323);--color-sky-600:oklch(.588 .158 241.966);--color-sky-700:oklch(.5 .134 242.749);--color-sky-800:oklch(.443 .11 240.79);--color-sky-900:oklch(.391 .09 240.876);--color-sky-950:oklch(.293 .066 243.157);--color-blue-50:oklch(.97 .014 254.604);--color-blue-100:oklch(.932 .032 255Line truncated
@layer theme{:root{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-serif:ui-serif,Georgia,Cambria,"Times New Roman",Times,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(.971 .013 17.38);--color-red-100:oklch(.936 .032 17.717);--color-red-200:oklch(.885 .062 18.334);--color-red-300:oklch(.808 .114 19.571);--color-red-400:oklch(.704 .191 22.216);--color-red-500:oklch(.637 .237 25.331);--color-red-600:oklch(.577 .245 27.325);--color-red-700:oklch(.505 .213 27.518);--color-red-800:oklch(.444 .177 26.899);--color-red-900:oklch(.396 .141 25.723);--color-red-950:oklch(.258 .092 26.042);--color-orange-50:oklch(.98 .016 73.684);--color-orange-100:oklch(.954 .038 75.164);--color-orange-200:oklch(.901 .076 70.697);--color-orange-300:oklch(.837 .128 66.29);--color-orange-400:oklch(.75 .183 55.934);--color-orange-500:oklch(.705 .213 47.604);--color-orange-600:oklch(.646 .222 41.116);--color-orange-700:oklch(.553 .195 38.402);--color-orange-800:oklch(.47 .157 37.304);--color-orange-900:oklch(.408 .123 38.172);--color-orange-950:oklch(.266 .079 36.259);--color-amber-50:oklch(.987 .022 95.277);--color-amber-100:oklch(.962 .059 95.617);--color-amber-200:oklch(.924 .12 95.746);--color-amber-300:oklch(.879 .169 91.605);--color-amber-400:oklch(.828 .189 84.429);--color-amber-500:oklch(.769 .188 70.08);--color-amber-600:oklch(.666 .179 58.318);--color-amber-700:oklch(.555 .163 48.998);--color-amber-800:oklch(.473 .137 46.201);--color-amber-900:oklch(.414 .112 45.904);--color-amber-950:oklch(.279 .077 45.635);--color-yellow-50:oklch(.987 .026 102.212);--color-yellow-100:oklch(.973 .071 103.193);--color-yellow-200:oklch(.945 .129 101.54);--color-yellow-300:oklch(.905 .182 98.111);--color-yellow-400:oklch(.852 .199 91.936);--color-yellow-500:oklch(.795 .184 86.047);--color-yellow-600:oklch(.681 .162 75.834);--color-yellow-700:oklch(.554 .135 66.442);--color-yellow-800:oklch(.476 .114 61.907);--color-yellow-900:oklch(.421 .095 57.708);--color-yellow-950:oklch(.286 .066 53.813);--color-lime-50:oklch(.986 .031 120.757);--color-lime-100:oklch(.967 .067 122.328);--color-lime-200:oklch(.938 .127 124.321);--color-lime-300:oklch(.897 .196 126.665);--color-lime-400:oklch(.841 .238 128.85);--color-lime-500:oklch(.768 .233 130.85);--color-lime-600:oklch(.648 .2 131.684);--color-lime-700:oklch(.532 .157 131.589);--color-lime-800:oklch(.453 .124 130.933);--color-lime-900:oklch(.405 .101 131.063);--color-lime-950:oklch(.274 .072 132.109);--color-green-50:oklch(.982 .018 155.826);--color-green-100:oklch(.962 .044 156.743);--color-green-200:oklch(.925 .084 155.995);--color-green-300:oklch(.871 .15 154.449);--color-green-400:oklch(.792 .209 151.711);--color-green-500:oklch(.723 .219 149.579);--color-green-600:oklch(.627 .194 149.214);--color-green-700:oklch(.527 .154 150.069);--color-green-800:oklch(.448 .119 151.328);--color-green-900:oklch(.393 .095 152.535);--color-green-950:oklch(.266 .065 152.934);--color-emerald-50:oklch(.979 .021 166.113);--color-emerald-100:oklch(.95 .052 163.051);--color-emerald-200:oklch(.905 .093 164.15);--color-emerald-300:oklch(.845 .143 164.978);--color-emerald-400:oklch(.765 .177 163.223);--color-emerald-500:oklch(.696 .17 162.48);--color-emerald-600:oklch(.596 .145 163.225);--color-emerald-700:oklch(.508 .118 165.612);--color-emerald-800:oklch(.432 .095 166.913);--color-emerald-900:oklch(.378 .077 168.94);--color-emerald-950:oklch(.262 .051 172.552);--color-teal-50:oklch(.984 .014 180.72);--color-teal-100:oklch(.953 .051 180.801);--color-teal-200:oklch(.91 .096 180.426);--color-teal-300:oklch(.855 .138 181.071);--color-teal-400:oklch(.777 .152 181.912);--color-teal-500:oklch(.704 .14 182.503);--color-teal-600:oklch(.6 .118 184.704);--color-teal-700:oklch(.511 .096 186.391);--color-teal-800:oklch(.437 .078 188.216);--color-teal-900:oklch(.386 .063 188.416);--color-teal-950:oklch(.277 .046 192.524);--color-cyan-50:oklch(.984 .019 200.873);--color-cyan-100:oklch(.956 .045 203.388);--color-cyan-200:oklch(.917 .08 205.041);--color-cyan-300:oklch(.865 .127 207.078);--color-cyan-400:oklch(.789 .154 211.53);--color-cyan-500:oklch(.715 .143 215.221);--color-cyan-600:oklch(.609 .126 221.723);--color-cyan-700:oklch(.52 .105 223.128);--color-cyan-800:oklch(.45 .085 224.283);--color-cyan-900:oklch(.398 .07 227.392);--color-cyan-950:oklch(.302 .056 229.695);--color-sky-50:oklch(.977 .013 236.62);--color-sky-100:oklch(.951 .026 236.824);--color-sky-200:oklch(.901 .058 230.902);--color-sky-300:oklch(.828 .111 230.318);--color-sky-400:oklch(.746 .16 232.661);--color-sky-500:oklch(.685 .169 237.323);--color-sky-600:oklch(.588 .158 241.966);--color-sky-700:oklch(.5 .134 242.749);--color-sky-800:oklch(.443 .11 240.79);--color-sky-900:oklch(.391 .09 240.876);--color-sky-950:oklch(.293 .066 243.157);--color-blue-50:oklch(.97 .014 254.604);--color-blue-100:oklch(.932 .032 255Line truncated
+2 -2
View File
@@ -30,7 +30,7 @@ use zettle_db::{
documentation::index::show_documentation_index,
gdpr::{accept_gdpr, deny_gdpr},
index::index_handler,
ingress::{process_ingress_form, show_ingress_form},
ingress_form::{process_ingress_form, show_ingress_form},
ingress_tasks::{delete_task, show_queue_tasks},
privacy_policy::show_privacy_policy,
search_result::search_result_handler,
@@ -175,7 +175,7 @@ fn html_routes(
.route("/signout", get(sign_out_user))
.route("/signin", get(show_signin_form).post(authenticate_user))
.route(
"/ingress",
"/ingress-form",
get(show_ingress_form).post(process_ingress_form),
)
.route("/queue", get(show_queue_tasks))
+20 -1
View File
@@ -109,6 +109,26 @@ impl IntoResponse for ApiError {
(status, Json(body)).into_response()
}
}
pub type TemplateResult<T> = Result<T, HtmlError>;
// Helper trait for converting to HtmlError with templates
pub trait IntoHtmlError {
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError;
}
// // Implement for AppError
impl IntoHtmlError for AppError {
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError {
HtmlError::new(self, templates)
}
}
// // Implement for minijinja::Error directly
impl IntoHtmlError for minijinja::Error {
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError {
HtmlError::from_template_error(self, templates)
}
}
#[derive(Clone)]
pub struct ErrorContext {
#[allow(dead_code)]
@@ -129,7 +149,6 @@ pub enum HtmlError {
Template(String, Arc<AutoReloader>),
}
// Implement From<ApiError> for HtmlError
impl HtmlError {
pub fn new(error: AppError, templates: Arc<AutoReloader>) -> Self {
match error {
+2 -4
View File
@@ -35,8 +35,7 @@ pub async fn show_account_page(
AccountData::template_name(),
AccountData { user },
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
)?;
Ok(output.into_response())
}
@@ -70,8 +69,7 @@ pub async fn set_api_key(
"api_key_section",
AccountData { user: updated_user },
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
)?;
Ok(output.into_response())
}
@@ -24,8 +24,7 @@ pub async fn show_documentation_index(
user: auth.current_user,
},
state.templates.clone(),
)
.map_err(|e| HtmlError::from_template_error(e, state.templates.clone()))?;
)?;
Ok(output.into_response())
}
+2 -3
View File
@@ -6,7 +6,7 @@ use surrealdb::{engine::any::Any, Surreal};
use tracing::info;
use crate::{
error::{AppError, HtmlError},
error::HtmlError,
page_data,
server::{routes::html::render_template, AppState},
storage::types::user::User,
@@ -57,8 +57,7 @@ pub async fn index_handler(
user: auth.current_user,
},
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
)?;
Ok(output.into_response())
}
@@ -6,25 +6,20 @@ use axum_session_auth::AuthSession;
use axum_session_surreal::SessionSurrealPool;
use axum_typed_multipart::{FieldData, TryFromMultipart, TypedMultipart};
use futures::{future::try_join_all, TryFutureExt};
use serde::Serialize;
use surrealdb::{engine::any::Any, Surreal};
use tempfile::NamedTempFile;
use tracing::info;
use crate::{
error::{AppError, HtmlError},
error::{AppError, HtmlError, IntoHtmlError},
ingress::types::ingress_input::{create_ingress_objects, IngressInput},
page_data,
server::AppState,
storage::types::{file_info::FileInfo, user::User},
};
use super::render_template;
#[derive(Serialize)]
struct PageData {
// name: String,
}
pub async fn show_ingress_form(
State(state): State<AppState>,
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
@@ -33,8 +28,7 @@ pub async fn show_ingress_form(
return Ok(Redirect::to("/").into_response());
}
let output = render_template("ingress_form.html", PageData {}, state.templates.clone())
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
let output = render_template("ingress_form.html", {}, state.templates.clone())?;
Ok(output.into_response())
}
@@ -49,15 +43,47 @@ pub struct IngressParams {
pub files: Vec<FieldData<NamedTempFile>>,
}
page_data!(IngressFormData, "ingress_form.html", {
instructions: String,
content: String,
});
pub async fn process_ingress_form(
State(state): State<AppState>,
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
TypedMultipart(input): TypedMultipart<IngressParams>,
) -> Result<impl IntoResponse, HtmlError> {
let user = match auth.current_user {
Some(user) => user,
None => return Ok(Redirect::to("/").into_response()),
};
let user = auth.current_user.ok_or_else(|| {
AppError::Auth("You must be signed in".to_string()).with_template(state.templates.clone())
})?;
// let user = match auth.current_user {
// Some(user) => user,
// None => {
// return Err(HtmlError::new(
// AppError::Auth("You must be signed in".to_string()),
// state.templates,
// ))
// }
// };
if input.content.clone().is_some_and(|c| c.len() < 2) && input.files.is_empty() {
let output = render_template(
IngressFormData::template_name(),
IngressFormData {
instructions: input.instructions.clone(),
content: input.content.clone().unwrap(),
},
state.templates.clone(),
)?;
return Ok(output.into_response());
// return Ok((
// StatusCode::UNAUTHORIZED,
// Html("Invalid input, make sure you fill in either content or add files"),
// )
// .into_response());
}
info!("{:?}", input);
@@ -88,5 +114,8 @@ pub async fn process_ingress_form(
.map_err(AppError::from)
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
Ok(Html("SuccessBRO!").into_response())
Ok(Html(
"<a class='btn btn-primary' hx-get='/ingress-form' hx-swap='outerHTML'>Add Content</a>",
)
.into_response())
}
+2 -3
View File
@@ -1,5 +1,5 @@
use crate::{
error::{AppError, HtmlError},
error::HtmlError,
page_data,
server::AppState,
storage::types::{job::Job, user::User},
@@ -35,8 +35,7 @@ pub async fn show_queue_tasks(
ShowQueueTasks::template_name(),
ShowQueueTasks { jobs, user },
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
)?;
Ok(rendered.into_response())
}
+27 -12
View File
@@ -3,11 +3,13 @@ use std::sync::Arc;
use axum::response::Html;
use minijinja_autoreload::AutoReloader;
use crate::error::{HtmlError, IntoHtmlError};
pub mod account;
pub mod documentation;
pub mod gdpr;
pub mod index;
pub mod ingress;
pub mod ingress_form;
pub mod ingress_tasks;
pub mod privacy_policy;
pub mod search_result;
@@ -19,21 +21,26 @@ pub trait PageData {
fn template_name() -> &'static str;
}
// Helper function for render_template
pub fn render_template<T>(
template_name: &str,
context: T,
templates: Arc<AutoReloader>,
) -> Result<Html<String>, minijinja::Error>
) -> Result<Html<String>, HtmlError>
where
T: serde::Serialize,
{
let env = templates.acquire_env()?;
let tmpl = env.get_template(template_name)?;
let env = templates
.acquire_env()
.map_err(|e| e.with_template(templates.clone()))?;
let tmpl = env
.get_template(template_name)
.map_err(|e| e.with_template(templates.clone()))?;
let context = minijinja::Value::from_serialize(&context);
let output = tmpl.render(context)?;
Ok(output.into())
let output = tmpl
.render(context)
.map_err(|e| e.with_template(templates.clone()))?;
Ok(Html(output))
}
pub fn render_block<T>(
@@ -41,15 +48,23 @@ pub fn render_block<T>(
block: &str,
context: T,
templates: Arc<AutoReloader>,
) -> Result<Html<String>, minijinja::Error>
) -> Result<Html<String>, HtmlError>
where
T: serde::Serialize,
{
let env = templates.acquire_env()?;
let tmpl = env.get_template(template_name)?;
let env = templates
.acquire_env()
.map_err(|e| e.with_template(templates.clone()))?;
let tmpl = env
.get_template(template_name)
.map_err(|e| e.with_template(templates.clone()))?;
let context = minijinja::Value::from_serialize(&context);
let output = tmpl.eval_to_state(context)?.render_block(block)?;
let output = tmpl
.eval_to_state(context)
.map_err(|e| e.with_template(templates.clone()))?
.render_block(block)
.map_err(|e| e.with_template(templates.clone()))?;
Ok(output.into())
}
+1 -2
View File
@@ -24,8 +24,7 @@ pub async fn show_privacy_policy(
user: auth.current_user,
},
state.templates.clone(),
)
.map_err(|e| HtmlError::from_template_error(e, state.templates.clone()))?;
)?;
Ok(output.into_response())
}
+3 -10
View File
@@ -9,12 +9,7 @@ use axum_session_auth::AuthSession;
use axum_session_surreal::SessionSurrealPool;
use surrealdb::{engine::any::Any, Surreal};
use crate::{
error::{AppError, HtmlError},
page_data,
server::AppState,
storage::types::user::User,
};
use crate::{error::HtmlError, page_data, server::AppState, storage::types::user::User};
use super::{render_block, render_template};
@@ -41,14 +36,12 @@ pub async fn show_signin_form(
"body",
ShowSignInForm {},
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?,
)?,
false => render_template(
ShowSignInForm::template_name(),
ShowSignInForm {},
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?,
)?,
};
Ok(output.into_response())
+3 -9
View File
@@ -11,11 +11,7 @@ use serde::{Deserialize, Serialize};
use surrealdb::{engine::any::Any, Surreal};
use tracing::info;
use crate::{
error::{AppError, HtmlError},
server::AppState,
storage::types::user::User,
};
use crate::{error::HtmlError, server::AppState, storage::types::user::User};
use super::{render_block, render_template};
@@ -44,14 +40,12 @@ pub async fn show_signup_form(
"body",
PageData {},
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?,
)?,
false => render_template(
"auth/signup_form.html",
PageData {},
state.templates.clone(),
)
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?,
)?,
};
Ok(output.into_response())
+2 -2
View File
@@ -19,11 +19,11 @@
</div>
<!-- Quick Actions - Modified button to link to ingress form -->
<div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl mt-4">
<div class="card-body">
<h2 class="card-title">Quick Actions</h2>
<div class="flex gap-4">
<a class="btn btn-primary" href="/ingress" hx-boost="true">Add Content</a>
<a class="btn btn-primary" hx-get="/ingress-form" hx-swap="outerHTML">Add Content</a>
</div>
</div>
</div>
+17 -20
View File
@@ -1,22 +1,19 @@
{% extends "body_base.html" %}
{% block main %}
<div class="flex justify-center grow mt-2 sm:mt-4">
<div class="container">
<div class="card">
<form class="space-y-2" hx-post="/ingress-form" enctype="multipart/form-data">
<h1 class="text-2xl">Add content to the database </h1>
<form class="space-y-2" hx-post="/ingress" enctype="multipart/form-data">
<label class="label label-text">Instructions</label>
<textarea name="instructions" class="textarea w-full input-bordered"
placeholder="Enter instructions for the AI here, help it understand what its seeing or how it should relate to the database"></textarea>
<label class="label label-text">Content (optional)</label>
<textarea name="content" class="textarea w-full input-bordered" placeholder="Additional content"></textarea>
<label class="label label-text">Category</label>
<input type="text" name="category" class="input input-bordered" placeholder="Category for ingress">
<label class="label label-text">Instructions</label>
<textarea name="instructions" class="textarea w-full input-bordered"
placeholder="Enter instructions for the AI here, help it understand what its seeing or how it should relate to the database">{{instructions
}}</textarea>
<label class="label label-text">Content (optional)</label>
<textarea name="content" class="textarea w-full input-bordered" placeholder="Additional content">{{content
}}</textarea>
<label class="label label-text">Category</label>
<input type="text" name="category" class="input input-bordered" placeholder="Category for ingress">
<label class="label label-text">Files</label>
<input type="file" name="files" multiple class="file-input file-input-bordered" />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div id="ingress-result"></div>
</div>
</div>
{% endblock %}
<label class="label label-text">Files</label>
<input type="file" name="files" multiple class="file-input file-input-bordered" />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div id="ingress-result"></div>
</div>