Various fixes on table, board, tasks (#983)

* Misc fixes

* Misc fixes

* Misc fixes

* Fix login
This commit is contained in:
Charles Bochet
2023-07-28 15:20:32 -07:00
committed by GitHub
parent 0bc80ce9ee
commit 557e56492a
17 changed files with 53 additions and 177 deletions

View File

@ -35,6 +35,7 @@ const StyledContainer = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)};
width: 100%;
`; `;
const StyledCheckboxContainer = styled.div` const StyledCheckboxContainer = styled.div`

View File

@ -6,8 +6,8 @@ import {
ChipSize, ChipSize,
ChipVariant, ChipVariant,
} from '@/ui/chip/components/Chip'; } from '@/ui/chip/components/Chip';
import { IconPhone } from '@/ui/icon'; import { IconCheckbox, IconNotes } from '@/ui/icon';
import { Activity } from '~/generated/graphql'; import { Activity, ActivityType } from '~/generated/graphql';
type OwnProps = { type OwnProps = {
activity: Pick<Activity, 'type'>; activity: Pick<Activity, 'type'>;
@ -18,7 +18,13 @@ export function ActivityTypeDropdown({ activity }: OwnProps) {
return ( return (
<Chip <Chip
label={activity.type} label={activity.type}
leftComponent={<IconPhone size={theme.icon.size.md} />} leftComponent={
activity.type === ActivityType.Note ? (
<IconNotes size={theme.icon.size.md} />
) : (
<IconCheckbox size={theme.icon.size.md} />
)
}
size={ChipSize.Large} size={ChipSize.Large}
accent={ChipAccent.TextSecondary} accent={ChipAccent.TextSecondary}
variant={ChipVariant.Highlighted} variant={ChipVariant.Highlighted}

View File

@ -1,23 +1,8 @@
import React, { useEffect, useMemo, useState } from 'react'; import React from 'react';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ActivityBodyEditor } from '@/activities/components/ActivityBodyEditor'; import { ActivityEditor } from '@/activities/components/ActivityEditor';
import { ActivityComments } from '@/activities/components/ActivityComments'; import { useGetActivityQuery } from '~/generated/graphql';
import { ActivityRelationPicker } from '@/activities/components/ActivityRelationPicker';
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
import { GET_ACTIVITY } from '@/activities/queries';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconArrowUpRight } from '@/ui/icon/index';
import {
useGetActivityQuery,
useUpdateActivityMutation,
} from '~/generated/graphql';
import { debounce } from '~/utils/debounce';
import { ActivityActionBar } from './ActivityActionBar';
import '@blocknote/core/style.css'; import '@blocknote/core/style.css';
@ -30,59 +15,6 @@ const StyledContainer = styled.div`
overflow-y: auto; overflow-y: auto;
`; `;
const StyledUpperPartContainer = styled.div`
align-items: flex-start;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
justify-content: flex-start;
`;
const StyledTopContainer = styled.div`
align-items: flex-start;
align-self: stretch;
background: ${({ theme }) => theme.background.secondary};
border-bottom: ${({ theme }) =>
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px 24px 24px 48px;
`;
const StyledEditableTitleInput = styled.input`
background: transparent;
border: none;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex: 1 0 0;
flex-direction: column;
font-family: Inter;
font-size: ${({ theme }) => theme.font.size.xl};
font-style: normal;
font-weight: ${({ theme }) => theme.font.weight.semiBold};
justify-content: center;
line-height: ${({ theme }) => theme.text.lineHeight.md};
outline: none;
width: calc(100% - ${({ theme }) => theme.spacing(2)});
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
}
`;
const StyledTopActionsContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
`;
type OwnProps = { type OwnProps = {
activityId: string; activityId: string;
showComment?: boolean; showComment?: boolean;
@ -101,50 +33,6 @@ export function Activity({
skip: !activityId, skip: !activityId,
}); });
const activity = data?.findManyActivities[0]; const activity = data?.findManyActivities[0];
const [hasUserManuallySetTitle, setHasUserManuallySetTitle] =
useState<boolean>(false);
const [title, setTitle] = useState<string | null>(null);
useEffect(() => {
if (!hasUserManuallySetTitle) {
setTitle(activity?.title ?? '');
}
}, [setTitle, activity?.title, hasUserManuallySetTitle]);
const [updateActivityMutation] = useUpdateActivityMutation();
const debounceUpdateTitle = useMemo(() => {
function updateTitle(title: string) {
if (activity) {
updateActivityMutation({
variables: {
id: activityId,
title: title ?? '',
},
refetchQueries: [getOperationName(GET_ACTIVITY) ?? ''],
optimisticResponse: {
__typename: 'Mutation',
updateOneActivity: {
__typename: 'Activity',
id: activityId,
title: title,
type: activity.type,
},
},
});
}
}
return debounce(updateTitle, 200);
}, [activityId, updateActivityMutation, activity]);
function updateTitleFromBody(body: string) {
const parsedTitle = JSON.parse(body)[0]?.content[0]?.text;
if (!hasUserManuallySetTitle && autoFillTitle) {
setTitle(parsedTitle);
debounceUpdateTitle(parsedTitle);
}
}
if (!activity) { if (!activity) {
return <></>; return <></>;
@ -152,50 +40,11 @@ export function Activity({
return ( return (
<StyledContainer> <StyledContainer>
<StyledUpperPartContainer> <ActivityEditor
<StyledTopContainer> activity={activity}
<StyledTopActionsContainer> showComment={showComment}
<ActivityTypeDropdown activity={activity} /> autoFillTitle={autoFillTitle}
<ActivityActionBar activityId={activity?.id ?? ''} /> />
</StyledTopActionsContainer>
<StyledEditableTitleInput
autoFocus
placeholder={`${activity.type} title (optional)`}
onChange={(event) => {
setHasUserManuallySetTitle(true);
setTitle(event.target.value);
debounceUpdateTitle(event.target.value);
}}
value={title ?? ''}
/>
<PropertyBox>
<PropertyBoxItem
icon={<IconArrowUpRight />}
value={
<ActivityRelationPicker
activity={{
id: activity.id,
activityTargets: activity.activityTargets ?? [],
}}
/>
}
label="Relations"
/>
</PropertyBox>
</StyledTopContainer>
<ActivityBodyEditor
activity={activity}
onChange={updateTitleFromBody}
/>
</StyledUpperPartContainer>
{showComment && (
<ActivityComments
activity={{
id: activity.id,
comments: activity.comments ?? [],
}}
/>
)}
</StyledContainer> </StyledContainer>
); );
} }

View File

@ -147,7 +147,7 @@ export function TimelineActivity({ activity }: OwnProps) {
</StyledIconContainer> </StyledIconContainer>
<StyledItemTitleContainer> <StyledItemTitleContainer>
<span>{activity.author.displayName}</span> <span>{activity.author.displayName}</span>
created a note created a {activity.type.toLowerCase()} created a {activity.type.toLowerCase()}
</StyledItemTitleContainer> </StyledItemTitleContainer>
<StyledItemTitleDate id={`id-${activity.id}`}> <StyledItemTitleDate id={`id-${activity.id}`}>
{beautifiedCreatedAt} ago {beautifiedCreatedAt} ago

View File

@ -12,7 +12,7 @@ const StyledTitleContainer = styled.div`
gap: ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)};
line-height: ${({ theme }) => theme.text.lineHeight.lg}; line-height: ${({ theme }) => theme.text.lineHeight.lg};
width: 100%; width: calc(100% - ${({ theme }) => theme.spacing(6)});
`; `;
const StyledTitleText = styled.div<{ completed?: boolean }>` const StyledTitleText = styled.div<{ completed?: boolean }>`
@ -24,7 +24,6 @@ const StyledCheckboxContainer = styled.div`
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%;
`; `;
type OwnProps = { type OwnProps = {

View File

@ -77,7 +77,7 @@ export function useAuth() {
const { loginToken } = await handleChallenge(email, password); const { loginToken } = await handleChallenge(email, password);
const { user } = await handleVerify(loginToken.token); const { user } = await handleVerify(loginToken.token);
return { user }; return user;
}, },
[handleChallenge, handleVerify], [handleChallenge, handleVerify],
); );
@ -112,7 +112,7 @@ export function useAuth() {
signUpResult.data?.signUp.loginToken.token, signUpResult.data?.signUp.loginToken.token,
); );
return { user }; return user;
}, },
[signUp, handleVerify], [signUp, handleVerify],
); );

