use std::str::FromStr;
use apalis_core::task::status::Status;
use leptos::prelude::*;
use leptos_meta::Title;
use leptos_router::{components::A, hooks::use_params_map};
use leptos_struct_table::*;
use crate::{
components::{pagination::Paginator, sidebar::queues_icon},
pages::queues::{CustomTableRowRenderer, provider::QueueProvider},
};
#[component]
pub fn SearchBox() -> impl IntoView {
let (search, set_search) = signal(String::new());
let on_input = move |ev: leptos::ev::Event| {
let input = event_target_value(&ev);
{
set_search.set(input.clone());
}
};
view! {
<div class="flex items-center has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-charcoal-650 has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-1 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text px-1 h-6 rounded hover:bg-charcoal-750 w-full">
<div class="pointer-events-none flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
class="size-3 ml-0.5 text-text-dimmed"
>
<path
fill-rule="evenodd"
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
></path>
</svg>
</div>
<input
class="grow h-full w-full text-text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:m-0 [&::-webkit-inner-spin-button]:m-0 [&]:[-moz-appearance:textfield] px-1 rounded text-xs"
placeholder="Search Queues..."
autofocus
prop:value=search
on:input=on_input
/>
</div>
}
}
#[component]
pub fn QueuePage() -> impl IntoView {
let rows = QueueProvider::new();
let pagination_controller = PaginationController::default();
view! {
<Title text="Queues" />
<div class="flex flex-col h-full w-full">
<div class="w-full bg-background-bright border-b border-gray-700 flex items-center h-[2.75rem]">
<span class="ml-2 mr-1 rounded p-1 bg-charcoal-700 text-text-bright p-2">
{queues_icon()}
</span>
<h3 class="text-base font-bold m-2 flex-1">Queues</h3>
</div>
<div class="flex items-center gap-1 p-2 border-b border-gray-700">
<SearchBox />
</div>
<div class="flex-1 overflow-hidden overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600 hover:scrollbar-thumb-charcoal-500 w-full">
<table class="whitespace-nowrap transition-all duration-200 w-full">
<TableContent
rows=rows
scroll_container="html"
sorting_mode=SortingMode::SingleColumn
display_strategy=DisplayStrategy::Pagination {
controller: pagination_controller,
row_count: 10,
}
row_renderer=CustomTableRowRenderer
/>
</table>
</div>
<Paginator pagination_controller />
</div>
}
}
#[component]
pub fn Card(title: String, status: String) -> impl IntoView {
view! {
<div class="rounded-sm border-grid-bright bg-background-bright">
<div class="p-6 flex items-center space-x-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-6 w-6 text-blue-600"
>
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>
</svg>
<div>
<h5 class="text-base ">{title}</h5>
<div class="text-sm text-gray-500">"Last seen less than a minute ago"</div>
</div>
<div class="inline-flex w-fit items-center whitespace-nowrap rounded-full border px-2.5 py-0.5 text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80 ml-auto">
{status}
</div>
</div>
</div>
}
}
#[component]
pub fn QueueNav() -> impl IntoView {
let params = use_params_map();
let queue = move || params.with(|p| p.get("queue").unwrap());
let queue_home = move || format!("/queues/{}", queue());
let worker_home = move |status: &Status| format!("/queues/{}/{}", queue(), status);
let calculate_active = move |status: &Status| {
let current_status =
params.with(|p| p.get("status").map(|s| Status::from_str(&s).unwrap()));
match current_status {
Some(s) => s == *status,
None => false,
}
};
let is_index = move || params.with(|p| p.get("status").is_none());
let calculate_class_active = move |active: bool| {
if active {
"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium ring-offset-background focus-custom text-text-bright h-8 px-2 border-b-2 border-gray-100"
} else {
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background focus-custom hover:bg-charcoal-750 hover:text-text-bright h-8 px-2 text-text-dimmed"
}
};
let statuses = [
Status::Pending,
Status::Queued,
Status::Running,
Status::Done,
Status::Failed,
Status::Killed,
];
view! {
<div class="flex flex-col items-left transition-all ">
<div class="flex space-x-4">
<div class="items-center px-1 text-sm font-medium text-center text-text-bright border-r pr-4 border-grid-bright">
<A
href=move || queue_home()
attr:class=move || calculate_class_active(is_index())
>
"Workers"
</A>
</div>
{statuses
.into_iter()
.map(move |status| {
let s = status.to_string();
let s1 = status.clone();
view! {
<A
href=move || worker_home(&status)
attr:class=move || calculate_class_active(calculate_active(&s1))
>
{s}
</A>
}
})
.collect::<Vec<_>>()}
</div>
</div>
}
}