Compare commits
2 Commits
feat/publi
...
refactor/c
Author | SHA1 | Date | |
---|---|---|---|
2de48e288b | |||
7b6624ecc3 |
47
conf/air.toml
Normal file
47
conf/air.toml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
|
||||||
|
|
||||||
|
# Working directory
|
||||||
|
# . or absolute path, please note that the directories following must be under root.
|
||||||
|
root = "."
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Just plain old shell command. You could use `make` as well.
|
||||||
|
cmd = "go build -o ./dist/taskcafe cmd/taskcafe/main.go"
|
||||||
|
# Binary file yields from `cmd`.
|
||||||
|
bin = "dist/taskcafe"
|
||||||
|
# Customize binary.
|
||||||
|
full_bin = "./dist/taskcafe web"
|
||||||
|
# Watch these filename extensions.
|
||||||
|
include_ext = ["go"]
|
||||||
|
# Ignore these filename extensions or directories.
|
||||||
|
exclude_dir = ["dist", "frontend"]
|
||||||
|
# Watch these directories if you specified.
|
||||||
|
include_dir = []
|
||||||
|
# Exclude files.
|
||||||
|
exclude_file = []
|
||||||
|
# This log file places in your tmp_dir.
|
||||||
|
log = "air.log"
|
||||||
|
# It's not necessary to trigger build each time file changes if it's too frequent.
|
||||||
|
delay = 1000 # ms
|
||||||
|
# Stop running old binary when build errors occur.
|
||||||
|
stop_on_error = true
|
||||||
|
# Send Interrupt signal before killing process (windows does not support this feature)
|
||||||
|
send_interrupt = false
|
||||||
|
# Delay after sending Interrupt signal
|
||||||
|
kill_delay = 500 # ms
|
||||||
|
|
||||||
|
[log]
|
||||||
|
# Show log time
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
# Customize each part's color. If no color found, use the raw app log.
|
||||||
|
main = "magenta"
|
||||||
|
watcher = "cyan"
|
||||||
|
build = "yellow"
|
||||||
|
runner = "green"
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
# Delete tmp directory on exit
|
||||||
|
clean_on_exit = true
|
@ -12,7 +12,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- taskcafe-postgres:/var/lib/postgresql/data
|
- taskcafe-postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 8855:5432
|
||||||
mailhog:
|
mailhog:
|
||||||
image: mailhog/mailhog:latest
|
image: mailhog/mailhog:latest
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
|
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
|
||||||
"no-case-declarations": "off",
|
"no-case-declarations": "off",
|
||||||
|
"no-plusplus": "off",
|
||||||
"react/prop-types": 0,
|
"react/prop-types": 0,
|
||||||
|
"no-continue": "off",
|
||||||
"react/jsx-props-no-spreading": "off",
|
"react/jsx-props-no-spreading": "off",
|
||||||
"no-param-reassign": "off",
|
"no-param-reassign": "off",
|
||||||
"import/extensions": [
|
"import/extensions": [
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"@types/jwt-decode": "^2.2.1",
|
"@types/jwt-decode": "^2.2.1",
|
||||||
"@types/lodash": "^4.14.149",
|
"@types/lodash": "^4.14.149",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
|
"@types/query-string": "^6.3.0",
|
||||||
"@types/react": "^16.9.21",
|
"@types/react": "^16.9.21",
|
||||||
"@types/react-beautiful-dnd": "^12.1.1",
|
"@types/react-beautiful-dnd": "^12.1.1",
|
||||||
"@types/react-datepicker": "^2.11.0",
|
"@types/react-datepicker": "^2.11.0",
|
||||||
@ -39,8 +40,9 @@
|
|||||||
"history": "^4.10.1",
|
"history": "^4.10.1",
|
||||||
"immer": "^6.0.3",
|
"immer": "^6.0.3",
|
||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.20",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
|
"query-string": "^6.13.7",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-autosize-textarea": "^7.0.0",
|
"react-autosize-textarea": "^7.0.0",
|
||||||
"react-beautiful-dnd": "^13.0.0",
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
|
@ -5,6 +5,7 @@ import GlobalTopNavbar from 'App/TopNavbar';
|
|||||||
import {
|
import {
|
||||||
useUsersQuery,
|
useUsersQuery,
|
||||||
useDeleteUserAccountMutation,
|
useDeleteUserAccountMutation,
|
||||||
|
useDeleteInvitedUserAccountMutation,
|
||||||
useCreateUserAccountMutation,
|
useCreateUserAccountMutation,
|
||||||
UsersDocument,
|
UsersDocument,
|
||||||
UsersQuery,
|
UsersQuery,
|
||||||
@ -81,7 +82,7 @@ const AddUserInput = styled(Input)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const InputError = styled.span`
|
const InputError = styled.span`
|
||||||
color: rgba(${props => props.theme.colors.danger});
|
color: ${props => props.theme.colors.danger};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -176,6 +177,17 @@ const AdminRoute = () => {
|
|||||||
const { loading, data } = useUsersQuery();
|
const { loading, data } = useUsersQuery();
|
||||||
const { showPopup, hidePopup } = usePopup();
|
const { showPopup, hidePopup } = usePopup();
|
||||||
const { user } = useCurrentUser();
|
const { user } = useCurrentUser();
|
||||||
|
const [deleteInvitedUser] = useDeleteInvitedUserAccountMutation({
|
||||||
|
update: (client, response) => {
|
||||||
|
updateApolloCache<UsersQuery>(client, UsersDocument, cache =>
|
||||||
|
produce(cache, draftCache => {
|
||||||
|
draftCache.invitedUsers = cache.invitedUsers.filter(
|
||||||
|
u => u.id !== response.data.deleteInvitedUserAccount.invitedUser.id,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
const [deleteUser] = useDeleteUserAccountMutation({
|
const [deleteUser] = useDeleteUserAccountMutation({
|
||||||
update: (client, response) => {
|
update: (client, response) => {
|
||||||
updateApolloCache<UsersQuery>(client, UsersDocument, cache =>
|
updateApolloCache<UsersQuery>(client, UsersDocument, cache =>
|
||||||
@ -215,11 +227,16 @@ const AdminRoute = () => {
|
|||||||
<Admin
|
<Admin
|
||||||
initialTab={0}
|
initialTab={0}
|
||||||
users={data.users}
|
users={data.users}
|
||||||
|
invitedUsers={data.invitedUsers}
|
||||||
canInviteUser={user.roles.org === 'admin'}
|
canInviteUser={user.roles.org === 'admin'}
|
||||||
onInviteUser={NOOP}
|
onInviteUser={NOOP}
|
||||||
onUpdateUserPassword={() => {
|
onUpdateUserPassword={() => {
|
||||||
hidePopup();
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
|
onDeleteInvitedUser={invitedUserID => {
|
||||||
|
deleteInvitedUser({ variables: { invitedUserID } });
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
onDeleteUser={(userID, newOwnerID) => {
|
onDeleteUser={(userID, newOwnerID) => {
|
||||||
deleteUser({ variables: { userID, newOwnerID } });
|
deleteUser({ variables: { userID, newOwnerID } });
|
||||||
hidePopup();
|
hidePopup();
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||||
import * as H from 'history';
|
import * as H from 'history';
|
||||||
|
|
||||||
import Dashboard from 'Dashboard';
|
import Dashboard from 'Dashboard';
|
||||||
import Admin from 'Admin';
|
import Admin from 'Admin';
|
||||||
|
import Confirm from 'Confirm';
|
||||||
import Projects from 'Projects';
|
import Projects from 'Projects';
|
||||||
import Project from 'Projects/Project';
|
import Project from 'Projects/Project';
|
||||||
import Teams from 'Teams';
|
import Teams from 'Teams';
|
||||||
import Login from 'Auth';
|
import Login from 'Auth';
|
||||||
import Install from 'Install';
|
import Register from 'Register';
|
||||||
import Profile from 'Profile';
|
import Profile from 'Profile';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import JwtDecode from 'jwt-decode';
|
||||||
|
import { setAccessToken } from 'shared/utils/accessToken';
|
||||||
|
import { useCurrentUser } from 'App/context';
|
||||||
|
|
||||||
const MainContent = styled.div`
|
const MainContent = styled.div`
|
||||||
padding: 0 0 0 0;
|
padding: 0 0 0 0;
|
||||||
@ -21,6 +25,50 @@ const MainContent = styled.div`
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const AuthorizedRoutes = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const { setUser } = useCurrentUser();
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/auth/refresh_token', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
}).then(async x => {
|
||||||
|
const { status } = x;
|
||||||
|
if (status === 400) {
|
||||||
|
history.replace('/login');
|
||||||
|
} else {
|
||||||
|
const response: RefreshTokenResponse = await x.json();
|
||||||
|
const { accessToken, setup } = response;
|
||||||
|
if (setup) {
|
||||||
|
history.replace(`/register?confirmToken=${setup.confirmToken}`);
|
||||||
|
} else {
|
||||||
|
const claims: JWTToken = JwtDecode(accessToken);
|
||||||
|
const currentUser = {
|
||||||
|
id: claims.userId,
|
||||||
|
roles: { org: claims.orgRole, teams: new Map<string, string>(), projects: new Map<string, string>() },
|
||||||
|
};
|
||||||
|
setUser(currentUser);
|
||||||
|
setAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return loading ? null : (
|
||||||
|
<Switch>
|
||||||
|
<MainContent>
|
||||||
|
<Route exact path="/" component={Dashboard} />
|
||||||
|
<Route exact path="/projects" component={Projects} />
|
||||||
|
<Route path="/projects/:projectID" component={Project} />
|
||||||
|
<Route path="/teams/:teamID" component={Teams} />
|
||||||
|
<Route path="/profile" component={Profile} />
|
||||||
|
<Route path="/admin" component={Admin} />
|
||||||
|
</MainContent>
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
type RoutesProps = {
|
type RoutesProps = {
|
||||||
history: H.History;
|
history: H.History;
|
||||||
};
|
};
|
||||||
@ -28,15 +76,9 @@ type RoutesProps = {
|
|||||||
const Routes: React.FC<RoutesProps> = () => (
|
const Routes: React.FC<RoutesProps> = () => (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/login" component={Login} />
|
<Route exact path="/login" component={Login} />
|
||||||
<Route exact path="/install" component={Install} />
|
<Route exact path="/register" component={Register} />
|
||||||
<MainContent>
|
<Route exact path="/confirm" component={Confirm} />
|
||||||
<Route exact path="/" component={Dashboard} />
|
<AuthorizedRoutes />
|
||||||
<Route exact path="/projects" component={Projects} />
|
|
||||||
<Route path="/projects/:projectID" component={Project} />
|
|
||||||
<Route path="/teams/:teamID" component={Teams} />
|
|
||||||
<Route path="/profile" component={Profile} />
|
|
||||||
<Route path="/admin" component={Admin} />
|
|
||||||
</MainContent>
|
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,26 +1,28 @@
|
|||||||
import { DefaultTheme } from 'styled-components';
|
import { DefaultTheme } from 'styled-components';
|
||||||
|
import Color from 'color';
|
||||||
|
|
||||||
const theme: DefaultTheme = {
|
const theme: DefaultTheme = {
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
primary: '3px',
|
primary: '3x',
|
||||||
alternate: '6px',
|
alternate: '6px',
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: '115, 103, 240',
|
multiColors: ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'],
|
||||||
secondary: '216, 93, 216',
|
primary: 'rgb(115, 103, 240)',
|
||||||
alternate: '65, 69, 97',
|
secondary: 'rgb(216, 93, 216)',
|
||||||
success: '40, 199, 111',
|
alternate: 'rgb(65, 69, 97)',
|
||||||
danger: '234, 84, 85',
|
success: 'rgb(40, 199, 111)',
|
||||||
warning: '255, 159, 67',
|
danger: 'rgb(234, 84, 85)',
|
||||||
dark: '30, 30, 30',
|
warning: 'rgb(255, 159, 67)',
|
||||||
|
dark: 'rgb(30, 30, 30)',
|
||||||
text: {
|
text: {
|
||||||
primary: '194, 198, 220',
|
primary: 'rgb(194, 198, 220)',
|
||||||
secondary: '255, 255, 255',
|
secondary: 'rgb(255, 255, 255)',
|
||||||
},
|
},
|
||||||
border: '65, 69, 97',
|
border: 'rgb(65, 69, 97)',
|
||||||
bg: {
|
bg: {
|
||||||
primary: '16, 22, 58',
|
primary: 'rgb(16, 22, 58)',
|
||||||
secondary: '38, 44, 73',
|
secondary: 'rgb(38, 44, 73)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@ import MiniProfile from 'shared/components/MiniProfile';
|
|||||||
import cache from 'App/cache';
|
import cache from 'App/cache';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
import NotificationPopup, { NotificationItem } from 'shared/components/NotifcationPopup';
|
import NotificationPopup, { NotificationItem } from 'shared/components/NotifcationPopup';
|
||||||
|
import theme from './ThemeStyles';
|
||||||
|
|
||||||
const TeamContainer = styled.div`
|
const TeamContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -62,7 +63,7 @@ const TeamProjectBackground = styled.div<{ color: string }>`
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
&:before {
|
&:before {
|
||||||
background: rgba(${props => props.theme.colors.bg.secondary});
|
background: ${props => props.theme.colors.bg.secondary};
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
content: '';
|
content: '';
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -114,7 +115,7 @@ const TeamProjectContainer = styled.div`
|
|||||||
margin: 0 4px 4px 0;
|
margin: 0 4px 4px 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
&:hover ${TeamProjectTitle} {
|
&:hover ${TeamProjectTitle} {
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
&:hover ${TeamProjectAvatar} {
|
&:hover ${TeamProjectAvatar} {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -124,7 +125,7 @@ const TeamProjectContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
const colors = [theme.colors.primary, theme.colors.secondary];
|
||||||
|
|
||||||
const ProjectFinder = () => {
|
const ProjectFinder = () => {
|
||||||
const { loading, data } = useGetProjectsQuery();
|
const { loading, data } = useGetProjectsQuery();
|
||||||
@ -230,10 +231,12 @@ type GlobalTopNavbarProps = {
|
|||||||
menuType?: Array<MenuItem>;
|
menuType?: Array<MenuItem>;
|
||||||
onChangeRole?: (userID: string, roleCode: RoleCode) => void;
|
onChangeRole?: (userID: string, roleCode: RoleCode) => void;
|
||||||
projectMembers?: null | Array<TaskUser>;
|
projectMembers?: null | Array<TaskUser>;
|
||||||
|
projectInvitedMembers?: null | Array<InvitedUser>;
|
||||||
onSaveProjectName?: (projectName: string) => void;
|
onSaveProjectName?: (projectName: string) => void;
|
||||||
onInviteUser?: ($target: React.RefObject<HTMLElement>) => void;
|
onInviteUser?: ($target: React.RefObject<HTMLElement>) => void;
|
||||||
onSetTab?: (tab: number) => void;
|
onSetTab?: (tab: number) => void;
|
||||||
onRemoveFromBoard?: (userID: string) => void;
|
onRemoveFromBoard?: (userID: string) => void;
|
||||||
|
onRemoveInvitedFromBoard?: (email: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
||||||
@ -246,8 +249,10 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
|||||||
name,
|
name,
|
||||||
popupContent,
|
popupContent,
|
||||||
projectMembers,
|
projectMembers,
|
||||||
|
projectInvitedMembers,
|
||||||
onInviteUser,
|
onInviteUser,
|
||||||
onSaveProjectName,
|
onSaveProjectName,
|
||||||
|
onRemoveInvitedFromBoard,
|
||||||
onRemoveFromBoard,
|
onRemoveFromBoard,
|
||||||
}) => {
|
}) => {
|
||||||
const { user, setUserRoles, setUser } = useCurrentUser();
|
const { user, setUserRoles, setUser } = useCurrentUser();
|
||||||
@ -324,7 +329,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</NotificationPopup>,
|
</NotificationPopup>,
|
||||||
{ width: 415, borders: false, diamondColor: '#7367f0' },
|
{ width: 415, borders: false, diamondColor: theme.colors.primary },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -333,6 +338,34 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const userIsTeamOrProjectAdmin = user.isAdmin(PermissionLevel.TEAM, PermissionObjectType.TEAM, teamID);
|
const userIsTeamOrProjectAdmin = user.isAdmin(PermissionLevel.TEAM, PermissionObjectType.TEAM, teamID);
|
||||||
|
const onInvitedMemberProfile = ($targetRef: React.RefObject<HTMLElement>, email: string) => {
|
||||||
|
const member = projectInvitedMembers ? projectInvitedMembers.find(u => u.email === email) : null;
|
||||||
|
if (member) {
|
||||||
|
showPopup(
|
||||||
|
$targetRef,
|
||||||
|
<MiniProfile
|
||||||
|
onRemoveFromBoard={() => {
|
||||||
|
if (onRemoveInvitedFromBoard) {
|
||||||
|
onRemoveInvitedFromBoard(member.email);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
invited
|
||||||
|
user={{
|
||||||
|
id: member.email,
|
||||||
|
fullName: member.email,
|
||||||
|
bio: 'Invited',
|
||||||
|
profileIcon: {
|
||||||
|
bgColor: '#000',
|
||||||
|
url: null,
|
||||||
|
initials: member.email.charAt(0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
bio=""
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onMemberProfile = ($targetRef: React.RefObject<HTMLElement>, memberID: string) => {
|
const onMemberProfile = ($targetRef: React.RefObject<HTMLElement>, memberID: string) => {
|
||||||
const member = projectMembers ? projectMembers.find(u => u.id === memberID) : null;
|
const member = projectMembers ? projectMembers.find(u => u.id === memberID) : null;
|
||||||
const warning =
|
const warning =
|
||||||
@ -382,6 +415,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
|||||||
canEditProjectName={userIsTeamOrProjectAdmin}
|
canEditProjectName={userIsTeamOrProjectAdmin}
|
||||||
canInviteUser={userIsTeamOrProjectAdmin}
|
canInviteUser={userIsTeamOrProjectAdmin}
|
||||||
onMemberProfile={onMemberProfile}
|
onMemberProfile={onMemberProfile}
|
||||||
|
onInvitedMemberProfile={onInvitedMemberProfile}
|
||||||
onInviteUser={onInviteUser}
|
onInviteUser={onInviteUser}
|
||||||
onChangeRole={onChangeRole}
|
onChangeRole={onChangeRole}
|
||||||
onChangeProjectOwner={onChangeProjectOwner}
|
onChangeProjectOwner={onChangeProjectOwner}
|
||||||
@ -392,6 +426,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
|
|||||||
history.push('/');
|
history.push('/');
|
||||||
}}
|
}}
|
||||||
projectMembers={projectMembers}
|
projectMembers={projectMembers}
|
||||||
|
projectInvitedMembers={projectInvitedMembers}
|
||||||
onProfileClick={onProfileClick}
|
onProfileClick={onProfileClick}
|
||||||
onSaveName={onSaveProjectName}
|
onSaveName={onSaveProjectName}
|
||||||
onOpenSettings={onOpenSettings}
|
onOpenSettings={onOpenSettings}
|
||||||
|
@ -28,13 +28,13 @@ const StyledContainer = styled(ToastContainer).attrs({
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.Toastify__toast--error {
|
.Toastify__toast--error {
|
||||||
background: rgba(${props => props.theme.colors.danger});
|
background: ${props => props.theme.colors.danger};
|
||||||
}
|
}
|
||||||
.Toastify__toast--warning {
|
.Toastify__toast--warning {
|
||||||
background: rgba(${props => props.theme.colors.warning});
|
background: ${props => props.theme.colors.warning};
|
||||||
}
|
}
|
||||||
.Toastify__toast--success {
|
.Toastify__toast--success {
|
||||||
background: rgba(${props => props.theme.colors.success});
|
background: ${props => props.theme.colors.success};
|
||||||
}
|
}
|
||||||
.Toastify__toast-body {
|
.Toastify__toast-body {
|
||||||
}
|
}
|
||||||
@ -52,7 +52,6 @@ type RefreshTokenResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [user, setUser] = useState<CurrentUserRaw | null>(null);
|
const [user, setUser] = useState<CurrentUserRaw | null>(null);
|
||||||
const setUserRoles = (roles: CurrentUserRoles) => {
|
const setUserRoles = (roles: CurrentUserRoles) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
@ -63,32 +62,6 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/auth/refresh_token', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
}).then(async x => {
|
|
||||||
const { status } = x;
|
|
||||||
if (status === 400) {
|
|
||||||
history.replace('/login');
|
|
||||||
} else {
|
|
||||||
const response: RefreshTokenResponse = await x.json();
|
|
||||||
const { accessToken, isInstalled } = response;
|
|
||||||
const claims: JWTToken = jwtDecode(accessToken);
|
|
||||||
const currentUser = {
|
|
||||||
id: claims.userId,
|
|
||||||
roles: { org: claims.orgRole, teams: new Map<string, string>(), projects: new Map<string, string>() },
|
|
||||||
};
|
|
||||||
setUser(currentUser);
|
|
||||||
setAccessToken(accessToken);
|
|
||||||
if (!isInstalled) {
|
|
||||||
history.replace('/install');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UserContext.Provider value={{ user, setUser, setUserRoles }}>
|
<UserContext.Provider value={{ user, setUser, setUserRoles }}>
|
||||||
@ -97,13 +70,7 @@ const App = () => {
|
|||||||
<BaseStyles />
|
<BaseStyles />
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<PopupProvider>
|
<PopupProvider>
|
||||||
{loading ? (
|
<Routes history={history} />
|
||||||
<div>loading</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Routes history={history} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</PopupProvider>
|
</PopupProvider>
|
||||||
</Router>
|
</Router>
|
||||||
<StyledContainer
|
<StyledContainer
|
||||||
|
@ -52,7 +52,20 @@ const Auth = () => {
|
|||||||
}).then(async x => {
|
}).then(async x => {
|
||||||
const { status } = x;
|
const { status } = x;
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
history.replace('/projects');
|
const response: RefreshTokenResponse = await x.json();
|
||||||
|
const { accessToken, setup } = response;
|
||||||
|
if (setup) {
|
||||||
|
history.replace(`/register?confirmToken=${setup.confirmToken}`);
|
||||||
|
} else {
|
||||||
|
const claims: JWTToken = JwtDecode(accessToken);
|
||||||
|
const currentUser = {
|
||||||
|
id: claims.userId,
|
||||||
|
roles: { org: claims.orgRole, teams: new Map<string, string>(), projects: new Map<string, string>() },
|
||||||
|
};
|
||||||
|
setUser(currentUser);
|
||||||
|
setAccessToken(accessToken);
|
||||||
|
history.replace('/projects');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
61
frontend/src/Confirm/index.tsx
Normal file
61
frontend/src/Confirm/index.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import Confirm from 'shared/components/Confirm';
|
||||||
|
import { useHistory, useLocation } from 'react-router';
|
||||||
|
import * as QueryString from 'query-string';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { Container, LoginWrapper } from './Styles';
|
||||||
|
import JwtDecode from 'jwt-decode';
|
||||||
|
import { setAccessToken } from 'shared/utils/accessToken';
|
||||||
|
import { useCurrentUser } from 'App/context';
|
||||||
|
|
||||||
|
const UsersConfirm = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
const [registered, setRegistered] = useState(false);
|
||||||
|
const params = QueryString.parse(location.search);
|
||||||
|
const { setUser } = useCurrentUser();
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<LoginWrapper>
|
||||||
|
<Confirm
|
||||||
|
hasConfirmToken={params.confirmToken !== undefined}
|
||||||
|
onConfirmUser={setFailed => {
|
||||||
|
fetch('/auth/confirm', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
confirmToken: params.confirmToken,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(async x => {
|
||||||
|
const { status } = x;
|
||||||
|
if (status === 200) {
|
||||||
|
const response = await x.json();
|
||||||
|
const { accessToken } = response;
|
||||||
|
const claims: JWTToken = JwtDecode(accessToken);
|
||||||
|
const currentUser = {
|
||||||
|
id: claims.userId,
|
||||||
|
roles: {
|
||||||
|
org: claims.orgRole,
|
||||||
|
teams: new Map<string, string>(),
|
||||||
|
projects: new Map<string, string>(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
setUser(currentUser);
|
||||||
|
setAccessToken(accessToken);
|
||||||
|
history.push('/');
|
||||||
|
} else {
|
||||||
|
setFailed();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setFailed();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</LoginWrapper>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UsersConfirm;
|
@ -1,88 +0,0 @@
|
|||||||
import React, { useEffect, useContext } from 'react';
|
|
||||||
import axios from 'axios';
|
|
||||||
import Register from 'shared/components/Register';
|
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import { getAccessToken, setAccessToken } from 'shared/utils/accessToken';
|
|
||||||
import UserContext from 'App/context';
|
|
||||||
import jwtDecode from 'jwt-decode';
|
|
||||||
import { Container, LoginWrapper } from './Styles';
|
|
||||||
|
|
||||||
const Install = () => {
|
|
||||||
const history = useHistory();
|
|
||||||
const { setUser } = useContext(UserContext);
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/auth/refresh_token', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
}).then(async x => {
|
|
||||||
const { status } = x;
|
|
||||||
const response: RefreshTokenResponse = await x.json();
|
|
||||||
const { isInstalled } = response;
|
|
||||||
if (status === 200 && isInstalled) {
|
|
||||||
history.replace('/projects');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<LoginWrapper>
|
|
||||||
<Register
|
|
||||||
onSubmit={(data, setComplete, setError) => {
|
|
||||||
const accessToken = getAccessToken();
|
|
||||||
if (data.password !== data.password_confirm) {
|
|
||||||
setError('password', { type: 'error', message: 'Passwords must match' });
|
|
||||||
setError('password_confirm', { type: 'error', message: 'Passwords must match' });
|
|
||||||
} else {
|
|
||||||
axios
|
|
||||||
.post(
|
|
||||||
'/auth/install',
|
|
||||||
{
|
|
||||||
user: {
|
|
||||||
username: data.username,
|
|
||||||
roleCode: 'admin',
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
initials: data.initials,
|
|
||||||
fullname: data.fullname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.then(async x => {
|
|
||||||
const { status } = x;
|
|
||||||
if (status === 400) {
|
|
||||||
history.replace('/login');
|
|
||||||
} else {
|
|
||||||
const response: RefreshTokenResponse = await x.data;
|
|
||||||
const { accessToken: newToken, isInstalled } = response;
|
|
||||||
const claims: JWTToken = jwtDecode(newToken);
|
|
||||||
const currentUser = {
|
|
||||||
id: claims.userId,
|
|
||||||
roles: {
|
|
||||||
org: claims.orgRole,
|
|
||||||
teams: new Map<string, string>(),
|
|
||||||
projects: new Map<string, string>(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
setUser(currentUser);
|
|
||||||
setAccessToken(newToken);
|
|
||||||
if (!isInstalled) {
|
|
||||||
history.replace('/install');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
history.push('/projects');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setComplete(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</LoginWrapper>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Install;
|
|
@ -12,7 +12,7 @@ const FilterMember = styled(Member)`
|
|||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: rgba(${props => props.theme.colors.primary});
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export const ActionItem = styled.li`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ export const ActionTitle = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ActionItemSeparator = styled.li`
|
const ActionItemSeparator = styled.li`
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
@ -30,7 +30,7 @@ export const ActionItem = styled.li`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
&:hover ${ActionExtraMenuContainer} {
|
&:hover ${ActionExtraMenuContainer} {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
@ -69,11 +69,11 @@ export const ActionExtraMenuItem = styled.li`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: rgb(${props => props.theme.colors.primary});
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const ActionExtraMenuSeparator = styled.li`
|
const ActionExtraMenuSeparator = styled.li`
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { TaskSorting, TaskSortingType, TaskSortingDirection } from 'shared/utils/sorting';
|
import { TaskSorting, TaskSortingType, TaskSortingDirection } from 'shared/utils/sorting';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const ActionsList = styled.ul`
|
export const ActionsList = styled.ul`
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -20,7 +21,7 @@ export const ActionItem = styled.li`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ export const ActionTitle = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ActionItemSeparator = styled.li`
|
const ActionItemSeparator = styled.li`
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
@ -136,14 +136,14 @@ const ProjectActionWrapper = styled.div<{ disabled?: boolean }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
&:not(:last-of-type) {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
${props =>
|
${props =>
|
||||||
props.disabled &&
|
props.disabled &&
|
||||||
|
@ -3,32 +3,11 @@ import updateApolloCache from 'shared/utils/cache';
|
|||||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import {
|
import {
|
||||||
useUpdateProjectMemberRoleMutation,
|
|
||||||
useCreateProjectMemberMutation,
|
|
||||||
useDeleteProjectMemberMutation,
|
|
||||||
useSetTaskCompleteMutation,
|
|
||||||
useToggleTaskLabelMutation,
|
|
||||||
useUpdateProjectNameMutation,
|
|
||||||
useFindProjectQuery,
|
|
||||||
useUpdateTaskGroupNameMutation,
|
|
||||||
useUpdateTaskNameMutation,
|
|
||||||
useUpdateProjectLabelMutation,
|
useUpdateProjectLabelMutation,
|
||||||
useCreateTaskMutation,
|
|
||||||
useDeleteProjectLabelMutation,
|
useDeleteProjectLabelMutation,
|
||||||
useDeleteTaskMutation,
|
|
||||||
useUpdateTaskLocationMutation,
|
|
||||||
useUpdateTaskGroupLocationMutation,
|
|
||||||
useCreateTaskGroupMutation,
|
|
||||||
useDeleteTaskGroupMutation,
|
|
||||||
useUpdateTaskDescriptionMutation,
|
|
||||||
useAssignTaskMutation,
|
|
||||||
DeleteTaskDocument,
|
|
||||||
FindProjectDocument,
|
FindProjectDocument,
|
||||||
useCreateProjectLabelMutation,
|
useCreateProjectLabelMutation,
|
||||||
useUnassignTaskMutation,
|
|
||||||
useUpdateTaskDueDateMutation,
|
|
||||||
FindProjectQuery,
|
FindProjectQuery,
|
||||||
useUsersQuery,
|
|
||||||
} from 'shared/generated/graphql';
|
} from 'shared/generated/graphql';
|
||||||
import LabelManager from 'shared/components/PopupMenu/LabelManager';
|
import LabelManager from 'shared/components/PopupMenu/LabelManager';
|
||||||
import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
|
import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
|
||||||
|
@ -3,6 +3,7 @@ import React, { useState, useRef, useEffect, useContext } from 'react';
|
|||||||
import updateApolloCache from 'shared/utils/cache';
|
import updateApolloCache from 'shared/utils/cache';
|
||||||
import GlobalTopNavbar, { ProjectPopup } from 'App/TopNavbar';
|
import GlobalTopNavbar, { ProjectPopup } from 'App/TopNavbar';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
|
import AsyncSelect from 'react-select/async';
|
||||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||||
import {
|
import {
|
||||||
useParams,
|
useParams,
|
||||||
@ -15,11 +16,12 @@ import {
|
|||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
useUpdateProjectMemberRoleMutation,
|
useUpdateProjectMemberRoleMutation,
|
||||||
useCreateProjectMemberMutation,
|
useInviteProjectMembersMutation,
|
||||||
useDeleteProjectMemberMutation,
|
useDeleteProjectMemberMutation,
|
||||||
useToggleTaskLabelMutation,
|
useToggleTaskLabelMutation,
|
||||||
useUpdateProjectNameMutation,
|
useUpdateProjectNameMutation,
|
||||||
useFindProjectQuery,
|
useFindProjectQuery,
|
||||||
|
useDeleteInvitedProjectMemberMutation,
|
||||||
useUpdateTaskNameMutation,
|
useUpdateTaskNameMutation,
|
||||||
useCreateTaskMutation,
|
useCreateTaskMutation,
|
||||||
useDeleteTaskMutation,
|
useDeleteTaskMutation,
|
||||||
@ -37,12 +39,21 @@ import Input from 'shared/components/Input';
|
|||||||
import Member from 'shared/components/Member';
|
import Member from 'shared/components/Member';
|
||||||
import EmptyBoard from 'shared/components/EmptyBoard';
|
import EmptyBoard from 'shared/components/EmptyBoard';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
|
import { Lock, Cross } from 'shared/icons';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
|
import { useApolloClient } from '@apollo/react-hooks';
|
||||||
|
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||||
|
import gql from 'graphql-tag';
|
||||||
|
import { colourStyles } from 'shared/components/Select';
|
||||||
import Board, { BoardLoading } from './Board';
|
import Board, { BoardLoading } from './Board';
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
import LabelManagerEditor from './LabelManagerEditor';
|
import LabelManagerEditor from './LabelManagerEditor';
|
||||||
|
import { mixin } from '../../shared/utils/styles';
|
||||||
|
|
||||||
const CARD_LABEL_VARIANT_STORAGE_KEY = 'card_label_variant';
|
const CARD_LABEL_VARIANT_STORAGE_KEY = 'card_label_variant';
|
||||||
|
|
||||||
|
const RFC2822_EMAIL = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
|
||||||
|
|
||||||
const useStateWithLocalStorage = (localStorageKey: string): [string, React.Dispatch<React.SetStateAction<string>>] => {
|
const useStateWithLocalStorage = (localStorageKey: string): [string, React.Dispatch<React.SetStateAction<string>>] => {
|
||||||
const [value, setValue] = React.useState<string>(localStorage.getItem(localStorageKey) || '');
|
const [value, setValue] = React.useState<string>(localStorage.getItem(localStorageKey) || '');
|
||||||
|
|
||||||
@ -61,7 +72,7 @@ const UserMember = styled(Member)`
|
|||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.4)};
|
||||||
}
|
}
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`;
|
`;
|
||||||
@ -70,29 +81,299 @@ const MemberList = styled.div`
|
|||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
type InviteUserData = {
|
||||||
|
email?: string;
|
||||||
|
suerID?: string;
|
||||||
|
};
|
||||||
type UserManagementPopupProps = {
|
type UserManagementPopupProps = {
|
||||||
|
projectID: string;
|
||||||
users: Array<User>;
|
users: Array<User>;
|
||||||
projectMembers: Array<TaskUser>;
|
projectMembers: Array<TaskUser>;
|
||||||
onAddProjectMember: (userID: string) => void;
|
onInviteProjectMembers: (data: Array<InviteUserData>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({ users, projectMembers, onAddProjectMember }) => {
|
const VisibiltyPrivateIcon = styled(Lock)`
|
||||||
|
padding-right: 4px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const VisibiltyButtonText = styled.span`
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ShareActions = styled.div`
|
||||||
|
border-top: 1px solid #414561;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const VisibiltyButton = styled.button`
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 2px 4px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
&:hover ${VisibiltyButtonText} {
|
||||||
|
color: rgba(${props => props.theme.colors.text.secondary});
|
||||||
|
}
|
||||||
|
&:hover ${VisibiltyPrivateIcon} {
|
||||||
|
fill: rgba(${props => props.theme.colors.text.secondary});
|
||||||
|
stroke: rgba(${props => props.theme.colors.text.secondary});
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
border-bottom: 1px solid rgba(${props => props.theme.colors.primary});
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type MemberFilterOptions = {
|
||||||
|
projectID?: null | string;
|
||||||
|
teamID?: null | string;
|
||||||
|
organization?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMembers = async (client: any, projectID: string, options: MemberFilterOptions, input: string, cb: any) => {
|
||||||
|
console.log(input.trim().length < 3);
|
||||||
|
if (input && input.trim().length < 3) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const res = await client.query({
|
||||||
|
query: gql`
|
||||||
|
query {
|
||||||
|
searchMembers(input: {searchFilter:"${input}", projectID:"${projectID}"}) {
|
||||||
|
id
|
||||||
|
similarity
|
||||||
|
status
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
fullName
|
||||||
|
email
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
let results: any = [];
|
||||||
|
const emails: Array<string> = [];
|
||||||
|
console.log(res.data && res.data.searchMembers);
|
||||||
|
if (res.data && res.data.searchMembers) {
|
||||||
|
results = [
|
||||||
|
...res.data.searchMembers.map((m: any) => {
|
||||||
|
if (m.status === 'INVITED') {
|
||||||
|
console.log(`${m.id} is added`);
|
||||||
|
return {
|
||||||
|
label: m.id,
|
||||||
|
value: {
|
||||||
|
id: m.id,
|
||||||
|
type: 2,
|
||||||
|
profileIcon: {
|
||||||
|
bgColor: '#ccc',
|
||||||
|
initials: m.id.charAt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.log(`${m.user.email} is added`);
|
||||||
|
emails.push(m.user.email);
|
||||||
|
return {
|
||||||
|
label: m.user.fullName,
|
||||||
|
value: { id: m.user.id, type: 0, profileIcon: m.user.profileIcon },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
console.log(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RFC2822_EMAIL.test(input) && !emails.find(e => e === input)) {
|
||||||
|
results = [
|
||||||
|
...results,
|
||||||
|
{
|
||||||
|
label: input,
|
||||||
|
value: {
|
||||||
|
id: input,
|
||||||
|
type: 1,
|
||||||
|
profileIcon: {
|
||||||
|
bgColor: '#ccc',
|
||||||
|
initials: input.charAt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UserOptionProps = {
|
||||||
|
innerProps: any;
|
||||||
|
isDisabled: boolean;
|
||||||
|
isFocused: boolean;
|
||||||
|
label: string;
|
||||||
|
data: any;
|
||||||
|
getValue: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OptionWrapper = styled.div<{ isFocused: boolean }>`
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 8px;
|
||||||
|
${props => props.isFocused && `background: rgba(${props.theme.colors.primary});`}
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
const OptionContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const OptionLabel = styled.span<{ fontSize: number; quiet: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: ${p => p.fontSize}px;
|
||||||
|
color: rgba(${p => (p.quiet ? p.theme.colors.text.primary : p.theme.colors.text.primary)});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UserOption: React.FC<UserOptionProps> = ({ isDisabled, isFocused, innerProps, label, data }) => {
|
||||||
|
console.log(data);
|
||||||
|
return !isDisabled ? (
|
||||||
|
<OptionWrapper {...innerProps} isFocused={isFocused}>
|
||||||
|
<TaskAssignee
|
||||||
|
size={32}
|
||||||
|
member={{
|
||||||
|
id: '',
|
||||||
|
fullName: data.value.label,
|
||||||
|
profileIcon: data.value.profileIcon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<OptionContent>
|
||||||
|
<OptionLabel fontSize={16} quiet={false}>
|
||||||
|
{label}
|
||||||
|
</OptionLabel>
|
||||||
|
{data.value.type === 2 && (
|
||||||
|
<OptionLabel fontSize={14} quiet>
|
||||||
|
Joined
|
||||||
|
</OptionLabel>
|
||||||
|
)}
|
||||||
|
</OptionContent>
|
||||||
|
</OptionWrapper>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OptionValueWrapper = styled.div`
|
||||||
|
background: rgba(${props => props.theme.colors.bg.primary});
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 3px 6px 3px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const OptionValueLabel = styled.span`
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(${props => props.theme.colors.text.secondary});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const OptionValueRemove = styled.button`
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 4px;
|
||||||
|
`;
|
||||||
|
const OptionValue = ({ data, removeProps }: any) => {
|
||||||
|
return (
|
||||||
|
<OptionValueWrapper>
|
||||||
|
<OptionValueLabel>{data.label}</OptionValueLabel>
|
||||||
|
<OptionValueRemove {...removeProps}>
|
||||||
|
<Cross width={14} height={14} />
|
||||||
|
</OptionValueRemove>
|
||||||
|
</OptionValueWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const InviteButton = styled(Button)`
|
||||||
|
margin-top: 12px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InviteContainer = styled.div`
|
||||||
|
min-height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({
|
||||||
|
projectID,
|
||||||
|
users,
|
||||||
|
projectMembers,
|
||||||
|
onInviteProjectMembers,
|
||||||
|
}) => {
|
||||||
|
const client = useApolloClient();
|
||||||
|
const [invitedUsers, setInvitedUsers] = useState<Array<any> | null>(null);
|
||||||
return (
|
return (
|
||||||
<Popup tab={0} title="Invite a user">
|
<Popup tab={0} title="Invite a user">
|
||||||
<SearchInput width="100%" variant="alternate" placeholder="Email address or name" name="search" />
|
<InviteContainer>
|
||||||
<MemberList>
|
<AsyncSelect
|
||||||
{users
|
getOptionValue={option => option.value.id}
|
||||||
.filter(u => u.id !== projectMembers.find(p => p.id === u.id)?.id)
|
placeholder="Email address or username"
|
||||||
.map(user => (
|
noOptionsMessage={() => null}
|
||||||
<UserMember
|
onChange={(e: any) => {
|
||||||
key={user.id}
|
setInvitedUsers(e);
|
||||||
onCardMemberClick={() => onAddProjectMember(user.id)}
|
}}
|
||||||
showName
|
isMulti
|
||||||
member={user}
|
autoFocus
|
||||||
taskID=""
|
cacheOptions
|
||||||
/>
|
styles={colourStyles}
|
||||||
))}
|
defaultOption
|
||||||
</MemberList>
|
components={{
|
||||||
|
MultiValue: OptionValue,
|
||||||
|
Option: UserOption,
|
||||||
|
IndicatorSeparator: null,
|
||||||
|
DropdownIndicator: null,
|
||||||
|
}}
|
||||||
|
loadOptions={(i, cb) => fetchMembers(client, projectID, {}, i, cb)}
|
||||||
|
/>
|
||||||
|
</InviteContainer>
|
||||||
|
<InviteButton
|
||||||
|
onClick={() => {
|
||||||
|
if (invitedUsers) {
|
||||||
|
onInviteProjectMembers(
|
||||||
|
invitedUsers.map(user => {
|
||||||
|
if (user.value.type === 0) {
|
||||||
|
return {
|
||||||
|
userID: user.value.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
email: user.value.id,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={invitedUsers === null}
|
||||||
|
hoverVariant="none"
|
||||||
|
fontSize="16px"
|
||||||
|
>
|
||||||
|
Send Invite
|
||||||
|
</InviteButton>
|
||||||
</Popup>
|
</Popup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -176,14 +457,36 @@ const Project = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [createProjectMember] = useCreateProjectMemberMutation({
|
const [inviteProjectMembers] = useInviteProjectMembersMutation({
|
||||||
update: (client, response) => {
|
update: (client, response) => {
|
||||||
updateApolloCache<FindProjectQuery>(
|
updateApolloCache<FindProjectQuery>(
|
||||||
client,
|
client,
|
||||||
FindProjectDocument,
|
FindProjectDocument,
|
||||||
cache =>
|
cache =>
|
||||||
produce(cache, draftCache => {
|
produce(cache, draftCache => {
|
||||||
draftCache.findProject.members.push({ ...response.data.createProjectMember.member });
|
draftCache.findProject.members = [
|
||||||
|
...cache.findProject.members,
|
||||||
|
...response.data.inviteProjectMembers.members,
|
||||||
|
];
|
||||||
|
draftCache.findProject.invitedMembers = [
|
||||||
|
...cache.findProject.invitedMembers,
|
||||||
|
...response.data.inviteProjectMembers.invitedMembers,
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
{ projectID },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [deleteInvitedProjectMember] = useDeleteInvitedProjectMemberMutation({
|
||||||
|
update: (client, response) => {
|
||||||
|
updateApolloCache<FindProjectQuery>(
|
||||||
|
client,
|
||||||
|
FindProjectDocument,
|
||||||
|
cache =>
|
||||||
|
produce(cache, draftCache => {
|
||||||
|
draftCache.findProject.invitedMembers = cache.findProject.invitedMembers.filter(
|
||||||
|
m => m.email !== response.data.deleteInvitedProjectMember.invitedMember.email,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
{ projectID },
|
{ projectID },
|
||||||
);
|
);
|
||||||
@ -243,6 +546,10 @@ const Project = () => {
|
|||||||
deleteProjectMember({ variables: { userID, projectID } });
|
deleteProjectMember({ variables: { userID, projectID } });
|
||||||
hidePopup();
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
|
onRemoveInvitedFromBoard={email => {
|
||||||
|
deleteInvitedProjectMember({ variables: { projectID, email } });
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
onSaveProjectName={projectName => {
|
onSaveProjectName={projectName => {
|
||||||
updateProjectName({ variables: { projectID, name: projectName } });
|
updateProjectName({ variables: { projectID, name: projectName } });
|
||||||
}}
|
}}
|
||||||
@ -250,8 +557,10 @@ const Project = () => {
|
|||||||
showPopup(
|
showPopup(
|
||||||
$target,
|
$target,
|
||||||
<UserManagementPopup
|
<UserManagementPopup
|
||||||
onAddProjectMember={userID => {
|
projectID={projectID}
|
||||||
createProjectMember({ variables: { userID, projectID } });
|
onInviteProjectMembers={members => {
|
||||||
|
inviteProjectMembers({ variables: { projectID, members } });
|
||||||
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
users={data.users}
|
users={data.users}
|
||||||
projectMembers={data.findProject.members}
|
projectMembers={data.findProject.members}
|
||||||
@ -262,6 +571,7 @@ const Project = () => {
|
|||||||
menuType={[{ name: 'Board', link: location.pathname }]}
|
menuType={[{ name: 'Board', link: location.pathname }]}
|
||||||
currentTab={0}
|
currentTab={0}
|
||||||
projectMembers={data.findProject.members}
|
projectMembers={data.findProject.members}
|
||||||
|
projectInvitedMembers={data.findProject.invitedMembers}
|
||||||
projectID={projectID}
|
projectID={projectID}
|
||||||
teamID={data.findProject.team ? data.findProject.team.id : null}
|
teamID={data.findProject.team ? data.findProject.team.id : null}
|
||||||
name={data.findProject.name}
|
name={data.findProject.name}
|
||||||
|
@ -20,6 +20,8 @@ import Input from 'shared/components/Input';
|
|||||||
import updateApolloCache from 'shared/utils/cache';
|
import updateApolloCache from 'shared/utils/cache';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
|
import { mixin } from '../shared/utils/styles';
|
||||||
|
|
||||||
type CreateTeamData = { teamName: string };
|
type CreateTeamData = { teamName: string };
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ const CreateTeamForm: React.FC<CreateTeamFormProps> = ({ onCreateTeam }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ProjectAddTile = styled.div`
|
const ProjectAddTile = styled.div`
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background-color: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.4)};
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@ -176,7 +178,7 @@ const SectionActionLink = styled(Link)`
|
|||||||
|
|
||||||
const ProjectSectionTitle = styled.h3`
|
const ProjectSectionTitle = styled.h3`
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ProjectsContainer = styled.div`
|
const ProjectsContainer = styled.div`
|
||||||
@ -229,7 +231,7 @@ const Projects = () => {
|
|||||||
return <GlobalTopNavbar onSaveProjectName={NOOP} projectID={null} name={null} />;
|
return <GlobalTopNavbar onSaveProjectName={NOOP} projectID={null} name={null} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
const colors = theme.colors.multiColors;
|
||||||
if (data && user) {
|
if (data && user) {
|
||||||
const { projects, teams, organizations } = data;
|
const { projects, teams, organizations } = data;
|
||||||
const organizationID = organizations[0].id ?? null;
|
const organizationID = organizations[0].id ?? null;
|
||||||
|
13
frontend/src/Register/Styles.ts
Normal file
13
frontend/src/Register/Styles.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LoginWrapper = styled.div`
|
||||||
|
width: 60%;
|
||||||
|
`;
|
62
frontend/src/Register/index.tsx
Normal file
62
frontend/src/Register/index.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import Register from 'shared/components/Register';
|
||||||
|
import { useHistory, useLocation } from 'react-router';
|
||||||
|
import * as QueryString from 'query-string';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { Container, LoginWrapper } from './Styles';
|
||||||
|
|
||||||
|
const UsersRegister = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
const [registered, setRegistered] = useState(false);
|
||||||
|
const params = QueryString.parse(location.search);
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<LoginWrapper>
|
||||||
|
<Register
|
||||||
|
registered={registered}
|
||||||
|
onSubmit={(data, setComplete, setError) => {
|
||||||
|
if (data.password !== data.password_confirm) {
|
||||||
|
setError('password', { type: 'error', message: 'Passwords must match' });
|
||||||
|
setError('password_confirm', { type: 'error', message: 'Passwords must match' });
|
||||||
|
} else {
|
||||||
|
// TODO: change to fetch?
|
||||||
|
fetch('/auth/register', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
user: {
|
||||||
|
username: data.username,
|
||||||
|
roleCode: 'admin',
|
||||||
|
email: data.email,
|
||||||
|
password: data.password,
|
||||||
|
initials: data.initials,
|
||||||
|
fullname: data.fullname,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(async x => {
|
||||||
|
const response = await x.json();
|
||||||
|
const { setup } = response;
|
||||||
|
console.log(response);
|
||||||
|
if (setup) {
|
||||||
|
history.replace(`/confirm?confirmToken=xxxx`);
|
||||||
|
} else if (params.confirmToken) {
|
||||||
|
history.replace(`/confirm?confirmToken=${params.confirmToken}`);
|
||||||
|
} else {
|
||||||
|
setRegistered(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast('There was an issue trying to register');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setComplete(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</LoginWrapper>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UsersRegister;
|
@ -21,6 +21,7 @@ import TaskAssignee from 'shared/components/TaskAssignee';
|
|||||||
import Member from 'shared/components/Member';
|
import Member from 'shared/components/Member';
|
||||||
import ControlledInput from 'shared/components/ControlledInput';
|
import ControlledInput from 'shared/components/ControlledInput';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
const MemberListWrapper = styled.div`
|
const MemberListWrapper = styled.div`
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
@ -34,7 +35,7 @@ const UserMember = styled(Member)`
|
|||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.4)};
|
||||||
}
|
}
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
`;
|
`;
|
||||||
@ -119,12 +120,12 @@ export const MiniProfileActionItem = styled.span<{ disabled?: boolean }>`
|
|||||||
? css`
|
? css`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: rgba(${props.theme.colors.text.primary}, 0.4);
|
color: ${mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -135,7 +136,7 @@ export const Content = styled.div`
|
|||||||
|
|
||||||
export const CurrentPermission = styled.span`
|
export const CurrentPermission = styled.span`
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.secondary, 0.4)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Separator = styled.div`
|
export const Separator = styled.div`
|
||||||
@ -146,13 +147,13 @@ export const Separator = styled.div`
|
|||||||
|
|
||||||
export const WarningText = styled.span`
|
export const WarningText = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DeleteDescription = styled.div`
|
export const DeleteDescription = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RemoveMemberButton = styled(Button)`
|
export const RemoveMemberButton = styled(Button)`
|
||||||
@ -305,14 +306,14 @@ const MemberItemOption = styled(Button)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberList = styled.div`
|
const MemberList = styled.div`
|
||||||
border-top: 1px solid rgba(${props => props.theme.colors.border});
|
border-top: 1px solid ${props => props.theme.colors.border};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberListItem = styled.div`
|
const MemberListItem = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-bottom: 1px solid rgba(${props => props.theme.colors.border});
|
border-bottom: 1px solid ${props => props.theme.colors.border};
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
padding: 12px 0 12px 40px;
|
padding: 12px 0 12px 40px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -336,11 +337,11 @@ const MemberProfile = styled(TaskAssignee)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberItemName = styled.p`
|
const MemberItemName = styled.p`
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberItemUsername = styled.p`
|
const MemberItemUsername = styled.p`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberListHeader = styled.div`
|
const MemberListHeader = styled.div`
|
||||||
@ -349,12 +350,12 @@ const MemberListHeader = styled.div`
|
|||||||
`;
|
`;
|
||||||
const ListTitle = styled.h3`
|
const ListTitle = styled.h3`
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
`;
|
`;
|
||||||
const ListDesc = styled.span`
|
const ListDesc = styled.span`
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
const FilterSearch = styled(Input)`
|
const FilterSearch = styled(Input)`
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -386,11 +387,11 @@ const FilterTabItem = styled.li`
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: rgba(${props => props.theme.colors.primary});
|
background: ${props => props.theme.colors.primary};
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
} from 'shared/generated/graphql';
|
} from 'shared/generated/graphql';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Input from 'shared/components/Input';
|
import Input from 'shared/components/Input';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
|
|
||||||
const FilterSearch = styled(Input)`
|
const FilterSearch = styled(Input)`
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -34,11 +35,11 @@ const FilterTabItem = styled.li`
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: rgba(${props => props.theme.colors.primary});
|
background: ${props => props.theme.colors.primary};
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ const FilterTabTitle = styled.h2`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ProjectAddTile = styled.div`
|
const ProjectAddTile = styled.div`
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background-color: ${props => props.theme.colors.bg.primary};
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@ -147,7 +148,7 @@ const ProjectListWrapper = styled.div`
|
|||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
const colors = theme.colors.multiColors;
|
||||||
|
|
||||||
type TeamProjectsProps = {
|
type TeamProjectsProps = {
|
||||||
teamID: string;
|
teamID: string;
|
||||||
|
0
frontend/src/outline.d.ts
vendored
Normal file
0
frontend/src/outline.d.ts
vendored
Normal file
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
import AddList from '.';
|
import AddList from '.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -7,7 +8,7 @@ export default {
|
|||||||
title: 'AddList',
|
title: 'AddList',
|
||||||
parameters: {
|
parameters: {
|
||||||
backgrounds: [
|
backgrounds: [
|
||||||
{ name: 'gray', value: '#262c49', default: true },
|
{ name: 'gray', value: theme.colors.bg.secondary, default: true },
|
||||||
{ name: 'white', value: '#ffffff' },
|
{ name: 'white', value: '#ffffff' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -67,7 +67,7 @@ export const ListNameEditorWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
export const ListNameEditor = styled(TextareaAutosize)`
|
export const ListNameEditor = styled(TextareaAutosize)`
|
||||||
background-color: ${props => mixin.lighten('#262c49', 0.05)};
|
background-color: ${props => mixin.lighten(props.theme.colors.bg.secondary, 0.05)};
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: inset 0 0 0 2px #0079bf;
|
box-shadow: inset 0 0 0 2px #0079bf;
|
||||||
transition: margin 85ms ease-in, background 85ms ease-in;
|
transition: margin 85ms ease-in, background 85ms ease-in;
|
||||||
@ -91,7 +91,7 @@ export const ListNameEditor = styled(TextareaAutosize)`
|
|||||||
|
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
l &:focus {
|
l &:focus {
|
||||||
background-color: ${props => mixin.lighten('#262c49', 0.05)};
|
background-color: ${props => mixin.lighten(props.theme.colors.bg.secondary, 0.05)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ export const Default = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
invitedUsers={[]}
|
||||||
onAddUser={action('add user')}
|
onAddUser={action('add user')}
|
||||||
|
onDeleteInvitedUser={action('delete invited user')}
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</>
|
</>
|
||||||
|
@ -8,6 +8,7 @@ import { RoleCode, useUpdateUserRoleMutation } from 'shared/generated/graphql';
|
|||||||
import Input from 'shared/components/Input';
|
import Input from 'shared/components/Input';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const RoleCheckmark = styled(Checkmark)`
|
export const RoleCheckmark = styled(Checkmark)`
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
@ -58,12 +59,12 @@ export const MiniProfileActionItem = styled.span<{ disabled?: boolean }>`
|
|||||||
? css`
|
? css`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: rgba(${props.theme.colors.text.primary}, 0.4);
|
color: ${mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -74,7 +75,7 @@ export const Content = styled.div`
|
|||||||
|
|
||||||
export const CurrentPermission = styled.span`
|
export const CurrentPermission = styled.span`
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.secondary, 0.4)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Separator = styled.div`
|
export const Separator = styled.div`
|
||||||
@ -85,13 +86,13 @@ export const Separator = styled.div`
|
|||||||
|
|
||||||
export const WarningText = styled.span`
|
export const WarningText = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DeleteDescription = styled.div`
|
export const DeleteDescription = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RemoveMemberButton = styled(Button)`
|
export const RemoveMemberButton = styled(Button)`
|
||||||
@ -104,8 +105,8 @@ type TeamRoleManagerPopupProps = {
|
|||||||
user: User;
|
user: User;
|
||||||
users: Array<User>;
|
users: Array<User>;
|
||||||
warning?: string | null;
|
warning?: string | null;
|
||||||
canChangeRole: boolean;
|
canChangeRole?: boolean;
|
||||||
onChangeRole: (roleCode: RoleCode) => void;
|
onChangeRole?: (roleCode: RoleCode) => void;
|
||||||
updateUserPassword?: (user: TaskUser, password: string) => void;
|
updateUserPassword?: (user: TaskUser, password: string) => void;
|
||||||
onDeleteUser?: (userID: string, newOwnerID: string | null) => void;
|
onDeleteUser?: (userID: string, newOwnerID: string | null) => void;
|
||||||
};
|
};
|
||||||
@ -333,14 +334,14 @@ const MemberItemOption = styled(Button)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberList = styled.div`
|
const MemberList = styled.div`
|
||||||
border-top: 1px solid rgba(${props => props.theme.colors.border});
|
border-top: 1px solid ${props => props.theme.colors.border};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberListItem = styled.div`
|
const MemberListItem = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-bottom: 1px solid rgba(${props => props.theme.colors.border});
|
border-bottom: 1px solid ${props => props.theme.colors.border};
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
padding: 12px 0 12px 40px;
|
padding: 12px 0 12px 40px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -364,11 +365,11 @@ const MemberProfile = styled(TaskAssignee)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberItemName = styled.p`
|
const MemberItemName = styled.p`
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberItemUsername = styled.p`
|
const MemberItemUsername = styled.p`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MemberListHeader = styled.div`
|
const MemberListHeader = styled.div`
|
||||||
@ -377,12 +378,12 @@ const MemberListHeader = styled.div`
|
|||||||
`;
|
`;
|
||||||
const ListTitle = styled.h3`
|
const ListTitle = styled.h3`
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
`;
|
`;
|
||||||
const ListDesc = styled.span`
|
const ListDesc = styled.span`
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
const FilterSearch = styled(Input)`
|
const FilterSearch = styled(Input)`
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -443,17 +444,17 @@ const TabNavItemButton = styled.button<{ active: boolean }>`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
color: ${props => (props.active ? 'rgba(115, 103, 240)' : '#c2c6dc')};
|
color: ${props => (props.active ? `${props.theme.colors.secondary}` : props.theme.colors.text.primary)};
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props => `${props.theme.colors.primary}`};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(115, 103, 240);
|
fill: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const TabItemUser = styled(User)<{ active: boolean }>`
|
const TabItemUser = styled(User)<{ active: boolean }>`
|
||||||
fill: ${props => (props.active ? 'rgba(115, 103, 240)' : '#c2c6dc')}
|
fill: ${props => (props.active ? `${props.theme.colors.primary}` : props.theme.colors.text.primary)}
|
||||||
stroke: ${props => (props.active ? 'rgba(115, 103, 240)' : '#c2c6dc')}
|
stroke: ${props => (props.active ? `${props.theme.colors.primary}` : props.theme.colors.text.primary)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TabNavItemSpan = styled.span`
|
const TabNavItemSpan = styled.span`
|
||||||
@ -470,8 +471,8 @@ const TabNavLine = styled.span<{ top: number }>`
|
|||||||
transform: scaleX(1);
|
transform: scaleX(1);
|
||||||
top: ${props => props.top}px;
|
top: ${props => props.top}px;
|
||||||
|
|
||||||
background: linear-gradient(30deg, rgba(115, 103, 240), rgba(115, 103, 240));
|
background: linear-gradient(30deg, ${props => props.theme.colors.primary}, ${props => props.theme.colors.primary});
|
||||||
box-shadow: 0 0 8px 0 rgba(115, 103, 240);
|
box-shadow: 0 0 8px 0 ${props => props.theme.colors.primary};
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
@ -530,8 +531,10 @@ type AdminProps = {
|
|||||||
onDeleteUser: (userID: string, newOwnerID: string | null) => void;
|
onDeleteUser: (userID: string, newOwnerID: string | null) => void;
|
||||||
onInviteUser: ($target: React.RefObject<HTMLElement>) => void;
|
onInviteUser: ($target: React.RefObject<HTMLElement>) => void;
|
||||||
users: Array<User>;
|
users: Array<User>;
|
||||||
|
invitedUsers: Array<InvitedUserAccount>;
|
||||||
canInviteUser: boolean;
|
canInviteUser: boolean;
|
||||||
onUpdateUserPassword: (user: TaskUser, password: string) => void;
|
onUpdateUserPassword: (user: TaskUser, password: string) => void;
|
||||||
|
onDeleteInvitedUser: (invitedUserID: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Admin: React.FC<AdminProps> = ({
|
const Admin: React.FC<AdminProps> = ({
|
||||||
@ -540,7 +543,9 @@ const Admin: React.FC<AdminProps> = ({
|
|||||||
onUpdateUserPassword,
|
onUpdateUserPassword,
|
||||||
canInviteUser,
|
canInviteUser,
|
||||||
onDeleteUser,
|
onDeleteUser,
|
||||||
|
onDeleteInvitedUser,
|
||||||
onInviteUser,
|
onInviteUser,
|
||||||
|
invitedUsers,
|
||||||
users,
|
users,
|
||||||
}) => {
|
}) => {
|
||||||
const warning =
|
const warning =
|
||||||
@ -577,7 +582,7 @@ const Admin: React.FC<AdminProps> = ({
|
|||||||
<TabContent>
|
<TabContent>
|
||||||
<MemberListWrapper>
|
<MemberListWrapper>
|
||||||
<MemberListHeader>
|
<MemberListHeader>
|
||||||
<ListTitle>{`Members (${users.length})`}</ListTitle>
|
<ListTitle>{`Members (${users.length + invitedUsers.length})`}</ListTitle>
|
||||||
<ListDesc>
|
<ListDesc>
|
||||||
Organization admins can create / manage / delete all projects & teams. Members only have access to teams
|
Organization admins can create / manage / delete all projects & teams. Members only have access to teams
|
||||||
or projects they have been added to.
|
or projects they have been added to.
|
||||||
@ -635,6 +640,65 @@ const Admin: React.FC<AdminProps> = ({
|
|||||||
</MemberListItem>
|
</MemberListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{invitedUsers.map(member => {
|
||||||
|
return (
|
||||||
|
<MemberListItem>
|
||||||
|
<MemberProfile
|
||||||
|
showRoleIcons
|
||||||
|
size={32}
|
||||||
|
onMemberProfile={NOOP}
|
||||||
|
member={{
|
||||||
|
id: member.id,
|
||||||
|
fullName: member.email,
|
||||||
|
profileIcon: {
|
||||||
|
bgColor: '#fff',
|
||||||
|
url: null,
|
||||||
|
initials: member.email.charAt(0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<MemberListItemDetails>
|
||||||
|
<MemberItemName>{member.email}</MemberItemName>
|
||||||
|
<MemberItemUsername>Invited</MemberItemUsername>
|
||||||
|
</MemberListItemDetails>
|
||||||
|
<MemberItemOptions>
|
||||||
|
<MemberItemOption
|
||||||
|
variant="outline"
|
||||||
|
onClick={$target => {
|
||||||
|
showPopup(
|
||||||
|
$target,
|
||||||
|
<TeamRoleManagerPopup
|
||||||
|
user={{
|
||||||
|
id: member.id,
|
||||||
|
fullName: member.email,
|
||||||
|
profileIcon: {
|
||||||
|
bgColor: '#fff',
|
||||||
|
url: null,
|
||||||
|
initials: member.email.charAt(0),
|
||||||
|
},
|
||||||
|
member: {
|
||||||
|
teams: [],
|
||||||
|
projects: [],
|
||||||
|
},
|
||||||
|
owned: {
|
||||||
|
teams: [],
|
||||||
|
projects: [],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
users={users}
|
||||||
|
onDeleteUser={() => {
|
||||||
|
onDeleteInvitedUser(member.id);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Manage
|
||||||
|
</MemberItemOption>
|
||||||
|
</MemberItemOptions>
|
||||||
|
</MemberListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</MemberList>
|
</MemberList>
|
||||||
</MemberListWrapper>
|
</MemberListWrapper>
|
||||||
</TabContent>
|
</TabContent>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import styled, { css } from 'styled-components/macro';
|
import styled, { css } from 'styled-components/macro';
|
||||||
|
import { mixin } from '../../utils/styles';
|
||||||
|
|
||||||
const Text = styled.span<{ fontSize: string; justifyTextContent: string; hasIcon?: boolean }>`
|
const Text = styled.span<{ fontSize: string; justifyTextContent: string; hasIcon?: boolean }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -8,7 +9,7 @@ const Text = styled.span<{ fontSize: string; justifyTextContent: string; hasIcon
|
|||||||
justify-content: ${props => props.justifyTextContent};
|
justify-content: ${props => props.justifyTextContent};
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
font-size: ${props => props.fontSize};
|
font-size: ${props => props.fontSize};
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
${props =>
|
${props =>
|
||||||
props.hasIcon &&
|
props.hasIcon &&
|
||||||
css`
|
css`
|
||||||
@ -35,32 +36,37 @@ const Base = styled.button<{ color: string; disabled: boolean }>`
|
|||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Filled = styled(Base)`
|
const Filled = styled(Base)<{ hoverVariant: HoverVariant }>`
|
||||||
background: rgba(${props => props.theme.colors[props.color]});
|
background: ${props => props.theme.colors[props.color]};
|
||||||
&:hover {
|
${props =>
|
||||||
box-shadow: 0 8px 25px -8px rgba(${props => props.theme.colors[props.color]});
|
props.hoverVariant === 'boxShadow' &&
|
||||||
}
|
css`
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 8px 25px -8px ${props.theme.colors[props.color]};
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Outline = styled(Base)<{ invert: boolean }>`
|
const Outline = styled(Base)<{ invert: boolean }>`
|
||||||
border: 1px solid rgba(${props => props.theme.colors[props.color]});
|
border: 1px solid ${props => props.theme.colors[props.color]};
|
||||||
background: transparent;
|
background: transparent;
|
||||||
${props =>
|
${props =>
|
||||||
props.invert
|
props.invert
|
||||||
? css`
|
? css`
|
||||||
background: rgba(${props.theme.colors[props.color]});
|
background: ${props.theme.colors[props.color]});
|
||||||
& ${Text} {
|
& ${Text} {
|
||||||
color: rgba(${props.theme.colors.text.secondary});
|
color: ${props.theme.colors.text.secondary});
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props.theme.colors[props.color]}, 0.8);
|
background: ${mixin.rgba(props.theme.colors[props.color], 0.8)};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
& ${Text} {
|
& ${Text} {
|
||||||
color: rgba(${props.theme.colors[props.color]});
|
color: ${props.theme.colors[props.color]});
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props.theme.colors[props.color]}, 0.08);
|
background: ${mixin.rgba(props.theme.colors[props.color], 0.08)};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -68,7 +74,7 @@ const Outline = styled(Base)<{ invert: boolean }>`
|
|||||||
const Flat = styled(Base)`
|
const Flat = styled(Base)`
|
||||||
background: transparent;
|
background: transparent;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props => props.theme.colors[props.color]}, 0.2);
|
background: ${props => mixin.rgba(props.theme.colors[props.color], 0.2)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -81,7 +87,7 @@ const LineX = styled.span<{ color: string }>`
|
|||||||
bottom: -2px;
|
bottom: -2px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%);
|
transform: translate(-50%);
|
||||||
background: rgba(${props => props.theme.colors[props.color]}, 1);
|
background: ${props => mixin.rgba(props.theme.colors[props.color], 1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LineDown = styled(Base)`
|
const LineDown = styled(Base)`
|
||||||
@ -90,7 +96,7 @@ const LineDown = styled(Base)`
|
|||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 2px;
|
||||||
border-color: rgba(${props => props.theme.colors[props.color]}, 0.2);
|
border-color: ${props => mixin.rgba(props.theme.colors[props.color], 0.2)};
|
||||||
|
|
||||||
&:hover ${LineX} {
|
&:hover ${LineX} {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -103,8 +109,8 @@ const LineDown = styled(Base)`
|
|||||||
const Gradient = styled(Base)`
|
const Gradient = styled(Base)`
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
30deg,
|
30deg,
|
||||||
rgba(${props => props.theme.colors[props.color]}, 1),
|
${props => mixin.rgba(props.theme.colors[props.color], 1)},
|
||||||
rgba(${props => props.theme.colors[props.color]}, 0.5)
|
${props => mixin.rgba(props.theme.colors[props.color], 0.5)}
|
||||||
);
|
);
|
||||||
text-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
text-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -113,7 +119,7 @@ const Gradient = styled(Base)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Relief = styled(Base)`
|
const Relief = styled(Base)`
|
||||||
background: rgba(${props => props.theme.colors[props.color]}, 1);
|
background: ${props => mixin.rgba(props.theme.colors[props.color], 1)};
|
||||||
-webkit-box-shadow: 0 -3px 0 0 rgba(0, 0, 0, 0.2) inset;
|
-webkit-box-shadow: 0 -3px 0 0 rgba(0, 0, 0, 0.2) inset;
|
||||||
box-shadow: inset 0 -3px 0 0 rgba(0, 0, 0, 0.2);
|
box-shadow: inset 0 -3px 0 0 rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
@ -123,9 +129,11 @@ const Relief = styled(Base)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
type HoverVariant = 'boxShadow' | 'none';
|
||||||
type ButtonProps = {
|
type ButtonProps = {
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
|
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
|
||||||
|
hoverVariant?: HoverVariant;
|
||||||
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
type?: 'button' | 'submit';
|
type?: 'button' | 'submit';
|
||||||
@ -142,6 +150,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
invert = false,
|
invert = false,
|
||||||
color = 'primary',
|
color = 'primary',
|
||||||
variant = 'filled',
|
variant = 'filled',
|
||||||
|
hoverVariant = 'boxShadow',
|
||||||
type = 'button',
|
type = 'button',
|
||||||
justifyTextContent = 'center',
|
justifyTextContent = 'center',
|
||||||
icon,
|
icon,
|
||||||
@ -158,7 +167,15 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
switch (variant) {
|
switch (variant) {
|
||||||
case 'filled':
|
case 'filled':
|
||||||
return (
|
return (
|
||||||
<Filled ref={$button} type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Filled
|
||||||
|
ref={$button}
|
||||||
|
hoverVariant={hoverVariant}
|
||||||
|
type={type}
|
||||||
|
onClick={handleClick}
|
||||||
|
className={className}
|
||||||
|
disabled={disabled}
|
||||||
|
color={color}
|
||||||
|
>
|
||||||
{icon && icon}
|
{icon && icon}
|
||||||
<Text hasIcon={typeof icon !== 'undefined'} justifyTextContent={justifyTextContent} fontSize={fontSize}>
|
<Text hasIcon={typeof icon !== 'undefined'} justifyTextContent={justifyTextContent} fontSize={fontSize}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -5,8 +5,8 @@ import { CheckCircle, CheckSquareOutline, Clock } from 'shared/icons';
|
|||||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||||
|
|
||||||
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
|
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
|
||||||
box-shadow: 0 0 0 2px rgba(${props => props.theme.colors.bg.secondary}),
|
box-shadow: 0 0 0 2px ${props => props.theme.colors.bg.secondary},
|
||||||
inset 0 0 0 1px rgba(${props => props.theme.colors.bg.secondary}, 0.07);
|
inset 0 0 0 1px ${props => mixin.rgba(props.theme.colors.bg.secondary, 0.07)};
|
||||||
z-index: ${props => props.zIndex};
|
z-index: ${props => props.zIndex};
|
||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
@ -14,8 +14,8 @@ export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'no
|
|||||||
${props =>
|
${props =>
|
||||||
props.color === 'success' &&
|
props.color === 'success' &&
|
||||||
css`
|
css`
|
||||||
fill: rgba(${props.theme.colors.success});
|
fill: ${props.theme.colors.success};
|
||||||
stroke: rgba(${props.theme.colors.success});
|
stroke: ${props.theme.colors.success};
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
export const ClockIcon = styled(Clock)<{ color: string }>`
|
export const ClockIcon = styled(Clock)<{ color: string }>`
|
||||||
@ -38,7 +38,7 @@ export const EditorTextarea = styled(TextareaAutosize)`
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
&:focus {
|
&:focus {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -89,7 +89,7 @@ export const ListCardBadgeText = styled.span<{ color?: 'success' | 'normal' }>`
|
|||||||
padding: 0 4px 0 6px;
|
padding: 0 4px 0 6px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
${props => props.color === 'success' && `color: rgba(${props.theme.colors.success});`}
|
${props => props.color === 'success' && `color: ${props.theme.colors.success};`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ListCardContainer = styled.div<{ isActive: boolean; editable: boolean }>`
|
export const ListCardContainer = styled.div<{ isActive: boolean; editable: boolean }>`
|
||||||
@ -101,7 +101,9 @@ export const ListCardContainer = styled.div<{ isActive: boolean; editable: boole
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
background-color: ${props =>
|
background-color: ${props =>
|
||||||
props.isActive && !props.editable ? mixin.darken('#262c49', 0.1) : `rgba(${props.theme.colors.bg.secondary})`};
|
props.isActive && !props.editable
|
||||||
|
? mixin.darken(props.theme.colors.bg.secondary, 0.1)
|
||||||
|
: `${props.theme.colors.bg.secondary}`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ListCardInnerContainer = styled.div`
|
export const ListCardInnerContainer = styled.div`
|
||||||
@ -221,7 +223,7 @@ export const ListCardOperation = styled.span`
|
|||||||
top: 2px;
|
top: 2px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${props => mixin.darken('#262c49', 0.25)};
|
background-color: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.25)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -233,7 +235,7 @@ export const CardTitle = styled.span`
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -246,7 +248,7 @@ export const CardMembers = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const CompleteIcon = styled(CheckCircle)`
|
export const CompleteIcon = styled(CheckCircle)`
|
||||||
fill: rgba(${props => props.theme.colors.success});
|
fill: ${props => props.theme.colors.success};
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
`;
|
`;
|
||||||
|
@ -22,7 +22,7 @@ export default {
|
|||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
width: 552px;
|
width: 552px;
|
||||||
margin: 25px;
|
margin: 25px;
|
||||||
border: 1px solid rgba(${props => props.theme.colors.bg.primary});
|
border: 1px solid ${props => props.theme.colors.bg.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const defaultItems = [
|
const defaultItems = [
|
||||||
|
@ -12,6 +12,7 @@ import Button from 'shared/components/Button';
|
|||||||
import TextareaAutosize from 'react-autosize-textarea';
|
import TextareaAutosize from 'react-autosize-textarea';
|
||||||
import Control from 'react-select/src/components/Control';
|
import Control from 'react-select/src/components/Control';
|
||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
@ -38,7 +39,7 @@ const WindowChecklistTitle = styled.div`
|
|||||||
|
|
||||||
const WindowTitleText = styled.h3`
|
const WindowTitleText = styled.h3`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -73,7 +74,7 @@ const ChecklistProgressPercent = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistProgressBar = styled.div`
|
const ChecklistProgressBar = styled.div`
|
||||||
background: rgba(${props => props.theme.colors.bg.primary});
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
clear: both;
|
clear: both;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
@ -83,7 +84,7 @@ const ChecklistProgressBar = styled.div`
|
|||||||
`;
|
`;
|
||||||
const ChecklistProgressBarCurrent = styled.div<{ width: number }>`
|
const ChecklistProgressBarCurrent = styled.div<{ width: number }>`
|
||||||
width: ${props => props.width}%;
|
width: ${props => props.width}%;
|
||||||
background: rgba(${props => (props.width === 100 ? props.theme.colors.success : props.theme.colors.primary)});
|
background: ${props => (props.width === 100 ? props.theme.colors.success : props.theme.colors.primary)};
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -111,7 +112,7 @@ const ChecklistIcon = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemCheckedIcon = styled(CheckSquare)`
|
const ChecklistItemCheckedIcon = styled(CheckSquare)`
|
||||||
fill: rgba(${props => props.theme.colors.primary});
|
fill: ${props => props.theme.colors.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemDetails = styled.div`
|
const ChecklistItemDetails = styled.div`
|
||||||
@ -133,7 +134,7 @@ const ChecklistItemTextControls = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemText = styled.span<{ complete: boolean }>`
|
const ChecklistItemText = styled.span<{ complete: boolean }>`
|
||||||
color: ${props => (props.complete ? '#5e6c84' : `rgba(${props.theme.colors.text.primary})`)};
|
color: ${props => (props.complete ? '#5e6c84' : `${props.theme.colors.text.primary}`)};
|
||||||
${props => props.complete && 'text-decoration: line-through;'}
|
${props => props.complete && 'text-decoration: line-through;'}
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -155,14 +156,14 @@ const ControlButton = styled.div`
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.8);
|
background-color: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.8)};
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(${props => props.theme.colors.primary}, 1);
|
background-color: ${props => mixin.rgba(props.theme.colors.primary, 1)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -189,27 +190,27 @@ export const ChecklistNameEditor = styled(TextareaAutosize)`
|
|||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
border: 1px solid rgba(${props => props.theme.colors.primary});
|
border: 1px solid ${props => props.theme.colors.primary};
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
|
|
||||||
border-color: rgba(${props => props.theme.colors.border});
|
border-color: ${props => props.theme.colors.border};
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background-color: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.4)};
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: rgba(${props => props.theme.colors.primary});
|
border-color: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const AssignUserButton = styled(AccountPlus)`
|
const AssignUserButton = styled(AccountPlus)`
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ClockButton = styled(Clock)`
|
const ClockButton = styled(Clock)`
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TrashButton = styled(Trash)`
|
const TrashButton = styled(Trash)`
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemWrapper = styled.div<{ ref: any }>`
|
const ChecklistItemWrapper = styled.div<{ ref: any }>`
|
||||||
@ -224,7 +225,7 @@ const ChecklistItemWrapper = styled.div<{ ref: any }>`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background-color: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.4)};
|
||||||
}
|
}
|
||||||
&:hover ${ControlButton} {
|
&:hover ${ControlButton} {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -246,10 +247,10 @@ const CancelButton = styled.div`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
& svg {
|
& svg {
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(${props => props.theme.colors.text.secondary});
|
fill: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -265,7 +266,7 @@ const EditableDeleteButton = styled.button`
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props => props.theme.colors.primary}, 0.8);
|
background: ${props => mixin.rgba(props.theme.colors.primary, 0.8)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ const LabelText = styled.span`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Container = styled.div<{ color?: string }>`
|
const Container = styled.div<{ color?: string }>`
|
||||||
@ -24,11 +24,11 @@ const Container = styled.div<{ color?: string }>`
|
|||||||
? css`
|
? css`
|
||||||
background: ${props.color};
|
background: ${props.color};
|
||||||
& ${LabelText} {
|
& ${LabelText} {
|
||||||
color: rgba(${props.theme.colors.text.secondary});
|
color: ${props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
background: rgba(${props.theme.colors.bg.primary});
|
background: ${props.theme.colors.bg.primary};
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
103
frontend/src/shared/components/Confirm/Styles.ts
Normal file
103
frontend/src/shared/components/Confirm/Styles.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
|
|
||||||
|
export const Wrapper = styled.div`
|
||||||
|
background: #eff2f7;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Column = styled.div`
|
||||||
|
width: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LoginFormWrapper = styled.div`
|
||||||
|
background: #10163a;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LoginFormContainer = styled.div`
|
||||||
|
min-height: 505px;
|
||||||
|
padding: 2rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Title = styled.h1`
|
||||||
|
color: #ebeefd;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SubTitle = styled.h2`
|
||||||
|
color: #c2c6dc;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
`;
|
||||||
|
export const Form = styled.form`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FormLabel = styled.label`
|
||||||
|
color: #c2c6dc;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FormTextInput = styled.input`
|
||||||
|
width: 100%;
|
||||||
|
background: #262c49;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
margin-top: 4px;
|
||||||
|
padding: 0.7rem 1rem 0.7rem 3rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #c2c6dc;
|
||||||
|
border-radius: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FormIcon = styled.div`
|
||||||
|
top: 30px;
|
||||||
|
left: 16px;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FormError = styled.span`
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: rgb(234, 84, 85);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LoginButton = styled(Button)``;
|
||||||
|
|
||||||
|
export const ActionButtons = styled.div`
|
||||||
|
margin-top: 17.5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RegisterButton = styled(Button)``;
|
||||||
|
|
||||||
|
export const LogoTitle = styled.div`
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 12px;
|
||||||
|
transition: visibility, opacity, transform 0.25s ease;
|
||||||
|
color: #7367f0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LogoWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
color: rgb(222, 235, 255);
|
||||||
|
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
||||||
|
`;
|
62
frontend/src/shared/components/Confirm/index.tsx
Normal file
62
frontend/src/shared/components/Confirm/index.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import AccessAccount from 'shared/undraw/AccessAccount';
|
||||||
|
import { User, Lock, Taskcafe } from 'shared/icons';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import LoadingSpinner from 'shared/components/LoadingSpinner';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
LogoWrapper,
|
||||||
|
LogoTitle,
|
||||||
|
ActionButtons,
|
||||||
|
RegisterButton,
|
||||||
|
FormError,
|
||||||
|
FormIcon,
|
||||||
|
FormLabel,
|
||||||
|
FormTextInput,
|
||||||
|
Wrapper,
|
||||||
|
Column,
|
||||||
|
LoginFormWrapper,
|
||||||
|
LoginFormContainer,
|
||||||
|
Title,
|
||||||
|
SubTitle,
|
||||||
|
} from './Styles';
|
||||||
|
|
||||||
|
const Confirm = ({ onConfirmUser, hasConfirmToken }: ConfirmProps) => {
|
||||||
|
const [hasFailed, setFailed] = useState(false);
|
||||||
|
const setHasFailed = () => {
|
||||||
|
setFailed(true);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
onConfirmUser(setHasFailed);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Column>
|
||||||
|
<AccessAccount width={275} height={250} />
|
||||||
|
</Column>
|
||||||
|
<Column>
|
||||||
|
<LoginFormWrapper>
|
||||||
|
<LoginFormContainer>
|
||||||
|
<LogoWrapper>
|
||||||
|
<Taskcafe width={42} height={42} />
|
||||||
|
<LogoTitle>Taskcafé</LogoTitle>
|
||||||
|
</LogoWrapper>
|
||||||
|
{hasConfirmToken ? (
|
||||||
|
<>
|
||||||
|
<Title>Confirming user...</Title>
|
||||||
|
{hasFailed ? <SubTitle>There was an error while confirming your user</SubTitle> : <LoadingSpinner />}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Title>There is no confirmation token</Title>
|
||||||
|
<SubTitle>There seems to have been an error.</SubTitle>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LoginFormContainer>
|
||||||
|
</LoginFormWrapper>
|
||||||
|
</Column>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Confirm;
|
@ -19,7 +19,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
background: rgba(${props => props.theme.colors.bg.primary});
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
padding: 45px;
|
padding: 45px;
|
||||||
margin: 25px;
|
margin: 25px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import styled, { css } from 'styled-components/macro';
|
import styled, { css } from 'styled-components/macro';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
const InputWrapper = styled.div<{ width: string }>`
|
const InputWrapper = styled.div<{ width: string }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -57,14 +58,14 @@ const InputInput = styled.input<{
|
|||||||
background: ${props => props.focusBg};
|
background: ${props => props.focusBg};
|
||||||
}
|
}
|
||||||
&:focus ~ ${InputLabel} {
|
&:focus ~ ${InputLabel} {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props => props.theme.colors.primary};
|
||||||
transform: translate(-3px, -90%);
|
transform: translate(-3px, -90%);
|
||||||
}
|
}
|
||||||
${props =>
|
${props =>
|
||||||
props.hasValue &&
|
props.hasValue &&
|
||||||
css`
|
css`
|
||||||
& ~ ${InputLabel} {
|
& ~ ${InputLabel} {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props.theme.colors.primary};
|
||||||
transform: translate(-3px, -90%);
|
transform: translate(-3px, -90%);
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
@ -115,8 +116,8 @@ const ControlledInput = ({
|
|||||||
}: ControlledInputProps) => {
|
}: ControlledInputProps) => {
|
||||||
const $input = useRef<HTMLInputElement>(null);
|
const $input = useRef<HTMLInputElement>(null);
|
||||||
const [hasValue, setHasValue] = useState(false);
|
const [hasValue, setHasValue] = useState(false);
|
||||||
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : theme.colors.alternate;
|
||||||
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
const focusBg = variant === 'normal' ? theme.colors.bg.secondary : theme.colors.bg.primary;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoFocus && $input && $input.current) {
|
if (autoFocus && $input && $input.current) {
|
||||||
$input.current.focus();
|
$input.current.focus();
|
||||||
|
@ -2,6 +2,7 @@ import React, { createRef, useState } from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import DropdownMenu from '.';
|
import DropdownMenu from '.';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: DropdownMenu,
|
component: DropdownMenu,
|
||||||
@ -10,7 +11,7 @@ export default {
|
|||||||
backgrounds: [
|
backgrounds: [
|
||||||
{ name: 'white', value: '#ffffff' },
|
{ name: 'white', value: '#ffffff' },
|
||||||
{ name: 'gray', value: '#f8f8f8' },
|
{ name: 'gray', value: '#f8f8f8' },
|
||||||
{ name: 'darkBlue', value: '#262c49', default: true },
|
{ name: 'darkBlue', value: theme.colors.bg.secondary, default: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -59,7 +59,7 @@ export const ActionItem = styled.li`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -19,23 +19,23 @@ display: flex
|
|||||||
}
|
}
|
||||||
|
|
||||||
& .react-datepicker-time__header {
|
& .react-datepicker-time__header {
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
& .react-datepicker__time-list-item {
|
& .react-datepicker__time-list-item {
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
& .react-datepicker__time-container .react-datepicker__time
|
& .react-datepicker__time-container .react-datepicker__time
|
||||||
.react-datepicker__time-box ul.react-datepicker__time-list
|
.react-datepicker__time-box ul.react-datepicker__time-list
|
||||||
li.react-datepicker__time-list-item:hover {
|
li.react-datepicker__time-list-item:hover {
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
background: rgba(${props => props.theme.colors.bg.secondary});
|
background: ${props => props.theme.colors.bg.secondary};
|
||||||
}
|
}
|
||||||
& .react-datepicker__time-container .react-datepicker__time {
|
& .react-datepicker__time-container .react-datepicker__time {
|
||||||
background: rgba(${props => props.theme.colors.bg.primary});
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
}
|
}
|
||||||
& .react-datepicker--time-only {
|
& .react-datepicker--time-only {
|
||||||
background: rgba(${props => props.theme.colors.bg.primary});
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
border: 1px solid rgba(${props => props.theme.colors.border});
|
border: 1px solid ${props => props.theme.colors.border};
|
||||||
}
|
}
|
||||||
|
|
||||||
& .react-datepicker * {
|
& .react-datepicker * {
|
||||||
@ -75,12 +75,12 @@ display: flex
|
|||||||
}
|
}
|
||||||
& .react-datepicker__day--selected {
|
& .react-datepicker__day--selected {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
& .react-datepicker__day--selected:hover {
|
& .react-datepicker__day--selected:hover {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
& .react-datepicker__header {
|
& .react-datepicker__header {
|
||||||
@ -88,7 +88,7 @@ display: flex
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
& .react-datepicker__header--time {
|
& .react-datepicker__header--time {
|
||||||
border-bottom: 1px solid rgba(${props => props.theme.colors.border});
|
border-bottom: 1px solid ${props => props.theme.colors.border};
|
||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
@ -43,7 +43,7 @@ const HeaderSelectLabel = styled.div`
|
|||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -60,8 +60,8 @@ const HeaderSelect = styled.select`
|
|||||||
appearance: none;
|
appearance: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #262c49;
|
background: ${props => props.theme.colors.bg.secondary};
|
||||||
border: 1px solid rgba(115, 103, 240);
|
border: 1px solid ${props => props.theme.colors.primary};
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
@ -93,7 +93,7 @@ const HeaderButton = styled.button`
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled, { keyframes } from 'styled-components/macro';
|
import styled, { keyframes } from 'styled-components/macro';
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
export const BoardContainer = styled.div`
|
export const BoardContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -34,9 +35,9 @@ export const Container = styled.div`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const defaultBaseColor = '#10163a';
|
export const defaultBaseColor = theme.colors.bg.primary;
|
||||||
|
|
||||||
export const defaultHighlightColor = mixin.lighten('#10163a', 0.25);
|
export const defaultHighlightColor = mixin.lighten(theme.colors.bg.primary, 0.25);
|
||||||
|
|
||||||
export const skeletonKeyframes = keyframes`
|
export const skeletonKeyframes = keyframes`
|
||||||
0% {
|
0% {
|
||||||
|
@ -19,7 +19,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
background: rgba(${props => props.theme.colors.bg.primary});
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
padding: 45px;
|
padding: 45px;
|
||||||
margin: 25px;
|
margin: 25px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import styled, { css } from 'styled-components/macro';
|
import styled, { css } from 'styled-components/macro';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
const InputWrapper = styled.div<{ width: string }>`
|
const InputWrapper = styled.div<{ width: string }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -53,18 +54,18 @@ const InputInput = styled.input<{
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.15);
|
||||||
border: 1px solid rgba(115, 103, 240);
|
border: 1px solid ${props => props.theme.colors.primary};
|
||||||
background: ${props => props.focusBg};
|
background: ${props => props.focusBg};
|
||||||
}
|
}
|
||||||
&:focus ~ ${InputLabel} {
|
&:focus ~ ${InputLabel} {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props => props.theme.colors.primary};
|
||||||
transform: translate(-3px, -90%);
|
transform: translate(-3px, -90%);
|
||||||
}
|
}
|
||||||
${props =>
|
${props =>
|
||||||
props.hasValue &&
|
props.hasValue &&
|
||||||
css`
|
css`
|
||||||
& ~ ${InputLabel} {
|
& ~ ${InputLabel} {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props.theme.colors.primary};
|
||||||
transform: translate(-3px, -90%);
|
transform: translate(-3px, -90%);
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
@ -138,8 +139,8 @@ const Input = React.forwardRef(
|
|||||||
$ref: any,
|
$ref: any,
|
||||||
) => {
|
) => {
|
||||||
const [hasValue, setHasValue] = useState(defaultValue !== '');
|
const [hasValue, setHasValue] = useState(defaultValue !== '');
|
||||||
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
const borderColor = variant === 'normal' ? 'rgba(0,0,0,0.2)' : theme.colors.alternate;
|
||||||
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
const focusBg = variant === 'normal' ? theme.colors.bg.secondary : theme.colors.bg.primary;
|
||||||
|
|
||||||
// Merge forwarded ref and internal ref in order to be able to access the ref in the useEffect
|
// Merge forwarded ref and internal ref in order to be able to access the ref in the useEffect
|
||||||
// The forwarded ref is not accessible by itself, which is what the innerRef & combined ref is for
|
// The forwarded ref is not accessible by itself, which is what the innerRef & combined ref is for
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import TextareaAutosize from 'react-autosize-textarea';
|
import TextareaAutosize from 'react-autosize-textarea';
|
||||||
import { mixin } from 'shared/utils/styles';
|
|
||||||
|
|
||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
width: 272px;
|
width: 272px;
|
||||||
@ -34,7 +33,7 @@ export const AddCardButton = styled.a`
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
@ -73,7 +72,6 @@ export const HeaderName = styled(TextareaAutosize)`
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: -4px 0;
|
margin: -4px 0;
|
||||||
padding: 4px 8px;
|
|
||||||
|
|
||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
word-spacing: normal;
|
word-spacing: normal;
|
||||||
@ -97,7 +95,7 @@ export const Header = styled.div<{ isEditing: boolean }>`
|
|||||||
props.isEditing &&
|
props.isEditing &&
|
||||||
css`
|
css`
|
||||||
& ${HeaderName} {
|
& ${HeaderName} {
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
@ -21,7 +21,7 @@ export const ListActionItem = styled.span`
|
|||||||
margin: 0 -12px;
|
margin: 0 -12px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
import Lists from '.';
|
import Lists from '.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -7,7 +8,7 @@ export default {
|
|||||||
title: 'Lists',
|
title: 'Lists',
|
||||||
parameters: {
|
parameters: {
|
||||||
backgrounds: [
|
backgrounds: [
|
||||||
{ name: 'gray', value: '#262c49', default: true },
|
{ name: 'gray', value: theme.colors.bg.secondary, default: true },
|
||||||
{ name: 'white', value: '#ffffff' },
|
{ name: 'white', value: '#ffffff' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -22,10 +22,10 @@ export const LoadingSpinnerWrapper = styled.div<{ color: string; size: string; b
|
|||||||
width: ${props => props.size};
|
width: ${props => props.size};
|
||||||
height: ${props => props.size};
|
height: ${props => props.size};
|
||||||
margin: ${props => props.thickness};
|
margin: ${props => props.thickness};
|
||||||
border: ${props => props.thickness} solid rgba(${props => props.theme.colors[props.color]});
|
border: ${props => props.thickness} solid ${props => props.theme.colors[props.color]};
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: 1.2s ${LoadingSpinnerKeyframes} cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
animation: 1.2s ${LoadingSpinnerKeyframes} cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||||
border-color: rgba(${props => props.theme.colors[props.color]}) transparent transparent transparent;
|
border-color: ${props => props.theme.colors[props.color]} transparent transparent transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div:nth-child(1) {
|
& > div:nth-child(1) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { LoadingSpinnerWrapper} from './Styles';
|
import { LoadingSpinnerWrapper } from './Styles';
|
||||||
|
|
||||||
type LoadingSpinnerProps = {
|
type LoadingSpinnerProps = {
|
||||||
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
||||||
@ -30,11 +30,11 @@ const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
|
|||||||
borderSize = '80px',
|
borderSize = '80px',
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<LoadingSpinnerWrapper color={color} size={size} thickness={thickness} borderSize={borderSize}>
|
<LoadingSpinnerWrapper color={color} size={size} thickness={thickness} borderSize={borderSize}>
|
||||||
<div />
|
<div />
|
||||||
<div />
|
<div />
|
||||||
<div />
|
<div />
|
||||||
</LoadingSpinnerWrapper>
|
</LoadingSpinnerWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
background: #eff2f7;
|
background: #eff2f7;
|
||||||
@ -68,7 +69,7 @@ export const FormIcon = styled.div`
|
|||||||
|
|
||||||
export const FormError = styled.span`
|
export const FormError = styled.span`
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: rgb(234, 84, 85);
|
color: ${props => props.theme.colors.danger};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LoginButton = styled(Button)``;
|
export const LoginButton = styled(Button)``;
|
||||||
@ -99,5 +100,5 @@ export const LogoWrapper = styled.div`
|
|||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
color: rgb(222, 235, 255);
|
color: rgb(222, 235, 255);
|
||||||
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
border-bottom: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0.65)};
|
||||||
`;
|
`;
|
||||||
|
@ -20,14 +20,14 @@ export const MemberManagerSearch = styled(TextareaAutosize)`
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
background: #262c49;
|
background: ${props => props.theme.colors.bgColor.secondary};
|
||||||
outline: none;
|
outline: none;
|
||||||
color: #c2c6dc;
|
color: ${props => props.theme.colors.text.primary};
|
||||||
border-color: #414561;
|
border-color: ${props => props.theme.colors.border};
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props => props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
background: ${props => mixin.darken(props.theme.colors.bgColor.secondary, 0.15)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ export const BoardMemberListItemContent = styled(Member)`
|
|||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(${props => props.theme.colors.primary});
|
background-color: ${props => props.theme.colors.primary};
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ export const ProfileIcon = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
`;
|
`;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
import { Checkmark } from 'shared/icons';
|
import { Checkmark } from 'shared/icons';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const RoleCheckmark = styled(Checkmark)`
|
export const RoleCheckmark = styled(Checkmark)`
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
@ -80,36 +81,36 @@ export const MiniProfileActionItem = styled.span<{ disabled?: boolean }>`
|
|||||||
? css`
|
? css`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: rgba(${props.theme.colors.text.primary}, 0.4);
|
color: ${mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CurrentPermission = styled.span`
|
export const CurrentPermission = styled.span`
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.secondary, 0.4)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Separator = styled.div`
|
export const Separator = styled.div`
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-top: 1px solid #414561;
|
border-top: 1px solid ${props => props.theme.colors.alternate};
|
||||||
margin: 0.25rem !important;
|
margin: 0.25rem !important;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const WarningText = styled.span`
|
export const WarningText = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.4);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.4)};
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DeleteDescription = styled.div`
|
export const DeleteDescription = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RemoveMemberButton = styled(Button)`
|
export const RemoveMemberButton = styled(Button)`
|
||||||
|
@ -47,6 +47,7 @@ const permissions = [
|
|||||||
type MiniProfileProps = {
|
type MiniProfileProps = {
|
||||||
bio: string;
|
bio: string;
|
||||||
user: TaskUser;
|
user: TaskUser;
|
||||||
|
invited?: boolean;
|
||||||
onRemoveFromTask?: () => void;
|
onRemoveFromTask?: () => void;
|
||||||
onChangeRole?: (roleCode: RoleCode) => void;
|
onChangeRole?: (roleCode: RoleCode) => void;
|
||||||
onRemoveFromBoard?: () => void;
|
onRemoveFromBoard?: () => void;
|
||||||
@ -56,6 +57,7 @@ type MiniProfileProps = {
|
|||||||
const MiniProfile: React.FC<MiniProfileProps> = ({
|
const MiniProfile: React.FC<MiniProfileProps> = ({
|
||||||
user,
|
user,
|
||||||
bio,
|
bio,
|
||||||
|
invited,
|
||||||
canChangeRole,
|
canChangeRole,
|
||||||
onRemoveFromTask,
|
onRemoveFromTask,
|
||||||
onChangeRole,
|
onChangeRole,
|
||||||
@ -74,7 +76,7 @@ const MiniProfile: React.FC<MiniProfileProps> = ({
|
|||||||
)}
|
)}
|
||||||
<ProfileInfo>
|
<ProfileInfo>
|
||||||
<InfoTitle>{user.fullName}</InfoTitle>
|
<InfoTitle>{user.fullName}</InfoTitle>
|
||||||
<InfoUsername>{`@${user.username}`}</InfoUsername>
|
{invited ? <InfoUsername>Invited</InfoUsername> : <InfoUsername>{`@${user.username}`}</InfoUsername>}
|
||||||
<InfoBio>{bio}</InfoBio>
|
<InfoBio>{bio}</InfoBio>
|
||||||
</ProfileInfo>
|
</ProfileInfo>
|
||||||
</Profile>
|
</Profile>
|
||||||
|
@ -30,9 +30,9 @@ const CloseIcon = styled(Cross)`
|
|||||||
top: 16px;
|
top: 16px;
|
||||||
right: -32px;
|
right: -32px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
&:hover {
|
&:hover {
|
||||||
fill: rgba(${props => props.theme.colors.text.secondary});
|
fill: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const Logo = styled.div``;
|
export const Logo = styled.div``;
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ export const LogoTitle = styled.div`
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: visibility, opacity, transform 0.25s ease;
|
transition: visibility, opacity, transform 0.25s ease;
|
||||||
color: #7367f0;
|
color: #22ff00;
|
||||||
`;
|
`;
|
||||||
export const ActionContainer = styled.div`
|
export const ActionContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -46,8 +47,8 @@ export const ActionButtonWrapper = styled.div<{ active?: boolean }>`
|
|||||||
${props =>
|
${props =>
|
||||||
props.active &&
|
props.active &&
|
||||||
css`
|
css`
|
||||||
background: rgb(115, 103, 240);
|
background: ${props.theme.colors.primary};
|
||||||
box-shadow: 0 0 10px 1px rgba(115, 103, 240, 0.7);
|
box-shadow: 0 0 10px 1px ${mixin.rgba(props.theme.colors.primary, 0.7)};
|
||||||
`}
|
`}
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -73,7 +74,7 @@ export const LogoWrapper = styled.div`
|
|||||||
color: rgb(222, 235, 255);
|
color: rgb(222, 235, 255);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.1s ease 0s, border 0.1s ease 0s;
|
transition: color 0.1s ease 0s, border 0.1s ease 0s;
|
||||||
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
border-bottom: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0.65)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Container = styled.aside`
|
export const Container = styled.aside`
|
||||||
@ -87,12 +88,12 @@ export const Container = styled.aside`
|
|||||||
transform: translateZ(0px);
|
transform: translateZ(0px);
|
||||||
background: #10163a;
|
background: #10163a;
|
||||||
transition: all 0.1s ease 0s;
|
transition: all 0.1s ease 0s;
|
||||||
border-right: 1px solid rgba(65, 69, 97, 0.65);
|
border-right: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0.65)};
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 50px 0px;
|
box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 50px 0px;
|
||||||
border-right: 1px solid rgba(65, 69, 97, 0);
|
border-right: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0)};
|
||||||
}
|
}
|
||||||
&:hover ${LogoTitle} {
|
&:hover ${LogoTitle} {
|
||||||
bottom: -12px;
|
bottom: -12px;
|
||||||
@ -106,6 +107,6 @@ export const Container = styled.aside`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover ${LogoWrapper} {
|
&:hover ${LogoWrapper} {
|
||||||
border-bottom: 1px solid rgba(65, 69, 97, 0);
|
border-bottom: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -3,16 +3,17 @@ import styled from 'styled-components';
|
|||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { ArrowLeft, Cross } from 'shared/icons';
|
import { ArrowLeft, Cross } from 'shared/icons';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
function getBackgroundColor(isDisabled: boolean, isSelected: boolean, isFocused: boolean) {
|
function getBackgroundColor(isDisabled: boolean, isSelected: boolean, isFocused: boolean) {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return mixin.darken('#262c49', 0.25);
|
return mixin.darken(theme.colors.bg.secondary, 0.25);
|
||||||
}
|
}
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
return mixin.darken('#262c49', 0.15);
|
return mixin.darken(theme.colors.bg.secondary, 0.15);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -97,8 +98,8 @@ const ProjectName = styled.input`
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
background: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props => props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const ProjectNameLabel = styled.label`
|
const ProjectNameLabel = styled.label`
|
||||||
@ -126,35 +127,35 @@ const colourStyles = {
|
|||||||
control: (styles: any, data: any) => {
|
control: (styles: any, data: any) => {
|
||||||
return {
|
return {
|
||||||
...styles,
|
...styles,
|
||||||
backgroundColor: data.isMenuOpen ? mixin.darken('#262c49', 0.15) : '#262c49',
|
backgroundColor: data.isMenuOpen ? mixin.darken(theme.colors.bg.secondary, 0.15) : theme.colors.bg.secondary,
|
||||||
boxShadow: data.menuIsOpen ? 'rgb(115, 103, 240) 0px 0px 0px 1px' : 'none',
|
boxShadow: data.menuIsOpen ? `${theme.colors.primary} 0px 0px 0px 1px` : 'none',
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: '#414561',
|
borderColor: theme.colors.alternate,
|
||||||
':hover': {
|
':hover': {
|
||||||
boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
|
boxShadow: `${theme.colors.primary} 0px 0px 0px 1px`,
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: '#414561',
|
borderColor: theme.colors.alternate,
|
||||||
},
|
},
|
||||||
':active': {
|
':active': {
|
||||||
boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
|
boxShadow: `${theme.colors.primary} 0px 0px 0px 1px`,
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: 'rgb(115, 103, 240)',
|
borderColor: `${theme.colors.primary}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
menu: (styles: any) => {
|
menu: (styles: any) => {
|
||||||
return {
|
return {
|
||||||
...styles,
|
...styles,
|
||||||
backgroundColor: mixin.darken('#262c49', 0.15),
|
backgroundColor: mixin.darken(theme.colors.bg.secondary, 0.15),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
dropdownIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
|
dropdownIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
|
||||||
@ -167,11 +168,11 @@ const colourStyles = {
|
|||||||
cursor: isDisabled ? 'not-allowed' : 'default',
|
cursor: isDisabled ? 'not-allowed' : 'default',
|
||||||
':active': {
|
':active': {
|
||||||
...styles[':active'],
|
...styles[':active'],
|
||||||
backgroundColor: !isDisabled && (isSelected ? mixin.darken('#262c49', 0.25) : '#fff'),
|
backgroundColor: !isDisabled && (isSelected ? mixin.darken(theme.colors.bg.secondary, 0.25) : '#fff'),
|
||||||
},
|
},
|
||||||
':hover': {
|
':hover': {
|
||||||
...styles[':hover'],
|
...styles[':hover'],
|
||||||
backgroundColor: !isDisabled && (isSelected ? 'rgb(115, 103, 240)' : 'rgb(115, 103, 240)'),
|
backgroundColor: !isDisabled && (isSelected ? theme.colors.primary : theme.colors.primary),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -209,8 +210,8 @@ const CreateButton = styled.button`
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
border-color: rgb(115, 103, 240);
|
border-color: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
type NewProjectProps = {
|
type NewProjectProps = {
|
||||||
|
@ -37,7 +37,7 @@ const ItemTextContainer = styled.div`
|
|||||||
const ItemTextTitle = styled.span`
|
const ItemTextTitle = styled.span`
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
display: block;
|
display: block;
|
||||||
color: rgba(${props => props.theme.colors.primary});
|
color: ${props => props.theme.colors.primary};
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
`;
|
`;
|
||||||
const ItemTextDesc = styled.span`
|
const ItemTextDesc = styled.span`
|
||||||
@ -76,21 +76,21 @@ const NotificationHeader = styled.div`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
border-top-left-radius: 6px;
|
border-top-left-radius: 6px;
|
||||||
border-top-right-radius: 6px;
|
border-top-right-radius: 6px;
|
||||||
background: rgba(${props => props.theme.colors.primary});
|
background: ${props => props.theme.colors.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const NotificationHeaderTitle = styled.span`
|
const NotificationHeaderTitle = styled.span`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const NotificationFooter = styled.div`
|
const NotificationFooter = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: rgba(${props => props.theme.colors.primary});
|
color: ${props => props.theme.colors.primary};
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #10163a;
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
}
|
}
|
||||||
border-bottom-left-radius: 6px;
|
border-bottom-left-radius: 6px;
|
||||||
border-bottom-right-radius: 6px;
|
border-bottom-right-radius: 6px;
|
||||||
|
@ -4,7 +4,7 @@ import styled from 'styled-components';
|
|||||||
import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldName } from './Styles';
|
import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldName } from './Styles';
|
||||||
|
|
||||||
const WhiteCheckmark = styled(Checkmark)`
|
const WhiteCheckmark = styled(Checkmark)`
|
||||||
fill: rgba(${props => props.theme.colors.text.secondary});
|
fill: ${props => props.theme.colors.text.secondary};
|
||||||
`;
|
`;
|
||||||
type Props = {
|
type Props = {
|
||||||
labelColors: Array<LabelColor>;
|
labelColors: Array<LabelColor>;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
import ControlledInput from 'shared/components/ControlledInput';
|
import ControlledInput from 'shared/components/ControlledInput';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
|
|
||||||
export const Container = styled.div<{
|
export const Container = styled.div<{
|
||||||
invertY: boolean;
|
invertY: boolean;
|
||||||
@ -176,7 +177,7 @@ export const LabelIcon = styled.div`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -233,8 +234,8 @@ export const FieldName = styled.input`
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props => props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
background: ${mixin.darken(theme.colors.bg.secondary, 0.15)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -258,7 +259,7 @@ export const LabelBox = styled.span<{ color: string }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SaveButton = styled.input`
|
export const SaveButton = styled.input`
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@ -296,7 +297,7 @@ export const DeleteButton = styled.input`
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -317,7 +318,7 @@ export const CreateLabelButton = styled.button`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
ContainerDiamond,
|
ContainerDiamond,
|
||||||
@ -18,7 +19,7 @@ import {
|
|||||||
function getPopupOptions(options?: PopupOptions) {
|
function getPopupOptions(options?: PopupOptions) {
|
||||||
const popupOptions = {
|
const popupOptions = {
|
||||||
borders: true,
|
borders: true,
|
||||||
diamondColor: '#262c49',
|
diamondColor: theme.colors.bg.secondary,
|
||||||
targetPadding: '10px',
|
targetPadding: '10px',
|
||||||
showDiamond: true,
|
showDiamond: true,
|
||||||
width: 316,
|
width: 316,
|
||||||
|
@ -24,7 +24,7 @@ export const ListActionItem = styled.span`
|
|||||||
margin: 0 -12px;
|
margin: 0 -12px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import styled, { keyframes, css } from 'styled-components';
|
import styled, { keyframes, css } from 'styled-components';
|
||||||
import TextareaAutosize from 'react-autosize-textarea';
|
|
||||||
import { mixin } from 'shared/utils/styles';
|
|
||||||
|
|
||||||
export const Wrapper = styled.div<{ open: boolean }>`
|
export const Wrapper = styled.div<{ open: boolean }>`
|
||||||
background: rgba(0, 0, 0, 0.55);
|
background: rgba(0, 0, 0, 0.55);
|
||||||
@ -30,7 +28,7 @@ export const Container = styled.div<{ fixed: boolean; width: number; top: number
|
|||||||
|
|
||||||
export const SaveButton = styled.button`
|
export const SaveButton = styled.button`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
background: #eff2f7;
|
background: #eff2f7;
|
||||||
@ -68,7 +69,7 @@ export const FormIcon = styled.div`
|
|||||||
|
|
||||||
export const FormError = styled.span`
|
export const FormError = styled.span`
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: rgb(234, 84, 85);
|
color: ${props => props.theme.colors.danger};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LoginButton = styled(Button)``;
|
export const LoginButton = styled(Button)``;
|
||||||
@ -99,5 +100,5 @@ export const LogoWrapper = styled.div`
|
|||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
color: rgb(222, 235, 255);
|
color: rgb(222, 235, 255);
|
||||||
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
border-bottom: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0.65)};
|
||||||
`;
|
`;
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
const EMAIL_PATTERN = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i;
|
const EMAIL_PATTERN = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i;
|
||||||
const INITIALS_PATTERN = /[a-zA-Z]{2,3}/i;
|
const INITIALS_PATTERN = /[a-zA-Z]{2,3}/i;
|
||||||
|
|
||||||
const Register = ({ onSubmit }: RegisterProps) => {
|
const Register = ({ onSubmit, registered = false }: RegisterProps) => {
|
||||||
const [isComplete, setComplete] = useState(true);
|
const [isComplete, setComplete] = useState(true);
|
||||||
const { register, handleSubmit, errors, setError } = useForm<RegisterFormData>();
|
const { register, handleSubmit, errors, setError } = useForm<RegisterFormData>();
|
||||||
const loginSubmit = (data: RegisterFormData) => {
|
const loginSubmit = (data: RegisterFormData) => {
|
||||||
@ -43,103 +43,112 @@ const Register = ({ onSubmit }: RegisterProps) => {
|
|||||||
<Taskcafe width={42} height={42} />
|
<Taskcafe width={42} height={42} />
|
||||||
<LogoTitle>Taskcafé</LogoTitle>
|
<LogoTitle>Taskcafé</LogoTitle>
|
||||||
</LogoWrapper>
|
</LogoWrapper>
|
||||||
<Title>Register</Title>
|
{registered ? (
|
||||||
<SubTitle>Please create the system admin user</SubTitle>
|
<>
|
||||||
<Form onSubmit={handleSubmit(loginSubmit)}>
|
<Title>Thanks for registering</Title>
|
||||||
<FormLabel htmlFor="fullname">
|
<SubTitle>Please check your inbox for a confirmation email.</SubTitle>
|
||||||
Full name
|
</>
|
||||||
<FormTextInput
|
) : (
|
||||||
type="text"
|
<>
|
||||||
id="fullname"
|
<Title>Register</Title>
|
||||||
name="fullname"
|
<SubTitle>Please create your user</SubTitle>
|
||||||
ref={register({ required: 'Full name is required' })}
|
<Form onSubmit={handleSubmit(loginSubmit)}>
|
||||||
/>
|
<FormLabel htmlFor="fullname">
|
||||||
<FormIcon>
|
Full name
|
||||||
<User width={20} height={20} />
|
<FormTextInput
|
||||||
</FormIcon>
|
type="text"
|
||||||
</FormLabel>
|
id="fullname"
|
||||||
{errors.username && <FormError>{errors.username.message}</FormError>}
|
name="fullname"
|
||||||
<FormLabel htmlFor="username">
|
ref={register({ required: 'Full name is required' })}
|
||||||
Username
|
/>
|
||||||
<FormTextInput
|
<FormIcon>
|
||||||
type="text"
|
<User width={20} height={20} />
|
||||||
id="username"
|
</FormIcon>
|
||||||
name="username"
|
</FormLabel>
|
||||||
ref={register({ required: 'Username is required' })}
|
{errors.username && <FormError>{errors.username.message}</FormError>}
|
||||||
/>
|
<FormLabel htmlFor="username">
|
||||||
<FormIcon>
|
Username
|
||||||
<User width={20} height={20} />
|
<FormTextInput
|
||||||
</FormIcon>
|
type="text"
|
||||||
</FormLabel>
|
id="username"
|
||||||
{errors.username && <FormError>{errors.username.message}</FormError>}
|
name="username"
|
||||||
<FormLabel htmlFor="email">
|
ref={register({ required: 'Username is required' })}
|
||||||
Email
|
/>
|
||||||
<FormTextInput
|
<FormIcon>
|
||||||
type="text"
|
<User width={20} height={20} />
|
||||||
id="email"
|
</FormIcon>
|
||||||
name="email"
|
</FormLabel>
|
||||||
ref={register({
|
{errors.username && <FormError>{errors.username.message}</FormError>}
|
||||||
required: 'Email is required',
|
<FormLabel htmlFor="email">
|
||||||
pattern: { value: EMAIL_PATTERN, message: 'Must be a valid email' },
|
Email
|
||||||
})}
|
<FormTextInput
|
||||||
/>
|
type="text"
|
||||||
<FormIcon>
|
id="email"
|
||||||
<User width={20} height={20} />
|
name="email"
|
||||||
</FormIcon>
|
ref={register({
|
||||||
</FormLabel>
|
required: 'Email is required',
|
||||||
{errors.email && <FormError>{errors.email.message}</FormError>}
|
pattern: { value: EMAIL_PATTERN, message: 'Must be a valid email' },
|
||||||
<FormLabel htmlFor="initials">
|
})}
|
||||||
Initials
|
/>
|
||||||
<FormTextInput
|
<FormIcon>
|
||||||
type="text"
|
<User width={20} height={20} />
|
||||||
id="initials"
|
</FormIcon>
|
||||||
name="initials"
|
</FormLabel>
|
||||||
ref={register({
|
{errors.email && <FormError>{errors.email.message}</FormError>}
|
||||||
required: 'Initials is required',
|
<FormLabel htmlFor="initials">
|
||||||
pattern: {
|
Initials
|
||||||
value: INITIALS_PATTERN,
|
<FormTextInput
|
||||||
message: 'Initials must be between 2 to 3 characters.',
|
type="text"
|
||||||
},
|
id="initials"
|
||||||
})}
|
name="initials"
|
||||||
/>
|
ref={register({
|
||||||
<FormIcon>
|
required: 'Initials is required',
|
||||||
<User width={20} height={20} />
|
pattern: {
|
||||||
</FormIcon>
|
value: INITIALS_PATTERN,
|
||||||
</FormLabel>
|
message: 'Initials must be between 2 to 3 characters.',
|
||||||
{errors.initials && <FormError>{errors.initials.message}</FormError>}
|
},
|
||||||
<FormLabel htmlFor="password">
|
})}
|
||||||
Password
|
/>
|
||||||
<FormTextInput
|
<FormIcon>
|
||||||
type="password"
|
<User width={20} height={20} />
|
||||||
id="password"
|
</FormIcon>
|
||||||
name="password"
|
</FormLabel>
|
||||||
ref={register({ required: 'Password is required' })}
|
{errors.initials && <FormError>{errors.initials.message}</FormError>}
|
||||||
/>
|
<FormLabel htmlFor="password">
|
||||||
<FormIcon>
|
Password
|
||||||
<Lock width={20} height={20} />
|
<FormTextInput
|
||||||
</FormIcon>
|
type="password"
|
||||||
</FormLabel>
|
id="password"
|
||||||
{errors.password && <FormError>{errors.password.message}</FormError>}
|
name="password"
|
||||||
<FormLabel htmlFor="password_confirm">
|
ref={register({ required: 'Password is required' })}
|
||||||
Password (Confirm)
|
/>
|
||||||
<FormTextInput
|
<FormIcon>
|
||||||
type="password"
|
<Lock width={20} height={20} />
|
||||||
id="password_confirm"
|
</FormIcon>
|
||||||
name="password_confirm"
|
</FormLabel>
|
||||||
ref={register({ required: 'Password (confirm) is required' })}
|
{errors.password && <FormError>{errors.password.message}</FormError>}
|
||||||
/>
|
<FormLabel htmlFor="password_confirm">
|
||||||
<FormIcon>
|
Password (Confirm)
|
||||||
<Lock width={20} height={20} />
|
<FormTextInput
|
||||||
</FormIcon>
|
type="password"
|
||||||
</FormLabel>
|
id="password_confirm"
|
||||||
{errors.password_confirm && <FormError>{errors.password_confirm.message}</FormError>}
|
name="password_confirm"
|
||||||
|
ref={register({ required: 'Password (confirm) is required' })}
|
||||||
|
/>
|
||||||
|
<FormIcon>
|
||||||
|
<Lock width={20} height={20} />
|
||||||
|
</FormIcon>
|
||||||
|
</FormLabel>
|
||||||
|
{errors.password_confirm && <FormError>{errors.password_confirm.message}</FormError>}
|
||||||
|
|
||||||
<ActionButtons>
|
<ActionButtons>
|
||||||
<RegisterButton type="submit" disabled={!isComplete}>
|
<RegisterButton type="submit" disabled={!isComplete}>
|
||||||
Register
|
Register
|
||||||
</RegisterButton>
|
</RegisterButton>
|
||||||
</ActionButtons>
|
</ActionButtons>
|
||||||
</Form>
|
</Form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</LoginFormContainer>
|
</LoginFormContainer>
|
||||||
</LoginFormWrapper>
|
</LoginFormWrapper>
|
||||||
</Column>
|
</Column>
|
||||||
|
@ -2,53 +2,54 @@ import React from 'react';
|
|||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
import theme from 'App/ThemeStyles';
|
||||||
|
|
||||||
function getBackgroundColor(isDisabled: boolean, isSelected: boolean, isFocused: boolean) {
|
function getBackgroundColor(isDisabled: boolean, isSelected: boolean, isFocused: boolean) {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return mixin.darken('#262c49', 0.25);
|
return mixin.darken(theme.colors.bg.secondary, 0.25);
|
||||||
}
|
}
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
return mixin.darken('#262c49', 0.15);
|
return mixin.darken(theme.colors.bg.secondary, 0.15);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colourStyles = {
|
export const colourStyles = {
|
||||||
control: (styles: any, data: any) => {
|
control: (styles: any, data: any) => {
|
||||||
return {
|
return {
|
||||||
...styles,
|
...styles,
|
||||||
backgroundColor: data.isMenuOpen ? mixin.darken('#262c49', 0.15) : '#262c49',
|
backgroundColor: data.isMenuOpen ? mixin.darken(theme.colors.bg.secondary, 0.15) : theme.colors.bg.secondary,
|
||||||
boxShadow: data.menuIsOpen ? 'rgb(115, 103, 240) 0px 0px 0px 1px' : 'none',
|
boxShadow: data.menuIsOpen ? `${theme.colors.primary} 0px 0px 0px 1px` : 'none',
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: '#414561',
|
borderColor: theme.colors.alternate,
|
||||||
':hover': {
|
':hover': {
|
||||||
boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
|
boxShadow: `${theme.colors.primary} 0px 0px 0px 1px`,
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: '#414561',
|
borderColor: theme.colors.alternate,
|
||||||
},
|
},
|
||||||
':active': {
|
':active': {
|
||||||
boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
|
boxShadow: `${theme.colors.primary} 0px 0px 0px 1px`,
|
||||||
borderRadius: '3px',
|
borderRadius: '3px',
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderImage: 'initial',
|
borderImage: 'initial',
|
||||||
borderColor: 'rgb(115, 103, 240)',
|
borderColor: `${theme.colors.primary}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
menu: (styles: any) => {
|
menu: (styles: any) => {
|
||||||
return {
|
return {
|
||||||
...styles,
|
...styles,
|
||||||
backgroundColor: mixin.darken('#262c49', 0.15),
|
backgroundColor: mixin.darken(theme.colors.bg.secondary, 0.15),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
dropdownIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
|
dropdownIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
|
||||||
@ -61,11 +62,11 @@ const colourStyles = {
|
|||||||
cursor: isDisabled ? 'not-allowed' : 'default',
|
cursor: isDisabled ? 'not-allowed' : 'default',
|
||||||
':active': {
|
':active': {
|
||||||
...styles[':active'],
|
...styles[':active'],
|
||||||
backgroundColor: !isDisabled && (isSelected ? mixin.darken('#262c49', 0.25) : '#fff'),
|
backgroundColor: !isDisabled && (isSelected ? mixin.darken(theme.colors.bg.secondary, 0.25) : '#fff'),
|
||||||
},
|
},
|
||||||
':hover': {
|
':hover': {
|
||||||
...styles[':hover'],
|
...styles[':hover'],
|
||||||
backgroundColor: !isDisabled && (isSelected ? 'rgb(115, 103, 240)' : 'rgb(115, 103, 240)'),
|
backgroundColor: !isDisabled && (isSelected ? theme.colors.primary : theme.colors.primary),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -86,7 +87,7 @@ const colourStyles = {
|
|||||||
const InputLabel = styled.span<{ width: string }>`
|
const InputLabel = styled.span<{ width: string }>`
|
||||||
width: ${props => props.width};
|
width: ${props => props.width};
|
||||||
padding-left: 0.7rem;
|
padding-left: 0.7rem;
|
||||||
color: rgba(115, 103, 240);
|
color: ${props => props.theme.colors.primary};
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
@ -17,7 +17,7 @@ const UserInfoInput = styled(Input)`
|
|||||||
|
|
||||||
const FormError = styled.span`
|
const FormError = styled.span`
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: rgba(${props => props.theme.colors.warning});
|
color: ${props => props.theme.colors.warning};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ProfileContainer = styled.div`
|
const ProfileContainer = styled.div`
|
||||||
@ -152,12 +152,12 @@ const TabNavItemButton = styled.button<{ active: boolean }>`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
color: ${props => (props.active ? 'rgba(115, 103, 240)' : '#c2c6dc')};
|
color: ${props => (props.active ? `${props.theme.colors.primary}` : '#c2c6dc')};
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(115, 103, 240);
|
color: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(115, 103, 240);
|
fill: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -175,8 +175,8 @@ const TabNavLine = styled.span<{ top: number }>`
|
|||||||
transform: scaleX(1);
|
transform: scaleX(1);
|
||||||
top: ${props => props.top}px;
|
top: ${props => props.top}px;
|
||||||
|
|
||||||
background: linear-gradient(30deg, rgba(115, 103, 240), rgba(115, 103, 240));
|
background: linear-gradient(30deg, ${props => props.theme.colors.primary}, ${props => props.theme.colors.primary});
|
||||||
box-shadow: 0 0 8px 0 rgba(115, 103, 240);
|
box-shadow: 0 0 8px 0 ${props => props.theme.colors.primary};
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import { DoubleChevronUp, Crown } from 'shared/icons';
|
import { DoubleChevronUp, Crown } from 'shared/icons';
|
||||||
|
|
||||||
export const AdminIcon = styled(DoubleChevronUp)`
|
export const AdminIcon = styled(DoubleChevronUp)`
|
||||||
@ -24,46 +24,78 @@ const TaskDetailAssignee = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Wrapper = styled.div<{ size: number | string; bgColor: string | null; backgroundURL: string | null }>`
|
export const Wrapper = styled.div<{
|
||||||
|
size: number | string;
|
||||||
|
bgColor: string | null;
|
||||||
|
backgroundURL: string | null;
|
||||||
|
hasClick: boolean;
|
||||||
|
}>`
|
||||||
width: ${props => props.size}px;
|
width: ${props => props.size}px;
|
||||||
height: ${props => props.size}px;
|
height: ${props => props.size}px;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: rgba(${props => (props.backgroundURL ? props.theme.colors.text.primary : '0,0,0')});
|
color: ${props => (props.backgroundURL ? props.theme.colors.text.primary : 'rgb(0,0,0)')};
|
||||||
background: ${props => (props.backgroundURL ? `url(${props.backgroundURL})` : props.bgColor)};
|
background: ${props => (props.backgroundURL ? `url(${props.backgroundURL})` : props.bgColor)};
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
&:hover {
|
${props =>
|
||||||
opacity: 0.8;
|
props.hasClick &&
|
||||||
}
|
css`
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TaskAssigneeProps = {
|
type TaskAssigneeProps = {
|
||||||
size: number | string;
|
size: number | string;
|
||||||
showRoleIcons?: boolean;
|
showRoleIcons?: boolean;
|
||||||
member: TaskUser;
|
member: TaskUser;
|
||||||
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
invited?: boolean;
|
||||||
|
onMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TaskAssignee: React.FC<TaskAssigneeProps> = ({ showRoleIcons, member, onMemberProfile, size, className }) => {
|
const TaskAssignee: React.FC<TaskAssigneeProps> = ({
|
||||||
|
showRoleIcons,
|
||||||
|
member,
|
||||||
|
invited,
|
||||||
|
onMemberProfile,
|
||||||
|
size,
|
||||||
|
className,
|
||||||
|
}) => {
|
||||||
const $memberRef = useRef<HTMLDivElement>(null);
|
const $memberRef = useRef<HTMLDivElement>(null);
|
||||||
|
let profileIcon: ProfileIcon = {
|
||||||
|
url: null,
|
||||||
|
bgColor: null,
|
||||||
|
initials: null,
|
||||||
|
};
|
||||||
|
if (member.profileIcon) {
|
||||||
|
profileIcon = member.profileIcon;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<TaskDetailAssignee
|
<TaskDetailAssignee
|
||||||
className={className}
|
className={className}
|
||||||
ref={$memberRef}
|
ref={$memberRef}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onMemberProfile($memberRef, member.id);
|
if (onMemberProfile) {
|
||||||
|
onMemberProfile($memberRef, member.id);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
key={member.id}
|
key={member.id}
|
||||||
>
|
>
|
||||||
<Wrapper backgroundURL={member.profileIcon.url ?? null} bgColor={member.profileIcon.bgColor ?? null} size={size}>
|
<Wrapper
|
||||||
{(!member.profileIcon.url && member.profileIcon.initials) ?? ''}
|
hasClick={typeof onMemberProfile !== undefined}
|
||||||
|
backgroundURL={profileIcon.url ?? null}
|
||||||
|
bgColor={profileIcon.bgColor ?? null}
|
||||||
|
size={size}
|
||||||
|
>
|
||||||
|
{(!profileIcon.url && profileIcon.initials) ?? ''}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
{showRoleIcons && member.role && member.role.code === 'admin' && <AdminIcon width={10} height={10} />}
|
{showRoleIcons && member.role && member.role.code === 'admin' && <AdminIcon width={10} height={10} />}
|
||||||
{showRoleIcons && member.role && member.role.code === 'owner' && <OwnerIcon width={10} height={10} />}
|
{showRoleIcons && member.role && member.role.code === 'owner' && <OwnerIcon width={10} height={10} />}
|
||||||
|
@ -3,8 +3,6 @@ import TextareaAutosize from 'react-autosize-textarea';
|
|||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||||
import { User, Trash, Paperclip } from 'shared/icons';
|
|
||||||
import Member from 'shared/components/Member';
|
|
||||||
|
|
||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -33,35 +31,35 @@ export const MarkCompleteButton = styled.button<{ invert: boolean }>`
|
|||||||
${props =>
|
${props =>
|
||||||
props.invert
|
props.invert
|
||||||
? css`
|
? css`
|
||||||
background: rgba(${props.theme.colors.success});
|
background: ${props.theme.colors.success};
|
||||||
& svg {
|
& svg {
|
||||||
fill: rgba(${props.theme.colors.text.secondary});
|
fill: ${props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
& span {
|
& span {
|
||||||
color: rgba(${props.theme.colors.text.secondary});
|
color: ${props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props.theme.colors.success}, 0.8);
|
background: ${mixin.rgba(props.theme.colors.success, 0.8)};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
background: none;
|
background: none;
|
||||||
border: 1px solid rgba(${props.theme.colors.text.secondary});
|
border: 1px solid ${props.theme.colors.text.secondary};
|
||||||
& svg {
|
& svg {
|
||||||
fill: rgba(${props.theme.colors.text.secondary});
|
fill: ${props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
& span {
|
& span {
|
||||||
color: rgba(${props.theme.colors.text.secondary});
|
color: ${props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(${props.theme.colors.success}, 0.08);
|
background: ${mixin.rgba(props.theme.colors.success, 0.08)};
|
||||||
border: 1px solid rgba(${props.theme.colors.success});
|
border: 1px solid ${props.theme.colors.success};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(${props.theme.colors.success});
|
fill: ${props.theme.colors.success};
|
||||||
}
|
}
|
||||||
&:hover span {
|
&:hover span {
|
||||||
color: rgba(${props.theme.colors.success});
|
color: ${props.theme.colors.success};
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -85,7 +83,7 @@ export const SidebarTitle = styled.div`
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -93,7 +91,7 @@ export const SidebarTitle = styled.div`
|
|||||||
|
|
||||||
export const SidebarButton = styled.div`
|
export const SidebarButton = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@ -168,7 +166,7 @@ export const TaskDetailsTitle = styled(TextareaAutosize)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: rgba(${props => props.theme.colors.primary});
|
border-color: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -176,7 +174,7 @@ export const DueDateTitle = styled.div`
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -187,7 +185,7 @@ export const AssignedUsersSection = styled.div`
|
|||||||
padding-right: 32px;
|
padding-right: 32px;
|
||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
border-bottom: 1px solid #414561;
|
border-bottom: 1px solid ${props => props.theme.colors.alternate};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
@ -205,10 +203,10 @@ export const AssignUserIcon = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 1px solid rgba(${props => props.theme.colors.text.secondary}, 0.75);
|
border: 1px solid ${props => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(${props => props.theme.colors.text.secondary}, 0.75);
|
fill: ${props => mixin.rgba(props.theme.colors.text.secondary, 0.75)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -223,17 +221,17 @@ export const AssignUsersButton = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 1px solid ${mixin.darken('#414561', 0.15)};
|
border: 1px solid ${props => mixin.darken(props.theme.colors.alternate, 0.15)};
|
||||||
}
|
}
|
||||||
&:hover ${AssignUserIcon} {
|
&:hover ${AssignUserIcon} {
|
||||||
border: 1px solid #414561;
|
border: 1px solid ${props => props.theme.colors.alternate};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const AssignUserLabel = styled.span`
|
export const AssignUserLabel = styled.span`
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
line-height: 15px;
|
line-height: 15px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.75);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ExtraActionsSection = styled.div`
|
export const ExtraActionsSection = styled.div`
|
||||||
@ -245,7 +243,7 @@ export const ExtraActionsSection = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActionButtonsTitle = styled.h3`
|
export const ActionButtonsTitle = styled.h3`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
@ -255,7 +253,7 @@ export const ActionButton = styled(Button)`
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: rgba(${props => props.theme.colors.bg.primary}, 0.5);
|
background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.5)};
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
& span {
|
& span {
|
||||||
@ -264,7 +262,7 @@ export const ActionButton = styled(Button)`
|
|||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
transform: translateX(4px);
|
transform: translateX(4px);
|
||||||
background: rgba(${props => props.theme.colors.bg.primary}, 0.75);
|
background: ${props => mixin.rgba(props.theme.colors.bg.primary, 0.75)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -283,10 +281,10 @@ export const HeaderActionIcon = styled.div`
|
|||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
svg {
|
svg {
|
||||||
fill: rgba(${props => props.theme.colors.text.primary}, 0.75);
|
fill: ${props => mixin.rgba(props.theme.colors.text.primary, 0.75)};
|
||||||
}
|
}
|
||||||
&:hover svg {
|
&:hover svg {
|
||||||
fill: rgba(${props => props.theme.colors.primary});
|
fill: ${props => mixin.rgba(props.theme.colors.primary, 0.75)});
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -343,7 +341,7 @@ export const MetaDetail = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const MetaDetailTitle = styled.h3`
|
export const MetaDetailTitle = styled.h3`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
@ -362,7 +360,7 @@ export const MetaDetailContent = styled.div`
|
|||||||
`;
|
`;
|
||||||
export const TaskDetailsAddLabel = styled.div`
|
export const TaskDetailsAddLabel = styled.div`
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
background: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@ -377,7 +375,7 @@ export const TaskDetailsAddLabelIcon = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
background: ${props => mixin.darken(props.theme.colors.bg.secondary, 0.15)};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@ -452,11 +450,11 @@ export const TabBarSection = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const TabBarItem = styled.div`
|
export const TabBarItem = styled.div`
|
||||||
box-shadow: inset 0 -2px rgba(216, 93, 216);
|
box-shadow: inset 0 -2px ${props => props.theme.colors.primary};
|
||||||
padding: 12px 7px 14px 7px;
|
padding: 12px 7px 14px 7px;
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
margin-right: 36px;
|
margin-right: 36px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CommentContainer = styled.div`
|
export const CommentContainer = styled.div`
|
||||||
@ -491,7 +489,7 @@ export const CommentTextArea = styled(TextareaAutosize)`
|
|||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
background: #1f243e;
|
background: #1f243e;
|
||||||
border: none;
|
border: none;
|
||||||
transition: max-height 200ms, height 200ms, min-height 200ms;
|
transition: max-height 200ms, height 200ms, min-height 200ms;
|
||||||
@ -556,13 +554,13 @@ export const ActivityItemHeaderTitle = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActivityItemHeaderTitleName = styled.span`
|
export const ActivityItemHeaderTitleName = styled.span`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActivityItemTimestamp = styled.span<{ margin: number }>`
|
export const ActivityItemTimestamp = styled.span<{ margin: number }>`
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.65);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.65)};
|
||||||
margin-left: ${props => props.margin}px;
|
margin-left: ${props => props.margin}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -575,15 +573,15 @@ export const ActivityItemComment = styled.div`
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
${mixin.boxShadowCard}
|
${mixin.boxShadowCard}
|
||||||
position: relative;
|
position: relative;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
background-color: ${mixin.darken('#262c49', 0.1)};
|
background-color: ${props => mixin.darken(props.theme.colors.alternate, 0.1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActivityItemLog = styled.span`
|
export const ActivityItemLog = styled.span`
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ViewRawButton = styled.button`
|
export const ViewRawButton = styled.button`
|
||||||
@ -594,9 +592,9 @@ export const ViewRawButton = styled.button`
|
|||||||
right: 4px;
|
right: 4px;
|
||||||
bottom: -24px;
|
bottom: -24px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: rgba(${props => props.theme.colors.text.primary}, 0.25);
|
color: ${props => mixin.rgba(props.theme.colors.text.primary, 0.25)};
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const Textarea = styled(TextareaAutosize)`
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 3px 10px 3px 8px;
|
padding: 3px 10px 3px 8px;
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props => props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ export const ProjectMember = styled(TaskAssignee)<{ zIndex: number }>`
|
|||||||
z-index: ${props => props.zIndex};
|
z-index: ${props => props.zIndex};
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
box-shadow: 0 0 0 2px rgba(16, 22, 58), inset 0 0 0 1px rgba(16, 22, 58, 0.07);
|
box-shadow: 0 0 0 2px ${props => props.theme.colors.bg.primary},
|
||||||
|
inset 0 0 0 1px ${props => mixin.rgba(props.theme.colors.bg.primary, 0.07)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const NavbarWrapper = styled.div`
|
export const NavbarWrapper = styled.div`
|
||||||
@ -28,9 +29,9 @@ export const NavbarHeader = styled.header`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background: rgb(16, 22, 58);
|
background: ${props => props.theme.colors.bg.primary};
|
||||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
|
||||||
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
border-bottom: 1px solid ${props => mixin.rgba(props.theme.colors.alternate, 0.65)};
|
||||||
`;
|
`;
|
||||||
export const Breadcrumbs = styled.div`
|
export const Breadcrumbs = styled.div`
|
||||||
color: rgb(94, 108, 132);
|
color: rgb(94, 108, 132);
|
||||||
@ -124,7 +125,7 @@ export const ProjectTabs = styled.div`
|
|||||||
|
|
||||||
export const ProjectTab = styled(NavLink)`
|
export const ProjectTab = styled(NavLink)`
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -141,22 +142,22 @@ export const ProjectTab = styled(NavLink)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: inset 0 -2px rgba(${props => props.theme.colors.text.secondary});
|
box-shadow: inset 0 -2px ${props => props.theme.colors.text.secondary};
|
||||||
color: rgba(${props => props.theme.colors.text.secondary});
|
color: ${props => props.theme.colors.text.secondary};
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
box-shadow: inset 0 -2px rgba(${props => props.theme.colors.secondary});
|
box-shadow: inset 0 -2px ${props => props.theme.colors.secondary};
|
||||||
color: rgba(${props => props.theme.colors.secondary});
|
color: ${props => props.theme.colors.secondary};
|
||||||
}
|
}
|
||||||
&.active:hover {
|
&.active:hover {
|
||||||
box-shadow: inset 0 -2px rgba(${props => props.theme.colors.secondary});
|
box-shadow: inset 0 -2px ${props => props.theme.colors.secondary};
|
||||||
color: rgba(${props => props.theme.colors.secondary});
|
color: ${props => props.theme.colors.secondary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ProjectName = styled.h1`
|
export const ProjectName = styled.h1`
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 3px 10px 3px 8px;
|
padding: 3px 10px 3px 8px;
|
||||||
@ -185,7 +186,7 @@ export const ProjectNameTextarea = styled(TextareaAutosize)`
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 3px 10px 3px 8px;
|
padding: 3px 10px 3px 8px;
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
box-shadow: ${props => props.theme.colors.primary} 0px 0px 0px 1px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -203,7 +204,7 @@ export const ProjectSwitcher = styled.button`
|
|||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -227,7 +228,7 @@ export const ProjectSettingsButton = styled.button`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.theme.colors.primary};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -243,7 +244,7 @@ export const ProjectFinder = styled(Button)`
|
|||||||
|
|
||||||
export const NavSeparator = styled.div`
|
export const NavSeparator = styled.div`
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: rgba(${props => props.theme.colors.border});
|
background: ${props => props.theme.colors.border};
|
||||||
height: 34px;
|
height: 34px;
|
||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
`;
|
`;
|
||||||
@ -260,11 +261,11 @@ export const LogoContainer = styled(Link)`
|
|||||||
|
|
||||||
export const TaskcafeTitle = styled.h2`
|
export const TaskcafeTitle = styled.h2`
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: ${props => props.theme.colors.text.primary};
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TaskcafeLogo = styled(Taskcafe)`
|
export const TaskcafeLogo = styled(Taskcafe)`
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
stroke: rgba(${props => props.theme.colors.text.primary});
|
stroke: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
@ -2,8 +2,8 @@ import React, { useState } from 'react';
|
|||||||
import NormalizeStyles from 'App/NormalizeStyles';
|
import NormalizeStyles from 'App/NormalizeStyles';
|
||||||
import BaseStyles from 'App/BaseStyles';
|
import BaseStyles from 'App/BaseStyles';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import DropdownMenu from 'shared/components/DropdownMenu';
|
|
||||||
import TopNavbar from '.';
|
import TopNavbar from '.';
|
||||||
|
import theme from '../../../App/ThemeStyles';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: TopNavbar,
|
component: TopNavbar,
|
||||||
@ -15,7 +15,7 @@ export default {
|
|||||||
backgrounds: [
|
backgrounds: [
|
||||||
{ name: 'white', value: '#ffffff' },
|
{ name: 'white', value: '#ffffff' },
|
||||||
{ name: 'gray', value: '#f8f8f8' },
|
{ name: 'gray', value: '#f8f8f8' },
|
||||||
{ name: 'darkBlue', value: '#262c49', default: true },
|
{ name: 'darkBlue', value: theme.colors.bg.secondary, default: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import { Home, Star, Bell, AngleDown, BarChart, CheckCircle } from 'shared/icons';
|
import { Home, Star, Bell, AngleDown, BarChart, CheckCircle, ListUnordered } from 'shared/icons';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import ProfileIcon from 'shared/components/ProfileIcon';
|
import ProfileIcon from 'shared/components/ProfileIcon';
|
||||||
import { usePopup } from 'shared/components/PopupMenu';
|
import { usePopup } from 'shared/components/PopupMenu';
|
||||||
import { RoleCode } from 'shared/generated/graphql';
|
import { RoleCode } from 'shared/generated/graphql';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
import {
|
import {
|
||||||
TaskcafeLogo,
|
TaskcafeLogo,
|
||||||
TaskcafeTitle,
|
TaskcafeTitle,
|
||||||
@ -173,8 +174,11 @@ type NavBarProps = {
|
|||||||
user: TaskUser | null;
|
user: TaskUser | null;
|
||||||
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
|
onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
|
||||||
projectMembers?: Array<TaskUser> | null;
|
projectMembers?: Array<TaskUser> | null;
|
||||||
|
projectInvitedMembers?: Array<InvitedUser> | null;
|
||||||
|
|
||||||
onRemoveFromBoard?: (userID: string) => void;
|
onRemoveFromBoard?: (userID: string) => void;
|
||||||
onMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
onMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||||
|
onInvitedMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, email: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NavBar: React.FC<NavBarProps> = ({
|
const NavBar: React.FC<NavBarProps> = ({
|
||||||
@ -184,10 +188,12 @@ const NavBar: React.FC<NavBarProps> = ({
|
|||||||
onChangeProjectOwner,
|
onChangeProjectOwner,
|
||||||
currentTab,
|
currentTab,
|
||||||
onMemberProfile,
|
onMemberProfile,
|
||||||
|
onInvitedMemberProfile,
|
||||||
canEditProjectName = false,
|
canEditProjectName = false,
|
||||||
onOpenProjectFinder,
|
onOpenProjectFinder,
|
||||||
onFavorite,
|
onFavorite,
|
||||||
onSetTab,
|
onSetTab,
|
||||||
|
projectInvitedMembers,
|
||||||
onChangeRole,
|
onChangeRole,
|
||||||
name,
|
name,
|
||||||
onRemoveFromBoard,
|
onRemoveFromBoard,
|
||||||
@ -204,6 +210,7 @@ const NavBar: React.FC<NavBarProps> = ({
|
|||||||
onProfileClick($target);
|
onProfileClick($target);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const history = useHistory();
|
||||||
const { showPopup } = usePopup();
|
const { showPopup } = usePopup();
|
||||||
return (
|
return (
|
||||||
<NavbarWrapper>
|
<NavbarWrapper>
|
||||||
@ -245,19 +252,38 @@ const NavBar: React.FC<NavBarProps> = ({
|
|||||||
<TaskcafeTitle>Taskcafé</TaskcafeTitle>
|
<TaskcafeTitle>Taskcafé</TaskcafeTitle>
|
||||||
</LogoContainer>
|
</LogoContainer>
|
||||||
<GlobalActions>
|
<GlobalActions>
|
||||||
{projectMembers && onMemberProfile && (
|
{projectMembers && projectInvitedMembers && onMemberProfile && onInvitedMemberProfile && (
|
||||||
<>
|
<>
|
||||||
<ProjectMembers>
|
<ProjectMembers>
|
||||||
{projectMembers.map((member, idx) => (
|
{projectMembers.map((member, idx) => (
|
||||||
<ProjectMember
|
<ProjectMember
|
||||||
showRoleIcons
|
showRoleIcons
|
||||||
zIndex={projectMembers.length - idx}
|
zIndex={projectMembers.length - idx + projectInvitedMembers.length}
|
||||||
key={member.id}
|
key={member.id}
|
||||||
size={28}
|
size={28}
|
||||||
member={member}
|
member={member}
|
||||||
onMemberProfile={onMemberProfile}
|
onMemberProfile={onMemberProfile}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{projectInvitedMembers.map((member, idx) => (
|
||||||
|
<ProjectMember
|
||||||
|
showRoleIcons
|
||||||
|
zIndex={projectInvitedMembers.length - idx}
|
||||||
|
key={member.email}
|
||||||
|
size={28}
|
||||||
|
invited
|
||||||
|
member={{
|
||||||
|
id: member.email,
|
||||||
|
fullName: member.email,
|
||||||
|
profileIcon: {
|
||||||
|
url: null,
|
||||||
|
initials: member.email.charAt(0),
|
||||||
|
bgColor: '#fff',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onMemberProfile={onInvitedMemberProfile}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{canInviteUser && (
|
{canInviteUser && (
|
||||||
<InviteButton
|
<InviteButton
|
||||||
onClick={$target => {
|
onClick={$target => {
|
||||||
@ -283,6 +309,9 @@ const NavBar: React.FC<NavBarProps> = ({
|
|||||||
<IconContainer disabled onClick={NOOP}>
|
<IconContainer disabled onClick={NOOP}>
|
||||||
<CheckCircle width={20} height={20} />
|
<CheckCircle width={20} height={20} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
|
<IconContainer disabled onClick={NOOP}>
|
||||||
|
<ListUnordered width={20} height={20} />
|
||||||
|
</IconContainer>
|
||||||
<IconContainer disabled onClick={onNotificationClick}>
|
<IconContainer disabled onClick={onNotificationClick}>
|
||||||
<Bell color="#c2c6dc" size={20} />
|
<Bell color="#c2c6dc" size={20} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
|
@ -113,6 +113,14 @@ export type UserAccount = {
|
|||||||
member: MemberList;
|
member: MemberList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type InvitedUserAccount = {
|
||||||
|
__typename?: 'InvitedUserAccount';
|
||||||
|
id: Scalars['ID'];
|
||||||
|
email: Scalars['String'];
|
||||||
|
invitedOn: Scalars['Time'];
|
||||||
|
member: MemberList;
|
||||||
|
};
|
||||||
|
|
||||||
export type Team = {
|
export type Team = {
|
||||||
__typename?: 'Team';
|
__typename?: 'Team';
|
||||||
id: Scalars['ID'];
|
id: Scalars['ID'];
|
||||||
@ -121,6 +129,12 @@ export type Team = {
|
|||||||
members: Array<Member>;
|
members: Array<Member>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type InvitedMember = {
|
||||||
|
__typename?: 'InvitedMember';
|
||||||
|
email: Scalars['String'];
|
||||||
|
invitedOn: Scalars['Time'];
|
||||||
|
};
|
||||||
|
|
||||||
export type Project = {
|
export type Project = {
|
||||||
__typename?: 'Project';
|
__typename?: 'Project';
|
||||||
id: Scalars['ID'];
|
id: Scalars['ID'];
|
||||||
@ -129,6 +143,7 @@ export type Project = {
|
|||||||
team?: Maybe<Team>;
|
team?: Maybe<Team>;
|
||||||
taskGroups: Array<TaskGroup>;
|
taskGroups: Array<TaskGroup>;
|
||||||
members: Array<Member>;
|
members: Array<Member>;
|
||||||
|
invitedMembers: Array<InvitedMember>;
|
||||||
labels: Array<ProjectLabel>;
|
labels: Array<ProjectLabel>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -221,11 +236,13 @@ export type Query = {
|
|||||||
findTask: Task;
|
findTask: Task;
|
||||||
findTeam: Team;
|
findTeam: Team;
|
||||||
findUser: UserAccount;
|
findUser: UserAccount;
|
||||||
|
invitedUsers: Array<InvitedUserAccount>;
|
||||||
labelColors: Array<LabelColor>;
|
labelColors: Array<LabelColor>;
|
||||||
me: MePayload;
|
me: MePayload;
|
||||||
notifications: Array<Notification>;
|
notifications: Array<Notification>;
|
||||||
organizations: Array<Organization>;
|
organizations: Array<Organization>;
|
||||||
projects: Array<Project>;
|
projects: Array<Project>;
|
||||||
|
searchMembers: Array<MemberSearchResult>;
|
||||||
taskGroups: Array<TaskGroup>;
|
taskGroups: Array<TaskGroup>;
|
||||||
teams: Array<Team>;
|
teams: Array<Team>;
|
||||||
users: Array<UserAccount>;
|
users: Array<UserAccount>;
|
||||||
@ -256,6 +273,11 @@ export type QueryProjectsArgs = {
|
|||||||
input?: Maybe<ProjectsFilter>;
|
input?: Maybe<ProjectsFilter>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QuerySearchMembersArgs = {
|
||||||
|
input: MemberSearchFilter;
|
||||||
|
};
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
addTaskLabel: Task;
|
addTaskLabel: Task;
|
||||||
@ -263,7 +285,6 @@ export type Mutation = {
|
|||||||
clearProfileAvatar: UserAccount;
|
clearProfileAvatar: UserAccount;
|
||||||
createProject: Project;
|
createProject: Project;
|
||||||
createProjectLabel: ProjectLabel;
|
createProjectLabel: ProjectLabel;
|
||||||
createProjectMember: CreateProjectMemberPayload;
|
|
||||||
createRefreshToken: RefreshToken;
|
createRefreshToken: RefreshToken;
|
||||||
createTask: Task;
|
createTask: Task;
|
||||||
createTaskChecklist: TaskChecklist;
|
createTaskChecklist: TaskChecklist;
|
||||||
@ -272,6 +293,8 @@ export type Mutation = {
|
|||||||
createTeam: Team;
|
createTeam: Team;
|
||||||
createTeamMember: CreateTeamMemberPayload;
|
createTeamMember: CreateTeamMemberPayload;
|
||||||
createUserAccount: UserAccount;
|
createUserAccount: UserAccount;
|
||||||
|
deleteInvitedProjectMember: DeleteInvitedProjectMemberPayload;
|
||||||
|
deleteInvitedUserAccount: DeleteInvitedUserAccountPayload;
|
||||||
deleteProject: DeleteProjectPayload;
|
deleteProject: DeleteProjectPayload;
|
||||||
deleteProjectLabel: ProjectLabel;
|
deleteProjectLabel: ProjectLabel;
|
||||||
deleteProjectMember: DeleteProjectMemberPayload;
|
deleteProjectMember: DeleteProjectMemberPayload;
|
||||||
@ -284,6 +307,7 @@ export type Mutation = {
|
|||||||
deleteTeamMember: DeleteTeamMemberPayload;
|
deleteTeamMember: DeleteTeamMemberPayload;
|
||||||
deleteUserAccount: DeleteUserAccountPayload;
|
deleteUserAccount: DeleteUserAccountPayload;
|
||||||
duplicateTaskGroup: DuplicateTaskGroupPayload;
|
duplicateTaskGroup: DuplicateTaskGroupPayload;
|
||||||
|
inviteProjectMembers: InviteProjectMembersPayload;
|
||||||
logoutUser: Scalars['Boolean'];
|
logoutUser: Scalars['Boolean'];
|
||||||
removeTaskLabel: Task;
|
removeTaskLabel: Task;
|
||||||
setTaskChecklistItemComplete: TaskChecklistItem;
|
setTaskChecklistItemComplete: TaskChecklistItem;
|
||||||
@ -333,11 +357,6 @@ export type MutationCreateProjectLabelArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateProjectMemberArgs = {
|
|
||||||
input: CreateProjectMember;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateRefreshTokenArgs = {
|
export type MutationCreateRefreshTokenArgs = {
|
||||||
input: NewRefreshToken;
|
input: NewRefreshToken;
|
||||||
};
|
};
|
||||||
@ -378,6 +397,16 @@ export type MutationCreateUserAccountArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteInvitedProjectMemberArgs = {
|
||||||
|
input: DeleteInvitedProjectMember;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteInvitedUserAccountArgs = {
|
||||||
|
input: DeleteInvitedUserAccount;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteProjectArgs = {
|
export type MutationDeleteProjectArgs = {
|
||||||
input: DeleteProject;
|
input: DeleteProject;
|
||||||
};
|
};
|
||||||
@ -438,6 +467,11 @@ export type MutationDuplicateTaskGroupArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationInviteProjectMembersArgs = {
|
||||||
|
input: InviteProjectMembers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationLogoutUserArgs = {
|
export type MutationLogoutUserArgs = {
|
||||||
input: LogoutUser;
|
input: LogoutUser;
|
||||||
};
|
};
|
||||||
@ -688,15 +722,32 @@ export type UpdateProjectLabelColor = {
|
|||||||
labelColorID: Scalars['UUID'];
|
labelColorID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateProjectMember = {
|
export type DeleteInvitedProjectMember = {
|
||||||
projectID: Scalars['UUID'];
|
projectID: Scalars['UUID'];
|
||||||
userID: Scalars['UUID'];
|
email: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateProjectMemberPayload = {
|
export type DeleteInvitedProjectMemberPayload = {
|
||||||
__typename?: 'CreateProjectMemberPayload';
|
__typename?: 'DeleteInvitedProjectMemberPayload';
|
||||||
|
invitedMember: InvitedMember;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MemberInvite = {
|
||||||
|
userID?: Maybe<Scalars['UUID']>;
|
||||||
|
email?: Maybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InviteProjectMembers = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
members: Array<MemberInvite>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InviteProjectMembersPayload = {
|
||||||
|
__typename?: 'InviteProjectMembersPayload';
|
||||||
ok: Scalars['Boolean'];
|
ok: Scalars['Boolean'];
|
||||||
member: Member;
|
projectID: Scalars['UUID'];
|
||||||
|
members: Array<Member>;
|
||||||
|
invitedMembers: Array<InvitedMember>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteProjectMember = {
|
export type DeleteProjectMember = {
|
||||||
@ -989,6 +1040,29 @@ export type UpdateTeamMemberRolePayload = {
|
|||||||
member: Member;
|
member: Member;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeleteInvitedUserAccount = {
|
||||||
|
invitedUserID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteInvitedUserAccountPayload = {
|
||||||
|
__typename?: 'DeleteInvitedUserAccountPayload';
|
||||||
|
invitedUser: InvitedUserAccount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MemberSearchFilter = {
|
||||||
|
SearchFilter: Scalars['String'];
|
||||||
|
projectID?: Maybe<Scalars['UUID']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MemberSearchResult = {
|
||||||
|
__typename?: 'MemberSearchResult';
|
||||||
|
similarity: Scalars['Int'];
|
||||||
|
user: UserAccount;
|
||||||
|
confirmed: Scalars['Boolean'];
|
||||||
|
invited: Scalars['Boolean'];
|
||||||
|
joined: Scalars['Boolean'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateUserInfoPayload = {
|
export type UpdateUserInfoPayload = {
|
||||||
__typename?: 'UpdateUserInfoPayload';
|
__typename?: 'UpdateUserInfoPayload';
|
||||||
user: UserAccount;
|
user: UserAccount;
|
||||||
@ -1205,6 +1279,9 @@ export type FindProjectQuery = (
|
|||||||
{ __typename?: 'ProfileIcon' }
|
{ __typename?: 'ProfileIcon' }
|
||||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||||
) }
|
) }
|
||||||
|
)>, invitedMembers: Array<(
|
||||||
|
{ __typename?: 'InvitedMember' }
|
||||||
|
& Pick<InvitedMember, 'email' | 'invitedOn'>
|
||||||
)>, labels: Array<(
|
)>, labels: Array<(
|
||||||
{ __typename?: 'ProjectLabel' }
|
{ __typename?: 'ProjectLabel' }
|
||||||
& Pick<ProjectLabel, 'id' | 'createdDate' | 'name'>
|
& Pick<ProjectLabel, 'id' | 'createdDate' | 'name'>
|
||||||
@ -1390,31 +1467,6 @@ export type MeQuery = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
export type CreateProjectMemberMutationVariables = {
|
|
||||||
projectID: Scalars['UUID'];
|
|
||||||
userID: Scalars['UUID'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type CreateProjectMemberMutation = (
|
|
||||||
{ __typename?: 'Mutation' }
|
|
||||||
& { createProjectMember: (
|
|
||||||
{ __typename?: 'CreateProjectMemberPayload' }
|
|
||||||
& Pick<CreateProjectMemberPayload, 'ok'>
|
|
||||||
& { member: (
|
|
||||||
{ __typename?: 'Member' }
|
|
||||||
& Pick<Member, 'id' | 'fullName' | 'username'>
|
|
||||||
& { profileIcon: (
|
|
||||||
{ __typename?: 'ProfileIcon' }
|
|
||||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
|
||||||
), role: (
|
|
||||||
{ __typename?: 'Role' }
|
|
||||||
& Pick<Role, 'code' | 'name'>
|
|
||||||
) }
|
|
||||||
) }
|
|
||||||
) }
|
|
||||||
);
|
|
||||||
|
|
||||||
export type DeleteProjectMutationVariables = {
|
export type DeleteProjectMutationVariables = {
|
||||||
projectID: Scalars['UUID'];
|
projectID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
@ -1432,6 +1484,23 @@ export type DeleteProjectMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type DeleteInvitedProjectMemberMutationVariables = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
email: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteInvitedProjectMemberMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { deleteInvitedProjectMember: (
|
||||||
|
{ __typename?: 'DeleteInvitedProjectMemberPayload' }
|
||||||
|
& { invitedMember: (
|
||||||
|
{ __typename?: 'InvitedMember' }
|
||||||
|
& Pick<InvitedMember, 'email'>
|
||||||
|
) }
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type DeleteProjectMemberMutationVariables = {
|
export type DeleteProjectMemberMutationVariables = {
|
||||||
projectID: Scalars['UUID'];
|
projectID: Scalars['UUID'];
|
||||||
userID: Scalars['UUID'];
|
userID: Scalars['UUID'];
|
||||||
@ -1450,6 +1519,34 @@ export type DeleteProjectMemberMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type InviteProjectMembersMutationVariables = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
members: Array<MemberInvite>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type InviteProjectMembersMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { inviteProjectMembers: (
|
||||||
|
{ __typename?: 'InviteProjectMembersPayload' }
|
||||||
|
& Pick<InviteProjectMembersPayload, 'ok'>
|
||||||
|
& { invitedMembers: Array<(
|
||||||
|
{ __typename?: 'InvitedMember' }
|
||||||
|
& Pick<InvitedMember, 'email' | 'invitedOn'>
|
||||||
|
)>, members: Array<(
|
||||||
|
{ __typename?: 'Member' }
|
||||||
|
& Pick<Member, 'id' | 'fullName' | 'username'>
|
||||||
|
& { profileIcon: (
|
||||||
|
{ __typename?: 'ProfileIcon' }
|
||||||
|
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||||
|
), role: (
|
||||||
|
{ __typename?: 'Role' }
|
||||||
|
& Pick<Role, 'code' | 'name'>
|
||||||
|
) }
|
||||||
|
)> }
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type UpdateProjectMemberRoleMutationVariables = {
|
export type UpdateProjectMemberRoleMutationVariables = {
|
||||||
projectID: Scalars['UUID'];
|
projectID: Scalars['UUID'];
|
||||||
userID: Scalars['UUID'];
|
userID: Scalars['UUID'];
|
||||||
@ -2130,6 +2227,22 @@ export type CreateUserAccountMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type DeleteInvitedUserAccountMutationVariables = {
|
||||||
|
invitedUserID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteInvitedUserAccountMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { deleteInvitedUserAccount: (
|
||||||
|
{ __typename?: 'DeleteInvitedUserAccountPayload' }
|
||||||
|
& { invitedUser: (
|
||||||
|
{ __typename?: 'InvitedUserAccount' }
|
||||||
|
& Pick<InvitedUserAccount, 'id'>
|
||||||
|
) }
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type DeleteUserAccountMutationVariables = {
|
export type DeleteUserAccountMutationVariables = {
|
||||||
userID: Scalars['UUID'];
|
userID: Scalars['UUID'];
|
||||||
newOwnerID?: Maybe<Scalars['UUID']>;
|
newOwnerID?: Maybe<Scalars['UUID']>;
|
||||||
@ -2211,7 +2324,10 @@ export type UsersQueryVariables = {};
|
|||||||
|
|
||||||
export type UsersQuery = (
|
export type UsersQuery = (
|
||||||
{ __typename?: 'Query' }
|
{ __typename?: 'Query' }
|
||||||
& { users: Array<(
|
& { invitedUsers: Array<(
|
||||||
|
{ __typename?: 'InvitedUserAccount' }
|
||||||
|
& Pick<InvitedUserAccount, 'id' | 'email' | 'invitedOn'>
|
||||||
|
)>, users: Array<(
|
||||||
{ __typename?: 'UserAccount' }
|
{ __typename?: 'UserAccount' }
|
||||||
& Pick<UserAccount, 'id' | 'email' | 'fullName' | 'username'>
|
& Pick<UserAccount, 'id' | 'email' | 'fullName' | 'username'>
|
||||||
& { role: (
|
& { role: (
|
||||||
@ -2603,6 +2719,10 @@ export const FindProjectDocument = gql`
|
|||||||
bgColor
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
invitedMembers {
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
labels {
|
labels {
|
||||||
id
|
id
|
||||||
createdDate
|
createdDate
|
||||||
@ -2884,53 +3004,6 @@ export function useMeLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptio
|
|||||||
export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
|
export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
|
||||||
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
|
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
|
||||||
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
|
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
|
||||||
export const CreateProjectMemberDocument = gql`
|
|
||||||
mutation createProjectMember($projectID: UUID!, $userID: UUID!) {
|
|
||||||
createProjectMember(input: {projectID: $projectID, userID: $userID}) {
|
|
||||||
ok
|
|
||||||
member {
|
|
||||||
id
|
|
||||||
fullName
|
|
||||||
profileIcon {
|
|
||||||
url
|
|
||||||
initials
|
|
||||||
bgColor
|
|
||||||
}
|
|
||||||
username
|
|
||||||
role {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export type CreateProjectMemberMutationFn = ApolloReactCommon.MutationFunction<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useCreateProjectMemberMutation__
|
|
||||||
*
|
|
||||||
* To run a mutation, you first call `useCreateProjectMemberMutation` within a React component and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useCreateProjectMemberMutation` returns a tuple that includes:
|
|
||||||
* - A mutate function that you can call at any time to execute the mutation
|
|
||||||
* - An object with fields that represent the current status of the mutation's execution
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const [createProjectMemberMutation, { data, loading, error }] = useCreateProjectMemberMutation({
|
|
||||||
* variables: {
|
|
||||||
* projectID: // value for 'projectID'
|
|
||||||
* userID: // value for 'userID'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useCreateProjectMemberMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>) {
|
|
||||||
return ApolloReactHooks.useMutation<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>(CreateProjectMemberDocument, baseOptions);
|
|
||||||
}
|
|
||||||
export type CreateProjectMemberMutationHookResult = ReturnType<typeof useCreateProjectMemberMutation>;
|
|
||||||
export type CreateProjectMemberMutationResult = ApolloReactCommon.MutationResult<CreateProjectMemberMutation>;
|
|
||||||
export type CreateProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>;
|
|
||||||
export const DeleteProjectDocument = gql`
|
export const DeleteProjectDocument = gql`
|
||||||
mutation deleteProject($projectID: UUID!) {
|
mutation deleteProject($projectID: UUID!) {
|
||||||
deleteProject(input: {projectID: $projectID}) {
|
deleteProject(input: {projectID: $projectID}) {
|
||||||
@ -2966,6 +3039,41 @@ export function useDeleteProjectMutation(baseOptions?: ApolloReactHooks.Mutation
|
|||||||
export type DeleteProjectMutationHookResult = ReturnType<typeof useDeleteProjectMutation>;
|
export type DeleteProjectMutationHookResult = ReturnType<typeof useDeleteProjectMutation>;
|
||||||
export type DeleteProjectMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMutation>;
|
export type DeleteProjectMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMutation>;
|
||||||
export type DeleteProjectMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMutation, DeleteProjectMutationVariables>;
|
export type DeleteProjectMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMutation, DeleteProjectMutationVariables>;
|
||||||
|
export const DeleteInvitedProjectMemberDocument = gql`
|
||||||
|
mutation deleteInvitedProjectMember($projectID: UUID!, $email: String!) {
|
||||||
|
deleteInvitedProjectMember(input: {projectID: $projectID, email: $email}) {
|
||||||
|
invitedMember {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type DeleteInvitedProjectMemberMutationFn = ApolloReactCommon.MutationFunction<DeleteInvitedProjectMemberMutation, DeleteInvitedProjectMemberMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteInvitedProjectMemberMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteInvitedProjectMemberMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteInvitedProjectMemberMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [deleteInvitedProjectMemberMutation, { data, loading, error }] = useDeleteInvitedProjectMemberMutation({
|
||||||
|
* variables: {
|
||||||
|
* projectID: // value for 'projectID'
|
||||||
|
* email: // value for 'email'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteInvitedProjectMemberMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteInvitedProjectMemberMutation, DeleteInvitedProjectMemberMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<DeleteInvitedProjectMemberMutation, DeleteInvitedProjectMemberMutationVariables>(DeleteInvitedProjectMemberDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type DeleteInvitedProjectMemberMutationHookResult = ReturnType<typeof useDeleteInvitedProjectMemberMutation>;
|
||||||
|
export type DeleteInvitedProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteInvitedProjectMemberMutation>;
|
||||||
|
export type DeleteInvitedProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteInvitedProjectMemberMutation, DeleteInvitedProjectMemberMutationVariables>;
|
||||||
export const DeleteProjectMemberDocument = gql`
|
export const DeleteProjectMemberDocument = gql`
|
||||||
mutation deleteProjectMember($projectID: UUID!, $userID: UUID!) {
|
mutation deleteProjectMember($projectID: UUID!, $userID: UUID!) {
|
||||||
deleteProjectMember(input: {projectID: $projectID, userID: $userID}) {
|
deleteProjectMember(input: {projectID: $projectID, userID: $userID}) {
|
||||||
@ -3003,6 +3111,57 @@ export function useDeleteProjectMemberMutation(baseOptions?: ApolloReactHooks.Mu
|
|||||||
export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>;
|
export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>;
|
||||||
export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>;
|
export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>;
|
||||||
export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>;
|
export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>;
|
||||||
|
export const InviteProjectMembersDocument = gql`
|
||||||
|
mutation inviteProjectMembers($projectID: UUID!, $members: [MemberInvite!]!) {
|
||||||
|
inviteProjectMembers(input: {projectID: $projectID, members: $members}) {
|
||||||
|
ok
|
||||||
|
invitedMembers {
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
|
members {
|
||||||
|
id
|
||||||
|
fullName
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
username
|
||||||
|
role {
|
||||||
|
code
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type InviteProjectMembersMutationFn = ApolloReactCommon.MutationFunction<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useInviteProjectMembersMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useInviteProjectMembersMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useInviteProjectMembersMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [inviteProjectMembersMutation, { data, loading, error }] = useInviteProjectMembersMutation({
|
||||||
|
* variables: {
|
||||||
|
* projectID: // value for 'projectID'
|
||||||
|
* members: // value for 'members'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useInviteProjectMembersMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>(InviteProjectMembersDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type InviteProjectMembersMutationHookResult = ReturnType<typeof useInviteProjectMembersMutation>;
|
||||||
|
export type InviteProjectMembersMutationResult = ApolloReactCommon.MutationResult<InviteProjectMembersMutation>;
|
||||||
|
export type InviteProjectMembersMutationOptions = ApolloReactCommon.BaseMutationOptions<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>;
|
||||||
export const UpdateProjectMemberRoleDocument = gql`
|
export const UpdateProjectMemberRoleDocument = gql`
|
||||||
mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) {
|
mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) {
|
||||||
updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) {
|
updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) {
|
||||||
@ -4381,6 +4540,40 @@ export function useCreateUserAccountMutation(baseOptions?: ApolloReactHooks.Muta
|
|||||||
export type CreateUserAccountMutationHookResult = ReturnType<typeof useCreateUserAccountMutation>;
|
export type CreateUserAccountMutationHookResult = ReturnType<typeof useCreateUserAccountMutation>;
|
||||||
export type CreateUserAccountMutationResult = ApolloReactCommon.MutationResult<CreateUserAccountMutation>;
|
export type CreateUserAccountMutationResult = ApolloReactCommon.MutationResult<CreateUserAccountMutation>;
|
||||||
export type CreateUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateUserAccountMutation, CreateUserAccountMutationVariables>;
|
export type CreateUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateUserAccountMutation, CreateUserAccountMutationVariables>;
|
||||||
|
export const DeleteInvitedUserAccountDocument = gql`
|
||||||
|
mutation deleteInvitedUserAccount($invitedUserID: UUID!) {
|
||||||
|
deleteInvitedUserAccount(input: {invitedUserID: $invitedUserID}) {
|
||||||
|
invitedUser {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type DeleteInvitedUserAccountMutationFn = ApolloReactCommon.MutationFunction<DeleteInvitedUserAccountMutation, DeleteInvitedUserAccountMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteInvitedUserAccountMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteInvitedUserAccountMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteInvitedUserAccountMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [deleteInvitedUserAccountMutation, { data, loading, error }] = useDeleteInvitedUserAccountMutation({
|
||||||
|
* variables: {
|
||||||
|
* invitedUserID: // value for 'invitedUserID'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteInvitedUserAccountMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteInvitedUserAccountMutation, DeleteInvitedUserAccountMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<DeleteInvitedUserAccountMutation, DeleteInvitedUserAccountMutationVariables>(DeleteInvitedUserAccountDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type DeleteInvitedUserAccountMutationHookResult = ReturnType<typeof useDeleteInvitedUserAccountMutation>;
|
||||||
|
export type DeleteInvitedUserAccountMutationResult = ApolloReactCommon.MutationResult<DeleteInvitedUserAccountMutation>;
|
||||||
|
export type DeleteInvitedUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteInvitedUserAccountMutation, DeleteInvitedUserAccountMutationVariables>;
|
||||||
export const DeleteUserAccountDocument = gql`
|
export const DeleteUserAccountDocument = gql`
|
||||||
mutation deleteUserAccount($userID: UUID!, $newOwnerID: UUID) {
|
mutation deleteUserAccount($userID: UUID!, $newOwnerID: UUID) {
|
||||||
deleteUserAccount(input: {userID: $userID, newOwnerID: $newOwnerID}) {
|
deleteUserAccount(input: {userID: $userID, newOwnerID: $newOwnerID}) {
|
||||||
@ -4534,6 +4727,11 @@ export type UpdateUserRoleMutationResult = ApolloReactCommon.MutationResult<Upda
|
|||||||
export type UpdateUserRoleMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateUserRoleMutation, UpdateUserRoleMutationVariables>;
|
export type UpdateUserRoleMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateUserRoleMutation, UpdateUserRoleMutationVariables>;
|
||||||
export const UsersDocument = gql`
|
export const UsersDocument = gql`
|
||||||
query users {
|
query users {
|
||||||
|
invitedUsers {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
users {
|
users {
|
||||||
id
|
id
|
||||||
email
|
email
|
||||||
|
@ -22,6 +22,10 @@ query findProject($projectID: UUID!) {
|
|||||||
bgColor
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
invitedMembers {
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
labels {
|
labels {
|
||||||
id
|
id
|
||||||
createdDate
|
createdDate
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import gql from 'graphql-tag';
|
|
||||||
|
|
||||||
export const CREATE_PROJECT_MEMBER_MUTATION = gql`
|
|
||||||
mutation createProjectMember($projectID: UUID!, $userID: UUID!) {
|
|
||||||
createProjectMember(input: { projectID: $projectID, userID: $userID }) {
|
|
||||||
ok
|
|
||||||
member {
|
|
||||||
id
|
|
||||||
fullName
|
|
||||||
profileIcon {
|
|
||||||
url
|
|
||||||
initials
|
|
||||||
bgColor
|
|
||||||
}
|
|
||||||
username
|
|
||||||
role {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default CREATE_PROJECT_MEMBER_MUTATION;
|
|
@ -0,0 +1,13 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const DELETE_PROJECT_INVITED_MEMBER_MUTATION = gql`
|
||||||
|
mutation deleteInvitedProjectMember($projectID: UUID!, $email: String!) {
|
||||||
|
deleteInvitedProjectMember(input: { projectID: $projectID, email: $email }) {
|
||||||
|
invitedMember {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default DELETE_PROJECT_INVITED_MEMBER_MUTATION;
|
29
frontend/src/shared/graphql/project/inviteProjectMembers.ts
Normal file
29
frontend/src/shared/graphql/project/inviteProjectMembers.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const INVITE_PROJECT_MEMBERS_MUTATION = gql`
|
||||||
|
mutation inviteProjectMembers($projectID: UUID!, $members: [MemberInvite!]!) {
|
||||||
|
inviteProjectMembers(input: { projectID: $projectID, members: $members }) {
|
||||||
|
ok
|
||||||
|
invitedMembers {
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
|
members {
|
||||||
|
id
|
||||||
|
fullName
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
username
|
||||||
|
role {
|
||||||
|
code
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default INVITE_PROJECT_MEMBERS_MUTATION;
|
13
frontend/src/shared/graphql/user/deleteInvitedUser.ts
Normal file
13
frontend/src/shared/graphql/user/deleteInvitedUser.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const DELETE_INVITED_USER_MUTATION = gql`
|
||||||
|
mutation deleteInvitedUserAccount($invitedUserID: UUID!) {
|
||||||
|
deleteInvitedUserAccount(input: { invitedUserID: $invitedUserID }) {
|
||||||
|
invitedUser {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default DELETE_INVITED_USER_MUTATION;
|
@ -1,4 +1,9 @@
|
|||||||
query users {
|
query users {
|
||||||
|
invitedUsers {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
invitedOn
|
||||||
|
}
|
||||||
users {
|
users {
|
||||||
id
|
id
|
||||||
email
|
email
|
||||||
|
12
frontend/src/shared/icons/ArrowDown.tsx
Normal file
12
frontend/src/shared/icons/ArrowDown.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const ArrowDown: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 448 512">
|
||||||
|
<path d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArrowDown;
|
12
frontend/src/shared/icons/CaretDown.tsx
Normal file
12
frontend/src/shared/icons/CaretDown.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const CaretDown: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 320 512">
|
||||||
|
<path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CaretDown;
|
12
frontend/src/shared/icons/CaretRight.tsx
Normal file
12
frontend/src/shared/icons/CaretRight.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const CaretRight: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 192 512">
|
||||||
|
<path d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CaretRight;
|
12
frontend/src/shared/icons/Dot.tsx
Normal file
12
frontend/src/shared/icons/Dot.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const Dot: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 18 18">
|
||||||
|
<circle cx="9" cy="9" r="3.5" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dot;
|
12
frontend/src/shared/icons/DotCircle.tsx
Normal file
12
frontend/src/shared/icons/DotCircle.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const DotCircle: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||||
|
<path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm80 248c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DotCircle;
|
12
frontend/src/shared/icons/EyeSlash.tsx
Normal file
12
frontend/src/shared/icons/EyeSlash.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const EyeSlash: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 640 512">
|
||||||
|
<path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EyeSlash;
|
@ -17,8 +17,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Svg = styled.svg`
|
const Svg = styled.svg`
|
||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: ${props => props.theme.colors.text.primary};
|
||||||
stroke: rgba(${props => props.theme.colors.text.primary});
|
stroke: ${props => props.theme.colors.text.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Icon: React.FC<Props> = ({ width, height, viewBox, className, onClick, children }) => {
|
const Icon: React.FC<Props> = ({ width, height, viewBox, className, onClick, children }) => {
|
||||||
|
12
frontend/src/shared/icons/ListUnordered.tsx
Normal file
12
frontend/src/shared/icons/ListUnordered.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const ListUnordered: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||||
|
<path d="M48 48a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48 48 0 0 0-48-48zm448 16H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListUnordered;
|
@ -1,9 +1,16 @@
|
|||||||
import Cross from './Cross';
|
import Cross from './Cross';
|
||||||
import Cog from './Cog';
|
import Cog from './Cog';
|
||||||
|
import ArrowDown from './ArrowDown';
|
||||||
|
import ListUnordered from './ListUnordered';
|
||||||
|
import Dot from './Dot';
|
||||||
|
import CaretDown from './CaretDown';
|
||||||
import Eye from './Eye';
|
import Eye from './Eye';
|
||||||
|
import EyeSlash from './EyeSlash';
|
||||||
|
import CaretRight from './CaretRight';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
import At from './At';
|
import At from './At';
|
||||||
import Task from './Task';
|
import Task from './Task';
|
||||||
|
import DotCircle from './DotCircle';
|
||||||
import Smile from './Smile';
|
import Smile from './Smile';
|
||||||
import Paperclip from './Paperclip';
|
import Paperclip from './Paperclip';
|
||||||
import Calendar from './Calendar';
|
import Calendar from './Calendar';
|
||||||
@ -88,5 +95,12 @@ export {
|
|||||||
Paperclip,
|
Paperclip,
|
||||||
Share,
|
Share,
|
||||||
Eye,
|
Eye,
|
||||||
|
ListUnordered,
|
||||||
|
EyeSlash,
|
||||||
List,
|
List,
|
||||||
|
CaretDown,
|
||||||
|
Dot,
|
||||||
|
ArrowDown,
|
||||||
|
CaretRight,
|
||||||
|
DotCircle,
|
||||||
};
|
};
|
||||||
|
1
frontend/src/styled.d.ts
vendored
1
frontend/src/styled.d.ts
vendored
@ -10,6 +10,7 @@ declare module 'styled-components' {
|
|||||||
};
|
};
|
||||||
colors: {
|
colors: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
multiColors: string[];
|
||||||
primary: string;
|
primary: string;
|
||||||
secondary: string;
|
secondary: string;
|
||||||
success: string;
|
success: string;
|
||||||
|
147
frontend/src/taskcafe.d.ts
vendored
147
frontend/src/taskcafe.d.ts
vendored
@ -61,7 +61,7 @@ type User = TaskUser & {
|
|||||||
|
|
||||||
type RefreshTokenResponse = {
|
type RefreshTokenResponse = {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
isInstalled: boolean;
|
setup?: null | { confirmToken: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
type LoginFormData = {
|
type LoginFormData = {
|
||||||
@ -91,7 +91,14 @@ type ErrorOption =
|
|||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SetFailedFn = () => void;
|
||||||
|
type ConfirmProps = {
|
||||||
|
hasConfirmToken: boolean;
|
||||||
|
onConfirmUser: (setFailed: SetFailedFn) => void;
|
||||||
|
};
|
||||||
|
|
||||||
type RegisterProps = {
|
type RegisterProps = {
|
||||||
|
registered?: boolean;
|
||||||
onSubmit: (
|
onSubmit: (
|
||||||
data: RegisterFormData,
|
data: RegisterFormData,
|
||||||
setComplete: (val: boolean) => void,
|
setComplete: (val: boolean) => void,
|
||||||
@ -127,3 +134,141 @@ type ElementBounds = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type CardLabelVariant = 'large' | 'small';
|
type CardLabelVariant = 'large' | 'small';
|
||||||
|
|
||||||
|
type InvitedUser = {
|
||||||
|
email: string;
|
||||||
|
invitedOn: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InvitedUserAccount = {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
invitedOn: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodeDimensions = {
|
||||||
|
entry: React.RefObject<HTMLElement>;
|
||||||
|
children: React.RefObject<HTMLElement> | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OutlineNode = {
|
||||||
|
id: string;
|
||||||
|
parent: string;
|
||||||
|
depth: number;
|
||||||
|
position: number;
|
||||||
|
ancestors: Array<string>;
|
||||||
|
collapsed: boolean;
|
||||||
|
children: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RelationshipChild = {
|
||||||
|
position: number;
|
||||||
|
id: string;
|
||||||
|
depth: number;
|
||||||
|
children: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodeRelationships = {
|
||||||
|
self: { id: string; depth: number };
|
||||||
|
children: Array<RelationshipChild>;
|
||||||
|
numberOfSubChildren: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OutlineData = {
|
||||||
|
published: Map<string, string>;
|
||||||
|
nodes: Map<number, Map<string, OutlineNode>>;
|
||||||
|
relationships: Map<string, NodeRelationships>;
|
||||||
|
dimensions: Map<string, NodeDimensions>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactZoneData = {
|
||||||
|
node: OutlineNode;
|
||||||
|
dimensions: NodeDimensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactZone = {
|
||||||
|
above: ImpactZoneData | null;
|
||||||
|
below: ImpactZoneData | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactData = {
|
||||||
|
zone: ImpactZone;
|
||||||
|
depth: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactPosition = 'before' | 'after' | 'beforeChildren' | 'afterChildren';
|
||||||
|
|
||||||
|
type ImpactAction = {
|
||||||
|
on: 'children' | 'entry';
|
||||||
|
position: ImpactPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ItemElement = {
|
||||||
|
id: string;
|
||||||
|
parent: null | string;
|
||||||
|
position: number;
|
||||||
|
collapsed: boolean;
|
||||||
|
children?: Array<ItemElement>;
|
||||||
|
};
|
||||||
|
type NodeDimensions = {
|
||||||
|
entry: React.RefObject<HTMLElement>;
|
||||||
|
children: React.RefObject<HTMLElement> | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OutlineNode = {
|
||||||
|
id: string;
|
||||||
|
parent: string;
|
||||||
|
depth: number;
|
||||||
|
position: number;
|
||||||
|
ancestors: Array<string>;
|
||||||
|
children: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RelationshipChild = {
|
||||||
|
position: number;
|
||||||
|
id: string;
|
||||||
|
depth: number;
|
||||||
|
children: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodeRelationships = {
|
||||||
|
self: { id: string; depth: number };
|
||||||
|
children: Array<RelationshipChild>;
|
||||||
|
numberOfSubChildren: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OutlineData = {
|
||||||
|
published: Map<string, string>;
|
||||||
|
nodes: Map<number, Map<string, OutlineNode>>;
|
||||||
|
relationships: Map<string, NodeRelationships>;
|
||||||
|
dimensions: Map<string, NodeDimensions>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactZoneData = {
|
||||||
|
node: OutlineNode;
|
||||||
|
dimensions: NodeDimensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactZone = {
|
||||||
|
above: ImpactZoneData | null;
|
||||||
|
below: ImpactZoneData | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactData = {
|
||||||
|
zone: ImpactZone;
|
||||||
|
depth: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImpactPosition = 'before' | 'after' | 'beforeChildren' | 'afterChildren';
|
||||||
|
|
||||||
|
type ImpactAction = {
|
||||||
|
on: 'children' | 'entry';
|
||||||
|
position: ImpactPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ItemElement = {
|
||||||
|
id: string;
|
||||||
|
parent: null | string;
|
||||||
|
position: number;
|
||||||
|
children?: Array<ItemElement>;
|
||||||
|
};
|
||||||
|
@ -3112,6 +3112,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||||
|
|
||||||
|
"@types/query-string@^6.3.0":
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-6.3.0.tgz#b6fa172a01405abcaedac681118e78429d62ea39"
|
||||||
|
integrity sha512-yuIv/WRffRzL7cBW+sla4HwBZrEXRNf1MKQ5SklPEadth+BKbDxiVG8A3iISN5B3yC4EeSCzMZP8llHTcUhOzQ==
|
||||||
|
dependencies:
|
||||||
|
query-string "*"
|
||||||
|
|
||||||
"@types/reach__router@^1.2.3":
|
"@types/reach__router@^1.2.3":
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.3.0.tgz#4c05a947ccecca05c72bb335a0f7bb43fec12446"
|
resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.3.0.tgz#4c05a947ccecca05c72bb335a0f7bb43fec12446"
|
||||||
@ -10600,6 +10607,11 @@ lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||||
|
|
||||||
|
lodash@^4.17.20:
|
||||||
|
version "4.17.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
||||||
|
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
||||||
|
|
||||||
log-symbols@3.0.0:
|
log-symbols@3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
|
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
|
||||||
@ -13351,6 +13363,15 @@ qs@~6.5.2:
|
|||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||||
|
|
||||||
|
query-string@*, query-string@^6.13.7:
|
||||||
|
version "6.13.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.7.tgz#af53802ff6ed56f3345f92d40a056f93681026ee"
|
||||||
|
integrity sha512-CsGs8ZYb39zu0WLkeOhe0NMePqgYdAuCqxOYKDR5LVCytDZYMGx3Bb+xypvQvPHVPijRXB0HZNFllCzHRe4gEA==
|
||||||
|
dependencies:
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
split-on-first "^1.0.0"
|
||||||
|
strict-uri-encode "^2.0.0"
|
||||||
|
|
||||||
query-string@^4.1.0:
|
query-string@^4.1.0:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
||||||
@ -15249,6 +15270,11 @@ speedometer@~1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.0.0.tgz#cd671cb06752c22bca3370e2f334440be4fc62e2"
|
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.0.0.tgz#cd671cb06752c22bca3370e2f334440be4fc62e2"
|
||||||
integrity sha1-zWccsGdSwivKM3Di8zREC+T8YuI=
|
integrity sha1-zWccsGdSwivKM3Di8zREC+T8YuI=
|
||||||
|
|
||||||
|
split-on-first@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
|
||||||
|
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
|
||||||
|
|
||||||
split-string@^3.0.1, split-string@^3.0.2:
|
split-string@^3.0.1, split-string@^3.0.2:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||||
@ -15402,6 +15428,11 @@ strict-uri-encode@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||||
|
|
||||||
|
strict-uri-encode@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||||
|
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||||
|
|
||||||
string-length@^2.0.0:
|
string-length@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||||
|
3
go.mod
3
go.mod
@ -11,7 +11,9 @@ require (
|
|||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/jmoiron/sqlx v1.2.0
|
github.com/jmoiron/sqlx v1.2.0
|
||||||
github.com/lib/pq v1.3.0
|
github.com/lib/pq v1.3.0
|
||||||
|
github.com/lithammer/fuzzysearch v1.1.0
|
||||||
github.com/magefile/mage v1.9.0
|
github.com/magefile/mage v1.9.0
|
||||||
|
github.com/matcornic/hermes/v2 v2.1.0
|
||||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0
|
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0
|
||||||
@ -21,4 +23,5 @@ require (
|
|||||||
github.com/spf13/viper v1.4.0
|
github.com/spf13/viper v1.4.0
|
||||||
github.com/vektah/gqlparser/v2 v2.0.1
|
github.com/vektah/gqlparser/v2 v2.0.1
|
||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
|
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
|
||||||
|
gopkg.in/mail.v2 v2.3.1
|
||||||
)
|
)
|
||||||
|
41
go.sum
41
go.sum
@ -50,11 +50,17 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
|||||||
github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs=
|
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs=
|
||||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||||
|
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||||
|
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
|
github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY=
|
||||||
|
github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg=
|
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg=
|
||||||
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
|
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
|
||||||
github.com/RichardKnop/machinery v1.9.1 h1:Q4WInk0OWGMbXDH3Q8dm8uadN5Wcyquc+7IcM4p9ECs=
|
github.com/RichardKnop/machinery v1.9.1 h1:Q4WInk0OWGMbXDH3Q8dm8uadN5Wcyquc+7IcM4p9ECs=
|
||||||
@ -70,6 +76,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
|||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
|
||||||
|
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
@ -109,6 +119,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk=
|
||||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@ -150,6 +161,7 @@ github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxm
|
|||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
@ -257,6 +269,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
|||||||
github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
@ -264,6 +277,8 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
@ -290,7 +305,11 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
||||||
|
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||||
|
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
@ -317,6 +336,8 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
|
|||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||||
@ -357,6 +378,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lithammer/fuzzysearch v1.1.0 h1:go9v8tLCrNTTlH42OAaq4eHFe81TDHEnlrMEb6R4f+A=
|
||||||
|
github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H5HrkJ5CsN56HQ=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
|
github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
|
||||||
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
@ -365,6 +388,9 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
|||||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/matcornic/hermes v1.2.0 h1:AuqZpYcTOtTB7cahdevLfnhIpfzmpqw5Czv8vpdnFDU=
|
||||||
|
github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc=
|
||||||
|
github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI=
|
||||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg=
|
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg=
|
||||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
@ -373,6 +399,8 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||||
@ -391,6 +419,8 @@ github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86w
|
|||||||
github.com/neo4j-drivers/gobolt v1.7.4/go.mod h1:O9AUbip4Dgre+CD3p40dnMD4a4r52QBIfblg5k7CTbE=
|
github.com/neo4j-drivers/gobolt v1.7.4/go.mod h1:O9AUbip4Dgre+CD3p40dnMD4a4r52QBIfblg5k7CTbE=
|
||||||
github.com/neo4j/neo4j-go-driver v1.7.4/go.mod h1:aPO0vVr+WnhEJne+FgFjfsjzAnssPFLucHgGZ76Zb/U=
|
github.com/neo4j/neo4j-go-driver v1.7.4/go.mod h1:aPO0vVr+WnhEJne+FgFjfsjzAnssPFLucHgGZ76Zb/U=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
||||||
@ -479,6 +509,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
|
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
|
||||||
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@ -502,6 +534,10 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
|
|||||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
||||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
|
github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04 h1:L0rPdfzq43+NV8rfIx2kA4iSSLRj2jN5ijYHoeXRwvQ=
|
||||||
|
github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=
|
||||||
|
github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe h1:9YnI5plmy+ad6BM+JCLJb2ZV7/TNiE5l7SNKfumYKgc=
|
||||||
|
github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4=
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||||
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
|
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
|
||||||
@ -540,6 +576,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
|||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
@ -591,6 +628,7 @@ golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -654,6 +692,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -862,6 +901,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||||
|
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
@ -62,10 +62,7 @@ func initConfig() {
|
|||||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the root cobra command
|
|
||||||
func Execute() {
|
|
||||||
viper.SetDefault("server.hostname", "0.0.0.0:3333")
|
viper.SetDefault("server.hostname", "0.0.0.0:3333")
|
||||||
viper.SetDefault("database.host", "127.0.0.1")
|
viper.SetDefault("database.host", "127.0.0.1")
|
||||||
viper.SetDefault("database.name", "taskcafe")
|
viper.SetDefault("database.name", "taskcafe")
|
||||||
@ -75,6 +72,20 @@ func Execute() {
|
|||||||
viper.SetDefault("queue.broker", "amqp://guest:guest@localhost:5672/")
|
viper.SetDefault("queue.broker", "amqp://guest:guest@localhost:5672/")
|
||||||
viper.SetDefault("queue.store", "memcache://localhost:11211")
|
viper.SetDefault("queue.store", "memcache://localhost:11211")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the root cobra command
|
||||||
|
func Execute() {
|
||||||
|
viper.SetDefault("server.hostname", "0.0.0.0:3333")
|
||||||
|
viper.SetDefault("database.host", "127.0.0.1")
|
||||||
|
viper.SetDefault("database.name", "taskcafe")
|
||||||
|
viper.SetDefault("database.user", "taskcafe")
|
||||||
|
viper.SetDefault("database.password", "taskcafe_test")
|
||||||
|
viper.SetDefault("database.port", "5432")
|
||||||
|
|
||||||
|
viper.SetDefault("queue.broker", "amqp://guest:guest@localhost:5672/")
|
||||||
|
viper.SetDefault("queue.store", "memcache://localhost:11211")
|
||||||
|
|
||||||
rootCmd.SetVersionTemplate(versionTemplate)
|
rootCmd.SetVersionTemplate(versionTemplate)
|
||||||
rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd(), newWorkerCmd(), newResetPasswordCmd())
|
rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd(), newWorkerCmd(), newResetPasswordCmd())
|
||||||
rootCmd.Execute()
|
rootCmd.Execute()
|
||||||
|
@ -34,11 +34,12 @@ func newMigrateCmd() *cobra.Command {
|
|||||||
Short: "Run the database schema migrations",
|
Short: "Run the database schema migrations",
|
||||||
Long: "Run the database schema migrations",
|
Long: "Run the database schema migrations",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=disable",
|
connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s port=%s sslmode=disable",
|
||||||
viper.GetString("database.user"),
|
viper.GetString("database.user"),
|
||||||
viper.GetString("database.password"),
|
viper.GetString("database.password"),
|
||||||
viper.GetString("database.host"),
|
viper.GetString("database.host"),
|
||||||
viper.GetString("database.name"),
|
viper.GetString("database.name"),
|
||||||
|
viper.GetString("database.port"),
|
||||||
)
|
)
|
||||||
db, err := sqlx.Connect("postgres", connection)
|
db, err := sqlx.Connect("postgres", connection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -32,11 +32,12 @@ func newWebCmd() *cobra.Command {
|
|||||||
log.SetFormatter(Formatter)
|
log.SetFormatter(Formatter)
|
||||||
log.SetLevel(log.InfoLevel)
|
log.SetLevel(log.InfoLevel)
|
||||||
|
|
||||||
connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=disable",
|
connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s port=%s sslmode=disable",
|
||||||
viper.GetString("database.user"),
|
viper.GetString("database.user"),
|
||||||
viper.GetString("database.password"),
|
viper.GetString("database.password"),
|
||||||
viper.GetString("database.host"),
|
viper.GetString("database.host"),
|
||||||
viper.GetString("database.name"),
|
viper.GetString("database.name"),
|
||||||
|
viper.GetString("database.port"),
|
||||||
)
|
)
|
||||||
var db *sqlx.DB
|
var db *sqlx.DB
|
||||||
var err error
|
var err error
|
||||||
@ -78,8 +79,11 @@ func newWebCmd() *cobra.Command {
|
|||||||
return http.ListenAndServe(viper.GetString("server.hostname"), r)
|
return http.ListenAndServe(viper.GetString("server.hostname"), r)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.Flags().Bool("migrate", false, "if true, auto run's schema migrations before starting the web server")
|
cc.Flags().Bool("migrate", false, "if true, auto run's schema migrations before starting the web server")
|
||||||
|
|
||||||
viper.BindPFlag("migrate", cc.Flags().Lookup("migrate"))
|
viper.BindPFlag("migrate", cc.Flags().Lookup("migrate"))
|
||||||
|
|
||||||
viper.SetDefault("migrate", false)
|
viper.SetDefault("migrate", false)
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,12 @@ type ProjectMember struct {
|
|||||||
RoleCode string `json:"role_code"`
|
RoleCode string `json:"role_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProjectMemberInvited struct {
|
||||||
|
ProjectMemberInvitedID uuid.UUID `json:"project_member_invited_id"`
|
||||||
|
ProjectID uuid.UUID `json:"project_id"`
|
||||||
|
UserAccountInvitedID uuid.UUID `json:"user_account_invited_id"`
|
||||||
|
}
|
||||||
|
|
||||||
type RefreshToken struct {
|
type RefreshToken struct {
|
||||||
TokenID uuid.UUID `json:"token_id"`
|
TokenID uuid.UUID `json:"token_id"`
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
@ -164,4 +170,17 @@ type UserAccount struct {
|
|||||||
ProfileAvatarUrl sql.NullString `json:"profile_avatar_url"`
|
ProfileAvatarUrl sql.NullString `json:"profile_avatar_url"`
|
||||||
RoleCode string `json:"role_code"`
|
RoleCode string `json:"role_code"`
|
||||||
Bio string `json:"bio"`
|
Bio string `json:"bio"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAccountConfirmToken struct {
|
||||||
|
ConfirmTokenID uuid.UUID `json:"confirm_token_id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAccountInvited struct {
|
||||||
|
UserAccountInvitedID uuid.UUID `json:"user_account_invited_id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
InvitedOn time.Time `json:"invited_on"`
|
||||||
|
HasJoined bool `json:"has_joined"`
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,15 @@ func (q *Queries) CreateTeamProject(ctx context.Context, arg CreateTeamProjectPa
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteInvitedProjectMemberByID = `-- name: DeleteInvitedProjectMemberByID :exec
|
||||||
|
DELETE FROM project_member_invited WHERE project_member_invited_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteInvitedProjectMemberByID(ctx context.Context, projectMemberInvitedID uuid.UUID) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteInvitedProjectMemberByID, projectMemberInvitedID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const deleteProjectByID = `-- name: DeleteProjectByID :exec
|
const deleteProjectByID = `-- name: DeleteProjectByID :exec
|
||||||
DELETE FROM project WHERE project_id = $1
|
DELETE FROM project WHERE project_id = $1
|
||||||
`
|
`
|
||||||
@ -122,12 +131,12 @@ func (q *Queries) DeleteProjectMember(ctx context.Context, arg DeleteProjectMemb
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllProjects = `-- name: GetAllProjects :many
|
const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many
|
||||||
SELECT project_id, team_id, created_at, name FROM project
|
SELECT project_id, team_id, created_at, name FROM project WHERE team_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
|
func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) {
|
||||||
rows, err := q.db.QueryContext(ctx, getAllProjects)
|
rows, err := q.db.QueryContext(ctx, getAllProjectsForTeam, teamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -154,12 +163,12 @@ func (q *Queries) GetAllProjects(ctx context.Context) ([]Project, error) {
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many
|
const getAllTeamProjects = `-- name: GetAllTeamProjects :many
|
||||||
SELECT project_id, team_id, created_at, name FROM project WHERE team_id = $1
|
SELECT project_id, team_id, created_at, name FROM project WHERE team_id IS NOT null
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) {
|
func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) {
|
||||||
rows, err := q.db.QueryContext(ctx, getAllProjectsForTeam, teamID)
|
rows, err := q.db.QueryContext(ctx, getAllTeamProjects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -219,6 +228,42 @@ func (q *Queries) GetAllVisibleProjectsForUserID(ctx context.Context, userID uui
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getInvitedMembersForProjectID = `-- name: GetInvitedMembersForProjectID :many
|
||||||
|
SELECT uai.user_account_invited_id, email, invited_on FROM project_member_invited AS pmi
|
||||||
|
INNER JOIN user_account_invited AS uai
|
||||||
|
ON uai.user_account_invited_id = pmi.user_account_invited_id
|
||||||
|
WHERE project_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetInvitedMembersForProjectIDRow struct {
|
||||||
|
UserAccountInvitedID uuid.UUID `json:"user_account_invited_id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
InvitedOn time.Time `json:"invited_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetInvitedMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]GetInvitedMembersForProjectIDRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getInvitedMembersForProjectID, projectID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetInvitedMembersForProjectIDRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetInvitedMembersForProjectIDRow
|
||||||
|
if err := rows.Scan(&i.UserAccountInvitedID, &i.Email, &i.InvitedOn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getMemberProjectIDsForUserID = `-- name: GetMemberProjectIDsForUserID :many
|
const getMemberProjectIDsForUserID = `-- name: GetMemberProjectIDsForUserID :many
|
||||||
SELECT project_id FROM project_member WHERE user_id = $1
|
SELECT project_id FROM project_member WHERE user_id = $1
|
||||||
`
|
`
|
||||||
@ -296,6 +341,26 @@ func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Proj
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getProjectMemberInvitedIDByEmail = `-- name: GetProjectMemberInvitedIDByEmail :one
|
||||||
|
SELECT email, invited_on, project_member_invited_id FROM user_account_invited AS uai
|
||||||
|
inner join project_member_invited AS pmi
|
||||||
|
ON pmi.user_account_invited_id = uai.user_account_invited_id
|
||||||
|
WHERE email = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetProjectMemberInvitedIDByEmailRow struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
InvitedOn time.Time `json:"invited_on"`
|
||||||
|
ProjectMemberInvitedID uuid.UUID `json:"project_member_invited_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetProjectMemberInvitedIDByEmail(ctx context.Context, email string) (GetProjectMemberInvitedIDByEmailRow, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getProjectMemberInvitedIDByEmail, email)
|
||||||
|
var i GetProjectMemberInvitedIDByEmailRow
|
||||||
|
err := row.Scan(&i.Email, &i.InvitedOn, &i.ProjectMemberInvitedID)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getProjectMembersForProjectID = `-- name: GetProjectMembersForProjectID :many
|
const getProjectMembersForProjectID = `-- name: GetProjectMembersForProjectID :many
|
||||||
SELECT project_member_id, project_id, user_id, added_at, role_code FROM project_member WHERE project_id = $1
|
SELECT project_member_id, project_id, user_id, added_at, role_code FROM project_member WHERE project_id = $1
|
||||||
`
|
`
|
||||||
|
@ -9,6 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Querier interface {
|
type Querier interface {
|
||||||
|
CreateConfirmToken(ctx context.Context, email string) (UserAccountConfirmToken, error)
|
||||||
|
CreateInvitedProjectMember(ctx context.Context, arg CreateInvitedProjectMemberParams) (ProjectMemberInvited, error)
|
||||||
|
CreateInvitedUser(ctx context.Context, email string) (UserAccountInvited, error)
|
||||||
CreateLabelColor(ctx context.Context, arg CreateLabelColorParams) (LabelColor, error)
|
CreateLabelColor(ctx context.Context, arg CreateLabelColorParams) (LabelColor, error)
|
||||||
CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error)
|
CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error)
|
||||||
CreateNotificationObject(ctx context.Context, arg CreateNotificationObjectParams) (NotificationObject, error)
|
CreateNotificationObject(ctx context.Context, arg CreateNotificationObjectParams) (NotificationObject, error)
|
||||||
@ -30,10 +33,14 @@ type Querier interface {
|
|||||||
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
||||||
CreateTeamProject(ctx context.Context, arg CreateTeamProjectParams) (Project, error)
|
CreateTeamProject(ctx context.Context, arg CreateTeamProjectParams) (Project, error)
|
||||||
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
||||||
|
DeleteConfirmTokenForEmail(ctx context.Context, email string) error
|
||||||
DeleteExpiredTokens(ctx context.Context) error
|
DeleteExpiredTokens(ctx context.Context) error
|
||||||
|
DeleteInvitedProjectMemberByID(ctx context.Context, projectMemberInvitedID uuid.UUID) error
|
||||||
|
DeleteInvitedUserAccount(ctx context.Context, userAccountInvitedID uuid.UUID) (UserAccountInvited, error)
|
||||||
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
||||||
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error
|
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error
|
||||||
DeleteProjectMember(ctx context.Context, arg DeleteProjectMemberParams) error
|
DeleteProjectMember(ctx context.Context, arg DeleteProjectMemberParams) error
|
||||||
|
DeleteProjectMemberInvitedForEmail(ctx context.Context, email string) error
|
||||||
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
||||||
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
|
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
|
||||||
DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error)
|
DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error)
|
||||||
@ -47,20 +54,27 @@ type Querier interface {
|
|||||||
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
||||||
DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error
|
DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error
|
||||||
DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error
|
DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error
|
||||||
|
DeleteUserAccountInvitedForEmail(ctx context.Context, email string) error
|
||||||
GetAllNotificationsForUserID(ctx context.Context, notifierID uuid.UUID) ([]Notification, error)
|
GetAllNotificationsForUserID(ctx context.Context, notifierID uuid.UUID) ([]Notification, error)
|
||||||
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
||||||
GetAllProjects(ctx context.Context) ([]Project, error)
|
|
||||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||||
GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error)
|
GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error)
|
||||||
GetAllTasks(ctx context.Context) ([]Task, error)
|
GetAllTasks(ctx context.Context) ([]Task, error)
|
||||||
|
GetAllTeamProjects(ctx context.Context) ([]Project, error)
|
||||||
GetAllTeams(ctx context.Context) ([]Team, error)
|
GetAllTeams(ctx context.Context) ([]Team, error)
|
||||||
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||||
GetAllVisibleProjectsForUserID(ctx context.Context, userID uuid.UUID) ([]Project, error)
|
GetAllVisibleProjectsForUserID(ctx context.Context, userID uuid.UUID) ([]Project, error)
|
||||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||||
|
GetConfirmTokenByEmail(ctx context.Context, email string) (UserAccountConfirmToken, error)
|
||||||
|
GetConfirmTokenByID(ctx context.Context, confirmTokenID uuid.UUID) (UserAccountConfirmToken, error)
|
||||||
GetEntityForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetEntityForNotificationIDRow, error)
|
GetEntityForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetEntityForNotificationIDRow, error)
|
||||||
GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error)
|
GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error)
|
||||||
|
GetInvitedMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]GetInvitedMembersForProjectIDRow, error)
|
||||||
|
GetInvitedUserAccounts(ctx context.Context) ([]UserAccountInvited, error)
|
||||||
|
GetInvitedUserByEmail(ctx context.Context, email string) (UserAccountInvited, error)
|
||||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||||
GetLabelColors(ctx context.Context) ([]LabelColor, error)
|
GetLabelColors(ctx context.Context) ([]LabelColor, error)
|
||||||
|
GetMemberData(ctx context.Context, projectID uuid.UUID) ([]UserAccount, error)
|
||||||
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
||||||
GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
||||||
GetNotificationForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetNotificationForNotificationIDRow, error)
|
GetNotificationForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetNotificationForNotificationIDRow, error)
|
||||||
@ -72,8 +86,10 @@ type Querier interface {
|
|||||||
GetProjectIDForTaskGroup(ctx context.Context, taskGroupID uuid.UUID) (uuid.UUID, error)
|
GetProjectIDForTaskGroup(ctx context.Context, taskGroupID uuid.UUID) (uuid.UUID, error)
|
||||||
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
||||||
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
||||||
|
GetProjectMemberInvitedIDByEmail(ctx context.Context, email string) (GetProjectMemberInvitedIDByEmailRow, error)
|
||||||
GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error)
|
GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error)
|
||||||
GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetProjectRolesForUserIDRow, error)
|
GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetProjectRolesForUserIDRow, error)
|
||||||
|
GetProjectsForInvitedMember(ctx context.Context, email string) ([]uuid.UUID, error)
|
||||||
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
||||||
GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error)
|
GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error)
|
||||||
GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error)
|
GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error)
|
||||||
@ -97,12 +113,17 @@ type Querier interface {
|
|||||||
GetTeamRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetTeamRolesForUserIDRow, error)
|
GetTeamRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetTeamRolesForUserIDRow, error)
|
||||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||||
GetTeamsForUserIDWhereAdmin(ctx context.Context, userID uuid.UUID) ([]Team, error)
|
GetTeamsForUserIDWhereAdmin(ctx context.Context, userID uuid.UUID) ([]Team, error)
|
||||||
|
GetUserAccountByEmail(ctx context.Context, email string) (UserAccount, error)
|
||||||
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
||||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||||
GetUserRolesForProject(ctx context.Context, arg GetUserRolesForProjectParams) (GetUserRolesForProjectRow, error)
|
GetUserRolesForProject(ctx context.Context, arg GetUserRolesForProjectParams) (GetUserRolesForProjectRow, error)
|
||||||
|
HasActiveUser(ctx context.Context) (bool, error)
|
||||||
|
HasAnyUser(ctx context.Context) (bool, error)
|
||||||
|
SetFirstUserActive(ctx context.Context) (UserAccount, error)
|
||||||
SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error)
|
SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error)
|
||||||
SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error)
|
SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error)
|
||||||
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)
|
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)
|
||||||
|
SetUserActiveByEmail(ctx context.Context, email string) (UserAccount, error)
|
||||||
SetUserPassword(ctx context.Context, arg SetUserPasswordParams) (UserAccount, error)
|
SetUserPassword(ctx context.Context, arg SetUserPasswordParams) (UserAccount, error)
|
||||||
UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error)
|
UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error)
|
||||||
UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error)
|
UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user