Refactor/remove react table (#642)
* Refactored tables without tan stack * Fixed checkbox behavior with multiple handlers on click * Fixed hotkeys scope * Fix debounce in editable cells * Lowered coverage --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -166,7 +166,7 @@
|
|||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"lines": 65,
|
"lines": 65,
|
||||||
"statements": 65,
|
"statements": 60,
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"src/generated/**/*"
|
"src/generated/**/*"
|
||||||
]
|
]
|
||||||
|
|||||||
@ -3271,12 +3271,61 @@ export type GetPeopleQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type GetPeopleQuery = { __typename?: 'Query', people: Array<{ __typename?: 'Person', id: string, phone: string, email: string, city: string, firstName: string, lastName: string, createdAt: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null }> };
|
export type GetPeopleQuery = { __typename?: 'Query', people: Array<{ __typename?: 'Person', id: string, phone: string, email: string, city: string, firstName: string, lastName: string, createdAt: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null }> };
|
||||||
|
|
||||||
|
export type GetPersonPhoneByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonPhoneByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, phone: string } };
|
||||||
|
|
||||||
|
export type GetPersonEmailByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonEmailByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, email: string } };
|
||||||
|
|
||||||
|
export type GetPersonNamesAndCommentCountByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonNamesAndCommentCountByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, firstName: string, lastName: string, _commentThreadCount: number } };
|
||||||
|
|
||||||
|
export type GetPersonCompanyByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonCompanyByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null } };
|
||||||
|
|
||||||
|
export type GetPersonCommentCountByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonCommentCountByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, _commentThreadCount: number } };
|
||||||
|
|
||||||
|
export type GetPersonCreatedAtByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonCreatedAtByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, createdAt: string } };
|
||||||
|
|
||||||
|
export type GetPersonCityByIdQueryVariables = Exact<{
|
||||||
|
id: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetPersonCityByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, city: string } };
|
||||||
|
|
||||||
export type GetPersonQueryVariables = Exact<{
|
export type GetPersonQueryVariables = Exact<{
|
||||||
id: Scalars['String'];
|
id: Scalars['String'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, createdAt: string } };
|
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, email: string, createdAt: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string } | null } };
|
||||||
|
|
||||||
export type UpdatePeopleMutationVariables = Exact<{
|
export type UpdatePeopleMutationVariables = Exact<{
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -4456,6 +4505,264 @@ export function useGetPeopleLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<
|
|||||||
export type GetPeopleQueryHookResult = ReturnType<typeof useGetPeopleQuery>;
|
export type GetPeopleQueryHookResult = ReturnType<typeof useGetPeopleQuery>;
|
||||||
export type GetPeopleLazyQueryHookResult = ReturnType<typeof useGetPeopleLazyQuery>;
|
export type GetPeopleLazyQueryHookResult = ReturnType<typeof useGetPeopleLazyQuery>;
|
||||||
export type GetPeopleQueryResult = Apollo.QueryResult<GetPeopleQuery, GetPeopleQueryVariables>;
|
export type GetPeopleQueryResult = Apollo.QueryResult<GetPeopleQuery, GetPeopleQueryVariables>;
|
||||||
|
export const GetPersonPhoneByIdDocument = gql`
|
||||||
|
query GetPersonPhoneById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
phone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonPhoneByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonPhoneByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonPhoneByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonPhoneByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonPhoneByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>(GetPersonPhoneByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonPhoneByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>(GetPersonPhoneByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonPhoneByIdQueryHookResult = ReturnType<typeof useGetPersonPhoneByIdQuery>;
|
||||||
|
export type GetPersonPhoneByIdLazyQueryHookResult = ReturnType<typeof useGetPersonPhoneByIdLazyQuery>;
|
||||||
|
export type GetPersonPhoneByIdQueryResult = Apollo.QueryResult<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>;
|
||||||
|
export const GetPersonEmailByIdDocument = gql`
|
||||||
|
query GetPersonEmailById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonEmailByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonEmailByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonEmailByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonEmailByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonEmailByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonEmailByIdQuery, GetPersonEmailByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonEmailByIdQuery, GetPersonEmailByIdQueryVariables>(GetPersonEmailByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonEmailByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonEmailByIdQuery, GetPersonEmailByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonEmailByIdQuery, GetPersonEmailByIdQueryVariables>(GetPersonEmailByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonEmailByIdQueryHookResult = ReturnType<typeof useGetPersonEmailByIdQuery>;
|
||||||
|
export type GetPersonEmailByIdLazyQueryHookResult = ReturnType<typeof useGetPersonEmailByIdLazyQuery>;
|
||||||
|
export type GetPersonEmailByIdQueryResult = Apollo.QueryResult<GetPersonEmailByIdQuery, GetPersonEmailByIdQueryVariables>;
|
||||||
|
export const GetPersonNamesAndCommentCountByIdDocument = gql`
|
||||||
|
query GetPersonNamesAndCommentCountById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
_commentThreadCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonNamesAndCommentCountByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonNamesAndCommentCountByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonNamesAndCommentCountByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonNamesAndCommentCountByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonNamesAndCommentCountByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonNamesAndCommentCountByIdQuery, GetPersonNamesAndCommentCountByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonNamesAndCommentCountByIdQuery, GetPersonNamesAndCommentCountByIdQueryVariables>(GetPersonNamesAndCommentCountByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonNamesAndCommentCountByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonNamesAndCommentCountByIdQuery, GetPersonNamesAndCommentCountByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonNamesAndCommentCountByIdQuery, GetPersonNamesAndCommentCountByIdQueryVariables>(GetPersonNamesAndCommentCountByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonNamesAndCommentCountByIdQueryHookResult = ReturnType<typeof useGetPersonNamesAndCommentCountByIdQuery>;
|
||||||
|
export type GetPersonNamesAndCommentCountByIdLazyQueryHookResult = ReturnType<typeof useGetPersonNamesAndCommentCountByIdLazyQuery>;
|
||||||
|
export type GetPersonNamesAndCommentCountByIdQueryResult = Apollo.QueryResult<GetPersonNamesAndCommentCountByIdQuery, GetPersonNamesAndCommentCountByIdQueryVariables>;
|
||||||
|
export const GetPersonCompanyByIdDocument = gql`
|
||||||
|
query GetPersonCompanyById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
domainName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonCompanyByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonCompanyByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonCompanyByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonCompanyByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonCompanyByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonCompanyByIdQuery, GetPersonCompanyByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonCompanyByIdQuery, GetPersonCompanyByIdQueryVariables>(GetPersonCompanyByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonCompanyByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonCompanyByIdQuery, GetPersonCompanyByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonCompanyByIdQuery, GetPersonCompanyByIdQueryVariables>(GetPersonCompanyByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonCompanyByIdQueryHookResult = ReturnType<typeof useGetPersonCompanyByIdQuery>;
|
||||||
|
export type GetPersonCompanyByIdLazyQueryHookResult = ReturnType<typeof useGetPersonCompanyByIdLazyQuery>;
|
||||||
|
export type GetPersonCompanyByIdQueryResult = Apollo.QueryResult<GetPersonCompanyByIdQuery, GetPersonCompanyByIdQueryVariables>;
|
||||||
|
export const GetPersonCommentCountByIdDocument = gql`
|
||||||
|
query GetPersonCommentCountById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
_commentThreadCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonCommentCountByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonCommentCountByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonCommentCountByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonCommentCountByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonCommentCountByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonCommentCountByIdQuery, GetPersonCommentCountByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonCommentCountByIdQuery, GetPersonCommentCountByIdQueryVariables>(GetPersonCommentCountByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonCommentCountByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonCommentCountByIdQuery, GetPersonCommentCountByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonCommentCountByIdQuery, GetPersonCommentCountByIdQueryVariables>(GetPersonCommentCountByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonCommentCountByIdQueryHookResult = ReturnType<typeof useGetPersonCommentCountByIdQuery>;
|
||||||
|
export type GetPersonCommentCountByIdLazyQueryHookResult = ReturnType<typeof useGetPersonCommentCountByIdLazyQuery>;
|
||||||
|
export type GetPersonCommentCountByIdQueryResult = Apollo.QueryResult<GetPersonCommentCountByIdQuery, GetPersonCommentCountByIdQueryVariables>;
|
||||||
|
export const GetPersonCreatedAtByIdDocument = gql`
|
||||||
|
query GetPersonCreatedAtById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonCreatedAtByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonCreatedAtByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonCreatedAtByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonCreatedAtByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonCreatedAtByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonCreatedAtByIdQuery, GetPersonCreatedAtByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonCreatedAtByIdQuery, GetPersonCreatedAtByIdQueryVariables>(GetPersonCreatedAtByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonCreatedAtByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonCreatedAtByIdQuery, GetPersonCreatedAtByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonCreatedAtByIdQuery, GetPersonCreatedAtByIdQueryVariables>(GetPersonCreatedAtByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonCreatedAtByIdQueryHookResult = ReturnType<typeof useGetPersonCreatedAtByIdQuery>;
|
||||||
|
export type GetPersonCreatedAtByIdLazyQueryHookResult = ReturnType<typeof useGetPersonCreatedAtByIdLazyQuery>;
|
||||||
|
export type GetPersonCreatedAtByIdQueryResult = Apollo.QueryResult<GetPersonCreatedAtByIdQuery, GetPersonCreatedAtByIdQueryVariables>;
|
||||||
|
export const GetPersonCityByIdDocument = gql`
|
||||||
|
query GetPersonCityById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
city
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetPersonCityByIdQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetPersonCityByIdQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetPersonCityByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useGetPersonCityByIdQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetPersonCityByIdQuery(baseOptions: Apollo.QueryHookOptions<GetPersonCityByIdQuery, GetPersonCityByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetPersonCityByIdQuery, GetPersonCityByIdQueryVariables>(GetPersonCityByIdDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetPersonCityByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPersonCityByIdQuery, GetPersonCityByIdQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetPersonCityByIdQuery, GetPersonCityByIdQueryVariables>(GetPersonCityByIdDocument, options);
|
||||||
|
}
|
||||||
|
export type GetPersonCityByIdQueryHookResult = ReturnType<typeof useGetPersonCityByIdQuery>;
|
||||||
|
export type GetPersonCityByIdLazyQueryHookResult = ReturnType<typeof useGetPersonCityByIdLazyQuery>;
|
||||||
|
export type GetPersonCityByIdQueryResult = Apollo.QueryResult<GetPersonCityByIdQuery, GetPersonCityByIdQueryVariables>;
|
||||||
export const GetPersonDocument = gql`
|
export const GetPersonDocument = gql`
|
||||||
query GetPerson($id: String!) {
|
query GetPerson($id: String!) {
|
||||||
findUniquePerson(id: $id) {
|
findUniquePerson(id: $id) {
|
||||||
@ -4463,7 +4770,12 @@ export const GetPersonDocument = gql`
|
|||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
displayName
|
displayName
|
||||||
|
email
|
||||||
createdAt
|
createdAt
|
||||||
|
_commentThreadCount
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { useOpenRightDrawer } from '../../ui/layout/right-drawer/hooks/useOpenRi
|
|||||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
||||||
import { CommentableEntity } from '../types/CommentableEntity';
|
import { CommentableEntity } from '../types/CommentableEntity';
|
||||||
|
|
||||||
|
// TODO: refactor with recoil callback to avoid rerender
|
||||||
export function useOpenTimelineRightDrawer() {
|
export function useOpenTimelineRightDrawer() {
|
||||||
const openRightDrawer = useOpenRightDrawer();
|
const openRightDrawer = useOpenRightDrawer();
|
||||||
const [, setCommentableEntityArray] = useRecoilState(
|
const [, setCommentableEntityArray] = useRecoilState(
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
|
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
import { EditableCell } from '@/ui/components/editable-cell/EditableCell';
|
import { EditableCell } from '@/ui/components/editable-cell/EditableCell';
|
||||||
import { Company, User } from '~/generated/graphql';
|
import { Company, User } from '~/generated/graphql';
|
||||||
|
|
||||||
import { CompanyAccountOwnerPicker } from './CompanyAccountOwnerPicker';
|
import { CompanyAccountOwnerPicker } from './CompanyAccountOwnerPicker';
|
||||||
|
|
||||||
|
export type CompanyAccountOnwer = Pick<Company, 'id'> & {
|
||||||
|
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
company: Pick<Company, 'id'> & {
|
company: CompanyAccountOnwer;
|
||||||
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CompanyAccountOwnerCell({ company }: OwnProps) {
|
export function CompanyAccountOwnerCell({ company }: OwnProps) {
|
||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
|
editHotkeysScope={{ scope: InternalHotkeysScope.RelationPicker }}
|
||||||
editModeContent={<CompanyAccountOwnerPicker company={company} />}
|
editModeContent={<CompanyAccountOwnerPicker company={company} />}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
company.accountOwner?.displayName ? (
|
company.accountOwner?.displayName ? (
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { CompanyChip } from './CompanyChip';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
company: Pick<
|
company: Pick<
|
||||||
GetCompaniesQuery['companies'][0],
|
GetCompaniesQuery['companies'][0],
|
||||||
'id' | 'name' | 'domainName' | '_commentThreadCount' | 'accountOwner'
|
'id' | 'name' | 'domainName' | '_commentThreadCount'
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,16 +35,15 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableCellChip
|
<EditableCellChip
|
||||||
value={company.name || ''}
|
value={company.name ?? ''}
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
picture={getLogoUrlFromDomainName(company.domainName)}
|
picture={getLogoUrlFromDomainName(company.domainName)}
|
||||||
id={company.id}
|
id={company.id}
|
||||||
changeHandler={(value: string) => {
|
changeHandler={(value: string) => {
|
||||||
updateCompany({
|
updateCompany({
|
||||||
variables: {
|
variables: {
|
||||||
...company,
|
id: company.id,
|
||||||
name: value,
|
name: value,
|
||||||
accountOwnerId: company.accountOwner?.id,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyAccountOnwer } from '../components/CompanyAccountOwnerCell';
|
||||||
|
|
||||||
|
export const companyAccountOwnerFamilyState = atomFamily<
|
||||||
|
CompanyAccountOnwer['accountOwner'] | null,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'companyAccountOwnerFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyAddressFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'companyAddressFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyCommentCountFamilyState = atomFamily<number | null, string>(
|
||||||
|
{
|
||||||
|
key: 'companyCommentCountFamilyState',
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyCreatedAtFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'companyCreatedAtFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyDomainNameFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'companyDomainNameFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyEmployeesFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'companyEmployeesFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const companyNameFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'companyNameFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { defaultOrderBy } from '@/companies/services';
|
||||||
|
import { isFetchingEntityTableDataState } from '@/ui/tables/states/isFetchingEntityTableDataState';
|
||||||
|
import { tableRowIdsState } from '@/ui/tables/states/tableRowIdsState';
|
||||||
|
import {
|
||||||
|
PersonOrderByWithRelationInput,
|
||||||
|
useGetCompaniesQuery,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { useSetCompanyEntityTable } from '../hooks/useSetCompanyEntityTable';
|
||||||
|
|
||||||
|
export function CompanyEntityTableData({
|
||||||
|
orderBy = defaultOrderBy,
|
||||||
|
whereFilters,
|
||||||
|
}: {
|
||||||
|
orderBy?: PersonOrderByWithRelationInput[];
|
||||||
|
whereFilters?: any;
|
||||||
|
}) {
|
||||||
|
const [, setTableRowIds] = useRecoilState(tableRowIdsState);
|
||||||
|
|
||||||
|
const [, setIsFetchingEntityTableData] = useRecoilState(
|
||||||
|
isFetchingEntityTableDataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCompanyEntityTable = useSetCompanyEntityTable();
|
||||||
|
|
||||||
|
useGetCompaniesQuery({
|
||||||
|
variables: { orderBy, where: whereFilters },
|
||||||
|
onCompleted: (data) => {
|
||||||
|
const companies = data.companies ?? [];
|
||||||
|
|
||||||
|
const companyIds = companies.map((company) => company.id);
|
||||||
|
|
||||||
|
setTableRowIds((currentRowIds) => {
|
||||||
|
if (JSON.stringify(currentRowIds) !== JSON.stringify(companyIds)) {
|
||||||
|
return companyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRowIds;
|
||||||
|
});
|
||||||
|
|
||||||
|
setCompanyEntityTable(companies);
|
||||||
|
|
||||||
|
setIsFetchingEntityTableData(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyAccountOwnerCell } from '@/companies/components/CompanyAccountOwnerCell';
|
||||||
|
import { companyAccountOwnerFamilyState } from '@/companies/states/companyAccountOwnerFamilyState';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
|
||||||
|
export function EditableCompanyAccountOwnerCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const accountOwner = useRecoilValue(
|
||||||
|
companyAccountOwnerFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CompanyAccountOwnerCell
|
||||||
|
company={{
|
||||||
|
id: currentRowEntityId ?? '',
|
||||||
|
accountOwner: {
|
||||||
|
displayName: accountOwner?.displayName ?? '',
|
||||||
|
id: accountOwner?.id ?? '',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { companyAddressFamilyState } from '@/companies/states/companyAddressFamilyState';
|
||||||
|
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateCompanyMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditableCompanyAddressCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updateCompany] = useUpdateCompanyMutation();
|
||||||
|
|
||||||
|
const address = useRecoilValue(
|
||||||
|
companyAddressFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellText
|
||||||
|
value={address ?? ''}
|
||||||
|
onChange={async (newAddress: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
address: newAddress,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { companyCreatedAtFamilyState } from '@/companies/states/companyCreatedAtFamilyState';
|
||||||
|
import { EditableCellDate } from '@/ui/components/editable-cell/types/EditableCellDate';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateCompanyMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditableCompanyCreatedAtCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const createdAt = useRecoilValue(
|
||||||
|
companyCreatedAtFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [updateCompany] = useUpdateCompanyMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellDate
|
||||||
|
onChange={async (newDate: Date) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
createdAt: newDate.toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={createdAt ? DateTime.fromISO(createdAt).toJSDate() : new Date()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { companyDomainNameFamilyState } from '@/companies/states/companyDomainNameFamilyState';
|
||||||
|
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateCompanyMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditableCompanyDomainNameCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updateCompany] = useUpdateCompanyMutation();
|
||||||
|
|
||||||
|
const name = useRecoilValue(
|
||||||
|
companyDomainNameFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellText
|
||||||
|
value={name ?? ''}
|
||||||
|
onChange={async (domainName: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
domainName: domainName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { companyEmployeesFamilyState } from '@/companies/states/companyEmployeesFamilyState';
|
||||||
|
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdateCompanyMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditableCompanyEmployeesCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updateCompany] = useUpdateCompanyMutation();
|
||||||
|
|
||||||
|
const employees = useRecoilValue(
|
||||||
|
companyEmployeesFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
// TODO: Create an EditableCellNumber component
|
||||||
|
<EditableCellText
|
||||||
|
value={employees ?? ''}
|
||||||
|
onChange={async (newEmployees: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
employees: parseInt(newEmployees),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyEditableNameChipCell } from '@/companies/components/CompanyEditableNameCell';
|
||||||
|
import { companyCommentCountFamilyState } from '@/companies/states/companyCommentCountFamilyState';
|
||||||
|
import { companyDomainNameFamilyState } from '@/companies/states/companyDomainNameFamilyState';
|
||||||
|
import { companyNameFamilyState } from '@/companies/states/companyNameFamilyState';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
|
||||||
|
export function EditableCompanyNameCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const name = useRecoilValue(companyNameFamilyState(currentRowEntityId ?? ''));
|
||||||
|
|
||||||
|
const domainName = useRecoilValue(
|
||||||
|
companyDomainNameFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const commentCount = useRecoilValue(
|
||||||
|
companyCommentCountFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CompanyEditableNameChipCell
|
||||||
|
company={{
|
||||||
|
id: currentRowEntityId ?? '',
|
||||||
|
name: name ?? '',
|
||||||
|
domainName: domainName ?? '',
|
||||||
|
_commentThreadCount: commentCount ?? 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import { TableColumn } from '@/people/table/components/peopleColumns';
|
||||||
|
import {
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconCalendarEvent,
|
||||||
|
IconLink,
|
||||||
|
IconMap,
|
||||||
|
IconUser,
|
||||||
|
IconUsers,
|
||||||
|
} from '@/ui/icons/index';
|
||||||
|
|
||||||
|
import { EditableCompanyAccountOwnerCell } from './EditableCompanyAccountOwnerCell';
|
||||||
|
import { EditableCompanyAddressCell } from './EditableCompanyAddressCell';
|
||||||
|
import { EditableCompanyCreatedAtCell } from './EditableCompanyCreatedAtCell';
|
||||||
|
import { EditableCompanyDomainNameCell } from './EditableCompanyDomainNameCell';
|
||||||
|
import { EditableCompanyEmployeesCell } from './EditableCompanyEmployeesCell';
|
||||||
|
import { EditableCompanyNameCell } from './EditableCompanyNameCell';
|
||||||
|
|
||||||
|
export const companyColumns: TableColumn[] = [
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
title: 'Name',
|
||||||
|
icon: <IconBuildingSkyscraper size={16} />,
|
||||||
|
size: 180,
|
||||||
|
cellComponent: <EditableCompanyNameCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'domainName',
|
||||||
|
title: 'URL',
|
||||||
|
icon: <IconLink size={16} />,
|
||||||
|
size: 100,
|
||||||
|
cellComponent: <EditableCompanyDomainNameCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'employees',
|
||||||
|
title: 'Employees',
|
||||||
|
icon: <IconUsers size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditableCompanyEmployeesCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'address',
|
||||||
|
title: 'Address',
|
||||||
|
icon: <IconMap size={16} />,
|
||||||
|
size: 170,
|
||||||
|
cellComponent: <EditableCompanyAddressCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
title: 'Creation',
|
||||||
|
icon: <IconCalendarEvent size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditableCompanyCreatedAtCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'accountOwner',
|
||||||
|
title: 'Account owner',
|
||||||
|
icon: <IconUser size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditableCompanyAccountOwnerCell />,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { companyAccountOwnerFamilyState } from '@/companies/states/companyAccountOwnerFamilyState';
|
||||||
|
import { companyAddressFamilyState } from '@/companies/states/companyAddressFamilyState';
|
||||||
|
import { companyCommentCountFamilyState } from '@/companies/states/companyCommentCountFamilyState';
|
||||||
|
import { companyCreatedAtFamilyState } from '@/companies/states/companyCreatedAtFamilyState';
|
||||||
|
import { companyDomainNameFamilyState } from '@/companies/states/companyDomainNameFamilyState';
|
||||||
|
import { companyEmployeesFamilyState } from '@/companies/states/companyEmployeesFamilyState';
|
||||||
|
import { companyNameFamilyState } from '@/companies/states/companyNameFamilyState';
|
||||||
|
import { GetCompaniesQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function useSetCompanyEntityTable() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(newCompanyArray: GetCompaniesQuery['companies']) => {
|
||||||
|
for (const company of newCompanyArray) {
|
||||||
|
const currentName = snapshot
|
||||||
|
.getLoadable(companyNameFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentName !== company.name) {
|
||||||
|
set(companyNameFamilyState(company.id), company.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDomainName = snapshot
|
||||||
|
.getLoadable(companyDomainNameFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentDomainName !== company.domainName) {
|
||||||
|
set(companyDomainNameFamilyState(company.id), company.domainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentEmployees = snapshot
|
||||||
|
.getLoadable(companyEmployeesFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentEmployees !== company.employees) {
|
||||||
|
set(
|
||||||
|
companyEmployeesFamilyState(company.id),
|
||||||
|
company.employees?.toString() ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentAddress = snapshot
|
||||||
|
.getLoadable(companyAddressFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentAddress !== company.address) {
|
||||||
|
set(companyAddressFamilyState(company.id), company.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCommentCount = snapshot
|
||||||
|
.getLoadable(companyCommentCountFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentCommentCount !== company._commentThreadCount) {
|
||||||
|
set(
|
||||||
|
companyCommentCountFamilyState(company.id),
|
||||||
|
company._commentThreadCount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentAccountOwner = snapshot
|
||||||
|
.getLoadable(companyAccountOwnerFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (
|
||||||
|
JSON.stringify(currentAccountOwner) !==
|
||||||
|
JSON.stringify(company.accountOwner)
|
||||||
|
) {
|
||||||
|
set(
|
||||||
|
companyAccountOwnerFamilyState(company.id),
|
||||||
|
company.accountOwner,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCreatedAt = snapshot
|
||||||
|
.getLoadable(companyCreatedAtFamilyState(company.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentCreatedAt !== company.createdAt) {
|
||||||
|
set(companyCreatedAtFamilyState(company.id), company.createdAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -16,8 +16,8 @@ export const reduceSortsToOrderBy = <OrderByTemplate>(
|
|||||||
sorts: Array<SelectedSortType<OrderByTemplate>>,
|
sorts: Array<SelectedSortType<OrderByTemplate>>,
|
||||||
): OrderByTemplate[] => {
|
): OrderByTemplate[] => {
|
||||||
const mappedSorts = sorts.map((sort) => {
|
const mappedSorts = sorts.map((sort) => {
|
||||||
if (sort._type === 'custom_sort') {
|
if (sort.orderByTemplates) {
|
||||||
return sort.orderByTemplates.map((orderByTemplate) =>
|
return sort.orderByTemplates?.map((orderByTemplate) =>
|
||||||
orderByTemplate(mapOrderToOrder_By(sort.order)),
|
orderByTemplate(mapOrderToOrder_By(sort.order)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,20 +2,12 @@ import { ReactNode } from 'react';
|
|||||||
|
|
||||||
import { SortOrder as Order_By } from '~/generated/graphql';
|
import { SortOrder as Order_By } from '~/generated/graphql';
|
||||||
|
|
||||||
export type SortType<OrderByTemplate> =
|
export type SortType<OrderByTemplate> = {
|
||||||
| {
|
label: string;
|
||||||
_type: 'default_sort';
|
key: string;
|
||||||
label: string;
|
icon?: ReactNode;
|
||||||
key: keyof OrderByTemplate & string;
|
orderByTemplates?: Array<(order: Order_By) => OrderByTemplate>;
|
||||||
icon?: ReactNode;
|
};
|
||||||
}
|
|
||||||
| {
|
|
||||||
_type: 'custom_sort';
|
|
||||||
label: string;
|
|
||||||
key: string;
|
|
||||||
icon?: ReactNode;
|
|
||||||
orderByTemplates: Array<(order: Order_By) => OrderByTemplate>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectedSortType<OrderByTemplate> = SortType<OrderByTemplate> & {
|
export type SelectedSortType<OrderByTemplate> = SortType<OrderByTemplate> & {
|
||||||
order: 'asc' | 'desc';
|
order: 'asc' | 'desc';
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { CellCommentChip } from '@/comments/components/table/CellCommentChip';
|
import { CellCommentChip } from '@/comments/components/table/CellCommentChip';
|
||||||
@ -9,7 +8,12 @@ import { CommentableType, Person } from '~/generated/graphql';
|
|||||||
import { PersonChip } from './PersonChip';
|
import { PersonChip } from './PersonChip';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
person: Pick<Person, 'id' | 'firstName' | 'lastName' | '_commentThreadCount'>;
|
person:
|
||||||
|
| Partial<
|
||||||
|
Pick<Person, 'id' | 'firstName' | 'lastName' | '_commentThreadCount'>
|
||||||
|
>
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
onChange: (firstName: string, lastName: string) => void;
|
onChange: (firstName: string, lastName: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,17 +29,12 @@ const RightContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function EditablePeopleFullName({ person, onChange }: OwnProps) {
|
export function EditablePeopleFullName({ person, onChange }: OwnProps) {
|
||||||
const [firstNameValue, setFirstNameValue] = useState(person.firstName ?? '');
|
|
||||||
const [lastNameValue, setLastNameValue] = useState(person.lastName ?? '');
|
|
||||||
const openCommentRightDrawer = useOpenTimelineRightDrawer();
|
const openCommentRightDrawer = useOpenTimelineRightDrawer();
|
||||||
|
|
||||||
function handleDoubleTextChange(
|
function handleDoubleTextChange(
|
||||||
firstValue: string,
|
firstValue: string,
|
||||||
secondValue: string,
|
secondValue: string,
|
||||||
): void {
|
): void {
|
||||||
setFirstNameValue(firstValue);
|
|
||||||
setLastNameValue(secondValue);
|
|
||||||
|
|
||||||
onChange(firstValue, secondValue);
|
onChange(firstValue, secondValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,30 +42,34 @@ export function EditablePeopleFullName({ person, onChange }: OwnProps) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (!person) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
openCommentRightDrawer([
|
openCommentRightDrawer([
|
||||||
{
|
{
|
||||||
type: CommentableType.Person,
|
type: CommentableType.Person,
|
||||||
id: person.id,
|
id: person.id ?? '',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableCellDoubleText
|
<EditableCellDoubleText
|
||||||
firstValue={firstNameValue}
|
firstValue={person?.firstName ?? ''}
|
||||||
secondValue={lastNameValue}
|
secondValue={person?.lastName ?? ''}
|
||||||
firstValuePlaceholder="First name"
|
firstValuePlaceholder="First name"
|
||||||
secondValuePlaceholder="Last name"
|
secondValuePlaceholder="Last name"
|
||||||
onChange={handleDoubleTextChange}
|
onChange={handleDoubleTextChange}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
<NoEditModeContainer>
|
<NoEditModeContainer>
|
||||||
<PersonChip
|
<PersonChip
|
||||||
name={person.firstName + ' ' + person.lastName}
|
name={person?.firstName + ' ' + person?.lastName}
|
||||||
id={person.id}
|
id={person?.id ?? ''}
|
||||||
/>
|
/>
|
||||||
<RightContainer>
|
<RightContainer>
|
||||||
<CellCommentChip
|
<CellCommentChip
|
||||||
count={person._commentThreadCount ?? 0}
|
count={person?._commentThreadCount ?? 0}
|
||||||
onClick={handleCommentClick}
|
onClick={handleCommentClick}
|
||||||
/>
|
/>
|
||||||
</RightContainer>
|
</RightContainer>
|
||||||
|
|||||||
@ -9,6 +9,10 @@ import { Company, Person } from '~/generated/graphql';
|
|||||||
import { PeopleCompanyCreateCell } from './PeopleCompanyCreateCell';
|
import { PeopleCompanyCreateCell } from './PeopleCompanyCreateCell';
|
||||||
import { PeopleCompanyPicker } from './PeopleCompanyPicker';
|
import { PeopleCompanyPicker } from './PeopleCompanyPicker';
|
||||||
|
|
||||||
|
export type PeopleWithCompany = Pick<Person, 'id'> & {
|
||||||
|
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
people: Pick<Person, 'id'> & {
|
people: Pick<Person, 'id'> & {
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { isFetchingEntityTableDataState } from '@/ui/tables/states/isFetchingEntityTableDataState';
|
||||||
|
import { tableRowIdsState } from '@/ui/tables/states/tableRowIdsState';
|
||||||
|
import {
|
||||||
|
PersonOrderByWithRelationInput,
|
||||||
|
useGetPeopleQuery,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { useSetPeopleEntityTable } from '../hooks/useSetPeopleEntityTable';
|
||||||
|
import { defaultOrderBy } from '../services';
|
||||||
|
|
||||||
|
export function PeopleEntityTableData({
|
||||||
|
orderBy = defaultOrderBy,
|
||||||
|
whereFilters,
|
||||||
|
}: {
|
||||||
|
orderBy?: PersonOrderByWithRelationInput[];
|
||||||
|
whereFilters?: any;
|
||||||
|
}) {
|
||||||
|
const [, setTableRowIds] = useRecoilState(tableRowIdsState);
|
||||||
|
|
||||||
|
const [, setIsFetchingEntityTableData] = useRecoilState(
|
||||||
|
isFetchingEntityTableDataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setPeopleEntityTable = useSetPeopleEntityTable();
|
||||||
|
|
||||||
|
useGetPeopleQuery({
|
||||||
|
variables: { orderBy, where: whereFilters },
|
||||||
|
onCompleted: (data) => {
|
||||||
|
const people = data.people ?? [];
|
||||||
|
|
||||||
|
const peopleIds = people.map((person) => person.id);
|
||||||
|
|
||||||
|
setTableRowIds((currentRowIds) => {
|
||||||
|
if (JSON.stringify(currentRowIds) !== JSON.stringify(peopleIds)) {
|
||||||
|
return peopleIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRowIds;
|
||||||
|
});
|
||||||
|
|
||||||
|
setPeopleEntityTable(people);
|
||||||
|
|
||||||
|
setIsFetchingEntityTableData(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@ -50,6 +50,7 @@ const StyledName = styled.span`
|
|||||||
|
|
||||||
export function PersonChip({ id, name, picture }: PersonChipPropsType) {
|
export function PersonChip({ id, name, picture }: PersonChipPropsType) {
|
||||||
const ContainerComponent = id ? StyledContainerLink : StyledContainerNoLink;
|
const ContainerComponent = id ? StyledContainerLink : StyledContainerNoLink;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContainerComponent data-testid="person-chip" to={`/person/${id}`}>
|
<ContainerComponent data-testid="person-chip" to={`/person/${id}`}>
|
||||||
<Avatar
|
<Avatar
|
||||||
|
|||||||
78
front/src/modules/people/hooks/useSetPeopleEntityTable.ts
Normal file
78
front/src/modules/people/hooks/useSetPeopleEntityTable.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { GetPeopleQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
|
||||||
|
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
|
||||||
|
import { peopleCreatedAtFamilyState } from '../states/peopleCreatedAtFamilyState';
|
||||||
|
import { peopleEmailFamilyState } from '../states/peopleEmailFamilyState';
|
||||||
|
import { peopleNameCellFamilyState } from '../states/peopleNamesFamilyState';
|
||||||
|
import { peoplePhoneFamilyState } from '../states/peoplePhoneFamilyState';
|
||||||
|
|
||||||
|
export function useSetPeopleEntityTable() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(newPeopleArray: GetPeopleQuery['people']) => {
|
||||||
|
for (const person of newPeopleArray) {
|
||||||
|
const currentEmail = snapshot
|
||||||
|
.getLoadable(peopleEmailFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentEmail !== person.email) {
|
||||||
|
set(peopleEmailFamilyState(person.id), person.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCity = snapshot
|
||||||
|
.getLoadable(peopleCityFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentCity !== person.city) {
|
||||||
|
set(peopleCityFamilyState(person.id), person.city);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCompany = snapshot
|
||||||
|
.getLoadable(peopleCompanyFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (
|
||||||
|
JSON.stringify(currentCompany) !== JSON.stringify(person.company)
|
||||||
|
) {
|
||||||
|
set(peopleCompanyFamilyState(person.id), person.company);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPhone = snapshot
|
||||||
|
.getLoadable(peoplePhoneFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentPhone !== person.phone) {
|
||||||
|
set(peoplePhoneFamilyState(person.id), person.phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCreatedAt = snapshot
|
||||||
|
.getLoadable(peopleCreatedAtFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (currentCreatedAt !== person.createdAt) {
|
||||||
|
set(peopleCreatedAtFamilyState(person.id), person.createdAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentNameCell = snapshot
|
||||||
|
.getLoadable(peopleNameCellFamilyState(person.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentNameCell.firstName !== person.firstName ||
|
||||||
|
currentNameCell.lastName !== person.lastName ||
|
||||||
|
currentNameCell.commentCount !== person._commentThreadCount
|
||||||
|
) {
|
||||||
|
set(peopleNameCellFamilyState(person.id), {
|
||||||
|
firstName: person.firstName,
|
||||||
|
lastName: person.lastName,
|
||||||
|
commentCount: person._commentThreadCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -48,3 +48,72 @@ export const defaultOrderBy: People_Order_By[] = [
|
|||||||
createdAt: SortOrder.Desc,
|
createdAt: SortOrder.Desc,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const GET_PERSON_PHONE = gql`
|
||||||
|
query GetPersonPhoneById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
phone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_EMAIL = gql`
|
||||||
|
query GetPersonEmailById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_NAMES_AND_COMMENT_COUNT = gql`
|
||||||
|
query GetPersonNamesAndCommentCountById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
_commentThreadCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_COMPANY = gql`
|
||||||
|
query GetPersonCompanyById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
domainName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_COMMENT_COUNT = gql`
|
||||||
|
query GetPersonCommentCountById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
_commentThreadCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_CREATED_AT = gql`
|
||||||
|
query GetPersonCreatedAtById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_PERSON_CITY = gql`
|
||||||
|
query GetPersonCityById($id: String!) {
|
||||||
|
person: findUniquePerson(id: $id) {
|
||||||
|
id
|
||||||
|
city
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@ -9,7 +9,12 @@ export const GET_PERSON = gql`
|
|||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
displayName
|
displayName
|
||||||
|
email
|
||||||
createdAt
|
createdAt
|
||||||
|
_commentThreadCount
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
6
front/src/modules/people/states/peopleCityFamilyState.ts
Normal file
6
front/src/modules/people/states/peopleCityFamilyState.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const peopleCityFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'peopleCityFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
11
front/src/modules/people/states/peopleCompanyFamilyState.ts
Normal file
11
front/src/modules/people/states/peopleCompanyFamilyState.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { GetPeopleQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export const peopleCompanyFamilyState = atomFamily<
|
||||||
|
GetPeopleQuery['people'][0]['company'] | null,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'peopleCompanyFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const peopleCreatedAtFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'peopleCreatedAtFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const peopleEmailFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'peopleEmailFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { GetPeopleQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export const peopleEntityTableFamilyState = atomFamily<
|
||||||
|
GetPeopleQuery['people'][0] | null,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'peopleEntityTableFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
17
front/src/modules/people/states/peopleNamesFamilyState.ts
Normal file
17
front/src/modules/people/states/peopleNamesFamilyState.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const peopleNameCellFamilyState = atomFamily<
|
||||||
|
{
|
||||||
|
firstName: string | null;
|
||||||
|
lastName: string | null;
|
||||||
|
commentCount: number | null;
|
||||||
|
},
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'peopleNameCellFamilyState',
|
||||||
|
default: {
|
||||||
|
firstName: null,
|
||||||
|
lastName: null,
|
||||||
|
commentCount: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const peoplePhoneFamilyState = atomFamily<string | null, string>({
|
||||||
|
key: 'peoplePhoneFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { peopleCityFamilyState } from '@/people/states/peopleCityFamilyState';
|
||||||
|
import { EditableCellPhone } from '@/ui/components/editable-cell/types/EditableCellPhone';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdatePeopleMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditablePeopleCityCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updatePerson] = useUpdatePeopleMutation();
|
||||||
|
|
||||||
|
const city = useRecoilValue(peopleCityFamilyState(currentRowEntityId ?? ''));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellPhone
|
||||||
|
value={city ?? ''}
|
||||||
|
onChange={async (newCity: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updatePerson({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
city: newCity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { PeopleCompanyCell } from '@/people/components/PeopleCompanyCell';
|
||||||
|
import { peopleCompanyFamilyState } from '@/people/states/peopleCompanyFamilyState';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
|
||||||
|
export function EditablePeopleCompanyCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const company = useRecoilValue(
|
||||||
|
peopleCompanyFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PeopleCompanyCell
|
||||||
|
people={{
|
||||||
|
id: currentRowEntityId ?? '',
|
||||||
|
company: {
|
||||||
|
domainName: company?.domainName ?? '',
|
||||||
|
name: company?.name ?? '',
|
||||||
|
id: company?.id ?? '',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { peopleCreatedAtFamilyState } from '@/people/states/peopleCreatedAtFamilyState';
|
||||||
|
import { EditableCellDate } from '@/ui/components/editable-cell/types/EditableCellDate';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdatePeopleMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditablePeopleCreatedAtCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const createdAt = useRecoilValue(
|
||||||
|
peopleCreatedAtFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [updatePerson] = useUpdatePeopleMutation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellDate
|
||||||
|
onChange={async (newDate: Date) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updatePerson({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
createdAt: newDate.toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={createdAt ? DateTime.fromISO(createdAt).toJSDate() : new Date()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { peopleEmailFamilyState } from '@/people/states/peopleEmailFamilyState';
|
||||||
|
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdatePeopleMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditablePeopleEmailCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updatePerson] = useUpdatePeopleMutation();
|
||||||
|
|
||||||
|
const email = useRecoilValue(
|
||||||
|
peopleEmailFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableCellText
|
||||||
|
value={email ?? ''}
|
||||||
|
onChange={async (newEmail: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updatePerson({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
email: newEmail,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName';
|
||||||
|
import { peopleNameCellFamilyState } from '@/people/states/peopleNamesFamilyState';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdatePeopleMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditablePeopleFullNameCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updatePerson] = useUpdatePeopleMutation();
|
||||||
|
|
||||||
|
const { commentCount, firstName, lastName } = useRecoilValue(
|
||||||
|
peopleNameCellFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditablePeopleFullName
|
||||||
|
person={{
|
||||||
|
id: currentRowEntityId ?? undefined,
|
||||||
|
_commentThreadCount: commentCount ?? undefined,
|
||||||
|
firstName: firstName ?? undefined,
|
||||||
|
lastName: lastName ?? undefined,
|
||||||
|
}}
|
||||||
|
onChange={async (firstName: string, lastName: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updatePerson({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { peoplePhoneFamilyState } from '@/people/states/peoplePhoneFamilyState';
|
||||||
|
import { EditableCellPhone } from '@/ui/components/editable-cell/types/EditableCellPhone';
|
||||||
|
import { useCurrentRowEntityId } from '@/ui/tables/hooks/useCurrentEntityId';
|
||||||
|
import { useUpdatePeopleMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function EditablePeoplePhoneCell() {
|
||||||
|
const currentRowEntityId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [updatePerson] = useUpdatePeopleMutation();
|
||||||
|
|
||||||
|
const phone = useRecoilValue(
|
||||||
|
peoplePhoneFamilyState(currentRowEntityId ?? ''),
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<EditableCellPhone
|
||||||
|
value={phone ?? ''}
|
||||||
|
onChange={async (newPhone: string) => {
|
||||||
|
if (!currentRowEntityId) return;
|
||||||
|
|
||||||
|
await updatePerson({
|
||||||
|
variables: {
|
||||||
|
id: currentRowEntityId,
|
||||||
|
phone: newPhone,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
68
front/src/modules/people/table/components/peopleColumns.tsx
Normal file
68
front/src/modules/people/table/components/peopleColumns.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconCalendarEvent,
|
||||||
|
IconMail,
|
||||||
|
IconMap,
|
||||||
|
IconPhone,
|
||||||
|
IconUser,
|
||||||
|
} from '@/ui/icons/index';
|
||||||
|
|
||||||
|
import { EditablePeopleCityCell } from './EditablePeopleCityCell';
|
||||||
|
import { EditablePeopleCompanyCell } from './EditablePeopleCompanyCell';
|
||||||
|
import { EditablePeopleCreatedAtCell } from './EditablePeopleCreatedAtCell';
|
||||||
|
import { EditablePeopleEmailCell } from './EditablePeopleEmailCell';
|
||||||
|
import { EditablePeopleFullNameCell } from './EditablePeopleFullNameCell';
|
||||||
|
import { EditablePeoplePhoneCell } from './EditablePeoplePhoneCell';
|
||||||
|
|
||||||
|
export type TableColumn = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon: JSX.Element;
|
||||||
|
size: number;
|
||||||
|
cellComponent: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const peopleColumns: TableColumn[] = [
|
||||||
|
{
|
||||||
|
id: 'fullName',
|
||||||
|
title: 'People',
|
||||||
|
icon: <IconUser size={16} />,
|
||||||
|
size: 210,
|
||||||
|
cellComponent: <EditablePeopleFullNameCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'email',
|
||||||
|
title: 'Email',
|
||||||
|
icon: <IconMail size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditablePeopleEmailCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'company',
|
||||||
|
title: 'Company',
|
||||||
|
icon: <IconBuildingSkyscraper size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditablePeopleCompanyCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'phone',
|
||||||
|
title: 'Phone',
|
||||||
|
icon: <IconPhone size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditablePeoplePhoneCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
title: 'Creation',
|
||||||
|
icon: <IconCalendarEvent size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditablePeopleCreatedAtCell />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'city',
|
||||||
|
title: 'City',
|
||||||
|
icon: <IconMap size={16} />,
|
||||||
|
size: 150,
|
||||||
|
cellComponent: <EditablePeopleCityCell />,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import Skeleton from 'react-loading-skeleton';
|
||||||
|
|
||||||
|
export function CellSkeleton() {
|
||||||
|
return (
|
||||||
|
<div style={{ width: '100%', alignItems: 'center' }}>
|
||||||
|
<Skeleton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
import { ReactElement } from 'react';
|
import { ReactElement, useState } from 'react';
|
||||||
|
|
||||||
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
||||||
|
|
||||||
|
import { CellSkeleton } from '../CellSkeleton';
|
||||||
import { EditableCell } from '../EditableCell';
|
import { EditableCell } from '../EditableCell';
|
||||||
|
|
||||||
import { EditableCellDoubleTextEditMode } from './EditableCellDoubleTextEditMode';
|
import { EditableCellDoubleTextEditMode } from './EditableCellDoubleTextEditMode';
|
||||||
@ -13,6 +14,7 @@ type OwnProps = {
|
|||||||
secondValuePlaceholder: string;
|
secondValuePlaceholder: string;
|
||||||
nonEditModeContent: ReactElement;
|
nonEditModeContent: ReactElement;
|
||||||
onChange: (firstValue: string, secondValue: string) => void;
|
onChange: (firstValue: string, secondValue: string) => void;
|
||||||
|
loading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EditableCellDoubleText({
|
export function EditableCellDoubleText({
|
||||||
@ -22,20 +24,30 @@ export function EditableCellDoubleText({
|
|||||||
secondValuePlaceholder,
|
secondValuePlaceholder,
|
||||||
onChange,
|
onChange,
|
||||||
nonEditModeContent,
|
nonEditModeContent,
|
||||||
|
loading,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
|
const [firstInternalValue, setFirstInternalValue] = useState(firstValue);
|
||||||
|
const [secondInternalValue, setSecondInternalValue] = useState(secondValue);
|
||||||
|
|
||||||
|
function handleOnChange(firstValue: string, secondValue: string): void {
|
||||||
|
setFirstInternalValue(firstValue);
|
||||||
|
setSecondInternalValue(secondValue);
|
||||||
|
onChange(firstValue, secondValue);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
editHotkeysScope={{ scope: InternalHotkeysScope.CellDoubleTextInput }}
|
editHotkeysScope={{ scope: InternalHotkeysScope.CellDoubleTextInput }}
|
||||||
editModeContent={
|
editModeContent={
|
||||||
<EditableCellDoubleTextEditMode
|
<EditableCellDoubleTextEditMode
|
||||||
firstValue={firstValue}
|
firstValue={firstInternalValue}
|
||||||
secondValue={secondValue}
|
secondValue={secondInternalValue}
|
||||||
firstValuePlaceholder={firstValuePlaceholder}
|
firstValuePlaceholder={firstValuePlaceholder}
|
||||||
secondValuePlaceholder={secondValuePlaceholder}
|
secondValuePlaceholder={secondValuePlaceholder}
|
||||||
onChange={onChange}
|
onChange={handleOnChange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
nonEditModeContent={nonEditModeContent}
|
nonEditModeContent={loading ? <CellSkeleton /> : nonEditModeContent}
|
||||||
></EditableCell>
|
></EditableCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeEvent, useRef, useState } from 'react';
|
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { InplaceInputPhoneDisplayMode } from '@/ui/inplace-inputs/components/InplaceInputPhoneDisplayMode';
|
import { InplaceInputPhoneDisplayMode } from '@/ui/inplace-inputs/components/InplaceInputPhoneDisplayMode';
|
||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
|
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
|
||||||
@ -8,17 +8,17 @@ import { EditableCell } from '../EditableCell';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
value: string;
|
value: string;
|
||||||
changeHandler: (updated: string) => void;
|
onChange: (updated: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EditableCellPhone({
|
export function EditableCellPhone({ value, placeholder, onChange }: OwnProps) {
|
||||||
value,
|
|
||||||
placeholder,
|
|
||||||
changeHandler,
|
|
||||||
}: OwnProps) {
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [inputValue, setInputValue] = useState(value);
|
const [inputValue, setInputValue] = useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInputValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
editModeContent={
|
editModeContent={
|
||||||
@ -29,7 +29,7 @@ export function EditableCellPhone({
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
changeHandler(event.target.value);
|
onChange(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { ChangeEvent, useMemo, useState } from 'react';
|
import { ChangeEvent, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { InplaceInputTextDisplayMode } from '@/ui/inplace-inputs/components/InplaceInputTextDisplayMode';
|
import { InplaceInputTextDisplayMode } from '@/ui/inplace-inputs/components/InplaceInputTextDisplayMode';
|
||||||
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
|
import { InplaceInputTextEditMode } from '@/ui/inplace-inputs/components/InplaceInputTextEditMode';
|
||||||
import { debounce } from '@/utils/debounce';
|
|
||||||
|
|
||||||
|
import { CellSkeleton } from '../CellSkeleton';
|
||||||
import { EditableCell } from '../EditableCell';
|
import { EditableCell } from '../EditableCell';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -11,6 +11,7 @@ type OwnProps = {
|
|||||||
value: string;
|
value: string;
|
||||||
onChange: (newValue: string) => void;
|
onChange: (newValue: string) => void;
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
editModeHorizontalAlign?: 'left' | 'right';
|
||||||
|
loading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EditableCellText({
|
export function EditableCellText({
|
||||||
@ -18,12 +19,13 @@ export function EditableCellText({
|
|||||||
placeholder,
|
placeholder,
|
||||||
onChange,
|
onChange,
|
||||||
editModeHorizontalAlign,
|
editModeHorizontalAlign,
|
||||||
|
loading,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [internalValue, setInternalValue] = useState(value);
|
const [internalValue, setInternalValue] = useState(value);
|
||||||
|
|
||||||
const debouncedOnChange = useMemo(() => {
|
useEffect(() => {
|
||||||
return debounce(onChange, 200);
|
setInternalValue(value);
|
||||||
}, [onChange]);
|
}, [value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
@ -35,14 +37,18 @@ export function EditableCellText({
|
|||||||
value={internalValue}
|
value={internalValue}
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setInternalValue(event.target.value);
|
setInternalValue(event.target.value);
|
||||||
debouncedOnChange(event.target.value);
|
onChange(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
nonEditModeContent={
|
nonEditModeContent={
|
||||||
<InplaceInputTextDisplayMode>
|
loading ? (
|
||||||
{internalValue}
|
<CellSkeleton />
|
||||||
</InplaceInputTextDisplayMode>
|
) : (
|
||||||
|
<InplaceInputTextDisplayMode>
|
||||||
|
{internalValue}
|
||||||
|
</InplaceInputTextDisplayMode>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
></EditableCell>
|
></EditableCell>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import { ChangeEvent, ComponentType, ReactNode, useRef, useState } from 'react';
|
import {
|
||||||
|
ChangeEvent,
|
||||||
|
ComponentType,
|
||||||
|
ReactNode,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { textInputStyle } from '@/ui/themes/effects';
|
import { textInputStyle } from '@/ui/themes/effects';
|
||||||
@ -55,6 +62,10 @@ export function EditableCellChip({
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [inputValue, setInputValue] = useState(value);
|
const [inputValue, setInputValue] = useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInputValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
const handleRightEndContentClick = (
|
const handleRightEndContentClick = (
|
||||||
event: React.MouseEvent<HTMLDivElement>,
|
event: React.MouseEvent<HTMLDivElement>,
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@ -2,9 +2,7 @@ import * as React from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
name?: string;
|
checked: boolean;
|
||||||
id?: string;
|
|
||||||
checked?: boolean;
|
|
||||||
indeterminate?: boolean;
|
indeterminate?: boolean;
|
||||||
onChange?: (newCheckedValue: boolean) => void;
|
onChange?: (newCheckedValue: boolean) => void;
|
||||||
};
|
};
|
||||||
@ -41,13 +39,7 @@ const StyledContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Checkbox({
|
export function Checkbox({ checked, onChange, indeterminate }: OwnProps) {
|
||||||
name,
|
|
||||||
id,
|
|
||||||
checked,
|
|
||||||
onChange,
|
|
||||||
indeterminate,
|
|
||||||
}: OwnProps) {
|
|
||||||
const ref = React.useRef<HTMLInputElement>(null);
|
const ref = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -57,10 +49,8 @@ export function Checkbox({
|
|||||||
}
|
}
|
||||||
}, [ref, indeterminate, checked]);
|
}, [ref, indeterminate, checked]);
|
||||||
|
|
||||||
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
if (onChange) {
|
onChange?.(event.target.checked);
|
||||||
onChange(event.target.checked);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -69,10 +59,8 @@ export function Checkbox({
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
data-testid="input-checkbox"
|
data-testid="input-checkbox"
|
||||||
id={id}
|
|
||||||
name={name}
|
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={handleInputChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -35,7 +35,6 @@ const StyledChildrenContainer = styled.div`
|
|||||||
export function DropdownMenuCheckableItem({
|
export function DropdownMenuCheckableItem({
|
||||||
checked,
|
checked,
|
||||||
onChange,
|
onChange,
|
||||||
id,
|
|
||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren<Props>) {
|
}: React.PropsWithChildren<Props>) {
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
@ -45,7 +44,7 @@ export function DropdownMenuCheckableItem({
|
|||||||
return (
|
return (
|
||||||
<DropdownMenuCheckableItemContainer onClick={handleClick}>
|
<DropdownMenuCheckableItemContainer onClick={handleClick}>
|
||||||
<StyledLeftContainer>
|
<StyledLeftContainer>
|
||||||
<Checkbox id={id} name={id} checked={checked} />
|
<Checkbox checked={checked} />
|
||||||
<StyledChildrenContainer>{children}</StyledChildrenContainer>
|
<StyledChildrenContainer>{children}</StyledChildrenContainer>
|
||||||
</StyledLeftContainer>
|
</StyledLeftContainer>
|
||||||
</DropdownMenuCheckableItemContainer>
|
</DropdownMenuCheckableItemContainer>
|
||||||
|
|||||||
@ -2,18 +2,11 @@ import * as React from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useCurrentRowSelected } from '@/ui/tables/hooks/useCurrentRowSelected';
|
||||||
import { contextMenuPositionState } from '@/ui/tables/states/contextMenuPositionState';
|
import { contextMenuPositionState } from '@/ui/tables/states/contextMenuPositionState';
|
||||||
|
|
||||||
import { Checkbox } from '../form/Checkbox';
|
import { Checkbox } from '../form/Checkbox';
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
checked?: boolean;
|
|
||||||
indeterminate?: boolean;
|
|
||||||
onChange?: (newCheckedValue: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@ -24,31 +17,19 @@ const StyledContainer = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function CheckboxCell({
|
export function CheckboxCell() {
|
||||||
name,
|
|
||||||
id,
|
|
||||||
checked,
|
|
||||||
onChange,
|
|
||||||
indeterminate,
|
|
||||||
}: OwnProps) {
|
|
||||||
const [internalChecked, setInternalChecked] = React.useState(checked);
|
|
||||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||||
|
|
||||||
|
const { currentRowSelected, setCurrentRowSelected } = useCurrentRowSelected();
|
||||||
|
|
||||||
function handleContainerClick() {
|
function handleContainerClick() {
|
||||||
handleCheckboxChange(!internalChecked);
|
handleCheckboxChange(!currentRowSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
setInternalChecked(checked);
|
|
||||||
}, [checked]);
|
|
||||||
|
|
||||||
function handleCheckboxChange(newCheckedValue: boolean) {
|
function handleCheckboxChange(newCheckedValue: boolean) {
|
||||||
setInternalChecked(newCheckedValue);
|
setCurrentRowSelected(newCheckedValue);
|
||||||
setContextMenuPosition({ x: null, y: null });
|
|
||||||
|
|
||||||
if (onChange) {
|
setContextMenuPosition({ x: null, y: null });
|
||||||
onChange(newCheckedValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -56,13 +37,7 @@ export function CheckboxCell({
|
|||||||
onClick={handleContainerClick}
|
onClick={handleContainerClick}
|
||||||
data-testid="input-checkbox-cell-container"
|
data-testid="input-checkbox-cell-container"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox checked={currentRowSelected} />
|
||||||
id={id}
|
|
||||||
name={name}
|
|
||||||
checked={internalChecked}
|
|
||||||
onChange={handleCheckboxChange}
|
|
||||||
indeterminate={indeterminate}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,17 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from '@tanstack/react-table';
|
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SelectedSortType,
|
SelectedSortType,
|
||||||
SortType,
|
SortType,
|
||||||
} from '@/lib/filters-and-sorts/interfaces/sorts/interface';
|
} from '@/lib/filters-and-sorts/interfaces/sorts/interface';
|
||||||
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
import { TableColumn } from '@/people/table/components/peopleColumns';
|
||||||
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
|
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
|
||||||
import { useLeaveTableFocus } from '@/ui/tables/hooks/useLeaveTableFocus';
|
import { useLeaveTableFocus } from '@/ui/tables/hooks/useLeaveTableFocus';
|
||||||
import { RowContext } from '@/ui/tables/states/RowContext';
|
|
||||||
|
|
||||||
import { currentRowSelectionState } from '../../tables/states/rowSelectionState';
|
|
||||||
|
|
||||||
import { TableHeader } from './table-header/TableHeader';
|
import { TableHeader } from './table-header/TableHeader';
|
||||||
import { EntityTableRow } from './EntityTableRow';
|
import { EntityTableBody } from './EntityTableBody';
|
||||||
|
import { EntityTableHeader } from './EntityTableHeader';
|
||||||
type OwnProps<TData extends { id: string }, SortField> = {
|
|
||||||
data: Array<TData>;
|
|
||||||
columns: Array<ColumnDef<TData, any>>;
|
|
||||||
viewName: string;
|
|
||||||
viewIcon?: React.ReactNode;
|
|
||||||
availableSorts?: Array<SortType<SortField>>;
|
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
|
||||||
onRowSelectionChange?: (rowSelection: string[]) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledTable = styled.table`
|
const StyledTable = styled.table`
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@ -91,32 +72,24 @@ const StyledTableWithHeader = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function EntityTable<TData extends { id: string }, SortField>({
|
type OwnProps<SortField> = {
|
||||||
data,
|
columns: Array<TableColumn>;
|
||||||
|
viewName: string;
|
||||||
|
viewIcon?: React.ReactNode;
|
||||||
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
|
onRowSelectionChange?: (rowSelection: string[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EntityTable<SortField>({
|
||||||
columns,
|
columns,
|
||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
}: OwnProps<TData, SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
const tableBodyRef = React.useRef<HTMLDivElement>(null);
|
const tableBodyRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const [currentRowSelection, setCurrentRowSelection] = useRecoilState(
|
|
||||||
currentRowSelectionState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const table = useReactTable<TData>({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
state: {
|
|
||||||
rowSelection: currentRowSelection,
|
|
||||||
},
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
enableRowSelection: true,
|
|
||||||
onRowSelectionChange: setCurrentRowSelection,
|
|
||||||
getRowId: (row) => row.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const leaveTableFocus = useLeaveTableFocus();
|
const leaveTableFocus = useLeaveTableFocus();
|
||||||
|
|
||||||
useListenClickOutsideArrayOfRef([tableBodyRef], () => {
|
useListenClickOutsideArrayOfRef([tableBodyRef], () => {
|
||||||
@ -133,37 +106,8 @@ export function EntityTable<TData extends { id: string }, SortField>({
|
|||||||
/>
|
/>
|
||||||
<div ref={tableBodyRef}>
|
<div ref={tableBodyRef}>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
<thead>
|
<EntityTableHeader columns={columns} />
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<EntityTableBody columns={columns} />
|
||||||
<tr key={headerGroup.id}>
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<th
|
|
||||||
key={header.id}
|
|
||||||
style={{
|
|
||||||
width: header.column.getSize(),
|
|
||||||
minWidth: header.column.getSize(),
|
|
||||||
maxWidth: header.column.getSize(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext(),
|
|
||||||
)}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{table.getRowModel().rows.map((row, index) => (
|
|
||||||
<RecoilScope SpecificContext={RowContext} key={row.id}>
|
|
||||||
<EntityTableRow row={row} index={index} />
|
|
||||||
</RecoilScope>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</StyledTable>
|
</StyledTable>
|
||||||
</div>
|
</div>
|
||||||
</StyledTableWithHeader>
|
</StyledTableWithHeader>
|
||||||
|
|||||||
33
front/src/modules/ui/components/table/EntityTableBody.tsx
Normal file
33
front/src/modules/ui/components/table/EntityTableBody.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { TableColumn } from '@/people/table/components/peopleColumns';
|
||||||
|
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
||||||
|
import { isFetchingEntityTableDataState } from '@/ui/tables/states/isFetchingEntityTableDataState';
|
||||||
|
import { RowContext } from '@/ui/tables/states/RowContext';
|
||||||
|
import { tableRowIdsState } from '@/ui/tables/states/tableRowIdsState';
|
||||||
|
|
||||||
|
import { EntityTableRow } from './EntityTableRow';
|
||||||
|
|
||||||
|
export function EntityTableBody({ columns }: { columns: Array<TableColumn> }) {
|
||||||
|
const rowIds = useRecoilValue(tableRowIdsState);
|
||||||
|
|
||||||
|
const isFetchingEntityTableData = useRecoilValue(
|
||||||
|
isFetchingEntityTableDataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tbody>
|
||||||
|
{!isFetchingEntityTableData ? (
|
||||||
|
rowIds.map((rowId, index) => (
|
||||||
|
<RecoilScope SpecificContext={RowContext} key={rowId}>
|
||||||
|
<EntityTableRow columns={columns} rowId={rowId} index={index} />
|
||||||
|
</RecoilScope>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td>loading...</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,6 +1,4 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { flexRender } from '@tanstack/react-table';
|
|
||||||
import { Cell, Row } from '@tanstack/table-core';
|
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
|
||||||
@ -9,14 +7,16 @@ import { contextMenuPositionState } from '@/ui/tables/states/contextMenuPosition
|
|||||||
import { currentColumnNumberScopedState } from '@/ui/tables/states/currentColumnNumberScopedState';
|
import { currentColumnNumberScopedState } from '@/ui/tables/states/currentColumnNumberScopedState';
|
||||||
import { currentRowSelectionState } from '@/ui/tables/states/rowSelectionState';
|
import { currentRowSelectionState } from '@/ui/tables/states/rowSelectionState';
|
||||||
|
|
||||||
export function EntityTableCell<TData extends { id: string }>({
|
export function EntityTableCell({
|
||||||
row,
|
rowId,
|
||||||
cell,
|
|
||||||
cellIndex,
|
cellIndex,
|
||||||
|
children,
|
||||||
|
size,
|
||||||
}: {
|
}: {
|
||||||
row: Row<TData>;
|
size: number;
|
||||||
cell: Cell<TData, unknown>;
|
rowId: string;
|
||||||
cellIndex: number;
|
cellIndex: number;
|
||||||
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const [, setCurrentRowSelection] = useRecoilState(currentRowSelectionState);
|
const [, setCurrentRowSelection] = useRecoilState(currentRowSelectionState);
|
||||||
|
|
||||||
@ -43,14 +43,14 @@ export function EntityTableCell<TData extends { id: string }>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
onContextMenu={(event) => handleContextMenu(event, row.original.id)}
|
onContextMenu={(event) => handleContextMenu(event, rowId)}
|
||||||
style={{
|
style={{
|
||||||
width: cell.column.getSize(),
|
width: size,
|
||||||
minWidth: cell.column.getSize(),
|
minWidth: size,
|
||||||
maxWidth: cell.column.getSize(),
|
maxWidth: size,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{children}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
39
front/src/modules/ui/components/table/EntityTableHeader.tsx
Normal file
39
front/src/modules/ui/components/table/EntityTableHeader.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { TableColumn } from '@/people/table/components/peopleColumns';
|
||||||
|
|
||||||
|
import { ColumnHead } from './ColumnHead';
|
||||||
|
import { SelectAllCheckbox } from './SelectAllCheckbox';
|
||||||
|
|
||||||
|
export function EntityTableHeader({
|
||||||
|
columns,
|
||||||
|
}: {
|
||||||
|
columns: Array<TableColumn>;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
minWidth: 30,
|
||||||
|
maxWidth: 30,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectAllCheckbox />
|
||||||
|
</th>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<th
|
||||||
|
key={column.id.toString()}
|
||||||
|
style={{
|
||||||
|
width: column.size,
|
||||||
|
minWidth: column.size,
|
||||||
|
maxWidth: column.size,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ColumnHead viewName={column.title} viewIcon={column.icon} />
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,15 +1,17 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Row } from '@tanstack/table-core';
|
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { TableColumn } from '@/people/table/components/peopleColumns';
|
||||||
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
||||||
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { CellContext } from '@/ui/tables/states/CellContext';
|
import { CellContext } from '@/ui/tables/states/CellContext';
|
||||||
|
import { currentRowEntityIdScopedState } from '@/ui/tables/states/currentRowEntityIdScopedState';
|
||||||
import { currentRowNumberScopedState } from '@/ui/tables/states/currentRowNumberScopedState';
|
import { currentRowNumberScopedState } from '@/ui/tables/states/currentRowNumberScopedState';
|
||||||
import { RowContext } from '@/ui/tables/states/RowContext';
|
import { RowContext } from '@/ui/tables/states/RowContext';
|
||||||
import { currentRowSelectionState } from '@/ui/tables/states/rowSelectionState';
|
import { currentRowSelectionState } from '@/ui/tables/states/rowSelectionState';
|
||||||
|
|
||||||
|
import { CheckboxCell } from './CheckboxCell';
|
||||||
import { EntityTableCell } from './EntityTableCell';
|
import { EntityTableCell } from './EntityTableCell';
|
||||||
|
|
||||||
const StyledRow = styled.tr<{ selected: boolean }>`
|
const StyledRow = styled.tr<{ selected: boolean }>`
|
||||||
@ -17,42 +19,56 @@ const StyledRow = styled.tr<{ selected: boolean }>`
|
|||||||
props.selected ? props.theme.background.secondary : 'none'};
|
props.selected ? props.theme.background.secondary : 'none'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function EntityTableRow<TData extends { id: string }>({
|
export function EntityTableRow({
|
||||||
row,
|
columns,
|
||||||
|
rowId,
|
||||||
index,
|
index,
|
||||||
}: {
|
}: {
|
||||||
row: Row<TData>;
|
columns: TableColumn[];
|
||||||
|
rowId: string;
|
||||||
index: number;
|
index: number;
|
||||||
}) {
|
}) {
|
||||||
const [currentRowSelection] = useRecoilState(currentRowSelectionState);
|
const [currentRowSelection] = useRecoilState(currentRowSelectionState);
|
||||||
|
const [currentRowEntityId, setCurrentRowEntityId] = useRecoilScopedState(
|
||||||
|
currentRowEntityIdScopedState,
|
||||||
|
RowContext,
|
||||||
|
);
|
||||||
|
|
||||||
const [, setCurrentRowNumber] = useRecoilScopedState(
|
const [, setCurrentRowNumber] = useRecoilScopedState(
|
||||||
currentRowNumberScopedState,
|
currentRowNumberScopedState,
|
||||||
RowContext,
|
RowContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentRowEntityId !== rowId) {
|
||||||
|
setCurrentRowEntityId(rowId);
|
||||||
|
}
|
||||||
|
}, [rowId, setCurrentRowEntityId, currentRowEntityId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentRowNumber(index);
|
setCurrentRowNumber(index);
|
||||||
}, [index, setCurrentRowNumber]);
|
}, [index, setCurrentRowNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRow
|
<StyledRow
|
||||||
key={row.id}
|
key={rowId}
|
||||||
data-testid={`row-id-${row.index}`}
|
data-testid={`row-id-${rowId}`}
|
||||||
selected={!!currentRowSelection[row.id]}
|
selected={!!currentRowSelection[rowId]}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell, cellIndex) => {
|
<td>
|
||||||
|
<CheckboxCell />
|
||||||
|
</td>
|
||||||
|
{columns.map((column, columnIndex) => {
|
||||||
return (
|
return (
|
||||||
<RecoilScope
|
<RecoilScope SpecificContext={CellContext} key={column.id.toString()}>
|
||||||
SpecificContext={CellContext}
|
|
||||||
key={cell.id + row.original.id}
|
|
||||||
>
|
|
||||||
<RecoilScope>
|
<RecoilScope>
|
||||||
<EntityTableCell<TData>
|
<EntityTableCell
|
||||||
row={row}
|
rowId={rowId}
|
||||||
cell={cell}
|
size={column.size}
|
||||||
cellIndex={cellIndex}
|
cellIndex={columnIndex}
|
||||||
/>
|
>
|
||||||
|
{column.cellComponent}
|
||||||
|
</EntityTableCell>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,22 +5,19 @@ import { useMapKeyboardToSoftFocus } from '@/ui/tables/hooks/useMapKeyboardToSof
|
|||||||
|
|
||||||
export function HooksEntityTable({
|
export function HooksEntityTable({
|
||||||
numberOfColumns,
|
numberOfColumns,
|
||||||
numberOfRows,
|
availableFilters,
|
||||||
availableTableFilters,
|
|
||||||
}: {
|
}: {
|
||||||
numberOfColumns: number;
|
numberOfColumns: number;
|
||||||
numberOfRows: number;
|
availableFilters: FilterDefinition[];
|
||||||
availableTableFilters: FilterDefinition[];
|
|
||||||
}) {
|
}) {
|
||||||
useMapKeyboardToSoftFocus();
|
useMapKeyboardToSoftFocus();
|
||||||
|
|
||||||
useInitializeEntityTable({
|
useInitializeEntityTable({
|
||||||
numberOfColumns,
|
numberOfColumns,
|
||||||
numberOfRows,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useInitializeEntityTableFilters({
|
useInitializeEntityTableFilters({
|
||||||
availableTableFilters,
|
availableFilters,
|
||||||
});
|
});
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -1,18 +1,36 @@
|
|||||||
import { CheckboxCell } from './CheckboxCell';
|
import React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { useSelectAllRows } from '@/ui/tables/hooks/useSelectAllRows';
|
||||||
|
|
||||||
|
import { Checkbox } from '../form/Checkbox';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SelectAllCheckbox = () => {
|
||||||
|
const { selectAllRows, allRowsSelectedStatus } = useSelectAllRows();
|
||||||
|
|
||||||
|
function handleContainerClick() {
|
||||||
|
selectAllRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
const checked = allRowsSelectedStatus === 'all';
|
||||||
|
const indeterminate = allRowsSelectedStatus === 'some';
|
||||||
|
|
||||||
export const SelectAllCheckbox = ({
|
|
||||||
indeterminate,
|
|
||||||
onChange,
|
|
||||||
}: {
|
|
||||||
indeterminate?: boolean;
|
|
||||||
onChange?: (newCheckedValue: boolean) => void;
|
|
||||||
} & React.HTMLProps<HTMLInputElement>) => {
|
|
||||||
return (
|
return (
|
||||||
<CheckboxCell
|
<StyledContainer
|
||||||
name="select-all-checkbox"
|
onClick={handleContainerClick}
|
||||||
id="select-all-checkbox"
|
data-testid="input-checkbox-cell-container"
|
||||||
indeterminate={indeterminate}
|
>
|
||||||
onChange={onChange}
|
<Checkbox checked={checked} indeterminate={indeterminate} />
|
||||||
/>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export const TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN = 1;
|
|
||||||
18
front/src/modules/ui/tables/hooks/useCurrentEntityId.ts
Normal file
18
front/src/modules/ui/tables/hooks/useCurrentEntityId.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
|
import { currentRowEntityIdScopedState } from '../states/currentRowEntityIdScopedState';
|
||||||
|
import { RowContext } from '../states/RowContext';
|
||||||
|
|
||||||
|
export type TableDimensions = {
|
||||||
|
numberOfColumns: number;
|
||||||
|
numberOfRows: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useCurrentRowEntityId() {
|
||||||
|
const currentRowEntityIdScoped = useRecoilScopedValue(
|
||||||
|
currentRowEntityIdScopedState,
|
||||||
|
RowContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
return currentRowEntityIdScoped;
|
||||||
|
}
|
||||||
43
front/src/modules/ui/tables/hooks/useCurrentRowSelected.ts
Normal file
43
front/src/modules/ui/tables/hooks/useCurrentRowSelected.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { isRowSelectedFamilyState } from '../states/isRowSelectedFamilyState';
|
||||||
|
import { numberOfSelectedRowState } from '../states/numberOfSelectedRowState';
|
||||||
|
|
||||||
|
import { useCurrentRowEntityId } from './useCurrentEntityId';
|
||||||
|
|
||||||
|
export function useCurrentRowSelected() {
|
||||||
|
const currentRowId = useCurrentRowEntityId();
|
||||||
|
|
||||||
|
const [isRowSelected] = useRecoilState(
|
||||||
|
isRowSelectedFamilyState(currentRowId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentRowSelected = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(newSelectedState: boolean) => {
|
||||||
|
if (!currentRowId) return;
|
||||||
|
|
||||||
|
const isRowSelected = snapshot
|
||||||
|
.getLoadable(isRowSelectedFamilyState(currentRowId))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const numberOfSelectedRow = snapshot
|
||||||
|
.getLoadable(numberOfSelectedRowState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (newSelectedState && !isRowSelected) {
|
||||||
|
set(numberOfSelectedRowState, numberOfSelectedRow + 1);
|
||||||
|
set(isRowSelectedFamilyState(currentRowId), true);
|
||||||
|
} else if (!newSelectedState && isRowSelected) {
|
||||||
|
set(numberOfSelectedRowState, numberOfSelectedRow - 1);
|
||||||
|
set(isRowSelectedFamilyState(currentRowId), false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentRowId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentRowSelected: isRowSelected,
|
||||||
|
setCurrentRowSelected,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,21 +1,25 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { entityTableDimensionsState } from '../states/entityTableDimensionsState';
|
import { entityTableDimensionsState } from '../states/entityTableDimensionsState';
|
||||||
|
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||||
|
|
||||||
import { useResetTableRowSelection } from './useResetTableRowSelection';
|
import { useResetTableRowSelection } from './useResetTableRowSelection';
|
||||||
|
|
||||||
export type TableDimensions = {
|
export type TableDimensions = {
|
||||||
numberOfRows: number;
|
|
||||||
numberOfColumns: number;
|
numberOfColumns: number;
|
||||||
|
numberOfRows: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useInitializeEntityTable({
|
export function useInitializeEntityTable({
|
||||||
numberOfRows,
|
|
||||||
numberOfColumns,
|
numberOfColumns,
|
||||||
}: TableDimensions) {
|
}: {
|
||||||
|
numberOfColumns: number;
|
||||||
|
}) {
|
||||||
const resetTableRowSelection = useResetTableRowSelection();
|
const resetTableRowSelection = useResetTableRowSelection();
|
||||||
|
|
||||||
|
const tableRowIds = useRecoilValue(tableRowIdsState);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
resetTableRowSelection();
|
resetTableRowSelection();
|
||||||
}, [resetTableRowSelection]);
|
}, [resetTableRowSelection]);
|
||||||
@ -25,7 +29,7 @@ export function useInitializeEntityTable({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTableDimensions({
|
setTableDimensions({
|
||||||
numberOfColumns,
|
numberOfColumns,
|
||||||
numberOfRows,
|
numberOfRows: tableRowIds?.length,
|
||||||
});
|
});
|
||||||
}, [numberOfRows, numberOfColumns, setTableDimensions]);
|
}, [tableRowIds, numberOfColumns, setTableDimensions]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,16 +7,16 @@ import { useRecoilScopedState } from '@/recoil-scope/hooks/useRecoilScopedState'
|
|||||||
import { TableContext } from '../states/TableContext';
|
import { TableContext } from '../states/TableContext';
|
||||||
|
|
||||||
export function useInitializeEntityTableFilters({
|
export function useInitializeEntityTableFilters({
|
||||||
availableTableFilters,
|
availableFilters,
|
||||||
}: {
|
}: {
|
||||||
availableTableFilters: FilterDefinition[];
|
availableFilters: FilterDefinition[];
|
||||||
}) {
|
}) {
|
||||||
const [, setAvailableTableFilters] = useRecoilScopedState(
|
const [, setAvailableFilters] = useRecoilScopedState(
|
||||||
availableFiltersScopedState,
|
availableFiltersScopedState,
|
||||||
TableContext,
|
TableContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAvailableTableFilters(availableTableFilters);
|
setAvailableFilters(availableFilters);
|
||||||
}, [setAvailableTableFilters, availableTableFilters]);
|
}, [setAvailableFilters, availableFilters]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN } from '../constants';
|
|
||||||
import { numberOfTableColumnsSelectorState } from '../states/numberOfTableColumnsSelectorState';
|
import { numberOfTableColumnsSelectorState } from '../states/numberOfTableColumnsSelectorState';
|
||||||
import { numberOfTableRowsSelectorState } from '../states/numberOfTableRowsSelectorState';
|
import { numberOfTableRowsSelectorState } from '../states/numberOfTableRowsSelectorState';
|
||||||
import { softFocusPositionState } from '../states/softFocusPositionState';
|
import { softFocusPositionState } from '../states/softFocusPositionState';
|
||||||
@ -98,7 +97,7 @@ export function useMoveSoftFocus() {
|
|||||||
} else if (isLastColumnButNotLastRow) {
|
} else if (isLastColumnButNotLastRow) {
|
||||||
setSoftFocusPosition({
|
setSoftFocusPosition({
|
||||||
row: currentRowNumber + 1,
|
row: currentRowNumber + 1,
|
||||||
column: TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN,
|
column: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -120,18 +119,12 @@ export function useMoveSoftFocus() {
|
|||||||
const currentRowNumber = softFocusPosition.row;
|
const currentRowNumber = softFocusPosition.row;
|
||||||
|
|
||||||
const isFirstRowAndFirstColumn =
|
const isFirstRowAndFirstColumn =
|
||||||
currentColumnNumber ===
|
currentColumnNumber === 0 && currentRowNumber === 0;
|
||||||
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN &&
|
|
||||||
currentRowNumber === 0;
|
|
||||||
|
|
||||||
const isFirstColumnButNotFirstRow =
|
const isFirstColumnButNotFirstRow =
|
||||||
currentColumnNumber ===
|
currentColumnNumber === 0 && currentRowNumber > 0;
|
||||||
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN &&
|
|
||||||
currentRowNumber > 0;
|
|
||||||
|
|
||||||
const isNotFirstColumn =
|
const isNotFirstColumn = currentColumnNumber > 0;
|
||||||
currentColumnNumber >
|
|
||||||
TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN;
|
|
||||||
|
|
||||||
if (isFirstRowAndFirstColumn) {
|
if (isFirstRowAndFirstColumn) {
|
||||||
return;
|
return;
|
||||||
@ -149,7 +142,7 @@ export function useMoveSoftFocus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setSoftFocusPosition, TABLE_MIN_COLUMN_NUMBER_BECAUSE_OF_CHECKBOX_COLUMN],
|
[setSoftFocusPosition],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
48
front/src/modules/ui/tables/hooks/useSelectAllRows.ts
Normal file
48
front/src/modules/ui/tables/hooks/useSelectAllRows.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { allRowsSelectedStatusSelector } from '../states/allRowsSelectedStatusSelector';
|
||||||
|
import { isRowSelectedFamilyState } from '../states/isRowSelectedFamilyState';
|
||||||
|
import { numberOfSelectedRowState } from '../states/numberOfSelectedRowState';
|
||||||
|
import { numberOfTableRowsSelectorState } from '../states/numberOfTableRowsSelectorState';
|
||||||
|
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||||
|
|
||||||
|
export function useSelectAllRows() {
|
||||||
|
const allRowsSelectedStatus = useRecoilValue(allRowsSelectedStatusSelector);
|
||||||
|
|
||||||
|
const selectAllRows = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
() => {
|
||||||
|
const allRowsSelectedStatus = snapshot
|
||||||
|
.getLoadable(allRowsSelectedStatusSelector)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const numberOfRows = snapshot
|
||||||
|
.getLoadable(numberOfTableRowsSelectorState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const tableRowIds = snapshot
|
||||||
|
.getLoadable(tableRowIdsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (allRowsSelectedStatus === 'none') {
|
||||||
|
set(numberOfSelectedRowState, numberOfRows);
|
||||||
|
|
||||||
|
for (const rowId of tableRowIds) {
|
||||||
|
set(isRowSelectedFamilyState(rowId), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set(numberOfSelectedRowState, 0);
|
||||||
|
|
||||||
|
for (const rowId of tableRowIds) {
|
||||||
|
set(isRowSelectedFamilyState(rowId), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
allRowsSelectedStatus,
|
||||||
|
selectAllRows,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { selector } from 'recoil';
|
||||||
|
|
||||||
|
import { AllRowsSelectedStatus } from '../types/AllRowSelectedStatus';
|
||||||
|
|
||||||
|
import { numberOfSelectedRowState } from './numberOfSelectedRowState';
|
||||||
|
import { numberOfTableRowsSelectorState } from './numberOfTableRowsSelectorState';
|
||||||
|
|
||||||
|
export const allRowsSelectedStatusSelector = selector<AllRowsSelectedStatus>({
|
||||||
|
key: 'allRowsSelectedStatusSelector',
|
||||||
|
get: ({ get }) => {
|
||||||
|
const numberOfRows = get(numberOfTableRowsSelectorState);
|
||||||
|
|
||||||
|
const numberOfSelectedRows = get(numberOfSelectedRowState);
|
||||||
|
|
||||||
|
const allRowsSelectedStatus =
|
||||||
|
numberOfSelectedRows === 0
|
||||||
|
? 'none'
|
||||||
|
: numberOfRows === numberOfSelectedRows
|
||||||
|
? 'all'
|
||||||
|
: 'some';
|
||||||
|
|
||||||
|
return allRowsSelectedStatus;
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const currentRowEntityIdScopedState = atomFamily<string | null, string>({
|
||||||
|
key: 'currentRowEntityIdScopedState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
export const isFetchingEntityTableDataState = atom<boolean>({
|
||||||
|
key: 'isFetchingEntityTableDataState',
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const isRowSelectedFamilyState = atomFamily<boolean, string>({
|
||||||
|
key: 'isRowSelectedFamilyState',
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
export const numberOfSelectedRowState = atom<number>({
|
||||||
|
key: 'numberOfSelectedRowState',
|
||||||
|
default: 0,
|
||||||
|
});
|
||||||
6
front/src/modules/ui/tables/states/tableRowIdsState.ts
Normal file
6
front/src/modules/ui/tables/states/tableRowIdsState.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
export const tableRowIdsState = atom<string[]>({
|
||||||
|
key: 'tableRowIdsState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export type AllRowsSelectedStatus = 'none' | 'some' | 'all';
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { CellContext } from '@tanstack/react-table';
|
|
||||||
|
|
||||||
import { CheckboxCell } from '@/ui/components/table/CheckboxCell';
|
|
||||||
import { SelectAllCheckbox } from '@/ui/components/table/SelectAllCheckbox';
|
|
||||||
|
|
||||||
export function getCheckBoxColumn() {
|
|
||||||
return {
|
|
||||||
id: 'select',
|
|
||||||
header: ({ table }: any) => (
|
|
||||||
<SelectAllCheckbox
|
|
||||||
checked={table.getIsAllRowsSelected()}
|
|
||||||
indeterminate={table.getIsSomeRowsSelected()}
|
|
||||||
onChange={(newValue) => table.toggleAllRowsSelected(newValue)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props: CellContext<any, string>) => (
|
|
||||||
<CheckboxCell
|
|
||||||
id={`checkbox-selected-${props.row.original.id}`}
|
|
||||||
name={`checkbox-selected-${props.row.original.id}`}
|
|
||||||
checked={props.row.getIsSelected()}
|
|
||||||
onChange={(newValue) => props.row.toggleSelected(newValue)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 32,
|
|
||||||
maxSize: 32,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -4,8 +4,9 @@ import { IconList } from '@tabler/icons-react';
|
|||||||
import {
|
import {
|
||||||
CompaniesSelectedSortType,
|
CompaniesSelectedSortType,
|
||||||
defaultOrderBy,
|
defaultOrderBy,
|
||||||
useCompaniesQuery,
|
|
||||||
} from '@/companies/services';
|
} from '@/companies/services';
|
||||||
|
import { companyColumns } from '@/companies/table/components/companyColumns';
|
||||||
|
import { CompanyEntityTableData } from '@/companies/table/components/CompanyEntityTableData';
|
||||||
import { reduceSortsToOrderBy } from '@/lib/filters-and-sorts/helpers';
|
import { reduceSortsToOrderBy } from '@/lib/filters-and-sorts/helpers';
|
||||||
import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScopedState';
|
import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/lib/filters-and-sorts/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/lib/filters-and-sorts/utils/turnFilterIntoWhereClause';
|
||||||
@ -15,7 +16,6 @@ import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
|||||||
import { TableContext } from '@/ui/tables/states/TableContext';
|
import { TableContext } from '@/ui/tables/states/TableContext';
|
||||||
import { CompanyOrderByWithRelationInput } from '~/generated/graphql';
|
import { CompanyOrderByWithRelationInput } from '~/generated/graphql';
|
||||||
|
|
||||||
import { useCompaniesColumns } from './companies-columns';
|
|
||||||
import { companiesFilters } from './companies-filters';
|
import { companiesFilters } from './companies-filters';
|
||||||
import { availableSorts } from './companies-sorts';
|
import { availableSorts } from './companies-sorts';
|
||||||
|
|
||||||
@ -30,27 +30,18 @@ export function CompanyTable() {
|
|||||||
const filters = useRecoilScopedValue(filtersScopedState, TableContext);
|
const filters = useRecoilScopedValue(filtersScopedState, TableContext);
|
||||||
|
|
||||||
const whereFilters = useMemo(() => {
|
const whereFilters = useMemo(() => {
|
||||||
if (!filters.length) return undefined;
|
|
||||||
|
|
||||||
return { AND: filters.map(turnFilterIntoWhereClause) };
|
return { AND: filters.map(turnFilterIntoWhereClause) };
|
||||||
}, [filters]) as any;
|
}, [filters]) as any;
|
||||||
|
|
||||||
const companiesColumns = useCompaniesColumns();
|
|
||||||
|
|
||||||
const { data } = useCompaniesQuery(orderBy, whereFilters);
|
|
||||||
|
|
||||||
const companies = data?.companies ?? [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<CompanyEntityTableData orderBy={orderBy} whereFilters={whereFilters} />
|
||||||
<HooksEntityTable
|
<HooksEntityTable
|
||||||
numberOfColumns={companiesColumns.length}
|
numberOfColumns={companyColumns.length}
|
||||||
numberOfRows={companies.length}
|
availableFilters={companiesFilters}
|
||||||
availableTableFilters={companiesFilters}
|
|
||||||
/>
|
/>
|
||||||
<EntityTable
|
<EntityTable
|
||||||
data={companies}
|
columns={companyColumns}
|
||||||
columns={companiesColumns}
|
|
||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
|
|||||||
@ -1,28 +1,21 @@
|
|||||||
import { IconList } from '@tabler/icons-react';
|
import { IconList } from '@tabler/icons-react';
|
||||||
|
|
||||||
|
import { companyColumns } from '@/companies/table/components/companyColumns';
|
||||||
import { EntityTable } from '@/ui/components/table/EntityTable';
|
import { EntityTable } from '@/ui/components/table/EntityTable';
|
||||||
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
||||||
import { mockedCompaniesData } from '~/testing/mock-data/companies';
|
|
||||||
|
|
||||||
import { useCompaniesColumns } from './companies-columns';
|
|
||||||
import { companiesFilters } from './companies-filters';
|
import { companiesFilters } from './companies-filters';
|
||||||
import { availableSorts } from './companies-sorts';
|
import { availableSorts } from './companies-sorts';
|
||||||
|
|
||||||
export function CompanyTableMockMode() {
|
export function CompanyTableMockMode() {
|
||||||
const companiesColumns = useCompaniesColumns();
|
|
||||||
|
|
||||||
const companies = mockedCompaniesData;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HooksEntityTable
|
<HooksEntityTable
|
||||||
numberOfColumns={companiesColumns.length}
|
numberOfColumns={companyColumns.length}
|
||||||
numberOfRows={companies.length}
|
availableFilters={companiesFilters}
|
||||||
availableTableFilters={companiesFilters}
|
|
||||||
/>
|
/>
|
||||||
<EntityTable
|
<EntityTable
|
||||||
data={companies}
|
columns={companyColumns}
|
||||||
columns={companiesColumns}
|
|
||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
|
|||||||
@ -31,12 +31,6 @@ export const SortByName: Story = {
|
|||||||
|
|
||||||
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
||||||
|
|
||||||
expect(
|
|
||||||
(await canvas.findAllByRole('checkbox')).map((item) => {
|
|
||||||
return item.getAttribute('id');
|
|
||||||
})[1],
|
|
||||||
).toStrictEqual('checkbox-selected-89bb825c-171e-4bcc-9cf7-43448d6fb278');
|
|
||||||
|
|
||||||
const cancelButton = canvas.getByText('Cancel');
|
const cancelButton = canvas.getByText('Cancel');
|
||||||
await userEvent.click(cancelButton);
|
await userEvent.click(cancelButton);
|
||||||
|
|
||||||
|
|||||||
@ -1,150 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
|
||||||
|
|
||||||
import { CompanyAccountOwnerCell } from '@/companies/components/CompanyAccountOwnerCell';
|
|
||||||
import { CompanyEditableNameChipCell } from '@/companies/components/CompanyEditableNameCell';
|
|
||||||
import { EditableCellDate } from '@/ui/components/editable-cell/types/EditableCellDate';
|
|
||||||
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
|
||||||
import { ColumnHead } from '@/ui/components/table/ColumnHead';
|
|
||||||
import {
|
|
||||||
IconBuildingSkyscraper,
|
|
||||||
IconCalendarEvent,
|
|
||||||
IconLink,
|
|
||||||
IconMap,
|
|
||||||
IconUser,
|
|
||||||
IconUsers,
|
|
||||||
} from '@/ui/icons/index';
|
|
||||||
import { getCheckBoxColumn } from '@/ui/tables/utils/getCheckBoxColumn';
|
|
||||||
import {
|
|
||||||
GetCompaniesQuery,
|
|
||||||
useUpdateCompanyMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<GetCompaniesQuery['companies'][0]>();
|
|
||||||
|
|
||||||
export const useCompaniesColumns = () => {
|
|
||||||
const [updateCompany] = useUpdateCompanyMutation();
|
|
||||||
return useMemo(() => {
|
|
||||||
return [
|
|
||||||
getCheckBoxColumn(),
|
|
||||||
columnHelper.accessor('name', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead
|
|
||||||
viewName="Name"
|
|
||||||
viewIcon={<IconBuildingSkyscraper size={16} />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<CompanyEditableNameChipCell company={props.row.original} />
|
|
||||||
),
|
|
||||||
size: 180,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('domainName', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="URL" viewIcon={<IconLink size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellText
|
|
||||||
value={props.row.original.domainName || ''}
|
|
||||||
placeholder="Domain name"
|
|
||||||
onChange={(value) => {
|
|
||||||
const company = { ...props.row.original };
|
|
||||||
company.domainName = value;
|
|
||||||
updateCompany({
|
|
||||||
variables: {
|
|
||||||
...company,
|
|
||||||
accountOwnerId: company.accountOwner?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 100,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('employees', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="Employees" viewIcon={<IconUsers size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellText
|
|
||||||
value={props.row.original.employees?.toString() || ''}
|
|
||||||
placeholder="Employees"
|
|
||||||
onChange={(value) => {
|
|
||||||
const company = { ...props.row.original };
|
|
||||||
|
|
||||||
updateCompany({
|
|
||||||
variables: {
|
|
||||||
...company,
|
|
||||||
employees: value === '' ? null : Number(value),
|
|
||||||
accountOwnerId: company.accountOwner?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 150,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('address', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="Address" viewIcon={<IconMap size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellText
|
|
||||||
value={props.row.original.address || ''}
|
|
||||||
placeholder="Address"
|
|
||||||
onChange={(value) => {
|
|
||||||
const company = { ...props.row.original };
|
|
||||||
company.address = value;
|
|
||||||
updateCompany({
|
|
||||||
variables: {
|
|
||||||
...company,
|
|
||||||
accountOwnerId: company.accountOwner?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 170,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('createdAt', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead
|
|
||||||
viewName="Creation"
|
|
||||||
viewIcon={<IconCalendarEvent size={16} />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellDate
|
|
||||||
value={
|
|
||||||
props.row.original.createdAt
|
|
||||||
? new Date(props.row.original.createdAt)
|
|
||||||
: new Date()
|
|
||||||
}
|
|
||||||
onChange={(value: Date) => {
|
|
||||||
const company = { ...props.row.original };
|
|
||||||
company.createdAt = value.toISOString();
|
|
||||||
updateCompany({
|
|
||||||
variables: {
|
|
||||||
...company,
|
|
||||||
accountOwnerId: company.accountOwner?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 150,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('accountOwner', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead
|
|
||||||
viewName="Account owner"
|
|
||||||
viewIcon={<IconUser size={16} />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<CompanyAccountOwnerCell company={props.row.original} />
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}, [updateCompany]);
|
|
||||||
};
|
|
||||||
@ -13,30 +13,25 @@ export const availableSorts = [
|
|||||||
key: 'name',
|
key: 'name',
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
icon: <IconBuildingSkyscraper size={16} />,
|
icon: <IconBuildingSkyscraper size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'employees',
|
key: 'employees',
|
||||||
label: 'Employees',
|
label: 'Employees',
|
||||||
icon: <IconUsers size={16} />,
|
icon: <IconUsers size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'domainName',
|
key: 'domainName',
|
||||||
label: 'Url',
|
label: 'Url',
|
||||||
icon: <IconLink size={16} />,
|
icon: <IconLink size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'address',
|
key: 'address',
|
||||||
label: 'Address',
|
label: 'Address',
|
||||||
icon: <IconMap size={16} />,
|
icon: <IconMap size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'createdAt',
|
key: 'createdAt',
|
||||||
label: 'Creation',
|
label: 'Creation',
|
||||||
icon: <IconCalendarEvent size={16} />,
|
icon: <IconCalendarEvent size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
] satisfies Array<SortType<Companies_Order_By>>;
|
] satisfies Array<SortType<Companies_Order_By>>;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|||||||
import { GET_PEOPLE } from '@/people/services';
|
import { GET_PEOPLE } from '@/people/services';
|
||||||
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
||||||
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
|
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
|
||||||
import { IconUser } from '@/ui/icons/index';
|
import { IconBuildingSkyscraper } from '@/ui/icons/index';
|
||||||
import { FlexExpandingContainer } from '@/ui/layout/containers/FlexExpandingContainer';
|
import { FlexExpandingContainer } from '@/ui/layout/containers/FlexExpandingContainer';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||||
import { TableContext } from '@/ui/tables/states/TableContext';
|
import { TableContext } from '@/ui/tables/states/TableContext';
|
||||||
@ -38,8 +38,8 @@ export function People() {
|
|||||||
return (
|
return (
|
||||||
<RecoilScope SpecificContext={TableContext}>
|
<RecoilScope SpecificContext={TableContext}>
|
||||||
<WithTopBarContainer
|
<WithTopBarContainer
|
||||||
title="People"
|
title="Companies"
|
||||||
icon={<IconUser size={theme.icon.size.md} />}
|
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
||||||
onAddButtonClick={handleAddButtonClick}
|
onAddButtonClick={handleAddButtonClick}
|
||||||
>
|
>
|
||||||
<FlexExpandingContainer>
|
<FlexExpandingContainer>
|
||||||
|
|||||||
@ -5,14 +5,15 @@ import { defaultOrderBy } from '@/companies/services';
|
|||||||
import { reduceSortsToOrderBy } from '@/lib/filters-and-sorts/helpers';
|
import { reduceSortsToOrderBy } from '@/lib/filters-and-sorts/helpers';
|
||||||
import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScopedState';
|
import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/lib/filters-and-sorts/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/lib/filters-and-sorts/utils/turnFilterIntoWhereClause';
|
||||||
import { PeopleSelectedSortType, usePeopleQuery } from '@/people/services';
|
import { PeopleEntityTableData } from '@/people/components/PeopleEntityTableData';
|
||||||
|
import { PeopleSelectedSortType } from '@/people/services';
|
||||||
|
import { peopleColumns } from '@/people/table/components/peopleColumns';
|
||||||
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { EntityTable } from '@/ui/components/table/EntityTable';
|
import { EntityTable } from '@/ui/components/table/EntityTable';
|
||||||
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
||||||
import { TableContext } from '@/ui/tables/states/TableContext';
|
import { TableContext } from '@/ui/tables/states/TableContext';
|
||||||
import { PersonOrderByWithRelationInput } from '~/generated/graphql';
|
import { PersonOrderByWithRelationInput } from '~/generated/graphql';
|
||||||
|
|
||||||
import { usePeopleColumns } from './people-columns';
|
|
||||||
import { peopleFilters } from './people-filters';
|
import { peopleFilters } from './people-filters';
|
||||||
import { availableSorts } from './people-sorts';
|
import { availableSorts } from './people-sorts';
|
||||||
|
|
||||||
@ -30,21 +31,14 @@ export function PeopleTable() {
|
|||||||
return { AND: filters.map(turnFilterIntoWhereClause) };
|
return { AND: filters.map(turnFilterIntoWhereClause) };
|
||||||
}, [filters]) as any;
|
}, [filters]) as any;
|
||||||
|
|
||||||
const peopleColumns = usePeopleColumns();
|
|
||||||
|
|
||||||
const { data } = usePeopleQuery(orderBy, whereFilters);
|
|
||||||
|
|
||||||
const people = data?.people ?? [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PeopleEntityTableData orderBy={orderBy} whereFilters={whereFilters} />
|
||||||
<HooksEntityTable
|
<HooksEntityTable
|
||||||
numberOfColumns={peopleColumns.length}
|
numberOfColumns={peopleColumns.length}
|
||||||
numberOfRows={people.length}
|
availableFilters={peopleFilters}
|
||||||
availableTableFilters={peopleFilters}
|
|
||||||
/>
|
/>
|
||||||
<EntityTable
|
<EntityTable
|
||||||
data={people}
|
|
||||||
columns={peopleColumns}
|
columns={peopleColumns}
|
||||||
viewName="All People"
|
viewName="All People"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
|
|||||||
@ -32,18 +32,12 @@ export const InteractWithManyRows: Story = {
|
|||||||
|
|
||||||
let firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
let firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
||||||
|
|
||||||
let secondRowEmailCell = await canvas.findByText(mockedPeopleData[1].email);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
|
|
||||||
await userEvent.click(firstRowEmailCell);
|
await userEvent.click(firstRowEmailCell);
|
||||||
|
|
||||||
await sleep(100);
|
|
||||||
firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
|
||||||
await userEvent.click(firstRowEmailCell);
|
|
||||||
await sleep(100);
|
|
||||||
firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
||||||
await userEvent.click(firstRowEmailCell);
|
await userEvent.click(firstRowEmailCell);
|
||||||
|
|
||||||
@ -51,7 +45,9 @@ export const InteractWithManyRows: Story = {
|
|||||||
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
|
||||||
secondRowEmailCell = await canvas.findByText(mockedPeopleData[1].email);
|
const secondRowEmailCell = await canvas.findByText(
|
||||||
|
mockedPeopleData[1].email,
|
||||||
|
);
|
||||||
await userEvent.click(secondRowEmailCell);
|
await userEvent.click(secondRowEmailCell);
|
||||||
|
|
||||||
await sleep(25);
|
await sleep(25);
|
||||||
|
|||||||
@ -31,12 +31,6 @@ export const Email: Story = {
|
|||||||
expect(await canvas.getByTestId('remove-icon-email')).toBeInTheDocument();
|
expect(await canvas.getByTestId('remove-icon-email')).toBeInTheDocument();
|
||||||
|
|
||||||
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
|
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
|
||||||
|
|
||||||
expect(
|
|
||||||
(await canvas.findAllByRole('checkbox')).map((item) => {
|
|
||||||
return item.getAttribute('id');
|
|
||||||
})[1],
|
|
||||||
).toStrictEqual('checkbox-selected-7dfbc3f7-6e5e-4128-957e-8d86808cdf6b');
|
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,159 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
|
||||||
|
|
||||||
import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName';
|
|
||||||
import { PeopleCompanyCell } from '@/people/components/PeopleCompanyCell';
|
|
||||||
import { EditableCellDate } from '@/ui/components/editable-cell/types/EditableCellDate';
|
|
||||||
import { EditableCellPhone } from '@/ui/components/editable-cell/types/EditableCellPhone';
|
|
||||||
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
|
||||||
import { ColumnHead } from '@/ui/components/table/ColumnHead';
|
|
||||||
import {
|
|
||||||
IconBuildingSkyscraper,
|
|
||||||
IconCalendarEvent,
|
|
||||||
IconMail,
|
|
||||||
IconMap,
|
|
||||||
IconPhone,
|
|
||||||
IconUser,
|
|
||||||
} from '@/ui/icons/index';
|
|
||||||
import { getCheckBoxColumn } from '@/ui/tables/utils/getCheckBoxColumn';
|
|
||||||
import { GetPeopleQuery, useUpdatePeopleMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<GetPeopleQuery['people'][0]>();
|
|
||||||
|
|
||||||
export const usePeopleColumns = () => {
|
|
||||||
const [updatePerson] = useUpdatePeopleMutation();
|
|
||||||
|
|
||||||
return useMemo(() => {
|
|
||||||
return [
|
|
||||||
getCheckBoxColumn(),
|
|
||||||
columnHelper.accessor('firstName', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="People" viewIcon={<IconUser size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<>
|
|
||||||
<EditablePeopleFullName
|
|
||||||
person={props.row.original}
|
|
||||||
onChange={async (firstName: string, lastName: string) => {
|
|
||||||
const person = { ...props.row.original };
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
...person,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
companyId: person.company?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
size: 210,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('email', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="Email" viewIcon={<IconMail size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellText
|
|
||||||
placeholder="Email"
|
|
||||||
value={props.row.original.email || ''}
|
|
||||||
onChange={async (value: string) => {
|
|
||||||
const person = props.row.original;
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
...person,
|
|
||||||
email: value,
|
|
||||||
companyId: person.company?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 200,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('company', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead
|
|
||||||
viewName="Company"
|
|
||||||
viewIcon={<IconBuildingSkyscraper size={16} />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props) => <PeopleCompanyCell people={props.row.original} />,
|
|
||||||
size: 150,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('phone', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="Phone" viewIcon={<IconPhone size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellPhone
|
|
||||||
placeholder="Phone"
|
|
||||||
value={props.row.original.phone || ''}
|
|
||||||
changeHandler={async (value: string) => {
|
|
||||||
const person = { ...props.row.original };
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
...person,
|
|
||||||
phone: value,
|
|
||||||
companyId: person.company?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 130,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('createdAt', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead
|
|
||||||
viewName="Creation"
|
|
||||||
viewIcon={<IconCalendarEvent size={16} />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellDate
|
|
||||||
value={
|
|
||||||
props.row.original.createdAt
|
|
||||||
? new Date(props.row.original.createdAt)
|
|
||||||
: new Date()
|
|
||||||
}
|
|
||||||
onChange={async (value: Date) => {
|
|
||||||
const person = { ...props.row.original };
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
...person,
|
|
||||||
createdAt: value.toISOString(),
|
|
||||||
companyId: person.company?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 100,
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('city', {
|
|
||||||
header: () => (
|
|
||||||
<ColumnHead viewName="City" viewIcon={<IconMap size={16} />} />
|
|
||||||
),
|
|
||||||
cell: (props) => (
|
|
||||||
<EditableCellText
|
|
||||||
editModeHorizontalAlign="right"
|
|
||||||
placeholder="City"
|
|
||||||
value={props.row.original.city || ''}
|
|
||||||
onChange={async (value: string) => {
|
|
||||||
const person = { ...props.row.original };
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
...person,
|
|
||||||
city: value,
|
|
||||||
companyId: person.company?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}, [updatePerson]);
|
|
||||||
};
|
|
||||||
@ -17,7 +17,7 @@ export const availableSorts = [
|
|||||||
key: 'fullname',
|
key: 'fullname',
|
||||||
label: 'People',
|
label: 'People',
|
||||||
icon: <IconUser size={16} />,
|
icon: <IconUser size={16} />,
|
||||||
_type: 'custom_sort',
|
|
||||||
orderByTemplates: [
|
orderByTemplates: [
|
||||||
(order: Order_By) => ({
|
(order: Order_By) => ({
|
||||||
firstName: order,
|
firstName: order,
|
||||||
@ -31,31 +31,27 @@ export const availableSorts = [
|
|||||||
key: 'company_name',
|
key: 'company_name',
|
||||||
label: 'Company',
|
label: 'Company',
|
||||||
icon: <IconBuildingSkyscraper size={16} />,
|
icon: <IconBuildingSkyscraper size={16} />,
|
||||||
_type: 'custom_sort',
|
|
||||||
orderByTemplates: [(order: Order_By) => ({ company: { name: order } })],
|
orderByTemplates: [(order: Order_By) => ({ company: { name: order } })],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'email',
|
key: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
icon: <IconMail size={16} />,
|
icon: <IconMail size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'phone',
|
key: 'phone',
|
||||||
label: 'Phone',
|
label: 'Phone',
|
||||||
icon: <IconPhone size={16} />,
|
icon: <IconPhone size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'createdAt',
|
key: 'createdAt',
|
||||||
label: 'Created at',
|
label: 'Created at',
|
||||||
icon: <IconCalendarEvent size={16} />,
|
icon: <IconCalendarEvent size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'city',
|
key: 'city',
|
||||||
label: 'City',
|
label: 'City',
|
||||||
icon: <IconMap size={16} />,
|
icon: <IconMap size={16} />,
|
||||||
_type: 'default_sort',
|
|
||||||
},
|
},
|
||||||
] satisfies Array<SortType<People_Order_By>>;
|
] satisfies Array<SortType<People_Order_By>>;
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import { companiesFilters } from '~/pages/companies/companies-filters';
|
|||||||
import { ClientConfigProvider } from '~/providers/client-config/ClientConfigProvider';
|
import { ClientConfigProvider } from '~/providers/client-config/ClientConfigProvider';
|
||||||
import { UserProvider } from '~/providers/user/UserProvider';
|
import { UserProvider } from '~/providers/user/UserProvider';
|
||||||
|
|
||||||
import { mockedCompaniesData } from './mock-data/companies';
|
|
||||||
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
|
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
|
||||||
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
|
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
|
||||||
import { mockedClient } from './mockedClient';
|
import { mockedClient } from './mockedClient';
|
||||||
@ -64,9 +63,8 @@ export function getRenderWrapperForEntityTableComponent(
|
|||||||
<ApolloProvider client={mockedClient}>
|
<ApolloProvider client={mockedClient}>
|
||||||
<RecoilScope SpecificContext={TableContext}>
|
<RecoilScope SpecificContext={TableContext}>
|
||||||
<HooksEntityTable
|
<HooksEntityTable
|
||||||
availableTableFilters={companiesFilters}
|
availableFilters={companiesFilters}
|
||||||
numberOfColumns={5}
|
numberOfColumns={5}
|
||||||
numberOfRows={mockedCompaniesData.length}
|
|
||||||
/>
|
/>
|
||||||
<ComponentStorybookLayout>{children}</ComponentStorybookLayout>
|
<ComponentStorybookLayout>{children}</ComponentStorybookLayout>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
|
|||||||
@ -86,8 +86,19 @@ export class CompanyResolver {
|
|||||||
@PrismaSelector({ modelName: 'Company' })
|
@PrismaSelector({ modelName: 'Company' })
|
||||||
prismaSelect: PrismaSelect<'Company'>,
|
prismaSelect: PrismaSelect<'Company'>,
|
||||||
): Promise<Partial<Company> | null> {
|
): Promise<Partial<Company> | null> {
|
||||||
if (!args.data.accountOwner?.connect?.id) {
|
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||||
args.data.accountOwner = { disconnect: true };
|
for (const key in args.data) {
|
||||||
|
if (args.data[key]) {
|
||||||
|
for (const subKey in args.data[key]) {
|
||||||
|
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||||
|
delete args.data[key][subKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.stringify(args.data[key]) === '{}') {
|
||||||
|
delete args.data[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.companyService.update({
|
return this.companyService.update({
|
||||||
|
|||||||
@ -103,8 +103,19 @@ export class PersonResolver {
|
|||||||
@PrismaSelector({ modelName: 'Person' })
|
@PrismaSelector({ modelName: 'Person' })
|
||||||
prismaSelect: PrismaSelect<'Person'>,
|
prismaSelect: PrismaSelect<'Person'>,
|
||||||
): Promise<Partial<Person> | null> {
|
): Promise<Partial<Person> | null> {
|
||||||
if (!args.data.company?.connect?.id) {
|
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||||
args.data.company = { disconnect: true };
|
for (const key in args.data) {
|
||||||
|
if (args.data[key]) {
|
||||||
|
for (const subKey in args.data[key]) {
|
||||||
|
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||||
|
delete args.data[key][subKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.stringify(args.data[key]) === '{}') {
|
||||||
|
delete args.data[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.personService.update({
|
return this.personService.update({
|
||||||
|
|||||||
Reference in New Issue
Block a user