Add fibonacci time tracking. Use styled select state for values

This commit is contained in:
Adrian Wozniak 2020-04-26 11:37:31 +02:00
parent f4e1b41568
commit c13bb5acf7
5 changed files with 137 additions and 58 deletions

View File

@ -3,6 +3,7 @@ use seed::{prelude::*, *};
use jirs_data::*;
use crate::api::send_ws_msg;
use crate::modal::time_tracking::time_tracking_field;
use crate::model::{CommentForm, EditIssueModal, ModalType, Model};
use crate::shared::styled_avatar::StyledAvatar;
use crate::shared::styled_button::StyledButton;
@ -27,8 +28,11 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
modal.assignees_state.update(msg, orders);
modal.priority_state.update(msg, orders);
modal.estimate.update(msg);
modal.estimate_select.update(msg, orders);
modal.time_spent.update(msg);
modal.time_spent_select.update(msg, orders);
modal.time_remaining.update(msg);
modal.time_remaining_select.update(msg, orders);
match msg {
Msg::WsMsg(WsMsg::IssueUpdated(issue)) => {
@ -137,9 +141,10 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
),
));
}
// TimeSpent
Msg::StrInputChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
_value,
..,
) => {
modal.payload.time_spent = modal.time_spent.represent_f64_as_i32();
send_ws_msg(WsMsg::IssueUpdateRequest(
@ -148,9 +153,21 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_spent),
));
}
Msg::StyledSelectChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
StyledSelectChange::Changed(..),
) => {
modal.payload.time_spent = modal.time_spent_select.values.get(0).map(|n| *n as i32);
send_ws_msg(WsMsg::IssueUpdateRequest(
modal.id,
IssueFieldId::TimeSpent,
PayloadVariant::OptionI32(modal.payload.time_spent),
));
}
// Time Remaining
Msg::StrInputChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
_value,
..,
) => {
modal.payload.time_remaining = modal.time_remaining.represent_f64_as_i32();
send_ws_msg(WsMsg::IssueUpdateRequest(
@ -159,6 +176,41 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_remaining),
));
}
Msg::StyledSelectChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
StyledSelectChange::Changed(..),
) => {
modal.payload.time_remaining =
modal.time_remaining_select.values.get(0).map(|n| *n as i32);
send_ws_msg(WsMsg::IssueUpdateRequest(
modal.id,
IssueFieldId::TimeRemaining,
PayloadVariant::OptionI32(modal.payload.time_remaining),
));
}
// Estimate
Msg::StrInputChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
..,
) => {
modal.payload.estimate = modal.estimate.represent_f64_as_i32();
send_ws_msg(WsMsg::IssueUpdateRequest(
modal.id,
IssueFieldId::Estimate,
PayloadVariant::OptionI32(modal.payload.estimate),
));
}
Msg::StyledSelectChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
StyledSelectChange::Changed(..),
) => {
modal.payload.estimate = modal.estimate_select.values.get(0).map(|n| *n as i32);
send_ws_msg(WsMsg::IssueUpdateRequest(
modal.id,
IssueFieldId::Estimate,
PayloadVariant::OptionI32(modal.payload.estimate),
));
}
Msg::ModalChanged(FieldChange::TabChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Description)),
mode,
@ -175,24 +227,15 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
modal.comment_form.id = None;
}
}
//
// comments
//
Msg::StrInputChanged(
FieldId::EditIssueModal(EditIssueModalSection::Comment(CommentFieldId::Body)),
text,
) => {
modal.comment_form.body = text.clone();
}
Msg::StrInputChanged(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
_value,
) => {
modal.payload.estimate = modal.estimate.represent_f64_as_i32();
send_ws_msg(WsMsg::IssueUpdateRequest(
modal.id,
IssueFieldId::Estimate,
PayloadVariant::OptionI32(modal.payload.estimate),
));
}
Msg::SaveComment => {
let msg = match modal.comment_form.id {
Some(id) => WsMsg::UpdateComment(UpdateCommentPayload {
@ -668,18 +711,13 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
.unwrap_or_else(|| TimeTracking::Untracked);
let (estimate_field, tracking_field) = if time_tracking_type != TimeTracking::Untracked {
let estimate = StyledInput::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
IssueFieldId::Estimate,
)))
.valid(true)
.value(modal.estimate.value.as_str())
.build()
.into_node();
let estimate_field = StyledField::build()
.input(estimate)
.label("Original Estimate (hours)")
.build()
.into_node();
let estimate_field = time_tracking_field(
time_tracking_type,
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
"Original Estimate (hours)",
&modal.estimate,
&modal.estimate_select,
);
let tracking = tracking_link(model, modal);
let tracking_field = StyledField::build()

View File

@ -7,7 +7,7 @@ use crate::shared::styled_button::StyledButton;
use crate::shared::styled_field::StyledField;
use crate::shared::styled_input::{StyledInput, StyledInputState};
use crate::shared::styled_modal::StyledModal;
use crate::shared::styled_select::StyledSelect;
use crate::shared::styled_select::{StyledSelect, StyledSelectState};
use crate::shared::styled_select_child::*;
use crate::shared::tracking_widget::{fibonacci_values, tracking_widget};
use crate::shared::{find_issue, ToChild, ToNode};
@ -47,12 +47,14 @@ pub fn view(model: &Model, issue_id: IssueId) -> Node<Msg> {
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
"Time spent",
&edit_issue_modal.time_spent,
&edit_issue_modal.time_spent_select,
);
let time_remaining_field = time_tracking_field(
time_tracking_type,
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
"Time remaining",
&edit_issue_modal.time_remaining,
&edit_issue_modal.time_remaining_select,
);
let inputs = div![
@ -80,16 +82,24 @@ pub fn view(model: &Model, issue_id: IssueId) -> Node<Msg> {
.into_node()
}
fn time_tracking_field(
pub fn time_tracking_field(
time_tracking_type: TimeTracking,
field_id: FieldId,
label: &str,
state: &StyledInputState,
input_state: &StyledInputState,
select_state: &StyledSelectState,
) -> Node<Msg> {
let input = match time_tracking_type {
TimeTracking::Untracked => empty![],
TimeTracking::Fibonacci => StyledSelect::build(field_id)
.selected(vec![(state.to_i32().unwrap_or_default() as u32).to_child()])
.selected(
select_state
.values
.iter()
.map(|n| (*n).to_child())
.collect(),
)
.with_state(select_state)
.options(
fibonacci_values()
.into_iter()
@ -99,7 +109,7 @@ fn time_tracking_field(
.build()
.into_node(),
TimeTracking::Hourly => StyledInput::build(field_id)
.state(state)
.state(input_state)
.valid(true)
.build()
.into_node(),

View File

@ -40,8 +40,11 @@ pub struct EditIssueModal {
pub priority_state: StyledSelectState,
pub estimate: StyledInputState,
pub estimate_select: StyledSelectState,
pub time_spent: StyledInputState,
pub time_spent_select: StyledSelectState,
pub time_remaining: StyledInputState,
pub time_remaining_select: StyledSelectState,
pub description_editor_mode: Mode,
@ -69,33 +72,53 @@ impl EditIssueModal {
reporter_id: issue.reporter_id,
user_ids: issue.user_ids.clone(),
},
top_type_state: StyledSelectState::new(FieldId::EditIssueModal(
EditIssueModalSection::Issue(IssueFieldId::Type),
)),
status_state: StyledSelectState::new(FieldId::EditIssueModal(
EditIssueModalSection::Issue(IssueFieldId::Status),
)),
reporter_state: StyledSelectState::new(FieldId::EditIssueModal(
EditIssueModalSection::Issue(IssueFieldId::Reporter),
)),
assignees_state: StyledSelectState::new(FieldId::EditIssueModal(
EditIssueModalSection::Issue(IssueFieldId::Assignees),
)),
priority_state: StyledSelectState::new(FieldId::EditIssueModal(
EditIssueModalSection::Issue(IssueFieldId::Priority),
)),
top_type_state: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Type)),
issue.estimate.map(|v| vec![v as u32]).unwrap_or_default(),
),
status_state: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Status)),
vec![issue.status.into()],
),
reporter_state: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Reporter)),
vec![issue.reporter_id as u32],
),
assignees_state: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Assignees)),
issue.user_ids.iter().map(|n| *n as u32).collect(),
),
priority_state: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Priority)),
vec![issue.priority.into()],
),
estimate: StyledInputState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
value_for_time_tracking(&issue.estimate, &time_tracking_type),
),
estimate_select: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
issue.estimate.map(|n| vec![n as u32]).unwrap_or_default(),
),
time_spent: StyledInputState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
value_for_time_tracking(&issue.time_spent, &time_tracking_type),
),
time_spent_select: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
issue.time_spent.map(|n| vec![n as u32]).unwrap_or_default(),
),
time_remaining: StyledInputState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
value_for_time_tracking(&issue.time_remaining, &time_tracking_type),
),
time_remaining_select: StyledSelectState::new(
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
issue
.time_remaining
.map(|n| vec![n as u32])
.unwrap_or_default(),
),
description_editor_mode: Mode::View,
comment_form: CommentForm {
id: None,
@ -143,12 +166,19 @@ impl Default for AddIssueModal {
project_id: Default::default(),
user_ids: Default::default(),
reporter_id: Default::default(),
type_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Type)),
reporter_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Reporter)),
assignees_state: StyledSelectState::new(FieldId::AddIssueModal(
IssueFieldId::Assignees,
)),
priority_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Priority)),
type_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Type), vec![]),
reporter_state: StyledSelectState::new(
FieldId::AddIssueModal(IssueFieldId::Reporter),
vec![],
),
assignees_state: StyledSelectState::new(
FieldId::AddIssueModal(IssueFieldId::Assignees),
vec![],
),
priority_state: StyledSelectState::new(
FieldId::AddIssueModal(IssueFieldId::Priority),
vec![],
),
}
}
}
@ -243,9 +273,10 @@ impl ProjectSettingsPage {
time_tracking: Some(*time_tracking),
},
description_mode: EditorMode::View,
project_category_state: StyledSelectState::new(FieldId::ProjectSettings(
ProjectFieldId::Category,
)),
project_category_state: StyledSelectState::new(
FieldId::ProjectSettings(ProjectFieldId::Category),
vec![(*category).into()],
),
time_tracking: StyledCheckboxState::new(
FieldId::ProjectSettings(ProjectFieldId::TimeTracking),
(*time_tracking).into(),
@ -317,7 +348,7 @@ impl Default for UsersPage {
email: "".to_string(),
email_touched: false,
user_role: Default::default(),
user_role_state: StyledSelectState::new(FieldId::Users(UsersFieldId::UserRole)),
user_role_state: StyledSelectState::new(FieldId::Users(UsersFieldId::UserRole), vec![]),
pending_invitations: vec![],
error: "".to_string(),
form_state: Default::default(),

View File

@ -51,12 +51,12 @@ impl StyledSelectState {
}
impl StyledSelectState {
pub fn new(field_id: FieldId) -> Self {
pub fn new(field_id: FieldId, values: Vec<u32>) -> Self {
Self {
field_id,
opened: false,
text_filter: String::new(),
values: vec![],
values,
}
}

View File

@ -335,7 +335,7 @@ impl Into<UserRole> for u32 {
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
#[cfg_attr(feature = "backend", sql_type = "ProjectCategoryType")]
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
pub enum ProjectCategory {
Software,
Marketing,