diff --git a/jirs-client/js/css/issue.css b/jirs-client/js/css/issue.css index 10de0b51..5f933e57 100644 --- a/jirs-client/js/css/issue.css +++ b/jirs-client/js/css/issue.css @@ -3,6 +3,9 @@ padding: 0 30px 60px; } +/*===================================================*/ +/* LEFT */ +/*===================================================*/ .issueDetails > .content > .left { width: 65%; padding-right: 50px; @@ -108,11 +111,56 @@ margin-right: 6px; } +/*===================================================*/ +/* RIGHT */ +/*===================================================*/ .issueDetails > .content > .right { width: 35%; 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 { display: flex; justify-content: space-between; diff --git a/jirs-client/js/css/timeTracking.css b/jirs-client/js/css/timeTracking.css new file mode 100644 index 00000000..06e830e9 --- /dev/null +++ b/jirs-client/js/css/timeTracking.css @@ -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%; +} diff --git a/jirs-client/js/styles.css b/jirs-client/js/styles.css index b7fb57ae..dcf6dfd3 100644 --- a/jirs-client/js/styles.css +++ b/jirs-client/js/styles.css @@ -21,3 +21,4 @@ @import "./css/app.css"; @import "./css/issue.css"; @import "./css/project.css"; +@import "./css/timeTracking.css"; diff --git a/jirs-client/src/modal/issue_details.rs b/jirs-client/src/modal/issue_details.rs index 9567ff0a..77c4cfa9 100644 --- a/jirs-client/src/modal/issue_details.rs +++ b/jirs-client/src/modal/issue_details.rs @@ -8,7 +8,7 @@ use crate::shared::styled_avatar::StyledAvatar; use crate::shared::styled_button::StyledButton; use crate::shared::styled_editor::StyledEditor; 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_select::{StyledSelect, StyledSelectChange}; use crate::shared::styled_select_child::ToStyledSelectChild; @@ -593,12 +593,86 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node { .build() .into_node(); + let tracking = tracking_widget(model, modal); + let tracking_field = StyledField::build() + .label("TIME TRACKING") + .tip("") + .input(tracking) + .build() + .into_node(); + div![ attrs![At::Class => "right"], status_field, assignees_field, reporter_field, priority_field, - estimate_field + estimate_field, + tracking_field, ] } + +fn tracking_widget(_model: &Model, modal: &EditIssueModal) -> Node { + 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, + time_spent: &Option, + time_remaining: &Option, +) -> 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, + } +} diff --git a/jirs-client/src/modal/mod.rs b/jirs-client/src/modal/mod.rs index daac9b53..7ef378bb 100644 --- a/jirs-client/src/modal/mod.rs +++ b/jirs-client/src/modal/mod.rs @@ -12,6 +12,7 @@ use crate::{model, FieldChange, FieldId, Msg}; mod add_issue; mod confirm_delete_issue; mod issue_details; +mod time_tracking; pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders) { match msg { @@ -88,6 +89,7 @@ pub fn view(model: &model::Model) -> Node { .build() .into_node() } + ModalType::TimeTracking(issue_id) => time_tracking::view(model, *issue_id), }) .collect(); section![id!["modals"], modals] diff --git a/jirs-client/src/modal/time_tracking.rs b/jirs-client/src/modal/time_tracking.rs new file mode 100644 index 00000000..127e1eb8 --- /dev/null +++ b/jirs-client/src/modal/time_tracking.rs @@ -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 { + 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() +} diff --git a/jirs-client/src/model.rs b/jirs-client/src/model.rs index da94b8a7..5464af9e 100644 --- a/jirs-client/src/model.rs +++ b/jirs-client/src/model.rs @@ -15,6 +15,7 @@ pub enum ModalType { EditIssue(IssueId, EditIssueModal), DeleteIssueConfirm(IssueId), DeleteCommentConfirm(CommentId), + TimeTracking(IssueId), } #[derive(Clone, Debug, PartialOrd, PartialEq, Hash)] diff --git a/react-client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js b/react-client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js index 3ad2c133..26871900 100644 --- a/react-client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js +++ b/react-client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js @@ -7,7 +7,7 @@ export const TrackingLink = styled.div` border-radius: 4px; transition: background 0.1s; cursor: pointer; - user-select: none; + user-select: none; &:hover { background: ${color.backgroundLight}; } @@ -19,7 +19,8 @@ export const ModalContents = styled.div` export const ModalTitle = styled.div` padding-bottom: 14px; - ${ font.medium };font-weight: normal; + ${ font.medium }; + font-weight: normal; font-size: 20px `; diff --git a/react-client/src/Project/Board/IssueDetails/Styles.js b/react-client/src/Project/Board/IssueDetails/Styles.js index 8b933270..24690ea4 100644 --- a/react-client/src/Project/Board/IssueDetails/Styles.js +++ b/react-client/src/Project/Board/IssueDetails/Styles.js @@ -36,5 +36,6 @@ export const SectionTitle = styled.div` text-transform: uppercase; color: ${color.textMedium}; font-size: 12.5px - font-family: "CircularStdBold"; font-weight: normal + font-family: "CircularStdBold"; + font-weight: normal; `;