Additional invitation stuff
This commit is contained in:
parent
761305fbbd
commit
7c33a4943c
@ -2,6 +2,11 @@
|
|||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selectItem.capitalize,
|
||||||
|
.optionItem.capitalize {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
.selectItem.priority.highest > .styledIcon,
|
.selectItem.priority.highest > .styledIcon,
|
||||||
.optionItem.priority.highest > .styledIcon {
|
.optionItem.priority.highest > .styledIcon {
|
||||||
color: var(--highest);
|
color: var(--highest);
|
||||||
|
@ -256,6 +256,7 @@ impl ToStyledSelectChild for jirs_data::UserRole {
|
|||||||
|
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.add_class(name.as_str())
|
.add_class(name.as_str())
|
||||||
|
.add_class("capitalize")
|
||||||
.text(name)
|
.text(name)
|
||||||
.value(self.clone().into())
|
.value(self.clone().into())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::UserRole;
|
|
||||||
use jirs_data::{ToVec, UsersFieldId};
|
use jirs_data::{ToVec, UsersFieldId};
|
||||||
|
use jirs_data::{UserRole, WsMsg};
|
||||||
|
|
||||||
|
use crate::api::send_ws_msg;
|
||||||
use crate::model::{Model, Page, PageContent, UsersPage};
|
use crate::model::{Model, Page, PageContent, UsersPage};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_field::StyledField;
|
use crate::shared::styled_field::StyledField;
|
||||||
@ -50,6 +51,10 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model) -> Node<Msg> {
|
||||||
|
if model.user.is_none() {
|
||||||
|
return empty![];
|
||||||
|
}
|
||||||
|
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
PageContent::Users(page) => page,
|
PageContent::Users(page) => page,
|
||||||
_ => return empty![],
|
_ => return empty![],
|
||||||
|
@ -23,6 +23,7 @@ pub type ProjectId = i32;
|
|||||||
pub type UserId = i32;
|
pub type UserId = i32;
|
||||||
pub type CommentId = i32;
|
pub type CommentId = i32;
|
||||||
pub type TokenId = i32;
|
pub type TokenId = i32;
|
||||||
|
pub type InvitationId = i32;
|
||||||
pub type EmailString = String;
|
pub type EmailString = String;
|
||||||
pub type UsernameString = String;
|
pub type UsernameString = String;
|
||||||
|
|
||||||
@ -403,6 +404,31 @@ impl Into<ProjectCategory> for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
|
#[cfg_attr(feature = "backend", sql_type = "InvitationStateType")]
|
||||||
|
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum InvitationState {
|
||||||
|
Sent,
|
||||||
|
Accepted,
|
||||||
|
Revoked,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InvitationState {
|
||||||
|
fn default() -> Self {
|
||||||
|
InvitationState::Sent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for InvitationState {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
InvitationState::Sent => f.write_str("sent"),
|
||||||
|
InvitationState::Accepted => f.write_str("accepted"),
|
||||||
|
InvitationState::Revoked => f.write_str("revoked"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Debug, PartialEq)]
|
#[derive(Clone, Serialize, Debug, PartialEq)]
|
||||||
pub struct ErrorResponse {
|
pub struct ErrorResponse {
|
||||||
pub errors: Vec<String>,
|
pub errors: Vec<String>,
|
||||||
@ -632,6 +658,24 @@ pub enum WsMsg {
|
|||||||
SignUpSuccess,
|
SignUpSuccess,
|
||||||
SignUpPairTaken,
|
SignUpPairTaken,
|
||||||
|
|
||||||
|
// invitations
|
||||||
|
InvitationListRequest,
|
||||||
|
InvitationListLoaded(Vec<Invitation>),
|
||||||
|
InvitedUsersRequest,
|
||||||
|
InvitedUsersLoaded(Vec<User>),
|
||||||
|
InvitationSendRequest {
|
||||||
|
name: UsernameString,
|
||||||
|
email: EmailString,
|
||||||
|
},
|
||||||
|
InvitationSendSuccess,
|
||||||
|
InvitationSendFailure,
|
||||||
|
//
|
||||||
|
InvitationRevokeRequest(InvitationId),
|
||||||
|
InvitationRevokeSuccess(InvitationId),
|
||||||
|
//
|
||||||
|
InvitationAcceptRequest(InvitationId),
|
||||||
|
InvitationAcceptSuccess(InvitationId),
|
||||||
|
|
||||||
// project page
|
// project page
|
||||||
ProjectRequest,
|
ProjectRequest,
|
||||||
ProjectLoaded(Project),
|
ProjectLoaded(Project),
|
||||||
|
@ -2,7 +2,7 @@ use std::io::Write;
|
|||||||
|
|
||||||
use diesel::{deserialize::*, pg::*, serialize::*, *};
|
use diesel::{deserialize::*, pg::*, serialize::*, *};
|
||||||
|
|
||||||
use crate::{IssuePriority, IssueStatus, IssueType, ProjectCategory, UserRole};
|
use crate::{InvitationState, IssuePriority, IssueStatus, IssueType, ProjectCategory, UserRole};
|
||||||
|
|
||||||
#[derive(SqlType)]
|
#[derive(SqlType)]
|
||||||
#[postgres(type_name = "IssuePriorityType")]
|
#[postgres(type_name = "IssuePriorityType")]
|
||||||
@ -201,3 +201,43 @@ impl ToSql<UserRoleType, Pg> for UserRole {
|
|||||||
Ok(IsNull::No)
|
Ok(IsNull::No)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SqlType)]
|
||||||
|
#[postgres(type_name = "InvitationStateType")]
|
||||||
|
pub struct InvitationStateType;
|
||||||
|
|
||||||
|
impl diesel::query_builder::QueryId for InvitationState {
|
||||||
|
type QueryId = InvitationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invitation_state_from_sql(bytes: Option<&[u8]>) -> deserialize::Result<InvitationState> {
|
||||||
|
match not_none!(bytes) {
|
||||||
|
b"sent" => Ok(InvitationState::Sent),
|
||||||
|
b"accepted" => Ok(InvitationState::Accepted),
|
||||||
|
b"revoked" => Ok(InvitationState::Revoked),
|
||||||
|
_ => Ok(InvitationState::Sent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<InvitationStateType, Pg> for InvitationState {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
invitation_state_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<sql_types::Text, Pg> for InvitationState {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
invitation_state_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql<InvitationStateType, Pg> for InvitationState {
|
||||||
|
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
|
||||||
|
match *self {
|
||||||
|
InvitationState::Sent => out.write_all(b"sent")?,
|
||||||
|
InvitationState::Accepted => out.write_all(b"accepted")?,
|
||||||
|
InvitationState::Revoked => out.write_all(b"revoked")?,
|
||||||
|
}
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
drop TABLE IF EXISTS invitations CASCADE;
|
||||||
|
drop TYPE IF EXISTS "InvitationStateType" CASCADE;
|
@ -0,0 +1,16 @@
|
|||||||
|
create type "InvitationStateType" AS ENUM (
|
||||||
|
'sent',
|
||||||
|
'accepted',
|
||||||
|
'revoked'
|
||||||
|
);
|
||||||
|
|
||||||
|
create table invitations (
|
||||||
|
id serial primary key not null,
|
||||||
|
name text not null,
|
||||||
|
email text not null,
|
||||||
|
state "InvitationStateType" not null default 'sent',
|
||||||
|
project_id integer not null references projects (id),
|
||||||
|
invited_by_id integer not null references users (id),
|
||||||
|
created_at timestamp not null default now(),
|
||||||
|
updated_at timestamp not null default now()
|
||||||
|
);
|
@ -2,7 +2,10 @@ use chrono::NaiveDateTime;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use jirs_data::{IssuePriority, IssueStatus, IssueType, ProjectCategory, UserRole};
|
use jirs_data::{
|
||||||
|
InvitationState, InvitationStateType, IssuePriority, IssueStatus, IssueType, ProjectCategory,
|
||||||
|
UserRole,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::schema::*;
|
use crate::schema::*;
|
||||||
|
|
||||||
@ -217,7 +220,6 @@ pub struct UserForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Queryable)]
|
#[derive(Debug, Serialize, Deserialize, Queryable)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
@ -242,7 +244,6 @@ impl Into<jirs_data::Token> for Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[table_name = "tokens"]
|
#[table_name = "tokens"]
|
||||||
pub struct TokenForm {
|
pub struct TokenForm {
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
@ -250,3 +251,25 @@ pub struct TokenForm {
|
|||||||
pub refresh_token: Uuid,
|
pub refresh_token: Uuid,
|
||||||
pub bind_token: Option<Uuid>,
|
pub bind_token: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Queryable)]
|
||||||
|
pub struct Invitation {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub email: String,
|
||||||
|
pub state: InvitationState,
|
||||||
|
pub project_id: i32,
|
||||||
|
pub invited_by_id: i32,
|
||||||
|
pub created_at: NaiveDateTime,
|
||||||
|
pub updated_at: NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Insertable)]
|
||||||
|
#[table_name = "invitations"]
|
||||||
|
pub struct InvitationForm {
|
||||||
|
pub name: String,
|
||||||
|
pub email: String,
|
||||||
|
pub state: InvitationState,
|
||||||
|
pub project_id: i32,
|
||||||
|
pub invited_by_id: i32,
|
||||||
|
}
|
||||||
|
@ -47,6 +47,65 @@ table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
invitations (id) {
|
||||||
|
/// The `id` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
id -> Int4,
|
||||||
|
/// The `name` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
name -> Text,
|
||||||
|
/// The `email` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
email -> Text,
|
||||||
|
/// The `state` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `InvitationStateType`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
state -> InvitationStateType,
|
||||||
|
/// The `project_id` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
project_id -> Int4,
|
||||||
|
/// The `invited_by_id` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
invited_by_id -> Int4,
|
||||||
|
/// The `created_at` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `invitations` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
use diesel::sql_types::*;
|
use diesel::sql_types::*;
|
||||||
use jirs_data::sql::*;
|
use jirs_data::sql::*;
|
||||||
@ -356,6 +415,8 @@ table! {
|
|||||||
|
|
||||||
joinable!(comments -> issues (issue_id));
|
joinable!(comments -> issues (issue_id));
|
||||||
joinable!(comments -> users (user_id));
|
joinable!(comments -> users (user_id));
|
||||||
|
joinable!(invitations -> projects (project_id));
|
||||||
|
joinable!(invitations -> users (invited_by_id));
|
||||||
joinable!(issue_assignees -> issues (issue_id));
|
joinable!(issue_assignees -> issues (issue_id));
|
||||||
joinable!(issue_assignees -> users (user_id));
|
joinable!(issue_assignees -> users (user_id));
|
||||||
joinable!(issues -> projects (project_id));
|
joinable!(issues -> projects (project_id));
|
||||||
@ -365,6 +426,7 @@ joinable!(users -> projects (project_id));
|
|||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
comments,
|
comments,
|
||||||
|
invitations,
|
||||||
issue_assignees,
|
issue_assignees,
|
||||||
issues,
|
issues,
|
||||||
projects,
|
projects,
|
||||||
|
Loading…
Reference in New Issue
Block a user