Some issue information, better look, issues count

This commit is contained in:
eraden 2021-04-29 23:21:48 +02:00
parent 8ae4cc22ba
commit a1d7e14984
4 changed files with 140 additions and 35 deletions

View File

@ -1,5 +1,8 @@
#epics { #epics {
> section { > section {
max-width: 1024px;
margin: 0 auto;
> h1 { > h1 {
font-family: var(--font-bold); font-family: var(--font-bold);
} }
@ -7,7 +10,8 @@
> .description { > .description {
font-family: var(--font-regular); font-family: var(--font-regular);
margin: { margin: {
top: 5px; top: 16px;
bottom: 16px;
} }
} }
@ -22,19 +26,52 @@
> li.epic { > li.epic {
padding: 0; padding: 0;
margin: 5px 0 0;
> .firstRow {
display: flex;
justify-content: space-between;
margin: { margin: {
top: 5px; top: 5px;
bottom: 5px; bottom: 5px;
}; };
display: flex;
justify-content: space-between;
> .epicName { > .epicName {
// font-family: var(--font-black);
font-size: 16px;
} }
> .date { > .date {
width: 250px; width: 400px;
display: flex;
justify-content: space-between;
}
> .counter {
//
}
}
> .secondRow {
margin: {
top: 5px;
bottom: 5px;
left: 20px;
right: 20px;
};
> .issues {
> .issue {
display: flex;
justify-content: space-between;
> .flags {
display: flex;
justify-content: space-between;
width: 40px;
}
}
}
} }
} }
} }

View File

@ -1,8 +1,30 @@
#[derive(Debug)] use std::collections::HashMap;
pub struct EpicsPage {}
use jirs_data::{EpicId, IssueId};
use crate::model::Model;
#[derive(Debug, Default)]
pub struct EpicsPage {
pub(crate) issues_per_epic: HashMap<EpicId, Vec<IssueId>>,
}
impl EpicsPage { impl EpicsPage {
pub fn new() -> Self { pub fn new(model: &Model) -> Self {
Self {} let issues_per_epic = Self::build_issues_per_epic(model);
Self { issues_per_epic }
}
pub fn build_issues_per_epic(model: &Model) -> HashMap<EpicId, Vec<IssueId>> {
model.issues().iter().fold(HashMap::new(), |mut h, issue| {
if let Some(epic_id) = issue.epic_id.as_ref() {
h.entry(*epic_id).or_default().push(issue.id);
}
h
})
}
pub fn issues(&self, epic_id: EpicId) -> Option<&Vec<IssueId>> {
self.issues_per_epic.get(&epic_id)
} }
} }

View File

@ -1,6 +1,7 @@
use seed::app::Orders; use seed::app::Orders;
use crate::model::{Model, Page, PageContent}; use crate::model::{Model, Page, PageContent};
use crate::pages::epics_page::EpicsPage;
use crate::ws::board_load; use crate::ws::board_load;
use crate::{Msg, OperationKind, ResourceKind}; use crate::{Msg, OperationKind, ResourceKind};
@ -21,10 +22,17 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
board_load(model, orders); board_load(model, orders);
build_page_content(model); build_page_content(model);
} }
Msg::ResourceChanged(ResourceKind::Issue, OperationKind::ListLoaded, ..) => {
let hash = EpicsPage::build_issues_per_epic(model);
crate::match_page_mut!(model, Epics).issues_per_epic = hash;
}
_ => (), _ => (),
} }
} }
fn build_page_content(model: &mut Model) { fn build_page_content(model: &mut Model) {
model.page_content = PageContent::Epics(Box::new(super::EpicsPage::new())); if matches!(model.page_content, PageContent::Epics(..)) {
return;
}
model.page_content = PageContent::Epics(Box::new(super::EpicsPage::new(model)));
} }

View File

@ -1,38 +1,49 @@
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use jirs_data::Issue;
use seed::prelude::*; use seed::prelude::*;
use seed::*; use seed::*;
use crate::components::styled_icon::{Icon, StyledIcon};
use crate::model::Model; use crate::model::Model;
use crate::shared::inner_layout; use crate::shared::inner_layout;
use crate::Msg; use crate::Msg;
pub fn view(model: &Model) -> Node<Msg> { pub fn view(model: &Model) -> Node<Msg> {
let page = crate::match_page!(model, Epics; Empty);
let epics: Vec<Node<Msg>> = model let epics: Vec<Node<Msg>> = model
.epics .epics
.iter() .iter()
.map(|epic| { .map(|epic| {
let issues = page
.issues(epic.id)
.map(|v| {
v.iter()
.filter_map(|i| model.issues_by_id.get(i))
.collect::<Vec<&Issue>>()
})
.unwrap_or_default();
li![ li![
C!["epic"], C!["epic"],
div![
C!["firstRow"],
div![C!["epicName"], &epic.name], div![C!["epicName"], &epic.name],
div![ div![
C!["date"], C!["date"],
div![ date_field("Starts at:", "startsAt", epic.starts_at.as_ref()),
C!["startsAt"], date_field("Ends at:", "endsAt", epic.ends_at.as_ref()),
span!["Stats At:"], ],
span![epic div![C!["counter"], "Number of issues:", issues.len()],
.starts_at
.as_ref()
.map(|d| format!("{}", d.format("%e %B %Y")))
.unwrap_or_default()]
], ],
div![ div![
C!["endsAt"], C!["secondRow"],
span!["Ends At: "], div![
span![epic C!["issues"],
.ends_at issues
.as_ref() .into_iter()
.map(|d| format!("{}", d.format("%e %B %Y"))) .map(|issue| render_issue(issue))
.unwrap_or_default()] .collect::<Vec<Node<Msg>>>()
] ]
] ]
] ]
@ -44,14 +55,41 @@ pub fn view(model: &Model) -> Node<Msg> {
"epics", "epics",
&[section![ &[section![
h1!["Epics"], h1!["Epics"],
p!["Epics and issues grouped in them"], p![C!["description"], "Epics and issues grouped in them"],
ul![C!["epicsList"], epics] ul![C!["epicsList"], epics]
]], ]],
) )
} }
fn date_field(name: &str, date: Option<&NaiveDateTime>) -> Node<Msg> { fn date_field(
name: &'static str,
class_name: &'static str,
date: Option<&NaiveDateTime>,
) -> Node<Msg> {
match date { match date {
_ => Node::Empty, None => Node::Empty,
Some(d) => div![
C![class_name],
span![name],
span![format!("{}", d.format("%e %B %Y"))]
],
} }
} }
fn render_issue(issue: &Issue) -> Node<Msg> {
div![
C!["issue"],
div![C!["name"], issue.title.as_str()],
div![
C!["flags"],
div![
C!["type"],
StyledIcon::from(Icon::from(issue.issue_type)).render()
],
div![
C!["priority"],
StyledIcon::from(Icon::from(issue.priority)).render()
],
]
]
}