pib-viewer 0.3.0

A viewer for public governmental data served over OParl
// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use dioxus::{
    hooks::{use_resource, use_signal},
    prelude::{Props, component, dioxus_elements, rsx},
    signals::ReadableExt as _,
};
use dioxus_core::Element;
use dioxus_router::Link;
use oparl_types::PersonUrl;

use crate::{
    client::{get_body, get_organization, get_person},
    route::{Route, UrlEncoded},
};

#[derive(Debug, Clone, PartialEq, Props)]
pub struct PersonProps {
    person_url: UrlEncoded<PersonUrl>,
}

struct PersonData {
    person: oparl_types::Person,
    memberships: Vec<(oparl_types::Membership, oparl_types::Organization)>,
    body: oparl_types::Body,
}

impl PersonData {
    async fn get(person_url: &PersonUrl) -> Result<Self, String> {
        let person = get_person(person_url).await?;
        let body = get_body(person.body.as_ref().unwrap()).await?;

        let mut memberships = Vec::new();

        for membership in &person.membership {
            let organization = get_organization(membership.organization.as_ref().unwrap()).await?;
            memberships.push((membership.clone(), organization));
        }

        Ok(Self {
            person,
            memberships,
            body,
        })
    }
}

#[component]
pub fn Person(props: PersonProps) -> Element {
    let person_url = use_signal(|| props.person_url.get_inner().unwrap().clone());
    let data = { use_resource(move || async move { PersonData::get(&person_url()).await }) };

    rsx! {
        match &*data.read_unchecked() {
            Some(Ok(data)) => rsx! {
                if let Some(name) = &data.person.name {
                    dioxus::document::Title { "🧑 {name}" }
                } else {
                    dioxus::document::Title { "🧑 (Unnamed person)" }
                }
                h1 {
                    "🧑 "
                    if let Some(name) = &data.person.name {
                        "{name}"
                    } else {
                        "(Unnamed person)"
                    }
                }

                Link {
                    to: Route::Body{body_url: UrlEncoded::from(data.body.id.clone())},
                    "🏛️ { data.body.name }"
                }

                h2 {
                    "Memberships"
                }

                ul {
                    for (membership, organization) in &data.memberships {
                        li {
                            key: "{membership.id}",
                            span {
                                Link {
                                    to: Route::Organization { organization_url: UrlEncoded::from(organization.id.clone()) },
                                    if let Some(name) = &organization.name {
                                        "{ name }"
                                    } else {
                                        "(Unnamed organization)"
                                    }
                                }
                            }
                        }
                    }
                }
            },
            Some(Err(e)) => rsx! {
                dioxus::document::Title { "Loading failed." },
                p { "Loading person failed, {e}" }
            },
            None =>  rsx! {
                dioxus::document::Title { "Loading…" },
                p { "Loading..." }
            }
        }
    }
}