Change Epic to own type
This commit is contained in:
parent
940cdf8e31
commit
02a2ecdd01
@ -50,6 +50,7 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
||||
project_id: modal.project_id.unwrap_or(project_id),
|
||||
user_ids: modal.user_ids.clone(),
|
||||
reporter_id: modal.reporter_id.unwrap_or_else(|| user_id),
|
||||
epic_id: modal.epic_id,
|
||||
};
|
||||
send_ws_msg(
|
||||
jirs_data::WsMsg::IssueCreateRequest(payload),
|
||||
|
@ -161,10 +161,11 @@ pub struct AddIssueModal {
|
||||
pub estimate: Option<i32>,
|
||||
pub time_spent: Option<i32>,
|
||||
pub time_remaining: Option<i32>,
|
||||
pub project_id: Option<i32>,
|
||||
pub user_ids: Vec<i32>,
|
||||
pub reporter_id: Option<i32>,
|
||||
pub issue_status_id: i32,
|
||||
pub project_id: Option<jirs_data::ProjectId>,
|
||||
pub user_ids: Vec<jirs_data::UserId>,
|
||||
pub reporter_id: Option<jirs_data::UserId>,
|
||||
pub issue_status_id: jirs_data::IssueStatusId,
|
||||
pub epic_id: Option<jirs_data::UserId>,
|
||||
|
||||
// modal fields
|
||||
pub title_state: StyledInputState,
|
||||
@ -188,6 +189,7 @@ impl Default for AddIssueModal {
|
||||
user_ids: Default::default(),
|
||||
reporter_id: Default::default(),
|
||||
issue_status_id: Default::default(),
|
||||
epic_id: Default::default(),
|
||||
title_state: StyledInputState::new(FieldId::AddIssueModal(IssueFieldId::Title), ""),
|
||||
type_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Type), vec![]),
|
||||
reporter_state: StyledSelectState::new(
|
||||
|
@ -203,11 +203,11 @@ impl std::fmt::Display for Icon {
|
||||
|
||||
impl From<IssueType> for Icon {
|
||||
fn from(t: IssueType) -> Self {
|
||||
use IssueType::*;
|
||||
match t {
|
||||
IssueType::Task => Icon::Task,
|
||||
IssueType::Bug => Icon::Bug,
|
||||
IssueType::Story => Icon::Story,
|
||||
IssueType::Epic => Icon::Epic,
|
||||
Task => Icon::Task,
|
||||
Bug => Icon::Bug,
|
||||
Story => Icon::Story,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,9 @@ impl RteMsg {
|
||||
let res = seed::document().query_selector(format!("#{}", identifier).as_str());
|
||||
if let Ok(Some(el)) = res {
|
||||
if let Ok(el) = el.dyn_into::<web_sys::HtmlElement>() {
|
||||
el.focus().is_ok();
|
||||
if let Err(e) = el.focus() {
|
||||
log!(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -28,6 +28,7 @@ pub type IssueStatusId = i32;
|
||||
pub type InvitationId = i32;
|
||||
pub type Position = i32;
|
||||
pub type MessageId = i32;
|
||||
pub type EpicId = i32;
|
||||
pub type EmailString = String;
|
||||
pub type UsernameString = String;
|
||||
pub type TitleString = String;
|
||||
@ -41,19 +42,14 @@ pub enum IssueType {
|
||||
Task,
|
||||
Bug,
|
||||
Story,
|
||||
Epic,
|
||||
}
|
||||
|
||||
impl ToVec for IssueType {
|
||||
type Item = IssueType;
|
||||
|
||||
fn ordered() -> Vec<Self> {
|
||||
vec![
|
||||
IssueType::Task,
|
||||
IssueType::Bug,
|
||||
IssueType::Story,
|
||||
IssueType::Epic,
|
||||
]
|
||||
use IssueType::*;
|
||||
vec![Task, Bug, Story]
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,11 +61,11 @@ impl Default for IssueType {
|
||||
|
||||
impl IssueType {
|
||||
pub fn to_label(&self) -> &str {
|
||||
use IssueType::*;
|
||||
match self {
|
||||
IssueType::Task => "Task",
|
||||
IssueType::Bug => "Bug",
|
||||
IssueType::Story => "Story",
|
||||
IssueType::Epic => "Epic",
|
||||
Task => "Task",
|
||||
Bug => "Bug",
|
||||
Story => "Story",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,7 +76,6 @@ impl Into<u32> for IssueType {
|
||||
IssueType::Task => 1,
|
||||
IssueType::Bug => 2,
|
||||
IssueType::Story => 3,
|
||||
IssueType::Epic => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,7 +86,6 @@ impl Into<IssueType> for u32 {
|
||||
1 => IssueType::Task,
|
||||
2 => IssueType::Bug,
|
||||
3 => IssueType::Story,
|
||||
4 => IssueType::Epic,
|
||||
_ => IssueType::Task,
|
||||
}
|
||||
}
|
||||
@ -103,7 +97,6 @@ impl std::fmt::Display for IssueType {
|
||||
IssueType::Task => f.write_str("task"),
|
||||
IssueType::Bug => f.write_str("bug"),
|
||||
IssueType::Story => f.write_str("story"),
|
||||
IssueType::Epic => f.write_str("epic"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,6 +427,7 @@ pub struct Issue {
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub issue_status_id: IssueStatusId,
|
||||
pub epic_id: Option<EpicId>,
|
||||
|
||||
pub user_ids: Vec<i32>,
|
||||
}
|
||||
@ -612,6 +606,17 @@ pub struct Message {
|
||||
pub updated_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Epic {
|
||||
pub id: EpicId,
|
||||
pub name: String,
|
||||
pub user_id: UserId,
|
||||
pub project_id: ProjectId,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CreateCommentPayload {
|
||||
pub user_id: Option<UserId>,
|
||||
@ -639,6 +644,7 @@ pub struct CreateIssuePayload {
|
||||
pub user_ids: Vec<UserId>,
|
||||
pub reporter_id: UserId,
|
||||
pub issue_status_id: IssueStatusId,
|
||||
pub epic_id: Option<EpicId>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
|
@ -55,7 +55,6 @@ fn issue_type_from_sql(bytes: Option<&[u8]>) -> deserialize::Result<IssueType> {
|
||||
b"task" => Ok(IssueType::Task),
|
||||
b"bug" => Ok(IssueType::Bug),
|
||||
b"story" => Ok(IssueType::Story),
|
||||
b"epic" => Ok(IssueType::Epic),
|
||||
_ => Ok(IssueType::Task),
|
||||
}
|
||||
}
|
||||
@ -78,7 +77,6 @@ impl ToSql<IssueTypeType, Pg> for IssueType {
|
||||
IssueType::Task => out.write_all(b"task")?,
|
||||
IssueType::Story => out.write_all(b"story")?,
|
||||
IssueType::Bug => out.write_all(b"bug")?,
|
||||
IssueType::Epic => out.write_all(b"epic")?,
|
||||
}
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
ALTER TABLE "issues"
|
||||
ALTER COLUMN "issue_type"
|
||||
SET DATA TYPE TEXT
|
||||
USING "issue_type"::TEXT;
|
||||
|
||||
DROP TYPE IF EXISTS "IssueTypeType" CASCADE;
|
||||
CREATE TYPE "IssueTypeType" AS ENUM (
|
||||
'task',
|
||||
'bug',
|
||||
'story',
|
||||
'epic'
|
||||
);
|
||||
|
||||
ALTER TABLE "issues"
|
||||
ALTER COLUMN "issue_type"
|
||||
SET DATA TYPE "IssueTypeType"
|
||||
USING "issue_type"::"IssueTypeType";
|
||||
|
||||
ALTER TABLE "issues" DROP COLUMN "epic_id";
|
||||
|
||||
DROP TABLE IF EXISTS epics;
|
33
jirs-server/migrations/2020-08-10-194809_change_epic/up.sql
Normal file
33
jirs-server/migrations/2020-08-10-194809_change_epic/up.sql
Normal file
@ -0,0 +1,33 @@
|
||||
ALTER TABLE "issues"
|
||||
ALTER COLUMN "issue_type"
|
||||
SET DATA TYPE TEXT
|
||||
USING "issue_type"::TEXT;
|
||||
|
||||
UPDATE "issues"
|
||||
SET "issue_type" = 'task'
|
||||
WHERE "issue_type" = 'epic';
|
||||
|
||||
DROP TYPE IF EXISTS "IssueTypeType" CASCADE;
|
||||
CREATE TYPE "IssueTypeType" AS ENUM (
|
||||
'task',
|
||||
'bug',
|
||||
'story'
|
||||
);
|
||||
|
||||
ALTER TABLE "issues"
|
||||
ALTER COLUMN "issue_type"
|
||||
SET DATA TYPE "IssueTypeType"
|
||||
USING "issue_type"::"IssueTypeType";
|
||||
|
||||
CREATE TABLE epics (
|
||||
id serial primary key not null,
|
||||
name text not null,
|
||||
user_id integer not null references users (id),
|
||||
project_id integer not null references projects (id),
|
||||
created_at timestamp not null default now(),
|
||||
updated_at timestamp not null default now()
|
||||
);
|
||||
|
||||
ALTER TABLE "issues"
|
||||
ADD COLUMN "epic_id" integer
|
||||
REFERENCES "epics" ( "id" ) NULL;
|
@ -214,9 +214,10 @@ pub struct CreateIssue {
|
||||
pub estimate: Option<i32>,
|
||||
pub time_spent: Option<i32>,
|
||||
pub time_remaining: Option<i32>,
|
||||
pub project_id: i32,
|
||||
pub reporter_id: i32,
|
||||
pub user_ids: Vec<i32>,
|
||||
pub project_id: jirs_data::ProjectId,
|
||||
pub reporter_id: jirs_data::UserId,
|
||||
pub user_ids: Vec<jirs_data::UserId>,
|
||||
pub epic_id: Option<jirs_data::EpicId>,
|
||||
}
|
||||
|
||||
impl Message for CreateIssue {
|
||||
@ -272,6 +273,7 @@ impl Handler<CreateIssue> for DbExecutor {
|
||||
time_remaining: msg.time_remaining,
|
||||
reporter_id: msg.reporter_id,
|
||||
project_id: msg.project_id,
|
||||
epic_id: msg.epic_id,
|
||||
};
|
||||
|
||||
let issue = diesel::insert_into(issues)
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![feature(async_closure)]
|
||||
#![feature(vec_remove_item)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use jirs_data::{
|
||||
InvitationState, IssuePriority, IssueStatusId, IssueType, ProjectCategory, ProjectId,
|
||||
EpicId, InvitationState, IssuePriority, IssueStatusId, IssueType, ProjectCategory, ProjectId,
|
||||
TimeTracking, UserId,
|
||||
};
|
||||
|
||||
@ -34,6 +34,7 @@ pub struct Issue {
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub issue_status_id: IssueStatusId,
|
||||
pub epic_id: Option<EpicId>,
|
||||
}
|
||||
|
||||
impl Into<jirs_data::Issue> for Issue {
|
||||
@ -54,6 +55,7 @@ impl Into<jirs_data::Issue> for Issue {
|
||||
created_at: self.created_at,
|
||||
updated_at: self.updated_at,
|
||||
issue_status_id: self.issue_status_id,
|
||||
epic_id: self.epic_id,
|
||||
|
||||
user_ids: vec![],
|
||||
}
|
||||
@ -75,6 +77,7 @@ pub struct CreateIssueForm {
|
||||
pub reporter_id: UserId,
|
||||
pub project_id: ProjectId,
|
||||
pub issue_status_id: IssueStatusId,
|
||||
pub epic_id: Option<EpicId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
||||
|
@ -47,6 +47,53 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
|
||||
/// Representation of the `epics` table.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
epics (id) {
|
||||
/// The `id` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
id -> Int4,
|
||||
/// The `name` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Text`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
name -> Text,
|
||||
/// The `user_id` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
user_id -> Int4,
|
||||
/// The `project_id` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
project_id -> Int4,
|
||||
/// The `created_at` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
created_at -> Timestamp,
|
||||
/// The `updated_at` column of the `epics` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
updated_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
@ -304,6 +351,12 @@ table! {
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
issue_status_id -> Int4,
|
||||
/// The `epic_id` column of the `issues` table.
|
||||
///
|
||||
/// Its SQL type is `Nullable<Int4>`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
epic_id -> Nullable<Int4>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,11 +645,14 @@ table! {
|
||||
|
||||
joinable!(comments -> issues (issue_id));
|
||||
joinable!(comments -> users (user_id));
|
||||
joinable!(epics -> projects (project_id));
|
||||
joinable!(epics -> users (user_id));
|
||||
joinable!(invitations -> projects (project_id));
|
||||
joinable!(invitations -> users (invited_by_id));
|
||||
joinable!(issue_assignees -> issues (issue_id));
|
||||
joinable!(issue_assignees -> users (user_id));
|
||||
joinable!(issue_statuses -> projects (project_id));
|
||||
joinable!(issues -> epics (epic_id));
|
||||
joinable!(issues -> issue_statuses (issue_status_id));
|
||||
joinable!(issues -> projects (project_id));
|
||||
joinable!(issues -> users (reporter_id));
|
||||
@ -606,6 +662,7 @@ joinable!(user_projects -> users (user_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
comments,
|
||||
epics,
|
||||
invitations,
|
||||
issue_assignees,
|
||||
issue_statuses,
|
||||
|
@ -106,6 +106,7 @@ impl WsHandler<CreateIssuePayload> for WebSocketActor {
|
||||
project_id: msg.project_id,
|
||||
reporter_id: msg.reporter_id,
|
||||
user_ids: msg.user_ids,
|
||||
epic_id: msg.epic_id,
|
||||
};
|
||||
let m = match block_on(self.db.send(msg)) {
|
||||
Ok(Ok(issue)) => Some(WsMsg::IssueCreated(issue.into())),
|
||||
|
Loading…
Reference in New Issue
Block a user