Use Graphql types in FE and complete mappers removal (#348)

Fix Typescript build issues
This commit is contained in:
Charles Bochet
2023-06-21 10:52:00 -07:00
committed by GitHub
parent b179d1f1f0
commit 8a330b9746
35 changed files with 398 additions and 574 deletions

View File

@ -3,14 +3,9 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { v4 as uuidv4 } from 'uuid';
import {
Company,
mapToCompany,
} from '@/companies/interfaces/company.interface';
import {
CompaniesSelectedSortType,
defaultOrderBy,
insertCompany,
useCompaniesQuery,
} from '@/companies/services';
import {
@ -23,8 +18,13 @@ import { EntityTable } from '@/ui/components/table/EntityTable';
import { IconBuildingSkyscraper } from '@/ui/icons/index';
import { IconList } from '@/ui/icons/index';
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
import { BoolExpType } from '@/utils/interfaces/generic.interface';
import { CompanyOrderByWithRelationInput as Companies_Order_By } from '~/generated/graphql';
import {
CompanyOrderByWithRelationInput as Companies_Order_By,
CompanyWhereInput,
GetCompaniesQuery,
InsertCompanyMutationVariables,
useInsertCompanyMutation,
} from '~/generated/graphql';
import { TableActionBarButtonCreateCommentThreadCompany } from './table/TableActionBarButtonCreateCommentThreadCompany';
import { TableActionBarButtonDeleteCompanies } from './table/TableActionBarButtonDeleteCompanies';
@ -38,15 +38,16 @@ const StyledCompaniesContainer = styled.div`
`;
export function Companies() {
const [insertCompany] = useInsertCompanyMutation();
const [orderBy, setOrderBy] = useState<Companies_Order_By[]>(defaultOrderBy);
const [where, setWhere] = useState<BoolExpType<Company>>({});
const [where, setWhere] = useState<CompanyWhereInput>({});
const updateSorts = useCallback((sorts: Array<CompaniesSelectedSortType>) => {
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
}, []);
const updateFilters = useCallback(
(filters: Array<SelectedFilterType<Company>>) => {
(filters: Array<SelectedFilterType<GetCompaniesQuery['companies'][0]>>) => {
setWhere(reduceFiltersToWhere(filters));
},
[],
@ -54,21 +55,19 @@ export function Companies() {
const { data } = useCompaniesQuery(orderBy, where);
const companies = data?.companies.map(mapToCompany) ?? [];
const companies = data?.companies ?? [];
async function handleAddButtonClick() {
const newCompany: Company = {
const newCompany: InsertCompanyMutationVariables = {
id: uuidv4(),
name: '',
domainName: '',
employees: null,
address: '',
createdAt: new Date().toISOString(),
accountOwner: null,
__typename: 'Company',
};
await insertCompany(newCompany);
await insertCompany({ variables: newCompany });
}
const companiesColumns = useCompaniesColumns();

View File

@ -2,7 +2,6 @@ import { useMemo } from 'react';
import { createColumnHelper } from '@tanstack/react-table';
import { CompanyEditableNameChipCell } from '@/companies/components/CompanyEditableNameCell';
import { updateCompany } from '@/companies/services';
import {
PersonChip,
PersonChipPropsType,
@ -22,12 +21,16 @@ import {
IconUsers,
} from '@/ui/icons/index';
import { getCheckBoxColumn } from '@/ui/tables/utils/getCheckBoxColumn';
import { mapToUser, User } from '@/users/interfaces/user.interface';
import { GetCompaniesQueryHookResult, QueryMode } from '~/generated/graphql';
import {
GetCompaniesQuery,
QueryMode,
useUpdateCompanyMutation,
} from '~/generated/graphql';
const columnHelper = createColumnHelper<GetCompaniesQueryHookResult>();
const columnHelper = createColumnHelper<GetCompaniesQuery['companies'][0]>();
export const useCompaniesColumns = () => {
const [updateCompany] = useUpdateCompanyMutation();
return useMemo(() => {
return [
getCheckBoxColumn(),
@ -54,7 +57,12 @@ export const useCompaniesColumns = () => {
changeHandler={(value) => {
const company = { ...props.row.original };
company.domainName = value;
updateCompany(company);
updateCompany({
variables: {
...company,
accountOwnerId: company.accountOwner?.id,
},
});
}}
/>
),
@ -71,13 +79,13 @@ export const useCompaniesColumns = () => {
changeHandler={(value) => {
const company = { ...props.row.original };
if (value === '') {
company.employees = null;
updateCompany(company);
} else if (!Number.isNaN(Number(value))) {
company.employees = Number(value);
updateCompany(company);
}
updateCompany({
variables: {
...company,
employees: value === '' ? null : Number(value),
accountOwnerId: company.accountOwner?.id,
},
});
}}
/>
),
@ -94,7 +102,12 @@ export const useCompaniesColumns = () => {
changeHandler={(value) => {
const company = { ...props.row.original };
company.address = value;
updateCompany(company);
updateCompany({
variables: {
...company,
accountOwnerId: company.accountOwner?.id,
},
});
}}
/>
),
@ -117,7 +130,12 @@ export const useCompaniesColumns = () => {
changeHandler={(value: Date) => {
const company = { ...props.row.original };
company.createdAt = value.toISOString();
updateCompany(company);
updateCompany({
variables: {
...company,
accountOwnerId: company.accountOwner?.id,
},
});
}}
/>
),
@ -131,21 +149,24 @@ export const useCompaniesColumns = () => {
/>
),
cell: (props) => (
<EditableRelation<User, PersonChipPropsType>
<EditableRelation<any, PersonChipPropsType>
relation={props.row.original.accountOwner}
searchPlaceholder="Account Owner"
ChipComponent={PersonChip}
chipComponentPropsMapper={(
accountOwner: User,
accountOwner: any,
): PersonChipPropsType => {
return {
name: accountOwner.displayName || '',
};
}}
onChange={(relation: User) => {
const company = { ...props.row.original };
company.accountOwnerId = relation.id;
updateCompany(company);
onChange={(relation: any) => {
updateCompany({
variables: {
...props.row.original,
accountOwnerId: relation.id,
},
});
}}
searchConfig={
{
@ -156,15 +177,15 @@ export const useCompaniesColumns = () => {
mode: QueryMode.Insensitive,
},
}),
resultMapper: (accountOwner) => ({
render: (accountOwner) => accountOwner.displayName,
value: mapToUser(accountOwner),
resultMapper: (accountOwner: any) => ({
render: (accountOwner: any) => accountOwner.displayName,
value: accountOwner,
}),
} satisfies SearchConfigType<User>
} satisfies SearchConfigType
}
/>
),
}),
];
}, []);
}, [updateCompany]);
};

View File

@ -1,4 +1,3 @@
import { Company } from '@/companies/interfaces/company.interface';
import { FilterConfigType } from '@/filters-and-sorts/interfaces/filters/interface';
import { SEARCH_USER_QUERY } from '@/search/services/search';
import {
@ -9,8 +8,7 @@ import {
IconUser,
IconUsers,
} from '@/ui/icons/index';
import { mapToUser, User } from '@/users/interfaces/user.interface';
import { QueryMode } from '~/generated/graphql';
import { QueryMode, User } from '~/generated/graphql';
export const nameFilter = {
key: 'name',
@ -21,14 +19,14 @@ export const nameFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
name: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
},
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
name: {
@ -40,7 +38,7 @@ export const nameFilter = {
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const employeesFilter = {
key: 'employees',
@ -51,7 +49,7 @@ export const employeesFilter = {
{
label: 'Greater than',
id: 'greater_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
employees: {
gte: isNaN(Number(searchString)) ? undefined : Number(searchString),
},
@ -60,14 +58,14 @@ export const employeesFilter = {
{
label: 'Less than',
id: 'less_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
employees: {
lte: isNaN(Number(searchString)) ? undefined : Number(searchString),
},
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const urlFilter = {
key: 'domainName',
@ -78,7 +76,7 @@ export const urlFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
domainName: {
contains: `%${searchString}%`,
mode: QueryMode.Insensitive,
@ -88,7 +86,7 @@ export const urlFilter = {
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
domainName: {
@ -100,7 +98,7 @@ export const urlFilter = {
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const addressFilter = {
key: 'address',
@ -111,14 +109,14 @@ export const addressFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
address: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
},
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
address: {
@ -130,7 +128,7 @@ export const addressFilter = {
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const ccreatedAtFilter = {
key: 'createdAt',
@ -141,7 +139,7 @@ export const ccreatedAtFilter = {
{
label: 'Greater than',
id: 'greater_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
createdAt: {
gte: searchString,
},
@ -150,14 +148,14 @@ export const ccreatedAtFilter = {
{
label: 'Less than',
id: 'less_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
createdAt: {
lte: searchString,
},
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const accountOwnerFilter = {
key: 'accountOwner',
@ -172,24 +170,24 @@ export const accountOwnerFilter = {
mode: QueryMode.Insensitive,
},
}),
resultMapper: (data) => ({
value: mapToUser(data),
render: (owner) => owner.displayName,
resultMapper: (data: any) => ({
value: data,
render: (owner: any) => owner.displayName,
}),
},
selectedValueRender: (owner) => owner.displayName || '',
selectedValueRender: (owner: any) => owner.displayName || '',
operands: [
{
label: 'Is',
id: 'is',
whereTemplate: (owner) => ({
whereTemplate: (owner: any) => ({
accountOwner: { is: { displayName: { equals: owner.displayName } } },
}),
},
{
label: 'Is not',
id: 'is_not',
whereTemplate: (owner) => ({
whereTemplate: (owner: any) => ({
NOT: [
{
accountOwner: {
@ -200,7 +198,7 @@ export const accountOwnerFilter = {
}),
},
],
} satisfies FilterConfigType<Company, User>;
} satisfies FilterConfigType<User>;
export const availableFilters = [
nameFilter,

View File

@ -1,4 +1,5 @@
import { useCallback, useState } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { v4 as uuidv4 } from 'uuid';
@ -8,10 +9,9 @@ import {
reduceSortsToOrderBy,
} from '@/filters-and-sorts/helpers';
import { SelectedFilterType } from '@/filters-and-sorts/interfaces/filters/interface';
import { Person } from '@/people/interfaces/person.interface';
import {
defaultOrderBy,
insertPerson,
GET_PEOPLE,
PeopleSelectedSortType,
usePeopleQuery,
} from '@/people/services';
@ -19,7 +19,11 @@ import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTab
import { EntityTable } from '@/ui/components/table/EntityTable';
import { IconList, IconUser } from '@/ui/icons/index';
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
import { BoolExpType } from '@/utils/interfaces/generic.interface';
import {
GetPeopleQuery,
PersonWhereInput,
useInsertPersonMutation,
} from '~/generated/graphql';
import { TableActionBarButtonCreateCommentThreadPeople } from './table/TableActionBarButtonCreateCommentThreadPeople';
import { TableActionBarButtonDeletePeople } from './table/TableActionBarButtonDeletePeople';
@ -35,37 +39,38 @@ const StyledPeopleContainer = styled.div`
export function People() {
const [orderBy, setOrderBy] = useState(defaultOrderBy);
const [where, setWhere] = useState<BoolExpType<Person>>({});
const [where, setWhere] = useState<PersonWhereInput>({});
const updateSorts = useCallback((sorts: Array<PeopleSelectedSortType>) => {
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
}, []);
const updateFilters = useCallback(
(filters: Array<SelectedFilterType<Person>>) => {
(filters: Array<SelectedFilterType<GetPeopleQuery['people'][0]>>) => {
setWhere(reduceFiltersToWhere(filters));
},
[],
);
const [insertPersonMutation] = useInsertPersonMutation();
const { data } = usePeopleQuery(orderBy, where);
const people = data?.people ?? [];
async function handleAddButtonClick() {
const newPerson = {
__typename: 'Person',
id: uuidv4(),
firstname: '',
lastname: '',
email: '',
phone: '',
company: null,
createdAt: new Date().toISOString(),
city: '',
} as const;
await insertPerson(newPerson);
await insertPersonMutation({
variables: {
id: uuidv4(),
firstname: '',
lastname: '',
email: '',
phone: '',
createdAt: new Date().toISOString(),
city: '',
},
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
}
const peopleColumns = usePeopleColumns();

View File

@ -3,7 +3,6 @@ import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { graphql } from 'msw';
import { GraphqlQueryCompany } from '@/companies/interfaces/company.interface';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { fetchOneFromData } from '~/testing/mock-data';
import { mockedPeopleData } from '~/testing/mock-data/people';
@ -141,7 +140,7 @@ export const EditRelation: Story = {
name: 'Airbnb',
domainName: 'airbnb.com',
__typename: 'Company',
} satisfies GraphqlQueryCompany,
},
},
},
}),
@ -196,7 +195,7 @@ export const SelectRelationWithKeys: Story = {
name: 'Aircall',
domainName: 'aircall.io',
__typename: 'Company',
} satisfies GraphqlQueryCompany,
},
},
},
}),

View File

@ -4,10 +4,7 @@ describe('PeopleFilter', () => {
it(`should render the filter ${companyFilter.key} which relation search`, () => {
expect(
companyFilter.operands[0].whereTemplate({
id: 'test-id',
name: 'test-name',
domainName: 'test-domain-name',
__typename: 'Company',
}),
).toMatchSnapshot();
});

View File

@ -3,7 +3,6 @@ import { createColumnHelper } from '@tanstack/react-table';
import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName';
import { PeopleCompanyCell } from '@/people/components/PeopleCompanyCell';
import { updatePerson } from '@/people/services';
import { EditableDate } from '@/ui/components/editable-cell/types/EditableDate';
import { EditablePhone } from '@/ui/components/editable-cell/types/EditablePhone';
import { EditableText } from '@/ui/components/editable-cell/types/EditableText';
@ -17,12 +16,13 @@ import {
IconUser,
} from '@/ui/icons/index';
import { getCheckBoxColumn } from '@/ui/tables/utils/getCheckBoxColumn';
import { GetPeopleQuery, useUpdatePeopleMutation } from '~/generated/graphql';
import { GetPeopleQueryHookResult } from '../../generated/graphql';
const columnHelper = createColumnHelper<GetPeopleQueryHookResult>();
const columnHelper = createColumnHelper<GetPeopleQuery['people'][0]>();
export const usePeopleColumns = () => {
const [updatePerson] = useUpdatePeopleMutation();
return useMemo(() => {
return [
getCheckBoxColumn(),
@ -36,9 +36,14 @@ export const usePeopleColumns = () => {
person={props.row.original}
onChange={async (firstName: string, lastName: string) => {
const person = { ...props.row.original };
person.firstname = firstName;
person.lastname = lastName;
await updatePerson(person);
await updatePerson({
variables: {
...person,
firstname: firstName,
lastname: lastName,
companyId: person.company?.id,
},
});
}}
/>
</>
@ -53,10 +58,15 @@ export const usePeopleColumns = () => {
<EditableText
placeholder="Email"
content={props.row.original.email || ''}
changeHandler={(value: string) => {
changeHandler={async (value: string) => {
const person = props.row.original;
person.email = value;
updatePerson(person);
await updatePerson({
variables: {
...person,
email: value,
companyId: person.company?.id,
},
});
}}
/>
),
@ -80,10 +90,15 @@ export const usePeopleColumns = () => {
<EditablePhone
placeholder="Phone"
value={props.row.original.phone || ''}
changeHandler={(value: string) => {
changeHandler={async (value: string) => {
const person = { ...props.row.original };
person.phone = value;
updatePerson(person);
await updatePerson({
variables: {
...person,
phone: value,
companyId: person.company?.id,
},
});
}}
/>
),
@ -103,10 +118,15 @@ export const usePeopleColumns = () => {
? new Date(props.row.original.createdAt)
: new Date()
}
changeHandler={(value: Date) => {
changeHandler={async (value: Date) => {
const person = { ...props.row.original };
person.createdAt = value.toISOString();
updatePerson(person);
await updatePerson({
variables: {
...person,
createdAt: value.toISOString(),
companyId: person.company?.id,
},
});
}}
/>
),
@ -121,14 +141,19 @@ export const usePeopleColumns = () => {
editModeHorizontalAlign="right"
placeholder="City"
content={props.row.original.city || ''}
changeHandler={(value: string) => {
changeHandler={async (value: string) => {
const person = { ...props.row.original };
person.city = value;
updatePerson(person);
await updatePerson({
variables: {
...person,
city: value,
companyId: person.company?.id,
},
});
}}
/>
),
}),
];
}, []);
}, [updatePerson]);
};

View File

@ -1,9 +1,4 @@
import {
Company,
mapToCompany,
} from '@/companies/interfaces/company.interface';
import { FilterConfigType } from '@/filters-and-sorts/interfaces/filters/interface';
import { Person } from '@/people/interfaces/person.interface';
import { SEARCH_COMPANY_QUERY } from '@/search/services/search';
import {
IconBuildingSkyscraper,
@ -13,7 +8,7 @@ import {
IconPhone,
IconUser,
} from '@/ui/icons/index';
import { QueryMode } from '~/generated/graphql';
import { Company, QueryMode } from '~/generated/graphql';
export const fullnameFilter = {
key: 'fullname',
@ -24,7 +19,7 @@ export const fullnameFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
OR: [
{
firstname: {
@ -44,7 +39,7 @@ export const fullnameFilter = {
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
AND: [
@ -66,7 +61,7 @@ export const fullnameFilter = {
}),
},
],
} satisfies FilterConfigType<Person, string>;
} satisfies FilterConfigType<string>;
export const emailFilter = {
key: 'email',
@ -77,14 +72,14 @@ export const emailFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
email: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
},
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
email: {
@ -96,7 +91,7 @@ export const emailFilter = {
}),
},
],
} satisfies FilterConfigType<Person, string>;
} satisfies FilterConfigType<string>;
export const companyFilter = {
key: 'company_name',
@ -109,8 +104,8 @@ export const companyFilter = {
name: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
resultMapper: (data) => ({
value: mapToCompany(data),
render: (company) => company.name,
value: data,
render: (company: { name: string }) => company.name,
}),
},
selectedValueRender: (company) => company.name || '',
@ -118,19 +113,19 @@ export const companyFilter = {
{
label: 'Is',
id: 'is',
whereTemplate: (company) => ({
whereTemplate: (company: { name: string }) => ({
company: { is: { name: { equals: company.name } } },
}),
},
{
label: 'Is not',
id: 'is_not',
whereTemplate: (company) => ({
whereTemplate: (company: { name: string }) => ({
NOT: [{ company: { is: { name: { equals: company.name } } } }],
}),
},
],
} satisfies FilterConfigType<Person, Company>;
} satisfies FilterConfigType<Company>;
export const phoneFilter = {
key: 'phone',
@ -141,14 +136,14 @@ export const phoneFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
phone: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
},
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
phone: {
@ -160,7 +155,7 @@ export const phoneFilter = {
}),
},
],
} satisfies FilterConfigType<Person, string>;
} satisfies FilterConfigType<string>;
export const createdAtFilter = {
key: 'createdAt',
@ -171,7 +166,7 @@ export const createdAtFilter = {
{
label: 'Greater than',
id: 'greater_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
createdAt: {
gte: searchString,
},
@ -180,14 +175,14 @@ export const createdAtFilter = {
{
label: 'Less than',
id: 'less_than',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
createdAt: {
lte: searchString,
},
}),
},
],
} satisfies FilterConfigType<Company, string>;
} satisfies FilterConfigType<string>;
export const cityFilter = {
key: 'city',
@ -198,14 +193,14 @@ export const cityFilter = {
{
label: 'Contains',
id: 'like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
city: { contains: `%${searchString}%`, mode: QueryMode.Insensitive },
}),
},
{
label: 'Does not contain',
id: 'not_like',
whereTemplate: (searchString) => ({
whereTemplate: (searchString: string) => ({
NOT: [
{
city: {
@ -217,7 +212,7 @@ export const cityFilter = {
}),
},
],
} satisfies FilterConfigType<Person, string>;
} satisfies FilterConfigType<string>;
export const availableFilters = [
fullnameFilter,
@ -226,4 +221,4 @@ export const availableFilters = [
phoneFilter,
createdAtFilter,
cityFilter,
] satisfies FilterConfigType<Person>[];
];