Small refactor
This commit is contained in:
parent
1117e878a8
commit
e5280618aa
@ -307,11 +307,21 @@ impl Model {
|
|||||||
self.user.as_ref().map(|u| u.id)
|
self.user.as_ref().map(|u| u.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn user_name(&self) -> Option<&str> {
|
||||||
|
self.user.as_ref().map(|u| u.name())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn project_id(&self) -> Option<ProjectId> {
|
pub fn project_id(&self) -> Option<ProjectId> {
|
||||||
self.project.as_ref().map(|p| p.id)
|
self.project.as_ref().map(|p| p.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn project_name(&self) -> Option<&str> {
|
||||||
|
self.project.as_ref().map(|u| u.name())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn current_user_role(&self) -> UserRole {
|
pub fn current_user_role(&self) -> UserRole {
|
||||||
self.current_user_project
|
self.current_user_project
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -22,16 +22,11 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn breadcrumbs(model: &Model) -> Node<Msg> {
|
fn breadcrumbs(model: &Model) -> Node<Msg> {
|
||||||
let project_name = model
|
|
||||||
.project
|
|
||||||
.as_ref()
|
|
||||||
.map(|p| p.name.as_str())
|
|
||||||
.unwrap_or_default();
|
|
||||||
div![
|
div![
|
||||||
C!["breadcrumbsContainer"],
|
C!["breadcrumbsContainer"],
|
||||||
span!["Projects"],
|
span!["Projects"],
|
||||||
span![C!["breadcrumbsDivider"], "/"],
|
span![C!["breadcrumbsDivider"], "/"],
|
||||||
span![project_name],
|
span![model.project_name().unwrap_or_default()],
|
||||||
span![C!["breadcrumbsDivider"], "/"],
|
span![C!["breadcrumbsDivider"], "/"],
|
||||||
span!["Kanban Board"]
|
span!["Kanban Board"]
|
||||||
]
|
]
|
||||||
@ -41,17 +36,16 @@ fn header(model: &Model) -> Node<Msg> {
|
|||||||
if !model.show_extras {
|
if !model.show_extras {
|
||||||
return Node::Empty;
|
return Node::Empty;
|
||||||
}
|
}
|
||||||
let button = StyledButton::secondary_with_text_and_icon(
|
|
||||||
"Repository",
|
|
||||||
StyledIcon::from(Icon::Github).render(),
|
|
||||||
)
|
|
||||||
.render();
|
|
||||||
div![
|
div![
|
||||||
id!["projectBoardHeader"],
|
id!["projectBoardHeader"],
|
||||||
div![id!["boardName"], C!["headerChild"], "Kanban board"],
|
div![id!["boardName"], C!["headerChild"], "Kanban board"],
|
||||||
a![
|
a![
|
||||||
attrs![At::Href => "https://gitlab.com/adrian.wozniak/jirs", At::Target => "__blank", At::Rel => "noreferrer noopener"],
|
attrs![At::Href => "https://gitlab.com/adrian.wozniak/jirs", At::Target => "__blank", At::Rel => "noreferrer noopener"],
|
||||||
button
|
StyledButton::secondary_with_text_and_icon(
|
||||||
|
"Repository",
|
||||||
|
StyledIcon::from(Icon::Github).render(),
|
||||||
|
)
|
||||||
|
.render()
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,12 @@ use crate::components::styled_avatar::*;
|
|||||||
use crate::components::styled_button::{ButtonVariant, StyledButton};
|
use crate::components::styled_button::{ButtonVariant, StyledButton};
|
||||||
use crate::components::styled_icon::*;
|
use crate::components::styled_icon::*;
|
||||||
use crate::model::PageContent;
|
use crate::model::PageContent;
|
||||||
use crate::{BoardPageChange, Model, Msg, Page, PageChanged};
|
use crate::{match_page, BoardPageChange, Model, Msg, Page, PageChanged};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn project_board_lists(model: &Model) -> Node<Msg> {
|
pub fn project_board_lists(model: &Model) -> Node<Msg> {
|
||||||
let project_page = match &model.page_content {
|
let project_page = match_page!(model, Project; Empty);
|
||||||
PageContent::Project(project_page) => project_page,
|
|
||||||
_ => return empty![],
|
|
||||||
};
|
|
||||||
let rows = project_page.visible_issues.iter().map(|per_epic| {
|
let rows = project_page.visible_issues.iter().map(|per_epic| {
|
||||||
let columns: Vec<Node<Msg>> = per_epic
|
let columns: Vec<Node<Msg>> = per_epic
|
||||||
.per_status_issues
|
.per_status_issues
|
||||||
@ -78,6 +77,7 @@ pub fn project_board_lists(model: &Model) -> Node<Msg> {
|
|||||||
div![C!["rows"], rows]
|
div![C!["rows"], rows]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn project_issue_list(
|
fn project_issue_list(
|
||||||
model: &Model,
|
model: &Model,
|
||||||
status_id: IssueStatusId,
|
status_id: IssueStatusId,
|
||||||
@ -86,7 +86,7 @@ fn project_issue_list(
|
|||||||
) -> Node<Msg> {
|
) -> Node<Msg> {
|
||||||
let issues: Vec<Node<Msg>> = issues
|
let issues: Vec<Node<Msg>> = issues
|
||||||
.iter()
|
.iter()
|
||||||
.map(|issue| project_issue(model, issue))
|
.map(|issue| ProjectIssue { model, issue }.render())
|
||||||
.collect();
|
.collect();
|
||||||
let drop_handler = {
|
let drop_handler = {
|
||||||
let send_status = status_id;
|
let send_status = status_id;
|
||||||
@ -121,100 +121,107 @@ fn project_issue_list(
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
pub struct ProjectIssue<'l> {
|
||||||
let is_dragging = match &model.page_content {
|
pub model: &'l Model,
|
||||||
PageContent::Project(project_page) => project_page.issue_drag.is_dragging(),
|
pub issue: &'l Issue,
|
||||||
_ => false,
|
}
|
||||||
};
|
|
||||||
let avatars: Vec<Node<Msg>> = issue
|
|
||||||
.user_ids
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| model.users_by_id.get(id))
|
|
||||||
.map(|user| {
|
|
||||||
StyledAvatar {
|
|
||||||
avatar_url: user.avatar_url.as_deref(),
|
|
||||||
size: 24,
|
|
||||||
name: &user.name,
|
|
||||||
..StyledAvatar::default()
|
|
||||||
}
|
|
||||||
.render()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let issue_type_icon = StyledIcon {
|
impl<'l> ProjectIssue<'l> {
|
||||||
icon: issue.issue_type.into(),
|
#[inline(always)]
|
||||||
class_list: issue.issue_type.to_str(),
|
pub fn render(self) -> Node<Msg> {
|
||||||
color: Some(issue.issue_type.to_str()),
|
let is_dragging = match &self.model.page_content {
|
||||||
..Default::default()
|
PageContent::Project(project_page) => project_page.issue_drag.is_dragging(),
|
||||||
}
|
_ => false,
|
||||||
.render();
|
};
|
||||||
|
let avatars: Vec<Node<Msg>> = self
|
||||||
|
.issue
|
||||||
|
.user_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| self.model.users_by_id.get(id))
|
||||||
|
.map(|user| {
|
||||||
|
StyledAvatar {
|
||||||
|
avatar_url: user.avatar_url.as_deref(),
|
||||||
|
size: 24,
|
||||||
|
name: &user.name,
|
||||||
|
..StyledAvatar::default()
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let priority_icon = StyledIcon {
|
let issue_type_icon = StyledIcon {
|
||||||
icon: issue.priority.into(),
|
icon: self.issue.issue_type.into(),
|
||||||
class_list: issue.priority.to_str(),
|
class_list: self.issue.issue_type.to_str(),
|
||||||
color: Some(issue.priority.to_str()),
|
color: Some(self.issue.issue_type.to_str()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.render();
|
.render();
|
||||||
|
|
||||||
let issue_id = issue.id;
|
let priority_icon = StyledIcon {
|
||||||
let drag_started = drag_ev(Ev::DragStart, move |ev| {
|
icon: self.issue.priority.into(),
|
||||||
ev.stop_propagation();
|
class_list: self.issue.priority.to_str(),
|
||||||
Some(Msg::PageChanged(PageChanged::Board(
|
color: Some(self.issue.priority.to_str()),
|
||||||
BoardPageChange::IssueDragStarted(issue_id),
|
..Default::default()
|
||||||
)))
|
}
|
||||||
});
|
.render();
|
||||||
let drag_stopped = drag_ev(Ev::DragEnd, move |ev| {
|
|
||||||
ev.stop_propagation();
|
|
||||||
Some(Msg::PageChanged(PageChanged::Board(
|
|
||||||
BoardPageChange::IssueDragStopped(issue_id),
|
|
||||||
)))
|
|
||||||
});
|
|
||||||
let drag_over_handler = drag_ev(Ev::DragEnter, move |ev| {
|
|
||||||
ev.prevent_default();
|
|
||||||
ev.stop_propagation();
|
|
||||||
Some(Msg::PageChanged(PageChanged::Board(
|
|
||||||
BoardPageChange::ChangePosition(issue_id),
|
|
||||||
)))
|
|
||||||
});
|
|
||||||
|
|
||||||
let drag_out = drag_ev(Ev::DragLeave, move |_| {
|
let issue_id = self.issue.id;
|
||||||
Some(Msg::PageChanged(PageChanged::Board(
|
let drag_started = drag_ev(Ev::DragStart, move |ev| {
|
||||||
BoardPageChange::DragLeave(issue_id),
|
ev.stop_propagation();
|
||||||
)))
|
Some(Msg::PageChanged(PageChanged::Board(
|
||||||
});
|
BoardPageChange::IssueDragStarted(issue_id),
|
||||||
let on_click = mouse_ev("click", move |ev| {
|
)))
|
||||||
ev.prevent_default();
|
});
|
||||||
ev.stop_propagation();
|
let drag_stopped = drag_ev(Ev::DragEnd, move |ev| {
|
||||||
seed::Url::new()
|
ev.stop_propagation();
|
||||||
.add_path_part("issues")
|
Some(Msg::PageChanged(PageChanged::Board(
|
||||||
.add_path_part(format!("{}", issue_id))
|
BoardPageChange::IssueDragStopped(issue_id),
|
||||||
.go_and_push();
|
)))
|
||||||
Msg::ChangePage(Page::EditIssue(issue_id))
|
});
|
||||||
});
|
let drag_over_handler = drag_ev(Ev::DragEnter, move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
ev.stop_propagation();
|
||||||
|
Some(Msg::PageChanged(PageChanged::Board(
|
||||||
|
BoardPageChange::ChangePosition(issue_id),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
|
||||||
let href = format!("/issues/{id}", id = issue_id);
|
let drag_out = drag_ev(Ev::DragLeave, move |_| {
|
||||||
|
Some(Msg::PageChanged(PageChanged::Board(
|
||||||
|
BoardPageChange::DragLeave(issue_id),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let on_click = mouse_ev("click", move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
ev.stop_propagation();
|
||||||
|
seed::Url::new()
|
||||||
|
.add_path_part("issues")
|
||||||
|
.add_path_part(format!("{}", issue_id))
|
||||||
|
.go_and_push();
|
||||||
|
Msg::ChangePage(Page::EditIssue(issue_id))
|
||||||
|
});
|
||||||
|
|
||||||
a![
|
a![
|
||||||
drag_started,
|
drag_started,
|
||||||
on_click,
|
on_click,
|
||||||
C!["issueLink"],
|
C!["issueLink"],
|
||||||
attrs![At::Href => href],
|
attrs![At::Href => format!("/issues/{id}", id = issue_id)],
|
||||||
IF![is_dragging => div![C!["dragCover"], drag_over_handler]],
|
IF![is_dragging => div![C!["dragCover"], drag_over_handler]],
|
||||||
div![
|
|
||||||
C!["issue"],
|
|
||||||
attrs![At::Draggable => true],
|
|
||||||
drag_stopped,
|
|
||||||
drag_out,
|
|
||||||
p![C!["title"], issue.title.as_str()],
|
|
||||||
div![
|
div![
|
||||||
C!["bottom"],
|
C!["issue"],
|
||||||
|
attrs![At::Draggable => true],
|
||||||
|
drag_stopped,
|
||||||
|
drag_out,
|
||||||
|
p![C!["title"], self.issue.title.as_str()],
|
||||||
div![
|
div![
|
||||||
div![C!["issueTypeIcon"], issue_type_icon],
|
C!["bottom"],
|
||||||
div![C!["issuePriorityIcon"], priority_icon]
|
div![
|
||||||
],
|
div![C!["issueTypeIcon"], issue_type_icon],
|
||||||
div![C!["assignees"], avatars,],
|
div![C!["issuePriorityIcon"], priority_icon]
|
||||||
|
],
|
||||||
|
div![C!["assignees"], avatars,],
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,52 +43,48 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(m, model.ws.as_ref(), orders);
|
send_ws_msg(m, model.ws.as_ref(), orders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
||||||
let logo_svg = img![
|
let logo_svg = img![
|
||||||
attrs![At::Src => "/logo2.svg"; At::Style => "background: rgba(244,244,244,.8); border-radius: 24px;"]
|
attrs![At::Src => "/logo2.svg"; At::Style => "background: rgba(244,244,244,.8); border-radius: 24px;"]
|
||||||
];
|
];
|
||||||
|
|
||||||
let user_icon = match model.user.as_ref() {
|
let user_icon = model.user.as_ref().map_or_else(
|
||||||
Some(user) => {
|
|| {
|
||||||
let avatar = StyledAvatar {
|
StyledIcon {
|
||||||
avatar_url: user.avatar_url.as_deref(),
|
icon: Icon::User,
|
||||||
size: 27,
|
size: Some(21),
|
||||||
name: &user.name,
|
..Default::default()
|
||||||
..StyledAvatar::default()
|
|
||||||
}
|
}
|
||||||
.render();
|
.render()
|
||||||
i![C!["styledIcon"], avatar]
|
},
|
||||||
}
|
|user| {
|
||||||
_ => StyledIcon {
|
i![
|
||||||
icon: Icon::User,
|
C!["styledIcon"],
|
||||||
size: Some(21),
|
StyledAvatar {
|
||||||
..Default::default()
|
avatar_url: user.avatar_url.as_deref(),
|
||||||
}
|
size: 27,
|
||||||
.render(),
|
name: &user.name,
|
||||||
};
|
..StyledAvatar::default()
|
||||||
|
}
|
||||||
let messages = if model.messages.is_empty() {
|
.render()
|
||||||
empty![]
|
]
|
||||||
} else {
|
},
|
||||||
navbar_left_item(
|
);
|
||||||
"Messages",
|
|
||||||
Icon::Message,
|
|
||||||
None,
|
|
||||||
Some(mouse_ev(Ev::Click, |ev| {
|
|
||||||
ev.prevent_default();
|
|
||||||
Msg::ToggleTooltip(styled_tooltip::TooltipVariant::Messages)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let issue_nav = if model.issue_statuses.is_empty() {
|
let issue_nav = if model.issue_statuses.is_empty() {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![
|
vec![
|
||||||
navbar_left_item("Search issues", Icon::Search, None, None),
|
navbar_left_item(
|
||||||
|
"Search issues",
|
||||||
|
StyledIcon::from(Icon::Search).render(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
navbar_left_item(
|
navbar_left_item(
|
||||||
"Create Issue",
|
"Create Issue",
|
||||||
Icon::Plus,
|
StyledIcon::from(Icon::Plus).render(),
|
||||||
Some("/add-issue"),
|
Some("/add-issue"),
|
||||||
Some(mouse_ev("click", |ev| {
|
Some(mouse_ev("click", |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
@ -119,10 +115,18 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
|||||||
div![
|
div![
|
||||||
C!["bottom"],
|
C!["bottom"],
|
||||||
navbar_left_item("Profile", user_icon, Some("/profile"), Some(go_to_profile)),
|
navbar_left_item("Profile", user_icon, Some("/profile"), Some(go_to_profile)),
|
||||||
messages,
|
IF![!model.messages.is_empty() => navbar_left_item(
|
||||||
|
"Messages",
|
||||||
|
StyledIcon::from(Icon::Message).render(),
|
||||||
|
None,
|
||||||
|
Some(mouse_ev(Ev::Click, |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
Msg::ToggleTooltip(styled_tooltip::TooltipVariant::Messages)
|
||||||
|
})),
|
||||||
|
)],
|
||||||
IF![model.show_extras => about_tooltip(
|
IF![model.show_extras => about_tooltip(
|
||||||
model,
|
model,
|
||||||
navbar_left_item("About", Icon::Help, None, None)
|
navbar_left_item("About", StyledIcon::from(Icon::Help).render(), None, None)
|
||||||
)],
|
)],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -130,15 +134,12 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn navbar_left_item<I>(
|
fn navbar_left_item(
|
||||||
text: &str,
|
text: &str,
|
||||||
icon: I,
|
icon: Node<Msg>,
|
||||||
href: Option<&str>,
|
href: Option<&str>,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
) -> Node<Msg>
|
) -> Node<Msg> {
|
||||||
where
|
|
||||||
I: IntoNavItemIcon,
|
|
||||||
{
|
|
||||||
let styled_icon = icon.into_nav_item_icon();
|
let styled_icon = icon.into_nav_item_icon();
|
||||||
|
|
||||||
a![
|
a![
|
||||||
|
@ -221,6 +221,12 @@ pub struct Project {
|
|||||||
pub time_tracking: TimeTracking,
|
pub time_tracking: TimeTracking,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Project {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Issue {
|
pub struct Issue {
|
||||||
pub id: EpicId,
|
pub id: EpicId,
|
||||||
@ -291,6 +297,12 @@ pub struct User {
|
|||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(Queryable))]
|
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct UserProject {
|
pub struct UserProject {
|
||||||
|
Loading…
Reference in New Issue
Block a user