This commit is contained in:
JMARyA 2025-01-15 22:46:25 +01:00
parent 92cefff212
commit 36128864aa
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
5 changed files with 153 additions and 92 deletions

View file

@ -1,6 +1,7 @@
use based::auth::MaybeUser;
use based::page::{Shell, htmx_link, render_page};
use based::request::{RawResponse, RequestContext, StringResponse, respond_with};
use based::ui::components::Shell;
use based::ui::{prelude::*, render_page};
use maud::{PreEscaped, html};
use pacco::pkg::mirror::MirrorRepository;
use rocket::http::{ContentType, Status};
@ -24,25 +25,33 @@ pub async fn index_page(
let repos: Vec<String> = Repository::list();
let content = html!(
div class="flex justify-between" {
h1 class="text-4xl font-bold pb-6" { "Repositories" };
@if let Some(user) = user.take_user() {
a class="text-lg" href="/account" { (user.username) };
};
};
(Flex(html! {
h1 class="text-4xl font-bold pb-6" { "Repositories" };
@if let Some(user) = user.take_user() {
a class="text-lg" href="/account" { (user.username) };
};
}).justify(Justify::Between))
div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6" {
@for repo in repos {
(htmx_link(&format!("/{repo}"), "flex items-center gap-4 p-4 bg-gray-100 dark:bg-gray-700 rounded-lg shadow-md transition hover:bg-gray-200 dark:hover:bg-gray-600", "", html! {
p class="font-medium text-gray-800 dark:text-gray-100" {
(repo)
@if config.is_mirrored_repo(&repo) {
div class="inline-block px-3 py-1 text-sm font-medium text-white bg-blue-500 rounded-full" { "Mirrored" };
};
};
}))
// TODO : Implement class transition
(Link(&format!("/{repo}"),
Rounded(
Padding(
Shadow::medium(
Flex(Hover(
Background(Gray::_600, Nothing()),
Background(Gray::_700,
Div().vanish().push(Text(&repo).medium().color(&Gray::_100))
.push(html! {
@if config.is_mirrored_repo(&repo) {
(Padding(Rounded(Background(Blue::_500, Text("Mirrored").white().medium().sm())).size(Size::Full)).x(ScreenValue::_3).y(ScreenValue::_1))
};
})
))).items_center().gap(4))).all(ScreenValue::_4))
.size(Size::Large)))
}
}
};
);
render(content, "Repositories", ctx).await

View file

