Add delete epic modal

This commit is contained in:
Adrian Woźniak 2021-01-16 14:31:31 +01:00
parent 915ff46281
commit 87d2ea48a3
24 changed files with 267 additions and 78 deletions

View File

@ -28,21 +28,21 @@ pub struct LoadProjectIssues {
#[derive(Default, Execute)] #[derive(Default, Execute)]
#[db_exec(result = "Issue", schema = "issues")] #[db_exec(result = "Issue", schema = "issues")]
pub struct UpdateIssue { pub struct UpdateIssue {
pub issue_id: i32, pub issue_id: jirs_data::IssueId,
pub title: Option<String>, pub title: Option<String>,
pub issue_type: Option<IssueType>, pub issue_type: Option<IssueType>,
pub priority: Option<IssuePriority>, pub priority: Option<IssuePriority>,
pub list_position: Option<i32>, pub list_position: Option<jirs_data::ListPosition>,
pub description: Option<String>, pub description: Option<String>,
pub description_text: Option<String>, pub description_text: Option<String>,
pub estimate: Option<i32>, pub estimate: Option<i32>,
pub time_spent: Option<i32>, pub time_spent: Option<i32>,
pub time_remaining: Option<i32>, pub time_remaining: Option<i32>,
pub project_id: Option<i32>, pub project_id: Option<jirs_data::ProjectId>,
pub user_ids: Option<Vec<i32>>, pub user_ids: Option<Vec<jirs_data::UserId>>,
pub reporter_id: Option<i32>, pub reporter_id: Option<jirs_data::UserId>,
pub issue_status_id: Option<i32>, pub issue_status_id: Option<jirs_data::IssueStatusId>,
pub epic_id: Option<Option<i32>>, pub epic_id: Option<Option<jirs_data::EpicId>>,
} }
impl UpdateIssue { impl UpdateIssue {

View File

@ -1,4 +1,3 @@
use jirs_data::{EpicId, IssueStatusId, ListPosition};
use { use {
crate::{WebSocketActor, WsHandler, WsResult}, crate::{WebSocketActor, WsHandler, WsResult},
database_actor::{ database_actor::{
@ -6,7 +5,10 @@ use {
issues::{LoadProjectIssues, UpdateIssue}, issues::{LoadProjectIssues, UpdateIssue},
}, },
futures::executor::block_on, futures::executor::block_on,
jirs_data::{CreateIssuePayload, IssueAssignee, IssueFieldId, IssueId, PayloadVariant, WsMsg}, jirs_data::{
CreateIssuePayload, IssueAssignee, IssueFieldId, IssueId, IssueStatusId, ListPosition,
PayloadVariant, WsMsg,
},
std::collections::HashMap, std::collections::HashMap,
}; };
@ -257,7 +259,7 @@ impl WsHandler<LoadIssues> for WebSocketActor {
} }
} }
pub struct SyncIssueListPosition(pub Vec<(IssueId, ListPosition, IssueStatusId, Option<EpicId>)>); pub struct SyncIssueListPosition(pub Vec<(IssueId, ListPosition, IssueStatusId, Option<IssueId>)>);
impl WsHandler<SyncIssueListPosition> for WebSocketActor { impl WsHandler<SyncIssueListPosition> for WebSocketActor {
fn handle_msg(&mut self, msg: SyncIssueListPosition, ctx: &mut Self::Context) -> WsResult { fn handle_msg(&mut self, msg: SyncIssueListPosition, ctx: &mut Self::Context) -> WsResult {

View File

@ -108,6 +108,62 @@
&.debugModal { &.debugModal {
padding-left: 15px; padding-left: 15px;
} }
&.deleteEpic {
> section {
> .header {
background: var(--danger);
color: var(--secondary);
padding: {
top: 15px;
right: 40px;
left: 40px;
bottom: 15px;
};
}
> .warning {
margin: {
bottom: 10px;
top: 10px;
}
padding: {
right: 40px;
left: 40px;
};
}
> .relatedList {
list-style: none;
padding: {
right: 40px;
left: 40px;
bottom: 20px;
};
> li {
> .relatedIssue {
list-style: none;
line-height: 22px;
font-size: 14px;
> a {
color: var(--textLink);
.styledIcon.link {
font-size: 14px;
margin: {
right: 5px;
}
}
}
}
}
}
}
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
use seed::prelude::WebSocketMessage; use seed::prelude::WebSocketMessage;
use jirs_data::{IssueId, IssueStatusId, WsMsg}; use jirs_data::{EpicId, IssueStatusId, WsMsg};
use crate::shared::styled_editor::Mode as TabMode; use crate::shared::styled_editor::Mode as TabMode;
use crate::FieldId; use crate::FieldId;
@ -16,10 +16,10 @@ pub enum FieldChange {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum BoardPageChange { pub enum BoardPageChange {
// dragging // dragging
IssueDragStarted(IssueId), IssueDragStarted(EpicId),
IssueDragStopped(IssueId), IssueDragStopped(EpicId),
DragLeave(IssueId), DragLeave(EpicId),
ExchangePosition(IssueId), ExchangePosition(EpicId),
IssueDragOverStatus(IssueStatusId), IssueDragOverStatus(IssueStatusId),
IssueDropZone(IssueStatusId), IssueDropZone(IssueStatusId),
} }

View File

@ -104,7 +104,7 @@ pub enum Msg {
// issues // issues
AddIssue, AddIssue,
DeleteIssue(IssueId), DeleteIssue(EpicId),
// epics // epics
AddEpic, AddEpic,
@ -237,7 +237,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
crate::modals::update(&msg, model, orders); crate::modals::update(&msg, model, orders);
match model.page { match model.page {
Page::Project | Page::AddIssue | Page::EditIssue(..) => { Page::Project | Page::AddIssue | Page::EditIssue(..) | Page::DeleteEpic(..) => {
pages::project_page::update(msg, model, orders) pages::project_page::update(msg, model, orders)
} }
Page::ProjectSettings => pages::project_settings_page::update(msg, model, orders), Page::ProjectSettings => pages::project_settings_page::update(msg, model, orders),
@ -255,8 +255,9 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
fn view(model: &model::Model) -> Node<Msg> { fn view(model: &model::Model) -> Node<Msg> {
match model.page { match model.page {
Page::Project | Page::AddIssue => pages::project_page::view(model), Page::Project | Page::AddIssue | Page::DeleteEpic(..) | Page::EditIssue(..) => {
Page::EditIssue(_id) => pages::project_page::view(model), pages::project_page::view(model)
}
Page::ProjectSettings => pages::project_settings_page::view(model), Page::ProjectSettings => pages::project_settings_page::view(model),
Page::SignIn => pages::sign_in_page::view(model), Page::SignIn => pages::sign_in_page::view(model),
Page::SignUp => pages::sign_up_page::view(model), Page::SignUp => pages::sign_up_page::view(model),
@ -278,6 +279,10 @@ fn resolve_page(url: Url) -> Option<Page> {
Some(Ok(id)) => Page::EditIssue(id), Some(Ok(id)) => Page::EditIssue(id),
_ => return None, _ => return None,
}, },
"delete-epic" => match url.path().get(1).as_ref().map(|s| s.parse::<i32>()) {
Some(Ok(id)) => Page::DeleteEpic(id),
_ => return None,
},
"profile" => Page::Profile, "profile" => Page::Profile,
"add-issue" => Page::AddIssue, "add-issue" => Page::AddIssue,
"project-settings" => Page::ProjectSettings, "project-settings" => Page::ProjectSettings,

View File

@ -14,7 +14,7 @@ pub fn view(model: &Model) -> Node<Msg> {
.width(1200) .width(1200)
.add_class("debugModal") .add_class("debugModal")
.center() .center()
.children(vec![code]) .children(vec![code].into_iter())
.build() .build()
.into_node() .into_node()
} }

View File

@ -0,0 +1,5 @@
pub use {model::*, update::*, view::*};
mod model;
mod update;
mod view;

View File

@ -0,0 +1,30 @@
use {
crate::model,
jirs_data::{EpicId, IssueId},
};
#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct Model {
pub epic_id: EpicId,
pub related_issues: Vec<IssueId>,
}
impl Model {
pub fn new(issue_id: i32, model: &mut model::Model) -> Self {
let related_issues = model
.issues
.iter()
.filter_map(|issue| {
if issue.epic_id == Some(issue_id) {
Some(issue.id)
} else {
None
}
})
.collect();
Self {
epic_id: issue_id,
related_issues,
}
}
}

View File

@ -0,0 +1,10 @@
use {
crate::{shared::go_to_board, Msg},
seed::prelude::*,
};
pub fn update(msg: &Msg, _model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
if let Msg::ModalDropped = msg {
go_to_board(orders);
};
}

View File

@ -0,0 +1,55 @@
use {
crate::{
modals::epic_delete::Model,
model,
shared::{styled_confirm_modal::*, styled_icon::*, styled_modal::*, ToNode},
Msg,
},
seed::{prelude::*, *},
};
pub fn view(model: &model::Model, modal: &Model) -> Node<Msg> {
if modal.related_issues.is_empty() {
StyledConfirmModal::build()
.title("Delete empty epic")
.cancel_text("Cancel")
.confirm_text("Delete epic")
.build()
.into_node()
} else {
StyledModal::build()
.add_class("deleteEpic")
.center()
.width(600)
.child(warning(model, modal))
.build()
.into_node()
}
}
fn warning(model: &model::Model, modal: &Model) -> Node<Msg> {
let issues: Vec<Node<Msg>> = modal
.related_issues
.iter()
.flat_map(|id| model.issues_by_id.get(id))
.map(|issue| {
let link = StyledIcon::build(Icon::Link).build().into_node();
li![div![
C!["relatedIssue"],
a![
attrs! {"href" => format!("/issues/{}", issue.id)},
link,
issue.title.as_str()
]
]]
})
.collect();
section![
h3![C!["header"], "Cannot delete epic"],
div![
C!["warning"],
"This epic have related issues. Please move or delete them first."
],
ol![C!["relatedList"], issues]
]
}

View File

@ -114,7 +114,7 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
.add_class("addIssue") .add_class("addIssue")
.width(0) .width(0)
.variant(crate::shared::styled_modal::Variant::Center) .variant(crate::shared::styled_modal::Variant::Center)
.children(vec![form]) .child(form)
.build() .build()
.into_node() .into_node()
} }

View File

@ -9,13 +9,13 @@ use {
}, },
EditIssueModalSection, FieldId, Msg, EditIssueModalSection, FieldId, Msg,
}, },
jirs_data::{Issue, IssueFieldId, IssueId, TimeTracking, UpdateIssuePayload}, jirs_data::{EpicId, Issue, IssueFieldId, TimeTracking, UpdateIssuePayload},
seed::prelude::*, seed::prelude::*,
}; };
#[derive(Clone, Debug, PartialOrd, PartialEq)] #[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct Model { pub struct Model {
pub id: IssueId, pub id: EpicId,
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

@ -2,6 +2,7 @@ pub use {epic_field::*, update::*, view::*};
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub mod debug; pub mod debug;
pub mod epic_delete;
pub mod issue_statuses_delete; pub mod issue_statuses_delete;
pub mod issues_create; pub mod issues_create;
pub mod issues_delete; pub mod issues_delete;

View File

@ -12,7 +12,7 @@ use {
}, },
EditIssueModalSection, FieldId, Msg, EditIssueModalSection, FieldId, Msg,
}, },
jirs_data::{IssueFieldId, IssueId, TimeTracking}, jirs_data::{EpicId, IssueFieldId, TimeTracking},
seed::{prelude::*, *}, seed::{prelude::*, *},
}; };
@ -25,7 +25,7 @@ pub fn value_for_time_tracking(v: &Option<i32>, time_tracking_type: &TimeTrackin
} }
} }
pub fn view(model: &Model, issue_id: IssueId) -> Node<Msg> { pub fn view(model: &Model, issue_id: EpicId) -> Node<Msg> {
if model.issues_by_id.get(&issue_id).is_none() { if model.issues_by_id.get(&issue_id).is_none() {
return Node::Empty; return Node::Empty;
} }
@ -73,12 +73,7 @@ pub fn view(model: &Model, issue_id: IssueId) -> Node<Msg> {
StyledModal::build() StyledModal::build()
.add_class("timeTrackingModal") .add_class("timeTrackingModal")
.children(vec![ .children(vec![modal_title, tracking, inputs, div![C!["actions"], close]].into_iter())
modal_title,
tracking,
inputs,
div![C!["actions"], close],
])
.width(400) .width(400)
.build() .build()
.into_node() .into_node()

View File

@ -43,6 +43,9 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
Msg::ChangePage(Page::EditIssue(issue_id)) => push_edit_modal(*issue_id, model, orders), Msg::ChangePage(Page::EditIssue(issue_id)) => push_edit_modal(*issue_id, model, orders),
Msg::ChangePage(Page::AddIssue) => push_add_modal(model, orders), Msg::ChangePage(Page::AddIssue) => push_add_modal(model, orders),
Msg::ChangePage(Page::DeleteEpic(issue_id)) => {
push_delete_epic_modal(*issue_id, model, orders)
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
Msg::GlobalKeyDown { key, .. } if key.eq("#") => { Msg::GlobalKeyDown { key, .. } if key.eq("#") => {
@ -61,10 +64,13 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
_ => (), _ => (),
} }
use crate::modals::{issue_statuses_delete, issues_create, issues_edit}; {
issues_create::update(msg, model, orders); use crate::modals::*;
issues_edit::update(msg, model, orders); issues_create::update(msg, model, orders);
issue_statuses_delete::update(msg, model, orders); issues_edit::update(msg, model, orders);
issue_statuses_delete::update(msg, model, orders);
epic_delete::update(msg, model, orders);
}
} }
fn push_add_modal(model: &mut Model, _orders: &mut impl Orders<Msg>) { fn push_add_modal(model: &mut Model, _orders: &mut impl Orders<Msg>) {
@ -75,12 +81,18 @@ fn push_add_modal(model: &mut Model, _orders: &mut impl Orders<Msg>) {
}))); })));
} }
fn push_delete_epic_modal(issue_id: i32, model: &mut Model, _orders: &mut impl Orders<Msg>) {
use crate::modals::epic_delete::Model;
let modal = Model::new(issue_id, model);
model.modals.push(ModalType::DeleteEpic(Box::new(modal)));
}
fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Msg>) { fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Msg>) {
let time_tracking_type = model let time_tracking_type = model
.project .project
.as_ref() .as_ref()
.map(|p| p.time_tracking) .map(|p| p.time_tracking)
.unwrap_or_else(|| TimeTracking::Untracked); .unwrap_or(TimeTracking::Untracked);
let modal = { let modal = {
let issue = match model.issues_by_id.get(&issue_id) { let issue = match model.issues_by_id.get(&issue_id) {
Some(issue) => issue, Some(issue) => issue,

View File

@ -18,7 +18,7 @@ pub fn view(model: &Model) -> Node<Msg> {
StyledModal::build() StyledModal::build()
.variant(crate::shared::styled_modal::Variant::Center) .variant(crate::shared::styled_modal::Variant::Center)
.width(1040) .width(1040)
.children(vec![details]) .child(details)
.build() .build()
.into_node() .into_node()
} else { } else {
@ -45,6 +45,7 @@ pub fn view(model: &Model) -> Node<Msg> {
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
ModalType::DebugModal => crate::modals::debug::view(model), ModalType::DebugModal => crate::modals::debug::view(model),
ModalType::DeleteEpic(modal) => crate::modals::epic_delete::view(model, modal),
}) })
.collect(); .collect();
section![id!["modals"], modals] section![id!["modals"], modals]

View File

@ -27,10 +27,11 @@ pub trait IssueModal {
#[derive(Clone, Debug, PartialOrd, PartialEq)] #[derive(Clone, Debug, PartialOrd, PartialEq)]
pub enum ModalType { pub enum ModalType {
AddIssue(Box<crate::modals::issues_create::Model>), AddIssue(Box<crate::modals::issues_create::Model>),
EditIssue(IssueId, Box<crate::modals::issues_edit::Model>), EditIssue(EpicId, Box<crate::modals::issues_edit::Model>),
DeleteIssueConfirm(IssueId), DeleteEpic(Box<crate::modals::epic_delete::Model>),
DeleteIssueConfirm(EpicId),
DeleteCommentConfirm(CommentId), DeleteCommentConfirm(CommentId),
TimeTracking(IssueId), TimeTracking(EpicId),
DeleteIssueStatusModal(Box<crate::modals::issue_statuses_delete::Model>), DeleteIssueStatusModal(Box<crate::modals::issue_statuses_delete::Model>),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
DebugModal, DebugModal,
@ -46,7 +47,8 @@ pub struct CommentForm {
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum Page { pub enum Page {
Project, Project,
EditIssue(IssueId), EditIssue(EpicId),
DeleteEpic(EpicId),
AddIssue, AddIssue,
ProjectSettings, ProjectSettings,
SignIn, SignIn,
@ -62,6 +64,7 @@ impl Page {
match self { match self {
Page::Project => "/board".to_string(), Page::Project => "/board".to_string(),
Page::EditIssue(id) => format!("/issues/{id}", id = id), Page::EditIssue(id) => format!("/issues/{id}", id = id),
Page::DeleteEpic(id) => format!("/delete-epic/{id}", id = id),
Page::AddIssue => "/add-issue".to_string(), Page::AddIssue => "/add-issue".to_string(),
Page::ProjectSettings => "/project-settings".to_string(), Page::ProjectSettings => "/project-settings".to_string(),
Page::SignIn => "/login".to_string(), Page::SignIn => "/login".to_string(),
@ -147,7 +150,7 @@ pub struct Model {
pub current_user_project: Option<UserProject>, pub current_user_project: Option<UserProject>,
pub issues: Vec<Issue>, pub issues: Vec<Issue>,
pub issues_by_id: HashMap<IssueId, Issue>, pub issues_by_id: HashMap<EpicId, Issue>,
pub user: Option<User>, pub user: Option<User>,
pub users: Vec<User>, pub users: Vec<User>,

View File

@ -4,12 +4,12 @@ use {crate::shared::drag::DragState, jirs_data::*, std::collections::HashMap};
pub struct StatusIssueIds { pub struct StatusIssueIds {
pub status_id: IssueStatusId, pub status_id: IssueStatusId,
pub status_name: IssueStatusName, pub status_name: IssueStatusName,
pub issue_ids: Vec<IssueId>, pub issue_ids: Vec<EpicId>,
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct EpicIssuePerStatus { pub struct EpicIssuePerStatus {
pub epic_name: Option<EpicName>, pub epic_ref: Option<(EpicId, EpicName)>,
pub per_status_issues: Vec<StatusIssueIds>, pub per_status_issues: Vec<StatusIssueIds>,
} }
@ -57,7 +57,7 @@ impl ProjectPage {
for epic in epics { for epic in epics {
let mut per_epic_map = EpicIssuePerStatus { let mut per_epic_map = EpicIssuePerStatus {
epic_name: epic.map(|(_, name)| name.to_string()), epic_ref: epic.map(|(id, name)| (id, name.to_string())),
..Default::default() ..Default::default()
}; };

View File

@ -1,4 +1,3 @@
use crate::shared::styled_button::StyledButton;
use { use {
crate::{ crate::{
model::PageContent, model::PageContent,
@ -9,6 +8,8 @@ use {
seed::{prelude::*, *}, seed::{prelude::*, *},
}; };
use crate::shared::styled_button::StyledButton;
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 &model.page_content {
PageContent::Project(project_page) => project_page, PageContent::Project(project_page) => project_page,
@ -32,8 +33,9 @@ pub fn project_board_lists(model: &Model) -> Node<Msg> {
) )
}) })
.collect(); .collect();
let epic_name = match per_epic.epic_name.as_deref() { let epic_name = match per_epic.epic_ref.as_ref() {
Some(name) => { Some((id, name)) => {
let id = *id;
let edit_button = StyledButton::build() let edit_button = StyledButton::build()
.empty() .empty()
.icon(Icon::EditAlt) .icon(Icon::EditAlt)
@ -42,6 +44,15 @@ pub fn project_board_lists(model: &Model) -> Node<Msg> {
let delete_button = StyledButton::build() let delete_button = StyledButton::build()
.empty() .empty()
.icon(Icon::DeleteAlt) .icon(Icon::DeleteAlt)
.on_click(mouse_ev("click", move |ev| {
ev.stop_propagation();
ev.prevent_default();
seed::Url::new()
.add_path_part("delete-epic")
.add_path_part(id.to_string())
.go_and_push();
Msg::ChangePage(Page::DeleteEpic(id))
}))
.build() .build()
.into_node(); .into_node();

View File

@ -109,15 +109,9 @@ pub fn render(values: StyledConfirmModal) -> Node<Msg> {
StyledModal::build() StyledModal::build()
.width(600) .width(600)
.children(vec![ .child(div![C!["title"], title])
div![attrs![At::Class => "title"], title], .child(message_node)
message_node, .child(div![C!["actions"], confirm_button, cancel_button])
div![
attrs![At::Class => "actions"],
confirm_button,
cancel_button
],
])
.add_class("confirmModal") .add_class("confirmModal")
.build() .build()
.into_node() .into_node()

View File

@ -58,38 +58,48 @@ pub struct StyledModalBuilder<'l> {
} }
impl<'l> StyledModalBuilder<'l> { impl<'l> StyledModalBuilder<'l> {
#[inline]
pub fn variant(mut self, variant: Variant) -> Self { pub fn variant(mut self, variant: Variant) -> Self {
self.variant = Some(variant); self.variant = Some(variant);
self self
} }
#[inline]
pub fn center(self) -> Self { pub fn center(self) -> Self {
self.variant(Variant::Center) self.variant(Variant::Center)
} }
#[inline]
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
} }
// pub fn with_icon(mut self, with_icon: bool) -> Self { #[inline]
// self.with_icon = Some(with_icon); pub fn child(mut self, child: Node<Msg>) -> Self {
// self self.children.get_or_insert(vec![]).push(child);
// }
pub fn children(mut self, children: Vec<Node<Msg>>) -> Self {
self.children = Some(children);
self self
} }
#[inline]
pub fn children<ChildIter>(mut self, children: ChildIter) -> Self
where
ChildIter: Iterator<Item = Node<Msg>>,
{
self.children.get_or_insert(vec![]).extend(children);
self
}
#[inline]
pub fn add_class(mut self, name: &'l str) -> Self { pub fn add_class(mut self, name: &'l str) -> Self {
self.class_list.push(name); self.class_list.push(name);
self self
} }
#[inline]
pub fn build(self) -> StyledModal<'l> { pub fn build(self) -> StyledModal<'l> {
StyledModal { StyledModal {
variant: self.variant.unwrap_or_else(|| Variant::Center), variant: self.variant.unwrap_or(Variant::Center),
width: self.width, width: self.width,
with_icon: self.with_icon.unwrap_or_default(), with_icon: self.with_icon.unwrap_or_default(),
children: self.children.unwrap_or_default(), children: self.children.unwrap_or_default(),
@ -98,6 +108,7 @@ impl<'l> StyledModalBuilder<'l> {
} }
} }
#[inline]
pub fn render(values: StyledModal) -> Node<Msg> { pub fn render(values: StyledModal) -> Node<Msg> {
let StyledModal { let StyledModal {
variant, variant,

View File

@ -8,7 +8,7 @@ use {
seed::{prelude::Orders, *}, seed::{prelude::Orders, *},
}; };
pub fn drag_started(issue_id: IssueId, model: &mut Model) { pub fn drag_started(issue_id: EpicId, model: &mut Model) {
let project_page = match &mut model.page_content { let project_page = match &mut model.page_content {
PageContent::Project(project_page) => project_page, PageContent::Project(project_page) => project_page,
_ => return, _ => return,
@ -16,7 +16,7 @@ pub fn drag_started(issue_id: IssueId, model: &mut Model) {
project_page.issue_drag.drag(issue_id); project_page.issue_drag.drag(issue_id);
} }
pub fn exchange_position(below_id: IssueId, model: &mut Model) { pub fn exchange_position(below_id: EpicId, model: &mut Model) {
let project_page = match &mut model.page_content { let project_page = match &mut model.page_content {
PageContent::Project(project_page) => project_page, PageContent::Project(project_page) => project_page,
_ => return, _ => return,
@ -88,7 +88,7 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
_ => return, _ => return,
}; };
let changes: Vec<(IssueId, ListPosition, IssueStatusId, Option<EpicId>)> = dirty let changes: Vec<(EpicId, ListPosition, IssueStatusId, Option<EpicId>)> = dirty
.into_iter() .into_iter()
.filter_map(|id| { .filter_map(|id| {
model.issues_by_id.get(&id).map(|issue| { model.issues_by_id.get(&id).map(|issue| {

View File

@ -1,6 +1,8 @@
#[cfg(feature = "backend")] #[cfg(feature = "backend")]
use diesel::*; use diesel::*;
#[cfg(feature = "backend")]
use derive_enum_sql::EnumSql;
use { use {
chrono::NaiveDateTime, chrono::NaiveDateTime,
derive_enum_iter::EnumIter, derive_enum_iter::EnumIter,
@ -16,9 +18,6 @@ pub mod fields;
pub mod msg; pub mod msg;
mod payloads; mod payloads;
#[cfg(feature = "backend")]
use derive_enum_sql::EnumSql;
pub type NumberOfDeleted = usize; pub type NumberOfDeleted = usize;
pub type IssueId = i32; pub type IssueId = i32;
pub type ListPosition = i32; pub type ListPosition = i32;
@ -223,7 +222,7 @@ pub struct Project {
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct Issue { pub struct Issue {
pub id: IssueId, pub id: EpicId,
pub title: String, pub title: String,
pub issue_type: IssueType, pub issue_type: IssueType,
pub priority: IssuePriority, pub priority: IssuePriority,
@ -275,7 +274,7 @@ pub struct Comment {
pub id: CommentId, pub id: CommentId,
pub body: String, pub body: String,
pub user_id: UserId, pub user_id: UserId,
pub issue_id: IssueId, pub issue_id: EpicId,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
} }
@ -320,7 +319,7 @@ pub struct Token {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct IssueAssignee { pub struct IssueAssignee {
pub id: i32, pub id: i32,
pub issue_id: IssueId, pub issue_id: EpicId,
pub user_id: UserId, pub user_id: UserId,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,

View File

@ -1,10 +1,9 @@
use crate::ListPosition;
use { use {
crate::{ crate::{
AvatarUrl, BindToken, Code, Comment, CommentId, CreateCommentPayload, CreateIssuePayload, AvatarUrl, BindToken, Code, Comment, CommentId, CreateCommentPayload, CreateIssuePayload,
EmailString, Epic, EpicId, HighlightedCode, Invitation, InvitationId, InvitationToken, EmailString, Epic, EpicId, HighlightedCode, Invitation, InvitationId, InvitationToken,
Issue, IssueFieldId, IssueId, IssueStatus, IssueStatusId, Lang, Message, MessageId, Issue, IssueFieldId, IssueId, IssueStatus, IssueStatusId, Lang, ListPosition, Message,
NameString, NumberOfDeleted, PayloadVariant, Position, Project, TitleString, MessageId, NameString, NumberOfDeleted, PayloadVariant, Position, Project, TitleString,
UpdateCommentPayload, UpdateProjectPayload, User, UserId, UserProject, UserProjectId, UpdateCommentPayload, UpdateProjectPayload, User, UserId, UserProject, UserProjectId,
UserRole, UsernameString, UserRole, UsernameString,
}, },
@ -187,7 +186,7 @@ pub enum WsMsg {
IssueDeleted(IssueId, NumberOfDeleted), IssueDeleted(IssueId, NumberOfDeleted),
IssueCreate(CreateIssuePayload), IssueCreate(CreateIssuePayload),
IssueCreated(Issue), IssueCreated(Issue),
IssueSyncListPosition(Vec<(IssueId, ListPosition, IssueStatusId, Option<EpicId>)>), IssueSyncListPosition(Vec<(IssueId, ListPosition, IssueStatusId, Option<IssueId>)>),
// issue status // issue status
IssueStatusesLoad, IssueStatusesLoad,