Save estimated time. Change path on drop add issue. Better support for multi-page
This commit is contained in:
parent
cb6f41fe0a
commit
02d2c958b7
@ -3,6 +3,9 @@
|
|||||||
padding: 0 30px 60px;
|
padding: 0 30px 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===================================================*/
|
||||||
|
/* LEFT */
|
||||||
|
/*===================================================*/
|
||||||
.issueDetails > .content > .left {
|
.issueDetails > .content > .left {
|
||||||
width: 65%;
|
width: 65%;
|
||||||
padding-right: 50px;
|
padding-right: 50px;
|
||||||
@ -108,11 +111,56 @@
|
|||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===================================================*/
|
||||||
|
/* RIGHT */
|
||||||
|
/*===================================================*/
|
||||||
.issueDetails > .content > .right {
|
.issueDetails > .content > .right {
|
||||||
width: 35%;
|
width: 35%;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink {
|
||||||
|
padding: 4px 4px 2px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background 0.1s;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink:hover {
|
||||||
|
background: var(--backgroundLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink > .trackingWidget {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink > .trackingWidget > .watchIcon {
|
||||||
|
color: var(--textMedium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink > .trackingWidget > .right {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink > .trackingWidget > .right > .barCounter {
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--backgroundMedium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right > .styledField > .trackingLink > .trackingWidget > .right > .barCounter > .bar {
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--primary);
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===================================================*/
|
||||||
|
/* TOP ACTIONS */
|
||||||
|
/*===================================================*/
|
||||||
.issueDetails > .topActions {
|
.issueDetails > .topActions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
20
jirs-client/js/css/timeTracking.css
Normal file
20
jirs-client/js/css/timeTracking.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.timeTrackingModal {
|
||||||
|
padding: 20px 25px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeTrackingModal > .modalTitle {
|
||||||
|
padding-bottom: 14px;
|
||||||
|
font-family: var(--font-medium);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeTrackingModal > .inputs {
|
||||||
|
display: flex;
|
||||||
|
margin: 20px -5px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeTrackingModal > .inputs > .inputContainer {
|
||||||
|
margin: 0 5px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
@ -21,3 +21,4 @@
|
|||||||
@import "./css/app.css";
|
@import "./css/app.css";
|
||||||
@import "./css/issue.css";
|
@import "./css/issue.css";
|
||||||
@import "./css/project.css";
|
@import "./css/project.css";
|
||||||
|
@import "./css/timeTracking.css";
|
||||||
|
@ -8,7 +8,7 @@ use crate::shared::styled_avatar::StyledAvatar;
|
|||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_editor::StyledEditor;
|
use crate::shared::styled_editor::StyledEditor;
|
||||||
use crate::shared::styled_field::StyledField;
|
use crate::shared::styled_field::StyledField;
|
||||||
use crate::shared::styled_icon::Icon;
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
use crate::shared::styled_input::StyledInput;
|
use crate::shared::styled_input::StyledInput;
|
||||||
use crate::shared::styled_select::{StyledSelect, StyledSelectChange};
|
use crate::shared::styled_select::{StyledSelect, StyledSelectChange};
|
||||||
use crate::shared::styled_select_child::ToStyledSelectChild;
|
use crate::shared::styled_select_child::ToStyledSelectChild;
|
||||||
@ -593,12 +593,86 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
let tracking = tracking_widget(model, modal);
|
||||||
|
let tracking_field = StyledField::build()
|
||||||
|
.label("TIME TRACKING")
|
||||||
|
.tip("")
|
||||||
|
.input(tracking)
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "right"],
|
attrs![At::Class => "right"],
|
||||||
status_field,
|
status_field,
|
||||||
assignees_field,
|
assignees_field,
|
||||||
reporter_field,
|
reporter_field,
|
||||||
priority_field,
|
priority_field,
|
||||||
estimate_field
|
estimate_field,
|
||||||
|
tracking_field,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tracking_widget(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||||
|
let EditIssueModal {
|
||||||
|
id,
|
||||||
|
payload:
|
||||||
|
UpdateIssuePayload {
|
||||||
|
estimate,
|
||||||
|
time_spent,
|
||||||
|
time_remaining,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} = modal;
|
||||||
|
|
||||||
|
let issue_id = *id;
|
||||||
|
|
||||||
|
let icon = StyledIcon::build(Icon::Stopwatch)
|
||||||
|
.add_class("watchIcon")
|
||||||
|
.size(32)
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
let bar_width = calc_bar_width(estimate, time_spent, time_remaining);
|
||||||
|
let handler = mouse_ev(Ev::Click, move |_| {
|
||||||
|
Msg::ModalOpened(ModalType::TimeTracking(issue_id))
|
||||||
|
});
|
||||||
|
|
||||||
|
div![
|
||||||
|
class!["trackingLink"],
|
||||||
|
handler,
|
||||||
|
div![
|
||||||
|
class!["trackingWidget"],
|
||||||
|
icon,
|
||||||
|
div![
|
||||||
|
class!["right"],
|
||||||
|
div![
|
||||||
|
class!["barCounter"],
|
||||||
|
div![
|
||||||
|
class!["bar"],
|
||||||
|
attrs![At::Style => format!("width: {}%", bar_width)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn calc_bar_width(
|
||||||
|
estimate: &Option<i32>,
|
||||||
|
time_spent: &Option<i32>,
|
||||||
|
time_remaining: &Option<i32>,
|
||||||
|
) -> f64 {
|
||||||
|
match (estimate, time_spent, time_remaining) {
|
||||||
|
(Some(estimate), Some(spent), _) => {
|
||||||
|
((*spent as f64 / *estimate as f64) * 100f64).max(100f64)
|
||||||
|
}
|
||||||
|
(_, Some(spent), Some(remaining)) => {
|
||||||
|
(*spent as f64 / (*spent as f64 + *remaining as f64)) * 100f64
|
||||||
|
}
|
||||||
|
(None, None, _) => 100f64,
|
||||||
|
(None, _, _) => 0f64,
|
||||||
|
_ => 0f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ use crate::{model, FieldChange, FieldId, Msg};
|
|||||||
mod add_issue;
|
mod add_issue;
|
||||||
mod confirm_delete_issue;
|
mod confirm_delete_issue;
|
||||||
mod issue_details;
|
mod issue_details;
|
||||||
|
mod time_tracking;
|
||||||
|
|
||||||
pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
match msg {
|
match msg {
|
||||||
@ -88,6 +89,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
}
|
}
|
||||||
|
ModalType::TimeTracking(issue_id) => time_tracking::view(model, *issue_id),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
section![id!["modals"], modals]
|
section![id!["modals"], modals]
|
||||||
|
28
jirs-client/src/modal/time_tracking.rs
Normal file
28
jirs-client/src/modal/time_tracking.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
use jirs_data::IssueId;
|
||||||
|
|
||||||
|
use crate::model::Model;
|
||||||
|
use crate::shared::styled_input::StyledInput;
|
||||||
|
use crate::shared::styled_modal::StyledModal;
|
||||||
|
use crate::shared::{find_issue, ToNode};
|
||||||
|
use crate::Msg;
|
||||||
|
|
||||||
|
pub fn view(model: &Model, issue_id: IssueId) -> Node<Msg> {
|
||||||
|
let issue = match find_issue(model, issue_id) {
|
||||||
|
Some(issue) => issue,
|
||||||
|
_ => return empty![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let modal_title = div![class!["modalTitle"], "Time tracking"];
|
||||||
|
|
||||||
|
// let time_spent = StyledInput::build()
|
||||||
|
|
||||||
|
let inputs = div![class!["inputs"], ""];
|
||||||
|
|
||||||
|
StyledModal::build()
|
||||||
|
.add_class("timeTrackingModal")
|
||||||
|
.children(vec![modal_title, inputs])
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
}
|
@ -15,6 +15,7 @@ pub enum ModalType {
|
|||||||
EditIssue(IssueId, EditIssueModal),
|
EditIssue(IssueId, EditIssueModal),
|
||||||
DeleteIssueConfirm(IssueId),
|
DeleteIssueConfirm(IssueId),
|
||||||
DeleteCommentConfirm(CommentId),
|
DeleteCommentConfirm(CommentId),
|
||||||
|
TimeTracking(IssueId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
@ -7,7 +7,7 @@ export const TrackingLink = styled.div`
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: background 0.1s;
|
transition: background 0.1s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${color.backgroundLight};
|
background: ${color.backgroundLight};
|
||||||
}
|
}
|
||||||
@ -19,7 +19,8 @@ export const ModalContents = styled.div`
|
|||||||
|
|
||||||
export const ModalTitle = styled.div`
|
export const ModalTitle = styled.div`
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
${ font.medium };font-weight: normal;
|
${ font.medium };
|
||||||
|
font-weight: normal;
|
||||||
font-size: 20px
|
font-size: 20px
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -36,5 +36,6 @@ export const SectionTitle = styled.div`
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: ${color.textMedium};
|
color: ${color.textMedium};
|
||||||
font-size: 12.5px
|
font-size: 12.5px
|
||||||
font-family: "CircularStdBold"; font-weight: normal
|
font-family: "CircularStdBold";
|
||||||
|
font-weight: normal;
|
||||||
`;
|
`;
|
||||||
|
Loading…
Reference in New Issue
Block a user