@ -1,8 +1,6 @@
use based::{
page::htmx_link,
request::{RequestContext, StringResponse},
};
use maud::{PreEscaped, html};
use based::request::{RequestContext, StringResponse};
use based::ui::prelude::*;
use maud::{PreEscaped, Render, html};
use rocket::{State, get};
use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name};
@ -40,9 +38,10 @@ pub async fn pkg_ui(
let content = html! {
// Package Name
div class="flex flex-wrap gap-2 justify-center items-center" {
(htmx_link(&format!("/{}", repo.name), "text-3xl font-bold text-gray-800 dark:text-gray-100", "", html! {
(repo.name)
}))
(Link(&format!("/{}", repo.name),
Text(&repo.name).bold().color(&Gray::_100)._3xl()
).use_htmx())
p class="font-bold p-2 text-gray-400" { "/" };
h1 class="text-3xl font-bold text-gray-800 dark:text-gray-100 pr-2" {
(pkg.name)
@ -50,18 +49,20 @@ pub async fn pkg_ui(
@if pkg.is_signed() {
div class="flex items-center gap-2 text-slate-300 pr-4" {
span class="text-2xl font-bold" {
""
}
span class="text-sm font-medium" { "Signed" }
// TODO : Add more info: Who signed? + Public Key recv
(Span("")._2xl().bold())
(Span("Signed").sm().medium())
// TODO : Add more info: Who signed? + Public Key recv
}
}
@for arch in arch {
span class="px-3 py-1 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded-full" {
(arch.to_string())
};
(Rounded(
Padding(
Background(Gray::_700,
Span(&arch.to_string()).medium().sm())
).x(ScreenValue::_3).y(ScreenValue::_1)
).size(Size::Full)
)
}
a href=(format!("/pkg/{}/{}/{}", pkg.repo, pkg.arch.to_string(), pkg.file_name())) class="ml-4 inline-flex items-center px-2 py-2 bg-gray-200 text-black font-xs rounded-lg shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50" {
@ -103,9 +104,11 @@ pub async fn pkg_ui(
ul class="space-y-1" {
@for version in versions {
li class="text-gray-800 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400 transition" {
(htmx_link(&format!("/{}/{}?ver={version}", repo.name, &pkg.name), if pkg.version.as_ref().map(|x| *x == Package::version(&version).0).unwrap_or_default() { "text-blue-500" } else { "" }, "", html! {
(version)
}))
(Link(&format!("/{}/{}?ver={version}", repo.name, &pkg.name),
if pkg.version.as_ref()
.map(|x| *x == Package::version(&version).0).unwrap_or_default()
{ Text(&version).color(&Blue::_500) } else { Text(&version) }
).use_htmx())
};
}
}
@ -180,6 +183,28 @@ pub async fn pkg_ui(
Some(render(content, pkg_name, ctx).await)
}
pub fn arch_card(a: &str, repo_name: &str, current: bool) -> PreEscaped<String> {
let url = if current {
&format!("/{repo_name}")
} else {
&format!("/{repo_name}?arch={a}")
};
let link = Link(&url, Text(&a).sm().medium().color(&Gray::_300)).use_htmx();
Rounded(
Padding(if current {
Background(Blue::_500, link)
} else {
Background(Gray::_700, link)
})
.x(ScreenValue::_3)
.y(ScreenValue::_1),
)
.size(Size::Full)
.render()
}
#[get("/<repo>?<arch>")]
pub async fn repo_ui(
repo: &str,
@ -212,18 +237,12 @@ pub async fn repo_ui(
@for a in architectures {
@if let Some(arch) = arch.as_ref() {
@if arch.to_string() == a {
(htmx_link(&format!("/{}", repo.name), "px-3 py-1 text-sm font-medium bg-blue-400 dark:bg-blue-500 text-gray-600 dark:text-gray-300 rounded-full", "", html! {
(a)
}));
(arch_card(&a, &repo.name, true))
} @else {
(htmx_link(&format!("/{}?arch={}", repo.name, a), "px-3 py-1 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded-full", "", html! {
(a)
}));
(arch_card(&a, &repo.name, false))
}
} @else {
(htmx_link(&format!("/{}?arch={}", repo.name, a), "px-3 py-1 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded-full", "", html! {
(a)
}));
(arch_card(&a, &repo.name, false))
}
}
}
@ -232,18 +251,41 @@ pub async fn repo_ui(
// Package list
ul class="space-y-4" {
@for pkg in packages {
(htmx_link(&format!("/{}/{pkg}", repo.name), "flex items-center gap-4 p-4 bg-gray-100 dark:bg-gray-700 rounded-lg shadow-md transition hover:bg-gray-200 dark:hover:bg-gray-600", "", html! {
div class="w-10 h-10 flex items-center justify-center rounded-full bg-blue-500 text-white font-semibold" {
{(pkg.chars().next().unwrap_or_default().to_uppercase())}
};
p class="font-medium flex-1 text-gray-800 dark:text-gray-100 max-w-fit" {
(pkg)
};
div class="flex items-center gap-2 text-slate-300 pr-4" {
span class="text-2xl font-bold" { "" };
span class="text-sm font-medium" { "Signed" };
};
}))
(
Link(
&format!("/{}/{pkg}", repo.name),
// TODO : Implement transition class
Flex(
Padding(
Hover(
Background(Gray::_600, Nothing()),
Background(Gray::_700,
Rounded(
Shadow::medium(
Div().vanish()
.push(
html! { (Sized(ScreenValue::_10, ScreenValue::_10, Rounded(
Background(Blue::_500, Flex(Text(
&pkg.chars().next().unwrap_or_default().to_uppercase().to_string()
).white().semibold()).full_center().group()
)).size(Size::Full))) }
)
.push(html! { (Width(ScreenValue::fit, Text(&pkg).medium().color(&Gray::_100))) })
.push(html! {
(
Flex(
Padding(
Div().vanish()
.push(Span("")._2xl().bold().color(&Slate::_300))
.push(Span("Signed").sm().medium().color(&Slate::_300))
).right(ScreenValue::_4)
).items_center().gap(2).group()
)
})
)
).size(Size::Large)))).all(ScreenValue::_4)).items_center().gap(4)
).use_htmx()
)
}
}
};
@ -303,12 +345,15 @@ pub fn build_info(key: String, value: String) -> PreEscaped<String> {
}
pub fn key_value(key: String, value: String) -> PreEscaped<String> {
html! {
div class="flex items-center" {
span class="font-bold w-32" { (format!("{key}: ")) };
span class="ml-2" { (value) };
};
}
Flex(
Div()
.vanish()
.push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold()))
.push(Margin(Span(&value)).left(ScreenValue::_2)),
)
.items_center()
.group()
.render()
}
pub fn take_out<T>(v: &mut Vec<T>, f: impl Fn(&T) -> bool) -> (&mut Vec<T>, Option<T>) {
@ -350,13 +395,13 @@ pub fn pkg_list_info(key: &str, value: &str) -> PreEscaped<String> {
});
html! {
div class="flex items-center" {
(Flex(html! {
span class="font-bold w-32" { (format!("{key}: ")) };
div class="flex flex-wrap" {
@for pkg in pkgs {
(pkg)
};
};
};
}).items_center().group())
}
}

View file

@ -1,7 +1,7 @@
use based::{
auth::{Session, Sessions, User, csrf::CSRF},
page::htmx_link,
request::{RequestContext, StringResponse, api::to_uuid, respond_html},
ui::AttrExtendable,
};
use maud::{PreEscaped, html};
use rocket::{
@ -14,6 +14,7 @@ use rocket::{
};
use super::render;
use based::ui::prelude::*;
#[get("/login")]
pub async fn login(ctx: RequestContext) -> StringResponse {
@ -83,7 +84,7 @@ pub async fn new_api_key(user: User, csrf: &str, session_name: &str) -> StringRe
if session_name.is_empty() {
return respond_html(
html! {
div id="next_session" {};
(Div().id("next_session"))
(user.update_csrf().await)
}
.into_string(),
@ -99,7 +100,7 @@ pub async fn new_api_key(user: User, csrf: &str, session_name: &str) -> StringRe
span class="text-red-500" { (api.token) };
};
div id="next_session" {};
(Div().id("next_session"))
(user.update_csrf().await)
}
.into_string(),
@ -124,7 +125,14 @@ pub async fn account_page(user: User, ctx: RequestContext) -> StringResponse {
h2 class="text-xl font-semibold mb-2" { (user.username) };
};
(htmx_link("/passwd", "mb-6 bg-green-500 text-white py-2 px-6 rounded hover:bg-green-600", "", html! { "Change Password" }))
(Link("/passwd", Margin(
Rounded(Padding(
Hover(
Background(Green::_600, Nothing()),
Background(Green::_500, Text("Change Password").white())
)
).x(ScreenValue::_6).y(ScreenValue::_2))
).bottom(ScreenValue::_6)).use_htmx())
section class="mb-6 mt-6" {
h3 class="text-lg font-semibold mb-4" { "Active Sessions" };
@ -134,8 +142,7 @@ pub async fn account_page(user: User, ctx: RequestContext) -> StringResponse {
(build_session_block(&ses, &user).await)
};
div id="next_session" {};
(Div().id("next_session"))
}
}