Upload Workspace logo during onboarding (#542)

* Upload image

* Upload image

* Fix tests

* Remove pictures from seeds

* Fix storybook

* Fix storybook

* Fix storybook
This commit is contained in:
Charles Bochet
2023-07-08 16:46:04 -07:00
committed by GitHub
parent e03d5ed8a7
commit be7731b71a
22 changed files with 226 additions and 41 deletions

View File

@ -1044,6 +1044,7 @@ export type Mutation = {
uploadFile: Scalars['String'];
uploadImage: Scalars['String'];
uploadProfilePicture: Scalars['String'];
uploadWorkspaceLogo: Scalars['String'];
verify: Verify;
};
@ -1162,6 +1163,11 @@ export type MutationUploadProfilePictureArgs = {
};
export type MutationUploadWorkspaceLogoArgs = {
file: Scalars['Upload'];
};
export type MutationVerifyArgs = {
loginToken: Scalars['String'];
};
@ -3099,6 +3105,18 @@ export type UpdateWorkspaceMutationVariables = Exact<{
export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null } };
export type UploadWorkspaceLogoMutationVariables = Exact<{
file: Scalars['Upload'];
}>;
export type UploadWorkspaceLogoMutation = { __typename?: 'Mutation', uploadWorkspaceLogo: string };
export type RemoveWorkspaceLogoMutationVariables = Exact<{ [key: string]: never; }>;
export type RemoveWorkspaceLogoMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: string } };
export const CreateEventDocument = gql`
mutation CreateEvent($type: String!, $data: JSON!) {
@ -4686,4 +4704,67 @@ export function useUpdateWorkspaceMutation(baseOptions?: Apollo.MutationHookOpti
}
export type UpdateWorkspaceMutationHookResult = ReturnType<typeof useUpdateWorkspaceMutation>;
export type UpdateWorkspaceMutationResult = Apollo.MutationResult<UpdateWorkspaceMutation>;
export type UpdateWorkspaceMutationOptions = Apollo.BaseMutationOptions<UpdateWorkspaceMutation, UpdateWorkspaceMutationVariables>;
export type UpdateWorkspaceMutationOptions = Apollo.BaseMutationOptions<UpdateWorkspaceMutation, UpdateWorkspaceMutationVariables>;
export const UploadWorkspaceLogoDocument = gql`
mutation UploadWorkspaceLogo($file: Upload!) {
uploadWorkspaceLogo(file: $file)
}
`;
export type UploadWorkspaceLogoMutationFn = Apollo.MutationFunction<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>;
/**
* __useUploadWorkspaceLogoMutation__
*
* To run a mutation, you first call `useUploadWorkspaceLogoMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useUploadWorkspaceLogoMutation` 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 [uploadWorkspaceLogoMutation, { data, loading, error }] = useUploadWorkspaceLogoMutation({
* variables: {
* file: // value for 'file'
* },
* });
*/
export function useUploadWorkspaceLogoMutation(baseOptions?: Apollo.MutationHookOptions<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>(UploadWorkspaceLogoDocument, options);
}
export type UploadWorkspaceLogoMutationHookResult = ReturnType<typeof useUploadWorkspaceLogoMutation>;
export type UploadWorkspaceLogoMutationResult = Apollo.MutationResult<UploadWorkspaceLogoMutation>;
export type UploadWorkspaceLogoMutationOptions = Apollo.BaseMutationOptions<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>;
export const RemoveWorkspaceLogoDocument = gql`
mutation RemoveWorkspaceLogo {
updateWorkspace(data: {logo: {set: null}}) {
id
}
}
`;
export type RemoveWorkspaceLogoMutationFn = Apollo.MutationFunction<RemoveWorkspaceLogoMutation, RemoveWorkspaceLogoMutationVariables>;
/**
* __useRemoveWorkspaceLogoMutation__
*
* To run a mutation, you first call `useRemoveWorkspaceLogoMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useRemoveWorkspaceLogoMutation` 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 [removeWorkspaceLogoMutation, { data, loading, error }] = useRemoveWorkspaceLogoMutation({
* variables: {
* },
* });
*/
export function useRemoveWorkspaceLogoMutation(baseOptions?: Apollo.MutationHookOptions<RemoveWorkspaceLogoMutation, RemoveWorkspaceLogoMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<RemoveWorkspaceLogoMutation, RemoveWorkspaceLogoMutationVariables>(RemoveWorkspaceLogoDocument, options);
}
export type RemoveWorkspaceLogoMutationHookResult = ReturnType<typeof useRemoveWorkspaceLogoMutation>;
export type RemoveWorkspaceLogoMutationResult = Apollo.MutationResult<RemoveWorkspaceLogoMutation>;
export type RemoveWorkspaceLogoMutationOptions = Apollo.BaseMutationOptions<RemoveWorkspaceLogoMutation, RemoveWorkspaceLogoMutationVariables>;

View File

@ -10,7 +10,7 @@ import {
useUploadProfilePictureMutation,
} from '~/generated/graphql';
export function PictureUploader() {
export function ProfilePictureUploader() {
const [uploadPicture] = useUploadProfilePictureMutation();
const [removePicture] = useRemoveProfilePictureMutation();
const [currentUser] = useRecoilState(currentUserState);

View File

@ -0,0 +1,44 @@
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { ImageInput } from '@/ui/components/inputs/ImageInput';
import { GET_CURRENT_USER } from '@/users/queries';
import { getImageAbsoluteURI } from '@/users/utils/getProfilePictureAbsoluteURI';
import {
useRemoveWorkspaceLogoMutation,
useUploadWorkspaceLogoMutation,
} from '~/generated/graphql';
export function WorkspaceLogoUploader() {
const [uploadLogo] = useUploadWorkspaceLogoMutation();
const [removeLogo] = useRemoveWorkspaceLogoMutation();
const [currentUser] = useRecoilState(currentUserState);
async function onUpload(file: File) {
if (!file) {
return;
}
await uploadLogo({
variables: {
file,
},
refetchQueries: [getOperationName(GET_CURRENT_USER) ?? ''],
});
}
async function onRemove() {
await removeLogo({
refetchQueries: [getOperationName(GET_CURRENT_USER) ?? ''],
});
}
return (
<ImageInput
picture={getImageAbsoluteURI(
currentUser?.workspaceMember?.workspace.logo,
)}
onUpload={onUpload}
onRemove={onRemove}
/>
);
}

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { mockedUsersData } from '~/testing/mock-data/users';
import { getImageAbsoluteURI } from '@/users/utils/getProfilePictureAbsoluteURI';
import NavCollapseButton from './NavCollapseButton';
@ -50,14 +50,17 @@ function NavWorkspaceButton() {
const currentUser = useRecoilValue(currentUserState);
const currentWorkspace = currentUser?.workspaceMember?.workspace;
const DEFAULT_LOGO =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
return (
<StyledContainer>
<LogoAndNameContainer>
<StyledLogo
logo={
currentWorkspace?.logo ??
mockedUsersData[0].workspaceMember.workspace.logo
currentWorkspace?.logo
? getImageAbsoluteURI(currentWorkspace.logo)
: DEFAULT_LOGO
}
></StyledLogo>
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>

View File

@ -10,3 +10,17 @@ export const UPDATE_WORKSPACE = gql`
}
}
`;
export const UPDATE_WORKSPACE_LOGO = gql`
mutation UploadWorkspaceLogo($file: Upload!) {
uploadWorkspaceLogo(file: $file)
}
`;
export const REMOVE_WORKSPACE_LOGO = gql`
mutation RemoveWorkspaceLogo {
updateWorkspace(data: { logo: { set: null } }) {
id
}
}
`;

View File

@ -15,7 +15,7 @@ import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMou
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { NameFields } from '@/settings/profile/components/NameFields';
import { PictureUploader } from '@/settings/profile/components/PictureUploader';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { MainButton } from '@/ui/components/buttons/MainButton';
import { SubSectionTitle } from '@/ui/components/section-titles/SubSectionTitle';
import { GET_CURRENT_USER } from '@/users/queries';
@ -111,7 +111,7 @@ export function CreateProfile() {
<StyledContentContainer>
<StyledSectionContainer>
<SubSectionTitle title="Picture" />
<PictureUploader />
<ProfilePictureUploader />
</StyledSectionContainer>
<StyledSectionContainer>
<SubSectionTitle

View File

@ -12,8 +12,8 @@ import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMountOnly';
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
import { MainButton } from '@/ui/components/buttons/MainButton';
import { ImageInput } from '@/ui/components/inputs/ImageInput';
import { TextInput } from '@/ui/components/inputs/TextInput';
import { SubSectionTitle } from '@/ui/components/section-titles/SubSectionTitle';
import { GET_CURRENT_USER } from '@/users/queries';
@ -103,7 +103,7 @@ export function CreateWorkspace() {
<StyledContentContainer>
<StyledSectionContainer>
<SubSectionTitle title="Workspace logo" />
<ImageInput picture={null} disabled />
<WorkspaceLogoUploader />
</StyledSectionContainer>
<StyledSectionContainer>
<SubSectionTitle

View File

@ -4,7 +4,7 @@ import { useHotkeysScopeOnMountOnly } from '@/hotkeys/hooks/useHotkeysScopeOnMou
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
import { EmailField } from '@/settings/profile/components/EmailField';
import { NameFields } from '@/settings/profile/components/NameFields';
import { PictureUploader } from '@/settings/profile/components/PictureUploader';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { MainSectionTitle } from '@/ui/components/section-titles/MainSectionTitle';
import { SubSectionTitle } from '@/ui/components/section-titles/SubSectionTitle';
import { NoTopBarContainer } from '@/ui/layout/containers/NoTopBarContainer';
@ -37,7 +37,7 @@ export function SettingsProfile() {
<MainSectionTitle>Profile</MainSectionTitle>
<StyledSectionContainer>
<SubSectionTitle title="Picture" />
<PictureUploader />
<ProfilePictureUploader />
</StyledSectionContainer>
<StyledSectionContainer>
<SubSectionTitle

View File

@ -26,8 +26,7 @@ export const mockedUsersData: Array<MockedUser> = [
displayName: 'Charles Test',
firstName: 'Charles',
lastName: 'Test',
avatarUrl:
'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4',
avatarUrl: null,
workspaceMember: {
__typename: 'WorkspaceMember',
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
@ -36,7 +35,7 @@ export const mockedUsersData: Array<MockedUser> = [
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
displayName: 'Twenty',
domainName: 'twenty.com',
logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=',
logo: null,
},
},
},
@ -55,7 +54,7 @@ export const mockedUsersData: Array<MockedUser> = [
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
displayName: 'Twenty',
domainName: 'twenty.com',
logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=',
logo: null,
},
},
},