start work on admin cp
This commit is contained in:
parent
2941690f0a
commit
bd32c02e2c
6 changed files with 94 additions and 8 deletions
|
@ -12,7 +12,7 @@ reload_html = true
|
||||||
watch_path = ["src", "assets"]
|
watch_path = ["src", "assets"]
|
||||||
|
|
||||||
[web.resource]
|
[web.resource]
|
||||||
style = ["/assets/style/main.css"]
|
style = []
|
||||||
script = []
|
script = []
|
||||||
|
|
||||||
[web.resource.dev]
|
[web.resource.dev]
|
||||||
|
|
15
assets/admin/style/main.css
Normal file
15
assets/admin/style/main.css
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@import url(https://rsms.me/inter/inter.css);
|
||||||
|
|
||||||
|
:root {
|
||||||
|
background-color: #312337;
|
||||||
|
color: #fff6fe;
|
||||||
|
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
/* fix for Chrome */
|
||||||
|
font-feature-settings: 'liga' 1, 'calt' 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
color: #f0cf96;
|
||||||
|
}
|
|
@ -2,12 +2,16 @@ use dioxus::prelude::*;
|
||||||
|
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Routable, Debug, PartialEq)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
enum Route {
|
enum Route {
|
||||||
#[layout(AdminLayout)]
|
#[layout(AdminLayout)]
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
Home {},
|
Home {},
|
||||||
|
|
||||||
|
#[nest("/site")]
|
||||||
|
#[route("/:domain")]
|
||||||
|
Site { domain: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn App() -> Element {
|
pub fn App() -> Element {
|
||||||
|
@ -16,24 +20,33 @@ pub fn App() -> Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
fn Home() -> Element {
|
fn Home() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
h2 { "Hello!" }
|
|
||||||
SuspenseBoundary { fallback: |_context: SuspenseContext| rsx! { "Loading sites..." }, SiteList {} }
|
SuspenseBoundary { fallback: |_context: SuspenseContext| rsx! { "Loading sites..." }, SiteList {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
fn SiteList() -> Element {
|
fn SiteList() -> Element {
|
||||||
let sites = use_server_future(server::site_list)?.suspend()?;
|
let sites = use_server_future(server::site_list)?.suspend()?;
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
h3 { "Sites" }
|
h3 { "Sites" }
|
||||||
|
button { "New site" }
|
||||||
match &*sites.read() {
|
match &*sites.read() {
|
||||||
Ok(sites) => {
|
Ok(sites) => {
|
||||||
rsx! {
|
rsx! {
|
||||||
ul {
|
ul {
|
||||||
for site in sites {
|
for site in sites {
|
||||||
li { "{site.domain} - {site.title}" }
|
li {
|
||||||
|
Link {
|
||||||
|
to: Route::Site {
|
||||||
|
domain: site.domain.clone(),
|
||||||
|
},
|
||||||
|
"{site.domain} - {site.title}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,9 +60,35 @@ fn SiteList() -> Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Site(domain: String) -> Element {
|
||||||
|
let site = use_server_future(move || server::get_site(domain.clone()))?.suspend()?;
|
||||||
|
|
||||||
|
let info = match &*site.read() {
|
||||||
|
Ok(site) => site.clone(),
|
||||||
|
Err(err) => {
|
||||||
|
return rsx! {
|
||||||
|
h1 { "Error: {err}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
h1 { "{info.title}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CSS: Asset = asset!("/assets/admin/style/main.css");
|
||||||
|
|
||||||
|
#[component]
|
||||||
fn AdminLayout() -> Element {
|
fn AdminLayout() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
h1 { "Admin UI" }
|
document::Stylesheet { href: CSS }
|
||||||
|
header { "data-test-id": "admin-ui",
|
||||||
|
div {
|
||||||
|
Link { to: Route::Home {}, "Administration panel" }
|
||||||
|
}
|
||||||
|
}
|
||||||
SuspenseBoundary { fallback: |_context: SuspenseContext| rsx! { "..." },
|
SuspenseBoundary { fallback: |_context: SuspenseContext| rsx! { "..." },
|
||||||
main { Outlet::<Route> {} }
|
main { Outlet::<Route> {} }
|
||||||
}
|
}
|
||||||
|
@ -69,7 +108,7 @@ mod tests {
|
||||||
fn site_list_shows_sites() {
|
fn site_list_shows_sites() {
|
||||||
let mut app = VirtualDom::new(|| {
|
let mut app = VirtualDom::new(|| {
|
||||||
rsx! {
|
rsx! {
|
||||||
SiteList {}
|
Router::<Route> {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,17 @@ pub async fn site_list() -> Result<Vec<SiteInfo>, ServerFnError> {
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn get_site(domain: String) -> Result<SiteInfo, ServerFnError> {
|
||||||
|
use crate::outbound::services::site::SiteServiceProvider;
|
||||||
|
let FromContext(SiteServiceProvider { service }) = extract().await?;
|
||||||
|
|
||||||
|
let site = service.get_site(&domain).await?;
|
||||||
|
|
||||||
|
Ok(SiteInfo {
|
||||||
|
domain: site.domain.clone(),
|
||||||
|
title: site.title.clone(),
|
||||||
|
pages: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod server;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod testing;
|
mod testing;
|
||||||
|
|
||||||
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Routable, Debug, PartialEq)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
enum Route {
|
enum Route {
|
||||||
#[layout(SiteLayout)]
|
#[layout(SiteLayout)]
|
||||||
|
@ -31,6 +31,7 @@ enum Route {
|
||||||
PageNotFound { route: Vec<String> },
|
PageNotFound { route: Vec<String> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
pub fn App() -> Element {
|
pub fn App() -> Element {
|
||||||
// Check for special redirects
|
// Check for special redirects
|
||||||
let site_res = use_server_future(server::get_site_info)?.suspend()?;
|
let site_res = use_server_future(server::get_site_info)?.suspend()?;
|
||||||
|
@ -79,12 +80,15 @@ fn PageLink(page: String, title: String, current: Route) -> Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CSS: Asset = asset!("/assets/renderer/style/main.css");
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn SiteLayout() -> Element {
|
fn SiteLayout() -> Element {
|
||||||
let site = meta::site();
|
let site = meta::site();
|
||||||
let route = use_route::<Route>();
|
let route = use_route::<Route>();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
|
document::Stylesheet { href: CSS }
|
||||||
header { class: "site-header",
|
header { class: "site-header",
|
||||||
nav { class: "page-list",
|
nav { class: "page-list",
|
||||||
ul {
|
ul {
|
||||||
|
@ -228,6 +232,20 @@ mod tests {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
mock_service.expect_get_page().times(1).returning(|_, _| {
|
||||||
|
Box::pin(async {
|
||||||
|
Ok(entities::site::Page {
|
||||||
|
info: entities::site::PageInfo {
|
||||||
|
title: "Test page".to_string(),
|
||||||
|
name: "/".to_string(),
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
content: PageContent::Single {
|
||||||
|
content: entities::site::Post { blocks: vec![] },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
server_context().insert(AppConfig {
|
server_context().insert(AppConfig {
|
||||||
admin_host: "admin.local".to_string(),
|
admin_host: "admin.local".to_string(),
|
||||||
|
@ -337,6 +355,6 @@ mod tests {
|
||||||
app.rebuild_in_place();
|
app.rebuild_in_place();
|
||||||
let elem_str = dioxus::ssr::render(&app);
|
let elem_str = dioxus::ssr::render(&app);
|
||||||
println!("elem_str: {elem_str}");
|
println!("elem_str: {elem_str}");
|
||||||
assert!(elem_str.contains("Admin UI"));
|
assert!(elem_str.contains("admin-ui"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue