Delete issue status full logic

This commit is contained in:
Adrian Wozniak 2020-05-10 13:10:31 +02:00
parent b2cbb251d1
commit 4d07639e90
10 changed files with 148 additions and 21 deletions

View File

@ -0,0 +1,3 @@
.deleteIssueStatus {
min-width: 800px;
}

View File

@ -55,3 +55,21 @@
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
} }
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > .removeColumn {
text-align: center;
display: flex;
justify-content: space-around;
padding-top: 15px;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:hover > .columnName > .removeColumn,
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:focus > .columnName > .removeColumn,
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:active > .columnName > .removeColumn {
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > .issueCount {
text-transform: none;
padding-top: 15px;
}

View File

@ -67,7 +67,7 @@
--font-black: "CircularStdBlack"; --font-black: "CircularStdBlack";
} }
:root { :root { /* user without avatar */
--avatar-color-1: rgb(218, 118, 87); --avatar-color-1: rgb(218, 118, 87);
--avatar-color-2: rgb(106, 218, 87); --avatar-color-2: rgb(106, 218, 87);
--avatar-color-3: rgb(87, 132, 218); --avatar-color-3: rgb(87, 132, 218);

View File

@ -240,6 +240,9 @@ pub enum Msg {
AddIssue, AddIssue,
DeleteIssue(IssueId), DeleteIssue(IssueId),
// issue statuses
DeleteIssueStatus(IssueStatusId),
// comments // comments
SaveComment, SaveComment,
DeleteComment(CommentId), DeleteComment(CommentId),

View File

@ -0,0 +1,43 @@
use seed::prelude::*;
use jirs_data::{IssueStatusId, WsMsg};
use crate::api::send_ws_msg;
use crate::model::{DeleteIssueStatusModal, ModalType, Model};
use crate::shared::styled_confirm_modal::StyledConfirmModal;
use crate::shared::ToNode;
use crate::{model, Msg};
pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
let _modal: &mut Box<DeleteIssueStatusModal> =
match model.modals.iter_mut().find_map(|modal| match modal {
ModalType::DeleteIssueStatusModal(modal) => Some(modal),
_ => None,
}) {
Some(m) => m,
_ => return,
};
match msg {
Msg::DeleteIssueStatus(issue_status_id) => {
send_ws_msg(WsMsg::IssueStatusDelete(*issue_status_id));
}
Msg::WsMsg(WsMsg::IssueStatusDelete(_)) => {
orders.skip().perform_cmd(Msg::ModalDropped);
}
_ => (),
};
}
pub fn view(_model: &model::Model, issue_status_id: IssueStatusId) -> Node<Msg> {
StyledConfirmModal::build()
.title("Delete column")
.cancel_text("No")
.confirm_text("Yes")
.on_confirm(mouse_ev(Ev::Click, move |_| {
Msg::DeleteIssueStatus(issue_status_id)
}))
.message("Are you sure you want to delete column?")
.build()
.into_node()
}

View File

@ -292,10 +292,10 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
pub fn view(model: &Model, modal: &EditIssueModal) -> Node<Msg> { pub fn view(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
div![ div![
attrs![At::Class => "issueDetails"], class!["issueDetails"],
top_modal_row(model, modal), top_modal_row(model, modal),
div![ div![
attrs![At::Class => "content"], class!["content"],
left_modal_column(model, modal), left_modal_column(model, modal),
right_modal_column(model, modal), right_modal_column(model, modal),
], ],

View File

@ -11,6 +11,7 @@ use crate::{model, FieldChange, FieldId, Msg};
mod add_issue; mod add_issue;
mod confirm_delete_issue; mod confirm_delete_issue;
mod delete_issue_status;
mod issue_details; mod issue_details;
pub mod time_tracking; pub mod time_tracking;
@ -57,6 +58,7 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
} }
add_issue::update(msg, model, orders); add_issue::update(msg, model, orders);
issue_details::update(msg, model, orders); issue_details::update(msg, model, orders);
delete_issue_status::update(msg, model, orders);
} }
pub fn view(model: &model::Model) -> Node<Msg> { pub fn view(model: &model::Model) -> Node<Msg> {
@ -90,6 +92,9 @@ pub fn view(model: &model::Model) -> Node<Msg> {
.into_node() .into_node()
} }
ModalType::TimeTracking(issue_id) => time_tracking::view(model, *issue_id), ModalType::TimeTracking(issue_id) => time_tracking::view(model, *issue_id),
ModalType::DeleteIssueStatusModal(delete_issue_modal) => {
delete_issue_status::view(model, delete_issue_modal.delete_id)
}
}) })
.collect(); .collect();
section![id!["modals"], modals] section![id!["modals"], modals]

View File

@ -21,6 +21,7 @@ pub enum ModalType {
DeleteIssueConfirm(IssueId), DeleteIssueConfirm(IssueId),
DeleteCommentConfirm(CommentId), DeleteCommentConfirm(CommentId),
TimeTracking(IssueId), TimeTracking(IssueId),
DeleteIssueStatusModal(Box<DeleteIssueStatusModal>),
} }
#[derive(Clone, Debug, PartialOrd, PartialEq)] #[derive(Clone, Debug, PartialOrd, PartialEq)]
@ -30,9 +31,24 @@ pub struct CommentForm {
pub creating: bool, pub creating: bool,
} }
#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct DeleteIssueStatusModal {
pub delete_id: IssueStatusId,
pub receiver_id: Option<IssueStatusId>,
}
impl DeleteIssueStatusModal {
pub fn new(delete_id: IssueStatusId) -> Self {
Self {
delete_id,
receiver_id: None,
}
}
}
#[derive(Clone, Debug, PartialOrd, PartialEq)] #[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct EditIssueModal { pub struct EditIssueModal {
pub id: i32, pub id: IssueId,
pub link_copied: bool, pub link_copied: bool,
pub payload: UpdateIssuePayload, pub payload: UpdateIssuePayload,
pub top_type_state: StyledSelectState, pub top_type_state: StyledSelectState,

View File

