Add modal to add issue
This commit is contained in:
parent
e082444a7c
commit
8950277149
1
jirs-client/.gitignore
vendored
1
jirs-client/.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
pkg
|
pkg
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
.yarn-error.log
|
||||||
|
@ -42,6 +42,7 @@ aside#navbar-left .item {
|
|||||||
transition: color 0.1s;
|
transition: color 0.1s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside#navbar-left .item:hover {
|
aside#navbar-left .item:hover {
|
||||||
|
12
jirs-client/js/css/styledForm.css
Normal file
12
jirs-client/js/css/styledForm.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.styledForm {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement {
|
||||||
|
padding: 25px 40px 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement > .formHeading {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
@ -1,17 +1,18 @@
|
|||||||
@import "css/normalize.css";
|
@import "./css/normalize.css";
|
||||||
@import "css/fonts.css";
|
@import "./css/fonts.css";
|
||||||
@import "css/variables.css";
|
@import "./css/variables.css";
|
||||||
@import "css/global.css";
|
@import "./css/global.css";
|
||||||
@import "css/sidebar.css";
|
@import "./css/sidebar.css";
|
||||||
@import "css/aside.css";
|
@import "./css/aside.css";
|
||||||
@import "css/styledIcon.css";
|
@import "./css/styledIcon.css";
|
||||||
@import "css/shared.css";
|
@import "./css/shared.css";
|
||||||
@import "css/styledTooltip.css";
|
@import "./css/styledTooltip.css";
|
||||||
@import "css/styledAvatar.css";
|
@import "./css/styledAvatar.css";
|
||||||
@import "css/styledSelect.css";
|
@import "./css/styledSelect.css";
|
||||||
@import "css/styledButton.css";
|
@import "./css/styledButton.css";
|
||||||
@import "css/styledInput.css";
|
@import "./css/styledInput.css";
|
||||||
@import "css/styledModal.css";
|
@import "./css/styledModal.css";
|
||||||
@import "css/app.css";
|
@import "./css/styledForm.css";
|
||||||
@import "css/issue.css";
|
@import "./css/app.css";
|
||||||
@import "css/project.css";
|
@import "./css/issue.css";
|
||||||
|
@import "./css/project.css";
|
||||||
|
@ -2,16 +2,28 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc/core": "^1.1.37",
|
"@swc/core": "^1.1.37",
|
||||||
"@wasm-tool/wasm-pack-plugin": "^1.2.0",
|
"@wasm-tool/wasm-pack-plugin": "^1.2.0",
|
||||||
|
"autoprefixer": "^9.7.5",
|
||||||
"css-loader": "^3.4.2",
|
"css-loader": "^3.4.2",
|
||||||
|
"cssnano": "^4.1.10",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
|
"extract-text-webpack-plugin": "2.1.2",
|
||||||
|
"file-loader": "^6.0.0",
|
||||||
|
"glob": "^7.1.6",
|
||||||
"html-webpack-plugin": "^4.0.3",
|
"html-webpack-plugin": "^4.0.3",
|
||||||
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
|
"optipng": "^2.1.0",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
"style-loader": "^1.1.3",
|
"style-loader": "^1.1.3",
|
||||||
|
"sugarss": "^2.0.0",
|
||||||
|
"svgo": "^1.3.2",
|
||||||
|
"svgo-loader": "^2.2.1",
|
||||||
"swc-loader": "^0.1.8",
|
"swc-loader": "^0.1.8",
|
||||||
"webpack": "^4.42.1",
|
"webpack": "^4.42.1",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
"webpack-dev-server": "^3.10.3"
|
"webpack-dev-server": "^3.10.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server"
|
"start": "webpack-dev-server",
|
||||||
|
"build": "./scripts/build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
jirs-client/postcss.config.js
Normal file
7
jirs-client/postcss.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: 'sugarss',
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer')({}),
|
||||||
|
require('cssnano'),
|
||||||
|
],
|
||||||
|
};
|
16
jirs-client/scripts/build
Executable file
16
jirs-client/scripts/build
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export NODE_ENV=production
|
||||||
|
|
||||||
|
rm -Rf dist
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
cp -R ./dev/* ./dist
|
||||||
|
yarn svgo -r -o ./dist/ -f ./static
|
||||||
|
yarn svgo -r -o ./dist/ -f ./js
|
||||||
|
yarn svgo -r -o ./dist/ -f ./dev
|
||||||
|
|
||||||
|
for f in $(ls {js,static,dev}/*.png); do
|
||||||
|
yarn optipng -dir ./dist -o7 ${f}
|
||||||
|
done
|
||||||
|
NODE_ENV=production RUST_LOG=error yarn webpack
|
@ -36,3 +36,16 @@ pub async fn update_issue(
|
|||||||
Err(e) => return Ok(Msg::InternalFailure(e)),
|
Err(e) => return Ok(Msg::InternalFailure(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete_issue(host_url: String, id: i32) -> Result<Msg, Msg> {
|
||||||
|
match host_client(host_url, format!("/issues/{id}", id = id).as_str()) {
|
||||||
|
Ok(client) => {
|
||||||
|
client
|
||||||
|
.method(Method::Delete)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.fetch_string(Msg::IssueDeleteResult)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Err(e) => return Ok(Msg::InternalFailure(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ pub type AvatarFilterActive = bool;
|
|||||||
pub enum FieldId {
|
pub enum FieldId {
|
||||||
IssueTypeEditModalTop,
|
IssueTypeEditModalTop,
|
||||||
CopyButtonLabel,
|
CopyButtonLabel,
|
||||||
|
IssueTypeAddIssueModal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -56,6 +57,7 @@ pub enum Msg {
|
|||||||
|
|
||||||
// issues
|
// issues
|
||||||
IssueUpdateResult(FetchObject<String>),
|
IssueUpdateResult(FetchObject<String>),
|
||||||
|
IssueDeleteResult(FetchObject<String>),
|
||||||
DeleteIssue(IssueId),
|
DeleteIssue(IssueId),
|
||||||
|
|
||||||
// modals
|
// modals
|
||||||
@ -77,7 +79,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
crate::shared::update(&msg, model, orders);
|
crate::shared::update(&msg, model, orders);
|
||||||
crate::modal::update(&msg, model, orders);
|
crate::modal::update(&msg, model, orders);
|
||||||
match model.page {
|
match model.page {
|
||||||
Page::Project => project::update(msg, model, orders),
|
Page::Project | Page::AddIssue => project::update(msg, model, orders),
|
||||||
Page::EditIssue(_id) => project::update(msg, model, orders),
|
Page::EditIssue(_id) => project::update(msg, model, orders),
|
||||||
Page::ProjectSettings => project_settings::update(msg, model, orders),
|
Page::ProjectSettings => project_settings::update(msg, model, orders),
|
||||||
Page::Login => login::update(msg, model, orders),
|
Page::Login => login::update(msg, model, orders),
|
||||||
@ -90,7 +92,7 @@ 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 => project::view(model),
|
Page::Project | Page::AddIssue => project::view(model),
|
||||||
Page::EditIssue(_id) => project::view(model),
|
Page::EditIssue(_id) => project::view(model),
|
||||||
Page::ProjectSettings => project_settings::view(model),
|
Page::ProjectSettings => project_settings::view(model),
|
||||||
Page::Login => login::view(model),
|
Page::Login => login::view(model),
|
||||||
@ -109,6 +111,7 @@ fn routes(url: Url) -> Option<Msg> {
|
|||||||
Some(Ok(id)) => Some(Msg::ChangePage(model::Page::EditIssue(id))),
|
Some(Ok(id)) => Some(Msg::ChangePage(model::Page::EditIssue(id))),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
"add-issue" => Some(Msg::ChangePage(Page::AddIssue)),
|
||||||
"project-settings" => Some(Msg::ChangePage(model::Page::ProjectSettings)),
|
"project-settings" => Some(Msg::ChangePage(model::Page::ProjectSettings)),
|
||||||
"login" => Some(Msg::ChangePage(model::Page::Login)),
|
"login" => Some(Msg::ChangePage(model::Page::Login)),
|
||||||
"register" => Some(Msg::ChangePage(model::Page::Register)),
|
"register" => Some(Msg::ChangePage(model::Page::Register)),
|
||||||
|
84
jirs-client/src/modal/add_issue.rs
Normal file
84
jirs-client/src/modal/add_issue.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use crate::model::{AddIssueModal, Model};
|
||||||
|
use crate::shared::styled_button::StyledButton;
|
||||||
|
use crate::shared::styled_form::StyledForm;
|
||||||
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
|
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
||||||
|
use crate::shared::styled_select::StyledSelect;
|
||||||
|
use crate::shared::ToNode;
|
||||||
|
use crate::{FieldId, Msg};
|
||||||
|
use jirs_data::IssueType;
|
||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
pub fn view(_model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
|
let select_type = StyledSelect::build(FieldId::IssueTypeAddIssueModal)
|
||||||
|
.name("type")
|
||||||
|
.normal()
|
||||||
|
.text_filter(modal.type_select_filter.as_str())
|
||||||
|
.opened(modal.type_select_opened)
|
||||||
|
.valid(true)
|
||||||
|
.options(vec![
|
||||||
|
IssueTypeOption(IssueType::Story),
|
||||||
|
IssueTypeOption(IssueType::Task),
|
||||||
|
IssueTypeOption(IssueType::Bug),
|
||||||
|
])
|
||||||
|
.selected(vec![IssueTypeOption(modal.issue_type.clone())])
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
let form = StyledForm::build()
|
||||||
|
.heading("Create issue")
|
||||||
|
.add_field(select_type)
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
StyledModal::build()
|
||||||
|
.width(800)
|
||||||
|
.variant(ModalVariant::Center)
|
||||||
|
.children(vec![form])
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, PartialEq, Debug)]
|
||||||
|
pub struct IssueTypeOption(pub IssueType);
|
||||||
|
|
||||||
|
impl crate::shared::styled_select::SelectOption for IssueTypeOption {
|
||||||
|
fn into_option(self) -> Node<Msg> {
|
||||||
|
let name = self.0.to_label().to_owned();
|
||||||
|
|
||||||
|
let icon = StyledIcon::build(self.0.into())
|
||||||
|
.add_class("issueTypeIcon".to_string())
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "type"],
|
||||||
|
icon,
|
||||||
|
div![attrs![At::Class => "typeLabel"], name]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_value(self) -> Node<Msg> {
|
||||||
|
let name = self.0.to_label().to_owned();
|
||||||
|
|
||||||
|
let type_icon = StyledIcon::build(self.0.into()).build().into_node();
|
||||||
|
let chevron_icon = StyledIcon::build(Icon::ChevronDown).build().into_node();
|
||||||
|
div![attrs![At::Class => "option"], type_icon, name, chevron_icon]
|
||||||
|
// StyledButton::build()
|
||||||
|
// .secondary()
|
||||||
|
// .children(vec![span![format!("{}", name)]])
|
||||||
|
// .icon(StyledIcon::build(self.0.into()).build())
|
||||||
|
// .build()
|
||||||
|
// .into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_text_filter(&self, text_filter: &str) -> bool {
|
||||||
|
self.0
|
||||||
|
.to_string()
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(&text_filter.to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> u32 {
|
||||||
|
self.0.clone().into()
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
.title("Are you sure you want to delete this issue?")
|
.title("Are you sure you want to delete this issue?")
|
||||||
.message("Once you delete, it's gone for good.")
|
.message("Once you delete, it's gone for good.")
|
||||||
.confirm_text("Delete issue")
|
.confirm_text("Delete issue")
|
||||||
|
.cancel_text("Cancel")
|
||||||
.on_confirm(handle_issue_delete)
|
.on_confirm(handle_issue_delete)
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
|
@ -5,75 +5,27 @@ use jirs_data::{Issue, IssueType};
|
|||||||
use crate::model::{EditIssueModal, ModalType, Model};
|
use crate::model::{EditIssueModal, ModalType, Model};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
use crate::shared::styled_select::{StyledSelect, Variant as SelectVariant};
|
use crate::shared::styled_select::StyledSelect;
|
||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::{FieldChange, FieldId, IssueId, Msg};
|
use crate::{FieldChange, FieldId, IssueId, Msg};
|
||||||
|
|
||||||
#[derive(PartialOrd, PartialEq, Debug)]
|
|
||||||
struct IssueTypeOption(IssueId, IssueType);
|
|
||||||
|
|
||||||
impl crate::shared::styled_select::SelectOption for IssueTypeOption {
|
|
||||||
fn into_option(self) -> Node<Msg> {
|
|
||||||
let name = self.1.to_label().to_owned();
|
|
||||||
|
|
||||||
let icon = StyledIcon::build(self.1.into())
|
|
||||||
.add_class("issueTypeIcon".to_string())
|
|
||||||
.build()
|
|
||||||
.into_node();
|
|
||||||
|
|
||||||
div![
|
|
||||||
attrs![At::Class => "type"],
|
|
||||||
icon,
|
|
||||||
div![attrs![At::Class => "typeLabel"], name]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_value(self) -> Node<Msg> {
|
|
||||||
let issue_id = self.0;
|
|
||||||
let name = self.1.to_label().to_owned();
|
|
||||||
|
|
||||||
StyledButton::build()
|
|
||||||
.empty()
|
|
||||||
.children(vec![span![format!("{}-{}", name, issue_id)]])
|
|
||||||
.icon(StyledIcon::build(self.1.into()).build())
|
|
||||||
.build()
|
|
||||||
.into_node()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_text_filter(&self, text_filter: &str) -> bool {
|
|
||||||
self.1
|
|
||||||
.to_string()
|
|
||||||
.to_lowercase()
|
|
||||||
.contains(&text_filter.to_lowercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_value(&self) -> u32 {
|
|
||||||
self.1.clone().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg> {
|
pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg> {
|
||||||
let issue_id = issue.id;
|
let issue_id = issue.id;
|
||||||
|
|
||||||
let issue_type_select = StyledSelect {
|
let issue_type_select = StyledSelect::build(FieldId::IssueTypeEditModalTop)
|
||||||
id: FieldId::IssueTypeEditModalTop,
|
.dropdown_width(150)
|
||||||
variant: SelectVariant::Empty,
|
.name("type")
|
||||||
dropdown_width: Some(150),
|
.text_filter(modal.top_select_filter.as_str())
|
||||||
name: Some("type".to_string()),
|
.opened(modal.top_select_opened)
|
||||||
placeholder: None,
|
.valid(true)
|
||||||
text_filter: modal.top_select_filter.clone(),
|
.options(vec![
|
||||||
opened: modal.top_select_opened,
|
|
||||||
valid: true,
|
|
||||||
is_multi: false,
|
|
||||||
allow_clear: false,
|
|
||||||
options: vec![
|
|
||||||
IssueTypeOption(issue_id, IssueType::Story),
|
IssueTypeOption(issue_id, IssueType::Story),
|
||||||
IssueTypeOption(issue_id, IssueType::Task),
|
IssueTypeOption(issue_id, IssueType::Task),
|
||||||
IssueTypeOption(issue_id, IssueType::Bug),
|
IssueTypeOption(issue_id, IssueType::Bug),
|
||||||
],
|
])
|
||||||
selected: vec![IssueTypeOption(issue_id, modal.value.clone())],
|
.selected(vec![IssueTypeOption(issue_id, modal.value.clone())])
|
||||||
}
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let click_handler = mouse_ev(Ev::Click, move |_| {
|
let click_handler = mouse_ev(Ev::Click, move |_| {
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
@ -147,3 +99,46 @@ pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg>
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, PartialEq, Debug)]
|
||||||
|
pub struct IssueTypeOption(pub IssueId, pub IssueType);
|
||||||
|
|
||||||
|
impl crate::shared::styled_select::SelectOption for IssueTypeOption {
|
||||||
|
fn into_option(self) -> Node<Msg> {
|
||||||
|
let name = self.1.to_label().to_owned();
|
||||||
|
|
||||||
|
let icon = StyledIcon::build(self.1.into())
|
||||||
|
.add_class("issueTypeIcon".to_string())
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "type"],
|
||||||
|
icon,
|
||||||
|
div![attrs![At::Class => "typeLabel"], name]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_value(self) -> Node<Msg> {
|
||||||
|
let issue_id = self.0;
|
||||||
|
let name = self.1.to_label().to_owned();
|
||||||
|
|
||||||
|
StyledButton::build()
|
||||||
|
.empty()
|
||||||
|
.children(vec![span![format!("{}-{}", name, issue_id)]])
|
||||||
|
.icon(StyledIcon::build(self.1.into()).build())
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_text_filter(&self, text_filter: &str) -> bool {
|
||||||
|
self.1
|
||||||
|
.to_string()
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(&text_filter.to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> u32 {
|
||||||
|
self.1.clone().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,18 +3,23 @@ use seed::{prelude::*, *};
|
|||||||
use jirs_data::{Issue, IssueType, UpdateIssuePayload};
|
use jirs_data::{Issue, IssueType, UpdateIssuePayload};
|
||||||
|
|
||||||
use crate::api::update_issue;
|
use crate::api::update_issue;
|
||||||
use crate::model::{EditIssueModal, ModalType, Page};
|
use crate::model::{AddIssueModal, EditIssueModal, ModalType, Page};
|
||||||
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::shared::{find_issue, ToNode};
|
use crate::shared::{find_issue, ToNode};
|
||||||
use crate::{model, FieldChange, FieldId, Msg};
|
use crate::{model, FieldChange, FieldId, Msg};
|
||||||
|
|
||||||
|
mod add_issue;
|
||||||
mod confirm_delete_issue;
|
mod confirm_delete_issue;
|
||||||
mod issue_details;
|
mod issue_details;
|
||||||
|
|
||||||
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 {
|
||||||
Msg::ModalDropped => match model.modals.pop() {
|
Msg::ModalDropped => match model.modals.pop() {
|
||||||
|
Some(ModalType::EditIssue(..)) => {
|
||||||
|
seed::push_route(vec!["board"]);
|
||||||
|
orders.send_msg(Msg::ChangePage(Page::Project));
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -45,6 +50,11 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Msg::ChangePage(Page::AddIssue) => {
|
||||||
|
let mut modal = AddIssueModal::default();
|
||||||
|
modal.project_id = model.project.as_ref().map(|p| p.id).unwrap_or_default();
|
||||||
|
model.modals.push(ModalType::AddIssue(modal));
|
||||||
|
}
|
||||||
|
|
||||||
Msg::StyledSelectChanged(FieldId::IssueTypeEditModalTop, change) => {
|
Msg::StyledSelectChanged(FieldId::IssueTypeEditModalTop, change) => {
|
||||||
match (change, model.modals.last_mut()) {
|
match (change, model.modals.last_mut()) {
|
||||||
@ -125,7 +135,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ModalType::DeleteIssueConfirm(_id) => confirm_delete_issue::view(model),
|
ModalType::DeleteIssueConfirm(_id) => confirm_delete_issue::view(model),
|
||||||
_ => empty![],
|
ModalType::AddIssue(modal) => add_issue::view(model, modal),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
section![id!["modals"], modals]
|
section![id!["modals"], modals]
|
||||||
|
@ -11,6 +11,7 @@ pub type ProjectId = i32;
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
pub enum ModalType {
|
pub enum ModalType {
|
||||||
|
AddIssue(AddIssueModal),
|
||||||
EditIssue(IssueId, EditIssueModal),
|
EditIssue(IssueId, EditIssueModal),
|
||||||
DeleteIssueConfirm(IssueId),
|
DeleteIssueConfirm(IssueId),
|
||||||
}
|
}
|
||||||
@ -24,10 +25,31 @@ pub struct EditIssueModal {
|
|||||||
pub link_copied: bool,
|
pub link_copied: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialOrd, PartialEq)]
|
||||||
|
pub struct AddIssueModal {
|
||||||
|
pub title: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub issue_type: IssueType,
|
||||||
|
pub status: IssueStatus,
|
||||||
|
pub priority: IssuePriority,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub description_text: Option<String>,
|
||||||
|
pub estimate: Option<i32>,
|
||||||
|
pub time_spent: Option<i32>,
|
||||||
|
pub time_remaining: Option<i32>,
|
||||||
|
pub project_id: i32,
|
||||||
|
pub user_ids: Vec<i32>,
|
||||||
|
|
||||||
|
// modal fields
|
||||||
|
pub type_select_filter: String,
|
||||||
|
pub type_select_opened: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
Project,
|
Project,
|
||||||
EditIssue(IssueId),
|
EditIssue(IssueId),
|
||||||
|
AddIssue,
|
||||||
ProjectSettings,
|
ProjectSettings,
|
||||||
Login,
|
Login,
|
||||||
Register,
|
Register,
|
||||||
@ -38,6 +60,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::AddIssue => format!("/add-issues"),
|
||||||
Page::ProjectSettings => "/project-settings".to_string(),
|
Page::ProjectSettings => "/project-settings".to_string(),
|
||||||
Page::Login => "/login".to_string(),
|
Page::Login => "/login".to_string(),
|
||||||
Page::Register => "/register".to_string(),
|
Page::Register => "/register".to_string(),
|
||||||
|
@ -12,15 +12,9 @@ use crate::Msg;
|
|||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::ChangePage(Page::Project) => {
|
Msg::ChangePage(Page::Project)
|
||||||
orders
|
| Msg::ChangePage(Page::AddIssue)
|
||||||
.skip()
|
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||||
.perform_cmd(crate::api::fetch_current_project(model.host_url.clone()));
|
|
||||||
orders
|
|
||||||
.skip()
|
|
||||||
.perform_cmd(crate::api::fetch_current_user(model.host_url.clone()));
|
|
||||||
}
|
|
||||||
Msg::ChangePage(Page::EditIssue(_issue_id)) => {
|
|
||||||
orders
|
orders
|
||||||
.skip()
|
.skip()
|
||||||
.perform_cmd(crate::api::fetch_current_project(model.host_url.clone()));
|
.perform_cmd(crate::api::fetch_current_project(model.host_url.clone()));
|
||||||
@ -119,6 +113,11 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
Msg::IssueUpdateResult(fetched) => {
|
Msg::IssueUpdateResult(fetched) => {
|
||||||
crate::api_handlers::update_issue_response(&fetched, model);
|
crate::api_handlers::update_issue_response(&fetched, model);
|
||||||
}
|
}
|
||||||
|
Msg::DeleteIssue(issue_id) => {
|
||||||
|
orders
|
||||||
|
.skip()
|
||||||
|
.perform_cmd(crate::api::delete_issue(model.host_url.clone(), issue_id));
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ pub mod navbar_left;
|
|||||||
pub mod styled_avatar;
|
pub mod styled_avatar;
|
||||||
pub mod styled_button;
|
pub mod styled_button;
|
||||||
pub mod styled_confirm_modal;
|
pub mod styled_confirm_modal;
|
||||||
|
pub mod styled_form;
|
||||||
pub mod styled_icon;
|
pub mod styled_icon;
|
||||||
pub mod styled_input;
|
pub mod styled_input;
|
||||||
pub mod styled_modal;
|
pub mod styled_modal;
|
||||||
|
@ -18,7 +18,11 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
|||||||
div![attrs![At::Class => "styledLogo"], logo_svg]
|
div![attrs![At::Class => "styledLogo"], logo_svg]
|
||||||
],
|
],
|
||||||
navbar_left_item(model, "Search issues", Icon::Search),
|
navbar_left_item(model, "Search issues", Icon::Search),
|
||||||
navbar_left_item(model, "Create Issue", Icon::Plus),
|
a![
|
||||||
|
attrs![At::Class => "item"; At::Href=> "/add-issue"; ],
|
||||||
|
i![attrs![At::Class => format!("styledIcon {}", Icon::Plus)]],
|
||||||
|
span![attrs![At::Class => "itemText"], "Create Issue"]
|
||||||
|
],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "bottom"],
|
attrs![At::Class => "bottom"],
|
||||||
about_tooltip(model, navbar_left_item(model, "About", Icon::Help)),
|
about_tooltip(model, navbar_left_item(model, "About", Icon::Help)),
|
||||||
|
@ -46,13 +46,13 @@ impl StyledButtonBuilder {
|
|||||||
self.variant(Variant::Primary)
|
self.variant(Variant::Primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn success(self) -> Self {
|
// pub fn success(self) -> Self {
|
||||||
self.variant(Variant::Success)
|
// self.variant(Variant::Success)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn danger(self) -> Self {
|
// pub fn danger(self) -> Self {
|
||||||
self.variant(Variant::Danger)
|
// self.variant(Variant::Danger)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn secondary(self) -> Self {
|
pub fn secondary(self) -> Self {
|
||||||
self.variant(Variant::Secondary)
|
self.variant(Variant::Secondary)
|
||||||
@ -62,15 +62,15 @@ impl StyledButtonBuilder {
|
|||||||
self.variant(Variant::Empty)
|
self.variant(Variant::Empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disabled(mut self, value: bool) -> Self {
|
// pub fn disabled(mut self, value: bool) -> Self {
|
||||||
self.disabled = Some(value);
|
// self.disabled = Some(value);
|
||||||
self
|
// self
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn active(mut self, value: bool) -> Self {
|
// pub fn active(mut self, value: bool) -> Self {
|
||||||
self.active = Some(value);
|
// self.active = Some(value);
|
||||||
self
|
// self
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn text(mut self, value: String) -> Self {
|
pub fn text(mut self, value: String) -> Self {
|
||||||
self.text = Some(Some(value));
|
self.text = Some(Some(value));
|
||||||
|
@ -11,14 +11,8 @@ const MESSAGE: &str = "Are you sure you want to continue with this action?";
|
|||||||
const CONFIRM_TEXT: &str = "Confirm";
|
const CONFIRM_TEXT: &str = "Confirm";
|
||||||
const CANCEL_TEXT: &str = "Cancel";
|
const CANCEL_TEXT: &str = "Cancel";
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Variant {
|
|
||||||
Primary,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledConfirmModal {
|
pub struct StyledConfirmModal {
|
||||||
pub variant: Variant,
|
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
pub confirm_text: String,
|
pub confirm_text: String,
|
||||||
@ -40,7 +34,6 @@ impl ToNode for StyledConfirmModal {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledConfirmModalBuilder {
|
pub struct StyledConfirmModalBuilder {
|
||||||
variant: Option<Variant>,
|
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
confirm_text: Option<String>,
|
confirm_text: Option<String>,
|
||||||
@ -49,11 +42,6 @@ pub struct StyledConfirmModalBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StyledConfirmModalBuilder {
|
impl StyledConfirmModalBuilder {
|
||||||
pub fn variant(mut self, variant: Variant) -> Self {
|
|
||||||
self.variant = Some(variant);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title<S>(mut self, title: S) -> Self
|
pub fn title<S>(mut self, title: S) -> Self
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
@ -93,7 +81,6 @@ impl StyledConfirmModalBuilder {
|
|||||||
|
|
||||||
pub fn build(self) -> StyledConfirmModal {
|
pub fn build(self) -> StyledConfirmModal {
|
||||||
StyledConfirmModal {
|
StyledConfirmModal {
|
||||||
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
|
||||||
title: self.title.unwrap_or_else(|| TITLE.to_string()),
|
title: self.title.unwrap_or_else(|| TITLE.to_string()),
|
||||||
message: self.message.unwrap_or_else(|| MESSAGE.to_string()),
|
message: self.message.unwrap_or_else(|| MESSAGE.to_string()),
|
||||||
confirm_text: self
|
confirm_text: self
|
||||||
@ -107,7 +94,6 @@ impl StyledConfirmModalBuilder {
|
|||||||
|
|
||||||
pub fn render(values: StyledConfirmModal) -> Node<Msg> {
|
pub fn render(values: StyledConfirmModal) -> Node<Msg> {
|
||||||
let StyledConfirmModal {
|
let StyledConfirmModal {
|
||||||
variant,
|
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
confirm_text,
|
confirm_text,
|
||||||
|
61
jirs-client/src/shared/styled_form.rs
Normal file
61
jirs-client/src/shared/styled_form.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::shared::ToNode;
|
||||||
|
use crate::Msg;
|
||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StyledForm {
|
||||||
|
heading: String,
|
||||||
|
fields: Vec<Node<Msg>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyledForm {
|
||||||
|
pub fn build() -> StyledFormBuilder {
|
||||||
|
StyledFormBuilder::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNode for StyledForm {
|
||||||
|
fn into_node(self) -> Node<Msg> {
|
||||||
|
render(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct StyledFormBuilder {
|
||||||
|
fields: Vec<Node<Msg>>,
|
||||||
|
heading: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyledFormBuilder {
|
||||||
|
pub fn add_field(mut self, node: Node<Msg>) -> Self {
|
||||||
|
self.fields.push(node);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn heading<S>(mut self, heading: S) -> Self
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.heading = heading.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> StyledForm {
|
||||||
|
StyledForm {
|
||||||
|
heading: self.heading,
|
||||||
|
fields: self.fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(values: StyledForm) -> Node<Msg> {
|
||||||
|
let StyledForm { heading, fields } = values;
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "styledForm"],
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "formElement"],
|
||||||
|
div![attrs![At::Class => "heading"], heading],
|
||||||
|
fields
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
@ -68,10 +68,10 @@ impl StyledModalBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_icon(mut self, with_icon: bool) -> Self {
|
// pub fn with_icon(mut self, with_icon: bool) -> Self {
|
||||||
self.with_icon = Some(with_icon);
|
// self.with_icon = Some(with_icon);
|
||||||
self
|
// self
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn children(mut self, children: Vec<Node<Msg>>) -> Self {
|
pub fn children(mut self, children: Vec<Node<Msg>>) -> Self {
|
||||||
self.children = Some(children);
|
self.children = Some(children);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg};
|
||||||
@ -17,6 +18,12 @@ pub enum Variant {
|
|||||||
Normal,
|
Normal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Variant {
|
||||||
|
fn default() -> Self {
|
||||||
|
Variant::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Variant {
|
impl std::fmt::Display for Variant {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@ -40,18 +47,18 @@ pub struct StyledSelect<Child>
|
|||||||
where
|
where
|
||||||
Child: SelectOption + PartialEq,
|
Child: SelectOption + PartialEq,
|
||||||
{
|
{
|
||||||
pub id: FieldId,
|
id: FieldId,
|
||||||
pub variant: Variant,
|
variant: Variant,
|
||||||
pub dropdown_width: Option<usize>,
|
dropdown_width: Option<usize>,
|
||||||
pub name: Option<String>,
|
name: Option<String>,
|
||||||
pub placeholder: Option<String>,
|
placeholder: Option<String>,
|
||||||
pub valid: bool,
|
valid: bool,
|
||||||
pub is_multi: bool,
|
is_multi: bool,
|
||||||
pub allow_clear: bool,
|
allow_clear: bool,
|
||||||
pub options: Vec<Child>,
|
options: Vec<Child>,
|
||||||
pub selected: Vec<Child>,
|
selected: Vec<Child>,
|
||||||
pub text_filter: String,
|
text_filter: String,
|
||||||
pub opened: bool,
|
opened: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Child> ToNode for StyledSelect<Child>
|
impl<Child> ToNode for StyledSelect<Child>
|
||||||
@ -63,6 +70,115 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Child> StyledSelect<Child>
|
||||||
|
where
|
||||||
|
Child: SelectOption + PartialEq,
|
||||||
|
{
|
||||||
|
pub fn build(id: FieldId) -> StyledSelectBuilder<Child> {
|
||||||
|
StyledSelectBuilder {
|
||||||
|
id,
|
||||||
|
variant: None,
|
||||||
|
dropdown_width: None,
|
||||||
|
name: None,
|
||||||
|
placeholder: None,
|
||||||
|
valid: None,
|
||||||
|
is_multi: None,
|
||||||
|
allow_clear: None,
|
||||||
|
options: None,
|
||||||
|
selected: None,
|
||||||
|
text_filter: None,
|
||||||
|
opened: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StyledSelectBuilder<Child>
|
||||||
|
where
|
||||||
|
Child: SelectOption + PartialEq,
|
||||||
|
{
|
||||||
|
id: FieldId,
|
||||||
|
variant: Option<Variant>,
|
||||||
|
dropdown_width: Option<Option<usize>>,
|
||||||
|
name: Option<Option<String>>,
|
||||||
|
placeholder: Option<Option<String>>,
|
||||||
|
valid: Option<bool>,
|
||||||
|
is_multi: Option<bool>,
|
||||||
|
allow_clear: Option<bool>,
|
||||||
|
options: Option<Vec<Child>>,
|
||||||
|
selected: Option<Vec<Child>>,
|
||||||
|
text_filter: Option<String>,
|
||||||
|
opened: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Child> StyledSelectBuilder<Child>
|
||||||
|
where
|
||||||
|
Child: SelectOption + PartialEq,
|
||||||
|
{
|
||||||
|
pub fn build(self) -> StyledSelect<Child> {
|
||||||
|
StyledSelect {
|
||||||
|
id: self.id,
|
||||||
|
variant: self.variant.unwrap_or_default(),
|
||||||
|
dropdown_width: self.dropdown_width.unwrap_or_default(),
|
||||||
|
name: self.name.unwrap_or_default(),
|
||||||
|
placeholder: self.placeholder.unwrap_or_default(),
|
||||||
|
valid: self.valid.unwrap_or(true),
|
||||||
|
is_multi: self.is_multi.unwrap_or_default(),
|
||||||
|
allow_clear: self.allow_clear.unwrap_or_default(),
|
||||||
|
options: self.options.unwrap_or_default(),
|
||||||
|
selected: self.selected.unwrap_or_default(),
|
||||||
|
text_filter: self.text_filter.unwrap_or_default(),
|
||||||
|
opened: self.opened.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dropdown_width(mut self, dropdown_width: usize) -> Self {
|
||||||
|
self.dropdown_width = Some(Some(dropdown_width));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name<S>(mut self, name: S) -> Self
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.name = Some(Some(name.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_filter<S>(mut self, text_filter: S) -> Self
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.text_filter = Some(text_filter.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opened(mut self, opened: bool) -> Self {
|
||||||
|
self.opened = Some(opened);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn valid(mut self, valid: bool) -> Self {
|
||||||
|
self.valid = Some(valid);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(mut self, options: Vec<Child>) -> Self {
|
||||||
|
self.options = Some(options);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected(mut self, selected: Vec<Child>) -> Self {
|
||||||
|
self.selected = Some(selected);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normal(mut self) -> Self {
|
||||||
|
self.variant = Some(Variant::Normal);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render<Child>(values: StyledSelect<Child>) -> Node<Msg>
|
pub fn render<Child>(values: StyledSelect<Child>) -> Node<Msg>
|
||||||
where
|
where
|
||||||
Child: SelectOption + PartialEq,
|
Child: SelectOption + PartialEq,
|
||||||
|
5
jirs-client/svgo-config.yml
Normal file
5
jirs-client/svgo-config.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
plugins:
|
||||||
|
- removeTitle: true
|
||||||
|
- convertPathData: true
|
||||||
|
- convertColors:
|
||||||
|
shorthex: true
|
@ -4,39 +4,62 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
|
process.env.RUST_LOG = 'info';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: path.resolve(__dirname, 'js', 'index.js'),
|
entry: path.resolve(__dirname, 'js', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
path: path.resolve(__dirname, process.env.NODE_ENV === 'production' ? 'dist' : 'dev'),
|
path: path.resolve(__dirname, process.env.NODE_ENV === 'production' ? 'dist' : 'dev'),
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: path.join(__dirname, 'dev'),
|
contentBase: path.join(__dirname, 'dev'),
|
||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
hot: true,
|
hot: true,
|
||||||
port: process.env.JIRS_CLIENT_PORT || 6000,
|
port: process.env.JIRS_CLIENT_PORT || 6000,
|
||||||
host: process.env.JIRS_CLIENT_BIND || '0.0.0.0',
|
host: process.env.JIRS_CLIENT_BIND || '0.0.0.0',
|
||||||
allowedHosts: [
|
allowedHosts: [
|
||||||
'localhost:6000',
|
'localhost:6000',
|
||||||
'localhost:8000',
|
'localhost:8000',
|
||||||
],
|
],
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: ['style-loader', 'css-loader'],
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
// options: { importLoaders: 1 }
|
||||||
|
},
|
||||||
|
// 'postcss-loader'
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
use: [
|
||||||
|
{ loader: 'file-loader' },
|
||||||
|
{
|
||||||
|
loader: 'svgo-loader',
|
||||||
|
options: {
|
||||||
|
externalConfig: "svgo-config.yml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new WasmPackPlugin({
|
new WasmPackPlugin({
|
||||||
crateDirectory: path.resolve(__dirname),
|
crateDirectory: path.resolve(__dirname),
|
||||||
}),
|
}),
|
||||||
@ -51,5 +74,10 @@ module.exports = {
|
|||||||
'JIRS_SERVER_PORT',
|
'JIRS_SERVER_PORT',
|
||||||
'JIRS_SERVER_BIND',
|
'JIRS_SERVER_BIND',
|
||||||
]),
|
]),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: '[name].css',
|
||||||
|
chunkFilename: '[id].css',
|
||||||
|
ignoreOrder: true,
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,12 @@ pub enum IssueType {
|
|||||||
Story,
|
Story,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for IssueType {
|
||||||
|
fn default() -> Self {
|
||||||
|
IssueType::Task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IssueType {
|
impl IssueType {
|
||||||
pub fn to_label(&self) -> &str {
|
pub fn to_label(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
@ -74,6 +80,12 @@ pub enum IssueStatus {
|
|||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for IssueStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
IssueStatus::Backlog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for IssueStatus {
|
impl FromStr for IssueStatus {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
|
|
||||||
@ -138,6 +150,12 @@ impl FromStr for IssuePriority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for IssuePriority {
|
||||||
|
fn default() -> Self {
|
||||||
|
IssuePriority::Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for IssuePriority {
|
impl std::fmt::Display for IssuePriority {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -35,7 +35,6 @@ class ProjectIssueCreate extends React.Component {
|
|||||||
...this.state.form,
|
...this.state.form,
|
||||||
status: IssueStatus.BACKLOG,
|
status: IssueStatus.BACKLOG,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
// userIds: values.userIds,
|
|
||||||
});
|
});
|
||||||
await fetchProject();
|
await fetchProject();
|
||||||
toast.success('Issue has been successfully created.');
|
toast.success('Issue has been successfully created.');
|
||||||
|
Loading…
Reference in New Issue
Block a user