View File

@ -105,16 +105,21 @@ export function useSignInUp() {
if (!data.email || !data.password) { if (!data.email || !data.password) {
throw new Error('Email and password are required'); throw new Error('Email and password are required');
} }
let user;
if (signInUpMode === SignInUpMode.SignIn) { if (signInUpMode === SignInUpMode.SignIn) {
await signInWithCredentials(data.email, data.password); user = await signInWithCredentials(data.email, data.password);
} else { } else {
await signUpWithCredentials( user = await signUpWithCredentials(
data.email, data.email,
data.password, data.password,
workspaceInviteHash, workspaceInviteHash,
); );
} }
navigate('/create/workspace'); if (user?.workspaceMember?.workspace?.displayName) {
navigate('/');
} else {
navigate('/create/workspace');
}
} catch (err: any) { } catch (err: any) {
enqueueSnackBar(err?.message, { enqueueSnackBar(err?.message, {
variant: 'error', variant: 'error',

View File

@ -1,9 +1,9 @@
import { PersonChip } from '@/people/components/PersonChip';
import { EditableField } from '@/ui/editable-field/components/EditableField'; import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext'; import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { IconUserCircle } from '@/ui/icon'; import { IconUserCircle } from '@/ui/icon';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
import { UserChip } from '@/users/components/UserChip';
import { Company, User } from '~/generated/graphql'; import { Company, User } from '~/generated/graphql';
import { CompanyAccountOwnerPickerFieldEditMode } from './CompanyAccountOwnerPickerFieldEditMode'; import { CompanyAccountOwnerPickerFieldEditMode } from './CompanyAccountOwnerPickerFieldEditMode';
@ -28,7 +28,7 @@ export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
} }
displayModeContent={ displayModeContent={
company.accountOwner?.displayName ? ( company.accountOwner?.displayName ? (
<PersonChip <UserChip
id={company.accountOwner.id} id={company.accountOwner.id}
name={company.accountOwner?.displayName ?? ''} name={company.accountOwner?.displayName ?? ''}
pictureUrl={company.accountOwner?.avatarUrl ?? ''} pictureUrl={company.accountOwner?.avatarUrl ?? ''}

View File

@ -23,14 +23,14 @@ export function EditableCompanyAddressCell() {
return ( return (
<EditableCellText <EditableCellText
value={internalValue} value={internalValue}
onSubmit={() => onSubmit={(newValue: string) =>
updateCompany({ updateCompany({
variables: { variables: {
where: { where: {
id: currentRowEntityId, id: currentRowEntityId,
}, },
data: { data: {
address: internalValue, address: newValue,
}, },
}, },
}) })

View File

@ -2,6 +2,7 @@ import { getOperationName } from '@apollo/client/utilities';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { GET_COMPANIES } from '@/companies/queries'; import { GET_COMPANIES } from '@/companies/queries';
import { GET_PIPELINES } from '@/pipeline/queries';
import { IconTrash } from '@/ui/icon/index'; import { IconTrash } from '@/ui/icon/index';
import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton'; import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton';
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection'; import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
@ -14,7 +15,10 @@ export function TableActionBarButtonDeleteCompanies() {
const resetRowSelection = useResetTableRowSelection(); const resetRowSelection = useResetTableRowSelection();
const [deleteCompanies] = useDeleteManyCompaniesMutation({ const [deleteCompanies] = useDeleteManyCompaniesMutation({
refetchQueries: [getOperationName(GET_COMPANIES) ?? ''], refetchQueries: [
getOperationName(GET_COMPANIES) ?? '',
getOperationName(GET_PIPELINES) ?? '',
],
}); });
async function handleDeleteClick() { async function handleDeleteClick() {

View File

@ -21,6 +21,7 @@ export function PeopleCompanyEditableField({ people }: OwnProps) {
<RecoilScope SpecificContext={FieldContext}> <RecoilScope SpecificContext={FieldContext}>
<RecoilScope> <RecoilScope>
<EditableField <EditableField
useEditButton
customEditHotkeyScope={{ customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker, scope: RelationPickerHotkeyScope.RelationPicker,
}} }}

View File

@ -125,6 +125,10 @@ export function EntityBoardColumn({
{(draggableProvided) => ( {(draggableProvided) => (
<div <div
ref={draggableProvided?.innerRef} ref={draggableProvided?.innerRef}
{...{
...draggableProvided.dragHandleProps,
draggable: false,
}}
{...draggableProvided?.draggableProps} {...draggableProvided?.draggableProps}
> >
<StyledNewCardButtonContainer> <StyledNewCardButtonContainer>

View File

@ -82,6 +82,7 @@ export function PipelineProgressPointOfContactPicker({
return ( return (
<SingleEntitySelect <SingleEntitySelect
onCreate={handleCreate} onCreate={handleCreate}
onCancel={onCancel}
onEntitySelected={handleEntitySelected} onEntitySelected={handleEntitySelected}
entities={{ entities={{
entitiesToSelect: people.entitiesToSelect, entitiesToSelect: people.entitiesToSelect,

View File

@ -21,6 +21,7 @@ export function PipelineProgressPointOfContactEditableField({
<RecoilScope SpecificContext={FieldContext}> <RecoilScope SpecificContext={FieldContext}>
<RecoilScope> <RecoilScope>
<EditableField <EditableField
useEditButton
customEditHotkeyScope={{ customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker, scope: RelationPickerHotkeyScope.RelationPicker,
}} }}

View File

@ -36,9 +36,13 @@ const StyledCenteredButton = styled(Button)`
export const StyledDeleteButton = styled(StyledCenteredButton)` export const StyledDeleteButton = styled(StyledCenteredButton)`
border-color: ${({ theme }) => theme.color.red20}; border-color: ${({ theme }) => theme.color.red20};
box-shadow: none;
color: ${({ theme }) => theme.color.red}; color: ${({ theme }) => theme.color.red};
font-size: ${({ theme }) => theme.font.size.md}; font-size: ${({ theme }) => theme.font.size.md};
line-height: ${({ theme }) => theme.text.lineHeight.lg}; line-height: ${({ theme }) => theme.text.lineHeight.lg};
:hover {
background-color: ${({ theme }) => theme.color.red10};
}
`; `;
export function DeleteModal({ export function DeleteModal({

View File

@ -31,9 +31,9 @@ export function EntityChip({
const navigate = useNavigate(); const navigate = useNavigate();
function handleLinkClick(event: React.MouseEvent<HTMLDivElement>) { function handleLinkClick(event: React.MouseEvent<HTMLDivElement>) {
event.preventDefault();
event.stopPropagation();
if (linkToEntity) { if (linkToEntity) {
event.preventDefault();
event.stopPropagation();
navigate(linkToEntity); navigate(linkToEntity);
} }
} }

View File

@ -153,6 +153,7 @@ export class WorkspaceService {
refreshToken.deleteMany({ refreshToken.deleteMany({
where: { userId }, where: { userId },
}), }),
// Todo delete all users from this workspace
user.delete({ user.delete({
where: { where: {
id: userId, id: userId,