@ -1,11 +1,14 @@
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use wasm_bindgen::__rt::std::collections::HashMap;
use jirs_data::{ use jirs_data::{
IssueStatus, IssueStatusId, ProjectCategory, TimeTracking, ToVec, UpdateProjectPayload, WsMsg, IssueStatus, IssueStatusId, ProjectCategory, TimeTracking, ToVec, UpdateProjectPayload, WsMsg,
}; };
use crate::api::send_ws_msg; use crate::api::send_ws_msg;
use crate::model::{Model, Page, PageContent, ProjectSettingsPage}; use crate::model::{
DeleteIssueStatusModal, ModalType, Model, Page, PageContent, ProjectSettingsPage,
};
use crate::shared::styled_button::StyledButton; use crate::shared::styled_button::StyledButton;
use crate::shared::styled_checkbox::StyledCheckbox; use crate::shared::styled_checkbox::StyledCheckbox;
use crate::shared::styled_editor::StyledEditor; use crate::shared::styled_editor::StyledEditor;
@ -31,12 +34,14 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
Msg::WsMsg(WsMsg::AuthorizeLoaded(..)) => { Msg::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
send_ws_msg(WsMsg::ProjectRequest); send_ws_msg(WsMsg::ProjectRequest);
send_ws_msg(WsMsg::IssueStatusesRequest); send_ws_msg(WsMsg::IssueStatusesRequest);
send_ws_msg(WsMsg::ProjectIssuesRequest);
} }
Msg::ChangePage(Page::ProjectSettings) => { Msg::ChangePage(Page::ProjectSettings) => {
build_page_content(model); build_page_content(model);
if model.user.is_some() { if model.user.is_some() {
send_ws_msg(WsMsg::ProjectRequest); send_ws_msg(WsMsg::ProjectRequest);
send_ws_msg(WsMsg::IssueStatusesRequest); send_ws_msg(WsMsg::IssueStatusesRequest);
send_ws_msg(WsMsg::ProjectIssuesRequest);
} }
} }
Msg::WsMsg(WsMsg::ProjectLoaded(..)) => { Msg::WsMsg(WsMsg::ProjectLoaded(..)) => {
@ -245,6 +250,12 @@ pub fn view(model: &model::Model) -> Node<Msg> {
let width = 100f64 / (model.issue_statuses.len() + 1) as f64; let width = 100f64 / (model.issue_statuses.len() + 1) as f64;
let column_style = format!("width: calc({width}% - 10px)", width = width); let column_style = format!("width: calc({width}% - 10px)", width = width);
let mut per_column_issue_count = HashMap::new();
for issue in model.issues.iter() {
*per_column_issue_count
.entry(issue.issue_status_id)
.or_insert(0) += 1;
}
let columns: Vec<Node<Msg>> = model let columns: Vec<Node<Msg>> = model
.issue_statuses .issue_statuses
.iter() .iter()
@ -284,21 +295,40 @@ pub fn view(model: &model::Model) -> Node<Msg> {
.into_node(); .into_node();
div![ div![
class!["columnPreview"], class!["columnPreview"],
div![class!["columnName"], input] div![class!["columnName"], input]
] ]
} else { } else {
let edit = mouse_ev(Ev::Click, move |_| { let on_edit = mouse_ev(Ev::Click, move |_| {
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(Some(id)))) Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(Some(id))))
}); });
let issue_count_in_column = per_column_issue_count.get(&id).cloned().unwrap_or_default();
let delete_row = if issue_count_in_column == 0 {
let on_delete = mouse_ev(Ev::Click, move |ev| {
ev.prevent_default();
ev.stop_propagation();
Msg::ModalOpened(Box::new(ModalType::DeleteIssueStatusModal(Box::new(DeleteIssueStatusModal::new(id)))))
});
let delete = StyledButton::build()
.primary()
.add_class("removeColumn")
.icon(Icon::Trash)
.on_click(on_delete)
.build()
.into_node();
div![class!["removeColumn"], delete]
} else {
div![class!["issueCount"], format!("Issues in column: {}", issue_count_in_column)]
};
div![ div![
class!["columnPreview"], class!["columnPreview"],
attrs![At::Style => column_style.as_str(), At::Draggable => "true", At::DropZone => "true"], attrs![At::Style => column_style.as_str(), At::Draggable => "true", At::DropZone => "true"],
div![class!["columnName"], span![is.name], edit], div![class!["columnName"], span![is.name], on_edit, delete_row],
drag_started, drag_started,
drag_stopped, drag_stopped,
drag_over_handler, drag_over_handler,
drag_out, drag_out,
] ]
} }
}) })
@ -352,7 +382,12 @@ pub fn view(model: &model::Model) -> Node<Msg> {
let project_section = vec![div![class!["formContainer"], form]]; let project_section = vec![div![class!["formContainer"], form]];
inner_layout(model, "projectSettings", project_section, empty![]) inner_layout(
model,
"projectSettings",
project_section,
crate::modal::view(model),
)
} }
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) { fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {

View File

@ -29,11 +29,11 @@ impl Variant {
#[derive(Debug)] #[derive(Debug)]
pub struct StyledModal { pub struct StyledModal {
pub variant: Variant, variant: Variant,
pub width: usize, width: usize,
pub with_icon: bool, with_icon: bool,
pub children: Vec<Node<Msg>>, children: Vec<Node<Msg>>,
pub class_list: Vec<String>, class_list: Vec<String>,
} }
impl ToNode for StyledModal { impl ToNode for StyledModal {
@ -63,6 +63,10 @@ impl StyledModalBuilder {
self self
} }
// pub fn center(mut self) -> Self {
// self.variant(Variant::Center)
// }
pub fn width(mut self, width: usize) -> Self { pub fn width(mut self, width: usize) -> Self {
self.width = Some(width); self.width = Some(width);
self self