ezidam: branding page: logo update/delete, update business name
This commit is contained in:
parent
d8fe336b06
commit
3532f80882
10 changed files with 165 additions and 52 deletions
|
|
@ -20,6 +20,12 @@ impl From<url::ParseError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
Error::internal_server_error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Local crates
|
||||
|
||||
impl From<hash::Error> for Error {
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ impl AdminMenu {
|
|||
MainItem {
|
||||
id: AdminMenu::Settings.id(),
|
||||
label: "Server settings",
|
||||
link: uri!(routes::admin::settings::admin_settings).to_string(),
|
||||
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
||||
icon: icons::SETTINGS,
|
||||
sub: Some(vec![SubItem {
|
||||
label: "Branding",
|
||||
link: uri!(routes::admin::settings::admin_settings).to_string(),
|
||||
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
||||
}]),
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub enum Page {
|
|||
Authorize(Authorize),
|
||||
Redirect(Redirect),
|
||||
AdminDashboard(AdminDashboard),
|
||||
AdminSettings(AdminSettings),
|
||||
AdminSettingsBranding(AdminSettingsBranding),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -28,7 +28,7 @@ impl Page {
|
|||
Page::Authorize(_) => "pages/oauth/authorize",
|
||||
Page::Redirect(_) => "pages/oauth/redirect",
|
||||
Page::AdminDashboard(_) => "pages/admin/dashboard",
|
||||
Page::AdminSettings(_) => "pages/admin/settings",
|
||||
Page::AdminSettingsBranding(_) => "pages/admin/settings_branding",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ impl Page {
|
|||
Page::Authorize(_) => "Authorize app",
|
||||
Page::Redirect(_) => "Redirecting",
|
||||
Page::AdminDashboard(_) => "Admin dashboard",
|
||||
Page::AdminSettings(_) => "Server settings",
|
||||
Page::AdminSettingsBranding(_) => "Server branding",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ impl Page {
|
|||
Page::Authorize(_) => None,
|
||||
Page::Redirect(_) => None,
|
||||
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
||||
Page::AdminSettings(_) => Some(AdminMenu::Settings.into()),
|
||||
Page::AdminSettingsBranding(_) => Some(AdminMenu::Settings.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ impl Page {
|
|||
Page::Authorize(authorize) => Box::new(authorize),
|
||||
Page::Redirect(redirect) => Box::new(redirect),
|
||||
Page::AdminDashboard(dashboard) => Box::new(dashboard),
|
||||
Page::AdminSettings(settings) => Box::new(settings),
|
||||
Page::AdminSettingsBranding(branding) => Box::new(branding),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub mod dashboard;
|
|||
pub mod settings;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![admin_dashboard, admin_settings]
|
||||
routes![admin_dashboard, settings_branding, settings_update_branding]
|
||||
}
|
||||
|
||||
pub mod content {
|
||||
|
|
@ -23,7 +23,8 @@ pub mod content {
|
|||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
#[derive(Clone)]
|
||||
pub struct AdminSettings {
|
||||
pub struct AdminSettingsBranding {
|
||||
pub user: JwtClaims,
|
||||
pub business_name: String,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,61 @@
|
|||
use crate::routes::prelude::*;
|
||||
use rocket::get;
|
||||
use rocket::fs::TempFile;
|
||||
use rocket::{get, post};
|
||||
use settings::Settings;
|
||||
|
||||
#[get("/admin/settings")]
|
||||
pub async fn admin_settings(mut db: Connection<Database>, admin: JwtAdmin) -> Result<Page> {
|
||||
Ok(Page::AdminSettings(super::content::AdminSettings {
|
||||
#[get("/admin/settings/branding")]
|
||||
pub async fn settings_branding(mut db: Connection<Database>, admin: JwtAdmin) -> Result<Page> {
|
||||
let settings = Settings::get(&mut *db).await?;
|
||||
|
||||
Ok(Page::AdminSettingsBranding(
|
||||
super::content::AdminSettingsBranding {
|
||||
user: admin.0,
|
||||
}))
|
||||
business_name: settings.business_name().to_string(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct UpdateBranding<'r> {
|
||||
pub file: TempFile<'r>,
|
||||
pub business_name: Option<&'r str>,
|
||||
pub delete_logo: Option<bool>,
|
||||
}
|
||||
|
||||
#[post("/admin/settings/branding", data = "<form>")]
|
||||
pub async fn settings_update_branding(
|
||||
mut db: Connection<Database>,
|
||||
_admin: JwtAdmin,
|
||||
form: Form<UpdateBranding<'_>>,
|
||||
) -> Result<Redirect> {
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
match form.delete_logo {
|
||||
Some(delete_logo) => {
|
||||
if delete_logo {
|
||||
Settings::delete_business_logo(&mut transaction).await?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if let Some(business_name) = form.business_name {
|
||||
Settings::set_business_name(&mut transaction, business_name).await?;
|
||||
}
|
||||
|
||||
if form.file.len() != 0 {
|
||||
let file_path = form.file.path().ok_or_else(|| {
|
||||
Error::internal_server_error("Failed to get path of uploaded file")
|
||||
})?;
|
||||
|
||||
// Read bytes from file
|
||||
let file_bytes = rocket::tokio::fs::read(file_path).await?;
|
||||
|
||||
// Save in database
|
||||
Settings::set_business_logo(&mut transaction, &file_bytes).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(Redirect::to(uri!(settings_branding)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
{% extends "shell" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">
|
||||
Server settings
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page body -->
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="card">
|
||||
<div class="row g-0">
|
||||
<div class="col-3 d-none d-md-block border-end">
|
||||
<div class="card-body">
|
||||
<div class="list-group list-group-transparent">
|
||||
<a href="." class="list-group-item list-group-item-action d-flex align-items-center active">Branding</a>
|
||||
<a href="./security" class="list-group-item list-group-item-action d-flex align-items-center">Security</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col d-flex flex-column">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
{% extends "shell" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Page header -->
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="page-title">
|
||||
Server settings
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page body -->
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="card">
|
||||
<div class="row g-0">
|
||||
<div class="col-3 d-none d-md-block border-end">
|
||||
<div class="card-body">
|
||||
<div class="list-group list-group-transparent">
|
||||
<a href="./branding"
|
||||
class="list-group-item list-group-item-action d-flex align-items-center active">Branding</a>
|
||||
<a href="./security"
|
||||
class="list-group-item list-group-item-action d-flex align-items-center">Security</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col d-flex flex-column">
|
||||
<form action="" method="post" enctype="multipart/form-data">
|
||||
<div class="card-body">
|
||||
<h2 class="mb-4">Branding</h2>
|
||||
|
||||
<h3 class="card-title">Company logo</h3>
|
||||
<div class="row align-items-center">
|
||||
|
||||
<!-- Current logo -->
|
||||
<div class="col-auto my-2">
|
||||
<img src="/logo?time={{ now(timestamp=true) }}" alt="business logo" style="max-width: 13rem; padding: 1rem; background-color: white;">
|
||||
</div>
|
||||
|
||||
<!-- Update logo -->
|
||||
<div class="col-auto">
|
||||
<label for="file-input" class="btn">Choose File</label>
|
||||
<input id="file-input" name="file" type="file" class="d-none">
|
||||
</div>
|
||||
|
||||
<!-- Delete logo -->
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-ghost-danger" type="submit" name="delete_logo" value="true">
|
||||
Delete logo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Business info -->
|
||||
<h3 class="card-title mt-4">Business Profile</h3>
|
||||
<div class="row g-3">
|
||||
<div class="col-md">
|
||||
<div class="form-label">Business Name</div>
|
||||
<input type="text" class="form-control" name="business_name"
|
||||
value="{{ business_name }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save -->
|
||||
<div class="card-footer bg-transparent mt-auto">
|
||||
<div class="btn-list justify-content-end">
|
||||
<button class="btn" type="reset">Cancel</button>
|
||||
<button class="btn btn-primary" type="submit">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
<a href="/" class="navbar-brand navbar-brand-autodark">
|
||||
<img src="/logo" alt="business logo" height="96">
|
||||
<img src="/logo" alt="business logo" style="max-width: 13rem; padding: 1rem;">
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<a href="/">
|
||||
<img src="/logo" alt="business logo" class="navbar-brand-image navbar-brand-autodark">
|
||||
<img src="/logo" alt="business logo" style="width: 4rem;">
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ impl Settings {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_business_logo(conn: impl SqliteExecutor<'_>) -> Result<(), Error> {
|
||||
DatabaseSettings::set_business_logo(conn, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_first_admin(conn: impl SqliteExecutor<'_>, id: &UserID) -> Result<(), Error> {
|
||||
DatabaseSettings::set_first_admin(conn, &id.0).await?;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue