Import company and person from csv file (#1236)
* feat: wip implement back-end call csv import * fix: rebase IconBrandTwitter missing * feat: person and company csv import * fix: test & clean * fix: clean & test
This commit is contained in:
@ -37,7 +37,7 @@
|
|||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"luxon": "^3.3.0",
|
"luxon": "^3.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-data-grid": "7.0.0-beta.13",
|
"react-data-grid": "^7.0.0-beta.36",
|
||||||
"react-datepicker": "^4.11.0",
|
"react-datepicker": "^4.11.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
|
|||||||
@ -647,6 +647,18 @@ export type CompanyCreateInput = {
|
|||||||
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CompanyCreateManyInput = {
|
||||||
|
accountOwnerId?: InputMaybe<Scalars['String']>;
|
||||||
|
address: Scalars['String'];
|
||||||
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
|
domainName: Scalars['String'];
|
||||||
|
employees?: InputMaybe<Scalars['Int']>;
|
||||||
|
id?: InputMaybe<Scalars['String']>;
|
||||||
|
linkedinUrl?: InputMaybe<Scalars['String']>;
|
||||||
|
name: Scalars['String'];
|
||||||
|
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type CompanyCreateNestedOneWithoutActivityTargetInput = {
|
export type CompanyCreateNestedOneWithoutActivityTargetInput = {
|
||||||
connect?: InputMaybe<CompanyWhereUniqueInput>;
|
connect?: InputMaybe<CompanyWhereUniqueInput>;
|
||||||
};
|
};
|
||||||
@ -953,6 +965,8 @@ export type Mutation = {
|
|||||||
createEvent: Analytics;
|
createEvent: Analytics;
|
||||||
createFavoriteForCompany: Favorite;
|
createFavoriteForCompany: Favorite;
|
||||||
createFavoriteForPerson: Favorite;
|
createFavoriteForPerson: Favorite;
|
||||||
|
createManyCompany: AffectedRows;
|
||||||
|
createManyPerson: AffectedRows;
|
||||||
createManyView: AffectedRows;
|
createManyView: AffectedRows;
|
||||||
createManyViewField: AffectedRows;
|
createManyViewField: AffectedRows;
|
||||||
createManyViewSort: AffectedRows;
|
createManyViewSort: AffectedRows;
|
||||||
@ -1021,6 +1035,18 @@ export type MutationCreateFavoriteForPersonArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateManyCompanyArgs = {
|
||||||
|
data: Array<CompanyCreateManyInput>;
|
||||||
|
skipDuplicates?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateManyPersonArgs = {
|
||||||
|
data: Array<PersonCreateManyInput>;
|
||||||
|
skipDuplicates?: InputMaybe<Scalars['Boolean']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateManyViewArgs = {
|
export type MutationCreateManyViewArgs = {
|
||||||
data: Array<ViewCreateManyInput>;
|
data: Array<ViewCreateManyInput>;
|
||||||
skipDuplicates?: InputMaybe<Scalars['Boolean']>;
|
skipDuplicates?: InputMaybe<Scalars['Boolean']>;
|
||||||
@ -1383,6 +1409,22 @@ export type PersonCreateInput = {
|
|||||||
xUrl?: InputMaybe<Scalars['String']>;
|
xUrl?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PersonCreateManyInput = {
|
||||||
|
avatarUrl?: InputMaybe<Scalars['String']>;
|
||||||
|
city?: InputMaybe<Scalars['String']>;
|
||||||
|
companyId?: InputMaybe<Scalars['String']>;
|
||||||
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
|
email?: InputMaybe<Scalars['String']>;
|
||||||
|
firstName?: InputMaybe<Scalars['String']>;
|
||||||
|
id?: InputMaybe<Scalars['String']>;
|
||||||
|
jobTitle?: InputMaybe<Scalars['String']>;
|
||||||
|
lastName?: InputMaybe<Scalars['String']>;
|
||||||
|
linkedinUrl?: InputMaybe<Scalars['String']>;
|
||||||
|
phone?: InputMaybe<Scalars['String']>;
|
||||||
|
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
|
xUrl?: InputMaybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type PersonCreateNestedManyWithoutCompanyInput = {
|
export type PersonCreateNestedManyWithoutCompanyInput = {
|
||||||
connect?: InputMaybe<Array<PersonWhereUniqueInput>>;
|
connect?: InputMaybe<Array<PersonWhereUniqueInput>>;
|
||||||
};
|
};
|
||||||
@ -2851,6 +2893,13 @@ export type DeleteManyCompaniesMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type DeleteManyCompaniesMutation = { __typename?: 'Mutation', deleteManyCompany: { __typename?: 'AffectedRows', count: number } };
|
export type DeleteManyCompaniesMutation = { __typename?: 'Mutation', deleteManyCompany: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
|
export type InsertManyCompanyMutationVariables = Exact<{
|
||||||
|
data: Array<CompanyCreateManyInput> | CompanyCreateManyInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type InsertManyCompanyMutation = { __typename?: 'Mutation', createManyCompany: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
export type InsertOneCompanyMutationVariables = Exact<{
|
export type InsertOneCompanyMutationVariables = Exact<{
|
||||||
data: CompanyCreateInput;
|
data: CompanyCreateInput;
|
||||||
}>;
|
}>;
|
||||||
@ -2916,6 +2965,13 @@ export type DeleteManyPersonMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type DeleteManyPersonMutation = { __typename?: 'Mutation', deleteManyPerson: { __typename?: 'AffectedRows', count: number } };
|
export type DeleteManyPersonMutation = { __typename?: 'Mutation', deleteManyPerson: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
|
export type InsertManyPersonMutationVariables = Exact<{
|
||||||
|
data: Array<PersonCreateManyInput> | PersonCreateManyInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type InsertManyPersonMutation = { __typename?: 'Mutation', createManyPerson: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
export type InsertOnePersonMutationVariables = Exact<{
|
export type InsertOnePersonMutationVariables = Exact<{
|
||||||
data: PersonCreateInput;
|
data: PersonCreateInput;
|
||||||
}>;
|
}>;
|
||||||
@ -3147,13 +3203,6 @@ export type GetUsersQueryVariables = Exact<{ [key: string]: never; }>;
|
|||||||
|
|
||||||
export type GetUsersQuery = { __typename?: 'Query', findManyUser: Array<{ __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null }> };
|
export type GetUsersQuery = { __typename?: 'Query', findManyUser: Array<{ __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null }> };
|
||||||
|
|
||||||
export type CreateViewsMutationVariables = Exact<{
|
|
||||||
data: Array<ViewCreateManyInput> | ViewCreateManyInput;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type CreateViewsMutation = { __typename?: 'Mutation', createManyView: { __typename?: 'AffectedRows', count: number } };
|
|
||||||
|
|
||||||
export type CreateViewFieldsMutationVariables = Exact<{
|
export type CreateViewFieldsMutationVariables = Exact<{
|
||||||
data: Array<ViewFieldCreateManyInput> | ViewFieldCreateManyInput;
|
data: Array<ViewFieldCreateManyInput> | ViewFieldCreateManyInput;
|
||||||
}>;
|
}>;
|
||||||
@ -3168,6 +3217,13 @@ export type CreateViewSortsMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type CreateViewSortsMutation = { __typename?: 'Mutation', createManyViewSort: { __typename?: 'AffectedRows', count: number } };
|
export type CreateViewSortsMutation = { __typename?: 'Mutation', createManyViewSort: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
|
export type CreateViewsMutationVariables = Exact<{
|
||||||
|
data: Array<ViewCreateManyInput> | ViewCreateManyInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateViewsMutation = { __typename?: 'Mutation', createManyView: { __typename?: 'AffectedRows', count: number } };
|
||||||
|
|
||||||
export type DeleteViewSortsMutationVariables = Exact<{
|
export type DeleteViewSortsMutationVariables = Exact<{
|
||||||
where: ViewSortWhereInput;
|
where: ViewSortWhereInput;
|
||||||
}>;
|
}>;
|
||||||
@ -3199,13 +3255,6 @@ export type UpdateViewSortMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type UpdateViewSortMutation = { __typename?: 'Mutation', viewSort: { __typename?: 'ViewSort', direction: ViewSortDirection, key: string, name: string } };
|
export type UpdateViewSortMutation = { __typename?: 'Mutation', viewSort: { __typename?: 'ViewSort', direction: ViewSortDirection, key: string, name: string } };
|
||||||
|
|
||||||
export type GetViewsQueryVariables = Exact<{
|
|
||||||
where?: InputMaybe<ViewWhereInput>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type GetViewsQuery = { __typename?: 'Query', views: Array<{ __typename?: 'View', id: string, name: string }> };
|
|
||||||
|
|
||||||
export type GetViewFieldsQueryVariables = Exact<{
|
export type GetViewFieldsQueryVariables = Exact<{
|
||||||
where?: InputMaybe<ViewFieldWhereInput>;
|
where?: InputMaybe<ViewFieldWhereInput>;
|
||||||
orderBy?: InputMaybe<Array<ViewFieldOrderByWithRelationInput> | ViewFieldOrderByWithRelationInput>;
|
orderBy?: InputMaybe<Array<ViewFieldOrderByWithRelationInput> | ViewFieldOrderByWithRelationInput>;
|
||||||
@ -3221,6 +3270,13 @@ export type GetViewSortsQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type GetViewSortsQuery = { __typename?: 'Query', viewSorts: Array<{ __typename?: 'ViewSort', direction: ViewSortDirection, key: string, name: string }> };
|
export type GetViewSortsQuery = { __typename?: 'Query', viewSorts: Array<{ __typename?: 'ViewSort', direction: ViewSortDirection, key: string, name: string }> };
|
||||||
|
|
||||||
|
export type GetViewsQueryVariables = Exact<{
|
||||||
|
where?: InputMaybe<ViewWhereInput>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetViewsQuery = { __typename?: 'Query', views: Array<{ __typename?: 'View', id: string, name: string }> };
|
||||||
|
|
||||||
export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: never; }>;
|
export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
@ -4149,6 +4205,39 @@ export function useDeleteManyCompaniesMutation(baseOptions?: Apollo.MutationHook
|
|||||||
export type DeleteManyCompaniesMutationHookResult = ReturnType<typeof useDeleteManyCompaniesMutation>;
|
export type DeleteManyCompaniesMutationHookResult = ReturnType<typeof useDeleteManyCompaniesMutation>;
|
||||||
export type DeleteManyCompaniesMutationResult = Apollo.MutationResult<DeleteManyCompaniesMutation>;
|
export type DeleteManyCompaniesMutationResult = Apollo.MutationResult<DeleteManyCompaniesMutation>;
|
||||||
export type DeleteManyCompaniesMutationOptions = Apollo.BaseMutationOptions<DeleteManyCompaniesMutation, DeleteManyCompaniesMutationVariables>;
|
export type DeleteManyCompaniesMutationOptions = Apollo.BaseMutationOptions<DeleteManyCompaniesMutation, DeleteManyCompaniesMutationVariables>;
|
||||||
|
export const InsertManyCompanyDocument = gql`
|
||||||
|
mutation InsertManyCompany($data: [CompanyCreateManyInput!]!) {
|
||||||
|
createManyCompany(data: $data) {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type InsertManyCompanyMutationFn = Apollo.MutationFunction<InsertManyCompanyMutation, InsertManyCompanyMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useInsertManyCompanyMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useInsertManyCompanyMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useInsertManyCompanyMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [insertManyCompanyMutation, { data, loading, error }] = useInsertManyCompanyMutation({
|
||||||
|
* variables: {
|
||||||
|
* data: // value for 'data'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useInsertManyCompanyMutation(baseOptions?: Apollo.MutationHookOptions<InsertManyCompanyMutation, InsertManyCompanyMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<InsertManyCompanyMutation, InsertManyCompanyMutationVariables>(InsertManyCompanyDocument, options);
|
||||||
|
}
|
||||||
|
export type InsertManyCompanyMutationHookResult = ReturnType<typeof useInsertManyCompanyMutation>;
|
||||||
|
export type InsertManyCompanyMutationResult = Apollo.MutationResult<InsertManyCompanyMutation>;
|
||||||
|
export type InsertManyCompanyMutationOptions = Apollo.BaseMutationOptions<InsertManyCompanyMutation, InsertManyCompanyMutationVariables>;
|
||||||
export const InsertOneCompanyDocument = gql`
|
export const InsertOneCompanyDocument = gql`
|
||||||
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
||||||
createOneCompany(data: $data) {
|
createOneCompany(data: $data) {
|
||||||
@ -4517,6 +4606,39 @@ export function useDeleteManyPersonMutation(baseOptions?: Apollo.MutationHookOpt
|
|||||||
export type DeleteManyPersonMutationHookResult = ReturnType<typeof useDeleteManyPersonMutation>;
|
export type DeleteManyPersonMutationHookResult = ReturnType<typeof useDeleteManyPersonMutation>;
|
||||||
export type DeleteManyPersonMutationResult = Apollo.MutationResult<DeleteManyPersonMutation>;
|
export type DeleteManyPersonMutationResult = Apollo.MutationResult<DeleteManyPersonMutation>;
|
||||||
export type DeleteManyPersonMutationOptions = Apollo.BaseMutationOptions<DeleteManyPersonMutation, DeleteManyPersonMutationVariables>;
|
export type DeleteManyPersonMutationOptions = Apollo.BaseMutationOptions<DeleteManyPersonMutation, DeleteManyPersonMutationVariables>;
|
||||||
|
export const InsertManyPersonDocument = gql`
|
||||||
|
mutation InsertManyPerson($data: [PersonCreateManyInput!]!) {
|
||||||
|
createManyPerson(data: $data) {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type InsertManyPersonMutationFn = Apollo.MutationFunction<InsertManyPersonMutation, InsertManyPersonMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useInsertManyPersonMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useInsertManyPersonMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useInsertManyPersonMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [insertManyPersonMutation, { data, loading, error }] = useInsertManyPersonMutation({
|
||||||
|
* variables: {
|
||||||
|
* data: // value for 'data'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useInsertManyPersonMutation(baseOptions?: Apollo.MutationHookOptions<InsertManyPersonMutation, InsertManyPersonMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<InsertManyPersonMutation, InsertManyPersonMutationVariables>(InsertManyPersonDocument, options);
|
||||||
|
}
|
||||||
|
export type InsertManyPersonMutationHookResult = ReturnType<typeof useInsertManyPersonMutation>;
|
||||||
|
export type InsertManyPersonMutationResult = Apollo.MutationResult<InsertManyPersonMutation>;
|
||||||
|
export type InsertManyPersonMutationOptions = Apollo.BaseMutationOptions<InsertManyPersonMutation, InsertManyPersonMutationVariables>;
|
||||||
export const InsertOnePersonDocument = gql`
|
export const InsertOnePersonDocument = gql`
|
||||||
mutation InsertOnePerson($data: PersonCreateInput!) {
|
mutation InsertOnePerson($data: PersonCreateInput!) {
|
||||||
createOnePerson(data: $data) {
|
createOnePerson(data: $data) {
|
||||||
@ -5763,39 +5885,6 @@ export function useGetUsersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<G
|
|||||||
export type GetUsersQueryHookResult = ReturnType<typeof useGetUsersQuery>;
|
export type GetUsersQueryHookResult = ReturnType<typeof useGetUsersQuery>;
|
||||||
export type GetUsersLazyQueryHookResult = ReturnType<typeof useGetUsersLazyQuery>;
|
export type GetUsersLazyQueryHookResult = ReturnType<typeof useGetUsersLazyQuery>;
|
||||||
export type GetUsersQueryResult = Apollo.QueryResult<GetUsersQuery, GetUsersQueryVariables>;
|
export type GetUsersQueryResult = Apollo.QueryResult<GetUsersQuery, GetUsersQueryVariables>;
|
||||||
export const CreateViewsDocument = gql`
|
|
||||||
mutation CreateViews($data: [ViewCreateManyInput!]!) {
|
|
||||||
createManyView(data: $data) {
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export type CreateViewsMutationFn = Apollo.MutationFunction<CreateViewsMutation, CreateViewsMutationVariables>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useCreateViewsMutation__
|
|
||||||
*
|
|
||||||
* To run a mutation, you first call `useCreateViewsMutation` within a React component and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useCreateViewsMutation` returns a tuple that includes:
|
|
||||||
* - A mutate function that you can call at any time to execute the mutation
|
|
||||||
* - An object with fields that represent the current status of the mutation's execution
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const [createViewsMutation, { data, loading, error }] = useCreateViewsMutation({
|
|
||||||
* variables: {
|
|
||||||
* data: // value for 'data'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useCreateViewsMutation(baseOptions?: Apollo.MutationHookOptions<CreateViewsMutation, CreateViewsMutationVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useMutation<CreateViewsMutation, CreateViewsMutationVariables>(CreateViewsDocument, options);
|
|
||||||
}
|
|
||||||
export type CreateViewsMutationHookResult = ReturnType<typeof useCreateViewsMutation>;
|
|
||||||
export type CreateViewsMutationResult = Apollo.MutationResult<CreateViewsMutation>;
|
|
||||||
export type CreateViewsMutationOptions = Apollo.BaseMutationOptions<CreateViewsMutation, CreateViewsMutationVariables>;
|
|
||||||
export const CreateViewFieldsDocument = gql`
|
export const CreateViewFieldsDocument = gql`
|
||||||
mutation CreateViewFields($data: [ViewFieldCreateManyInput!]!) {
|
mutation CreateViewFields($data: [ViewFieldCreateManyInput!]!) {
|
||||||
createManyViewField(data: $data) {
|
createManyViewField(data: $data) {
|
||||||
@ -5862,6 +5951,39 @@ export function useCreateViewSortsMutation(baseOptions?: Apollo.MutationHookOpti
|
|||||||
export type CreateViewSortsMutationHookResult = ReturnType<typeof useCreateViewSortsMutation>;
|
export type CreateViewSortsMutationHookResult = ReturnType<typeof useCreateViewSortsMutation>;
|
||||||
export type CreateViewSortsMutationResult = Apollo.MutationResult<CreateViewSortsMutation>;
|
export type CreateViewSortsMutationResult = Apollo.MutationResult<CreateViewSortsMutation>;
|
||||||
export type CreateViewSortsMutationOptions = Apollo.BaseMutationOptions<CreateViewSortsMutation, CreateViewSortsMutationVariables>;
|
export type CreateViewSortsMutationOptions = Apollo.BaseMutationOptions<CreateViewSortsMutation, CreateViewSortsMutationVariables>;
|
||||||
|
export const CreateViewsDocument = gql`
|
||||||
|
mutation CreateViews($data: [ViewCreateManyInput!]!) {
|
||||||
|
createManyView(data: $data) {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type CreateViewsMutationFn = Apollo.MutationFunction<CreateViewsMutation, CreateViewsMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useCreateViewsMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useCreateViewsMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useCreateViewsMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [createViewsMutation, { data, loading, error }] = useCreateViewsMutation({
|
||||||
|
* variables: {
|
||||||
|
* data: // value for 'data'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useCreateViewsMutation(baseOptions?: Apollo.MutationHookOptions<CreateViewsMutation, CreateViewsMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<CreateViewsMutation, CreateViewsMutationVariables>(CreateViewsDocument, options);
|
||||||
|
}
|
||||||
|
export type CreateViewsMutationHookResult = ReturnType<typeof useCreateViewsMutation>;
|
||||||
|
export type CreateViewsMutationResult = Apollo.MutationResult<CreateViewsMutation>;
|
||||||
|
export type CreateViewsMutationOptions = Apollo.BaseMutationOptions<CreateViewsMutation, CreateViewsMutationVariables>;
|
||||||
export const DeleteViewSortsDocument = gql`
|
export const DeleteViewSortsDocument = gql`
|
||||||
mutation DeleteViewSorts($where: ViewSortWhereInput!) {
|
mutation DeleteViewSorts($where: ViewSortWhereInput!) {
|
||||||
deleteManyViewSort(where: $where) {
|
deleteManyViewSort(where: $where) {
|
||||||
@ -6004,42 +6126,6 @@ export function useUpdateViewSortMutation(baseOptions?: Apollo.MutationHookOptio
|
|||||||
export type UpdateViewSortMutationHookResult = ReturnType<typeof useUpdateViewSortMutation>;
|
export type UpdateViewSortMutationHookResult = ReturnType<typeof useUpdateViewSortMutation>;
|
||||||
export type UpdateViewSortMutationResult = Apollo.MutationResult<UpdateViewSortMutation>;
|
export type UpdateViewSortMutationResult = Apollo.MutationResult<UpdateViewSortMutation>;
|
||||||
export type UpdateViewSortMutationOptions = Apollo.BaseMutationOptions<UpdateViewSortMutation, UpdateViewSortMutationVariables>;
|
export type UpdateViewSortMutationOptions = Apollo.BaseMutationOptions<UpdateViewSortMutation, UpdateViewSortMutationVariables>;
|
||||||
export const GetViewsDocument = gql`
|
|
||||||
query GetViews($where: ViewWhereInput) {
|
|
||||||
views: findManyView(where: $where) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useGetViewsQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useGetViewsQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useGetViewsQuery` 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 } = useGetViewsQuery({
|
|
||||||
* variables: {
|
|
||||||
* where: // value for 'where'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useGetViewsQuery(baseOptions?: Apollo.QueryHookOptions<GetViewsQuery, GetViewsQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<GetViewsQuery, GetViewsQueryVariables>(GetViewsDocument, options);
|
|
||||||
}
|
|
||||||
export function useGetViewsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetViewsQuery, GetViewsQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<GetViewsQuery, GetViewsQueryVariables>(GetViewsDocument, options);
|
|
||||||
}
|
|
||||||
export type GetViewsQueryHookResult = ReturnType<typeof useGetViewsQuery>;
|
|
||||||
export type GetViewsLazyQueryHookResult = ReturnType<typeof useGetViewsLazyQuery>;
|
|
||||||
export type GetViewsQueryResult = Apollo.QueryResult<GetViewsQuery, GetViewsQueryVariables>;
|
|
||||||
export const GetViewFieldsDocument = gql`
|
export const GetViewFieldsDocument = gql`
|
||||||
query GetViewFields($where: ViewFieldWhereInput, $orderBy: [ViewFieldOrderByWithRelationInput!]) {
|
query GetViewFields($where: ViewFieldWhereInput, $orderBy: [ViewFieldOrderByWithRelationInput!]) {
|
||||||
viewFields: findManyViewField(where: $where, orderBy: $orderBy) {
|
viewFields: findManyViewField(where: $where, orderBy: $orderBy) {
|
||||||
@ -6117,6 +6203,42 @@ export function useGetViewSortsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptio
|
|||||||
export type GetViewSortsQueryHookResult = ReturnType<typeof useGetViewSortsQuery>;
|
export type GetViewSortsQueryHookResult = ReturnType<typeof useGetViewSortsQuery>;
|
||||||
export type GetViewSortsLazyQueryHookResult = ReturnType<typeof useGetViewSortsLazyQuery>;
|
export type GetViewSortsLazyQueryHookResult = ReturnType<typeof useGetViewSortsLazyQuery>;
|
||||||
export type GetViewSortsQueryResult = Apollo.QueryResult<GetViewSortsQuery, GetViewSortsQueryVariables>;
|
export type GetViewSortsQueryResult = Apollo.QueryResult<GetViewSortsQuery, GetViewSortsQueryVariables>;
|
||||||
|
export const GetViewsDocument = gql`
|
||||||
|
query GetViews($where: ViewWhereInput) {
|
||||||
|
views: findManyView(where: $where) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useGetViewsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useGetViewsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useGetViewsQuery` 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 } = useGetViewsQuery({
|
||||||
|
* variables: {
|
||||||
|
* where: // value for 'where'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useGetViewsQuery(baseOptions?: Apollo.QueryHookOptions<GetViewsQuery, GetViewsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<GetViewsQuery, GetViewsQueryVariables>(GetViewsDocument, options);
|
||||||
|
}
|
||||||
|
export function useGetViewsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetViewsQuery, GetViewsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<GetViewsQuery, GetViewsQueryVariables>(GetViewsDocument, options);
|
||||||
|
}
|
||||||
|
export type GetViewsQueryHookResult = ReturnType<typeof useGetViewsQuery>;
|
||||||
|
export type GetViewsLazyQueryHookResult = ReturnType<typeof useGetViewsLazyQuery>;
|
||||||
|
export type GetViewsQueryResult = Apollo.QueryResult<GetViewsQuery, GetViewsQueryVariables>;
|
||||||
export const DeleteCurrentWorkspaceDocument = gql`
|
export const DeleteCurrentWorkspaceDocument = gql`
|
||||||
mutation DeleteCurrentWorkspace {
|
mutation DeleteCurrentWorkspace {
|
||||||
deleteCurrentWorkspace {
|
deleteCurrentWorkspace {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { RecoilRoot } from 'recoil';
|
|||||||
|
|
||||||
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
||||||
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
|
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
|
||||||
import { SpreadsheetImportProvider } from '@/spreadsheet-import/components/SpreadsheetImportProvider';
|
|
||||||
import { DialogProvider } from '@/ui/dialog/components/DialogProvider';
|
import { DialogProvider } from '@/ui/dialog/components/DialogProvider';
|
||||||
import { SnackBarProvider } from '@/ui/snack-bar/components/SnackBarProvider';
|
import { SnackBarProvider } from '@/ui/snack-bar/components/SnackBarProvider';
|
||||||
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
|
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
|
||||||
@ -34,11 +33,9 @@ root.render(
|
|||||||
<AppThemeProvider>
|
<AppThemeProvider>
|
||||||
<SnackBarProvider>
|
<SnackBarProvider>
|
||||||
<DialogProvider>
|
<DialogProvider>
|
||||||
<SpreadsheetImportProvider>
|
<StrictMode>
|
||||||
<StrictMode>
|
<App />
|
||||||
<App />
|
</StrictMode>
|
||||||
</StrictMode>
|
|
||||||
</SpreadsheetImportProvider>
|
|
||||||
</DialogProvider>
|
</DialogProvider>
|
||||||
</SnackBarProvider>
|
</SnackBarProvider>
|
||||||
</AppThemeProvider>
|
</AppThemeProvider>
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const INSERT_MANY_COMPANY = gql`
|
||||||
|
mutation InsertManyCompany($data: [CompanyCreateManyInput!]!) {
|
||||||
|
createManyCompany(data: $data) {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
|
||||||
|
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
|
||||||
|
import { useUpsertEntityTableItems } from '@/ui/table/hooks/useUpsertEntityTableItems';
|
||||||
|
import { useUpsertTableRowIds } from '@/ui/table/hooks/useUpsertTableRowIds';
|
||||||
|
import {
|
||||||
|
GetPeopleDocument,
|
||||||
|
useInsertManyCompanyMutation,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { fieldsForCompany } from '../utils/fieldsForCompany';
|
||||||
|
|
||||||
|
export type FieldCompanyMapping = (typeof fieldsForCompany)[number]['key'];
|
||||||
|
|
||||||
|
export function useSpreadsheetCompanyImport() {
|
||||||
|
const { openSpreadsheetImport } = useSpreadsheetImport<FieldCompanyMapping>();
|
||||||
|
const upsertEntityTableItems = useUpsertEntityTableItems();
|
||||||
|
const upsertTableRowIds = useUpsertTableRowIds();
|
||||||
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
|
const [createManyCompany] = useInsertManyCompanyMutation();
|
||||||
|
|
||||||
|
const openCompanySpreadsheetImport = (
|
||||||
|
options?: Omit<
|
||||||
|
SpreadsheetOptions<FieldCompanyMapping>,
|
||||||
|
'fields' | 'isOpen' | 'onClose'
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
openSpreadsheetImport({
|
||||||
|
...options,
|
||||||
|
async onSubmit(data) {
|
||||||
|
// TODO: Add better type checking in spreadsheet import later
|
||||||
|
const createInputs = data.validData.map((company) => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
name: company.name as string,
|
||||||
|
domainName: company.domainName as string,
|
||||||
|
address: company.address as string,
|
||||||
|
employees: parseInt(company.employees as string, 10),
|
||||||
|
linkedinUrl: company.linkedinUrl as string | undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await createManyCompany({
|
||||||
|
variables: {
|
||||||
|
data: createInputs,
|
||||||
|
},
|
||||||
|
refetchQueries: [GetPeopleDocument],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors) {
|
||||||
|
throw result.errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertTableRowIds(createInputs.map((company) => company.id));
|
||||||
|
upsertEntityTableItems(createInputs);
|
||||||
|
} catch (error: any) {
|
||||||
|
enqueueSnackBar(error?.message || 'Something went wrong', {
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: fieldsForCompany,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openCompanySpreadsheetImport };
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { companyViewFields } from '@/companies/constants/companyViewFields';
|
import { companyViewFields } from '@/companies/constants/companyViewFields';
|
||||||
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
||||||
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
||||||
|
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState';
|
import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
@ -43,6 +44,7 @@ export function CompanyTable() {
|
|||||||
viewFieldDefinitions: companyViewFields,
|
viewFieldDefinitions: companyViewFields,
|
||||||
});
|
});
|
||||||
const { updateSorts } = useViewSorts({ availableSorts });
|
const { updateSorts } = useViewSorts({ availableSorts });
|
||||||
|
const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
|
||||||
|
|
||||||
const filters = useRecoilScopedValue(
|
const filters = useRecoilScopedValue(
|
||||||
filtersScopedState,
|
filtersScopedState,
|
||||||
@ -56,6 +58,10 @@ export function CompanyTable() {
|
|||||||
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
||||||
const { setActionBarEntries } = useCompanyTableActionBarEntries();
|
const { setActionBarEntries } = useCompanyTableActionBarEntries();
|
||||||
|
|
||||||
|
function handleImport() {
|
||||||
|
openCompanySpreadsheetImport();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GenericEntityTableData
|
<GenericEntityTableData
|
||||||
@ -81,6 +87,7 @@ export function CompanyTable() {
|
|||||||
onColumnsChange={handleColumnsChange}
|
onColumnsChange={handleColumnsChange}
|
||||||
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
||||||
onViewsChange={handleViewsChange}
|
onViewsChange={handleViewsChange}
|
||||||
|
onImport={handleImport}
|
||||||
updateEntityMutation={({
|
updateEntityMutation={({
|
||||||
variables,
|
variables,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
80
front/src/modules/companies/utils/fieldsForCompany.tsx
Normal file
80
front/src/modules/companies/utils/fieldsForCompany.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
IconBrandLinkedin,
|
||||||
|
IconBuildingSkyscraper,
|
||||||
|
IconMail,
|
||||||
|
IconMap,
|
||||||
|
IconUsers,
|
||||||
|
} from '@/ui/icon';
|
||||||
|
|
||||||
|
export const fieldsForCompany = [
|
||||||
|
{
|
||||||
|
icon: <IconBuildingSkyscraper />,
|
||||||
|
label: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
alternateMatches: ['name', 'company name', 'company'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'Tim',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'Name is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconMail />,
|
||||||
|
label: 'Domain name',
|
||||||
|
key: 'domainName',
|
||||||
|
alternateMatches: ['domain', 'domain name'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'apple.dev',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'Domain name is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBrandLinkedin />,
|
||||||
|
label: 'Linkedin URL',
|
||||||
|
key: 'linkedinUrl',
|
||||||
|
alternateMatches: ['linkedIn', 'linkedin', 'linkedin url'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'https://www.linkedin.com/in/apple',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconMap />,
|
||||||
|
label: 'Address',
|
||||||
|
key: 'address',
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'Maple street',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'Address is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconUsers />,
|
||||||
|
label: 'Employees',
|
||||||
|
key: 'employees',
|
||||||
|
alternateMatches: ['employees', 'total employees', 'number of employees'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: '150',
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const INSERT_MANY_PERSON = gql`
|
||||||
|
mutation InsertManyPerson($data: [PersonCreateManyInput!]!) {
|
||||||
|
createManyPerson(data: $data) {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
72
front/src/modules/people/hooks/useSpreadsheetPersonImport.ts
Normal file
72
front/src/modules/people/hooks/useSpreadsheetPersonImport.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
|
||||||
|
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
|
||||||
|
import { useUpsertEntityTableItems } from '@/ui/table/hooks/useUpsertEntityTableItems';
|
||||||
|
import { useUpsertTableRowIds } from '@/ui/table/hooks/useUpsertTableRowIds';
|
||||||
|
import {
|
||||||
|
GetPeopleDocument,
|
||||||
|
useInsertManyPersonMutation,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { fieldsForPerson } from '../utils/fieldsForPerson';
|
||||||
|
|
||||||
|
export type FieldPersonMapping = (typeof fieldsForPerson)[number]['key'];
|
||||||
|
|
||||||
|
export function useSpreadsheetPersonImport() {
|
||||||
|
const { openSpreadsheetImport } = useSpreadsheetImport<FieldPersonMapping>();
|
||||||
|
const upsertEntityTableItems = useUpsertEntityTableItems();
|
||||||
|
const upsertTableRowIds = useUpsertTableRowIds();
|
||||||
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
|
const [createManyPerson] = useInsertManyPersonMutation();
|
||||||
|
|
||||||
|
const openPersonSpreadsheetImport = (
|
||||||
|
options?: Omit<
|
||||||
|
SpreadsheetOptions<FieldPersonMapping>,
|
||||||
|
'fields' | 'isOpen' | 'onClose'
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
openSpreadsheetImport({
|
||||||
|
...options,
|
||||||
|
async onSubmit(data) {
|
||||||
|
// TODO: Add better type checking in spreadsheet import later
|
||||||
|
const createInputs = data.validData.map((person) => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
firstName: person.firstName as string | undefined,
|
||||||
|
lastName: person.lastName as string | undefined,
|
||||||
|
email: person.email as string | undefined,
|
||||||
|
linkedinUrl: person.linkedinUrl as string | undefined,
|
||||||
|
xUrl: person.xUrl as string | undefined,
|
||||||
|
jobTitle: person.jobTitle as string | undefined,
|
||||||
|
phone: person.phone as string | undefined,
|
||||||
|
city: person.city as string | undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await createManyPerson({
|
||||||
|
variables: {
|
||||||
|
data: createInputs,
|
||||||
|
},
|
||||||
|
refetchQueries: [GetPeopleDocument],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors) {
|
||||||
|
throw result.errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertTableRowIds(createInputs.map((person) => person.id));
|
||||||
|
upsertEntityTableItems(createInputs);
|
||||||
|
} catch (error: any) {
|
||||||
|
enqueueSnackBar(error?.message || 'Something went wrong', {
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: fieldsForPerson,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openPersonSpreadsheetImport };
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { peopleViewFields } from '@/people/constants/peopleViewFields';
|
import { peopleViewFields } from '@/people/constants/peopleViewFields';
|
||||||
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
|
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
|
||||||
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
|
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
|
||||||
|
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState';
|
import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
@ -35,6 +36,7 @@ export function PeopleTable() {
|
|||||||
);
|
);
|
||||||
const [updateEntityMutation] = useUpdateOnePersonMutation();
|
const [updateEntityMutation] = useUpdateOnePersonMutation();
|
||||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||||
|
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
|
||||||
|
|
||||||
const objectId = 'person';
|
const objectId = 'person';
|
||||||
const { handleViewsChange } = useTableViews({ objectId });
|
const { handleViewsChange } = useTableViews({ objectId });
|
||||||
@ -56,6 +58,10 @@ export function PeopleTable() {
|
|||||||
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
||||||
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
||||||
|
|
||||||
|
function handleImport() {
|
||||||
|
openPersonSpreadsheetImport();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GenericEntityTableData
|
<GenericEntityTableData
|
||||||
@ -81,6 +87,7 @@ export function PeopleTable() {
|
|||||||
onColumnsChange={handleColumnsChange}
|
onColumnsChange={handleColumnsChange}
|
||||||
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
||||||
onViewsChange={handleViewsChange}
|
onViewsChange={handleViewsChange}
|
||||||
|
onImport={handleImport}
|
||||||
updateEntityMutation={({
|
updateEntityMutation={({
|
||||||
variables,
|
variables,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
120
front/src/modules/people/utils/fieldsForPerson.tsx
Normal file
120
front/src/modules/people/utils/fieldsForPerson.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { isValidPhoneNumber } from 'libphonenumber-js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IconBrandLinkedin,
|
||||||
|
IconBrandTwitter,
|
||||||
|
IconBriefcase,
|
||||||
|
IconMail,
|
||||||
|
IconMap,
|
||||||
|
IconUser,
|
||||||
|
} from '@/ui/icon';
|
||||||
|
|
||||||
|
export const fieldsForPerson = [
|
||||||
|
{
|
||||||
|
icon: <IconUser />,
|
||||||
|
label: 'Firstname',
|
||||||
|
key: 'firstName',
|
||||||
|
alternateMatches: ['first name', 'first', 'firstname'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'Tim',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'Firstname is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconUser />,
|
||||||
|
label: 'Lastname',
|
||||||
|
key: 'lastName',
|
||||||
|
alternateMatches: ['last name', 'last', 'lastname'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'Cook',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'Lastname is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconMail />,
|
||||||
|
label: 'Email',
|
||||||
|
key: 'email',
|
||||||
|
alternateMatches: ['email', 'mail'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'tim@apple.dev',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
errorMessage: 'email is required',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBrandLinkedin />,
|
||||||
|
label: 'Linkedin URL',
|
||||||
|
key: 'linkedinUrl',
|
||||||
|
alternateMatches: ['linkedIn', 'linkedin', 'linkedin url'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'https://www.linkedin.com/in/timcook',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBrandTwitter />,
|
||||||
|
label: 'X URL',
|
||||||
|
key: 'xUrl',
|
||||||
|
alternateMatches: ['x', 'x url'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'https://x.com/tim_cook',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBriefcase />,
|
||||||
|
label: 'Job title',
|
||||||
|
key: 'jobTitle',
|
||||||
|
alternateMatches: ['job', 'job title'],
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'CEO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBriefcase />,
|
||||||
|
label: 'Phone',
|
||||||
|
key: 'phone',
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: '+1234567890',
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
rule: 'function',
|
||||||
|
isValid: (value: string) => isValidPhoneNumber(value),
|
||||||
|
errorMessage: 'phone is not valid',
|
||||||
|
level: 'error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconMap />,
|
||||||
|
label: 'City',
|
||||||
|
key: 'city',
|
||||||
|
fieldType: {
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
example: 'Seattle',
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
@ -195,6 +195,7 @@ export const MatchColumnSelect = ({
|
|||||||
value?.value !== option.value &&
|
value?.value !== option.value &&
|
||||||
createPortal(
|
createPortal(
|
||||||
<AppTooltip
|
<AppTooltip
|
||||||
|
key={option.value}
|
||||||
anchorSelect={`#${option.value}`}
|
anchorSelect={`#${option.value}`}
|
||||||
content="You are already importing this column."
|
content="You are already importing this column."
|
||||||
place="right"
|
place="right"
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { Modal } from '@/ui/modal/components/Modal';
|
import { Modal } from '@/ui/modal/components/Modal';
|
||||||
|
|
||||||
import { ModalCloseButton } from './ModalCloseButton';
|
import { ModalCloseButton } from './ModalCloseButton';
|
||||||
@ -27,7 +27,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ModalWrapper = ({ children, isOpen, onClose }: Props) => {
|
export const ModalWrapper = ({ children, isOpen, onClose }: Props) => {
|
||||||
const { rtl } = useRsi();
|
const { rtl } = useSpreadsheetImportInternal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledModal isOpen={isOpen}>
|
<StyledModal isOpen={isOpen}>
|
||||||
@ -1,25 +1,21 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
import type { RsiProps } from '@/spreadsheet-import/types';
|
import type { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
export const RsiContext = createContext({} as any);
|
export const RsiContext = createContext({} as any);
|
||||||
|
|
||||||
type ProvidersProps<T extends string> = {
|
type ProvidersProps<T extends string> = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
rsiValues: RsiProps<T>;
|
values: SpreadsheetOptions<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const rootId = 'chakra-modal-rsi';
|
|
||||||
|
|
||||||
export const Providers = <T extends string>({
|
export const Providers = <T extends string>({
|
||||||
children,
|
children,
|
||||||
rsiValues,
|
values,
|
||||||
}: ProvidersProps<T>) => {
|
}: ProvidersProps<T>) => {
|
||||||
if (!rsiValues.fields) {
|
if (!values.fields) {
|
||||||
throw new Error('Fields must be provided to spreadsheet-import');
|
throw new Error('Fields must be provided to spreadsheet-import');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <RsiContext.Provider value={values}>{children}</RsiContext.Provider>;
|
||||||
<RsiContext.Provider value={rsiValues}>{children}</RsiContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import type { RsiProps } from '../types';
|
|
||||||
|
|
||||||
import { ModalWrapper } from './core/ModalWrapper';
|
|
||||||
import { Providers } from './core/Providers';
|
|
||||||
import { Steps } from './steps/Steps';
|
|
||||||
|
|
||||||
export const defaultRSIProps: Partial<RsiProps<any>> = {
|
|
||||||
autoMapHeaders: true,
|
|
||||||
allowInvalidSubmit: true,
|
|
||||||
autoMapDistance: 2,
|
|
||||||
uploadStepHook: async (value) => value,
|
|
||||||
selectHeaderStepHook: async (headerValues, data) => ({ headerValues, data }),
|
|
||||||
matchColumnsStepHook: async (table) => table,
|
|
||||||
dateFormat: 'yyyy-mm-dd', // ISO 8601,
|
|
||||||
parseRaw: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SpreadsheetImport = <T extends string>(props: RsiProps<T>) => {
|
|
||||||
return (
|
|
||||||
<Providers rsiValues={props}>
|
|
||||||
<ModalWrapper isOpen={props.isOpen} onClose={props.onClose}>
|
|
||||||
<Steps />
|
|
||||||
</ModalWrapper>
|
|
||||||
</Providers>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SpreadsheetImport.defaultProps = defaultRSIProps;
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import DataGrid, { DataGridProps } from 'react-data-grid';
|
import DataGrid, { DataGridProps } from 'react-data-grid';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { rgba } from '@/ui/theme/constants/colors';
|
import { rgba } from '@/ui/theme/constants/colors';
|
||||||
|
|
||||||
const StyledDataGrid = styled(DataGrid)`
|
const StyledDataGrid = styled(DataGrid)`
|
||||||
@ -112,7 +112,7 @@ type Props<Data> = DataGridProps<Data> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Table = <Data,>(props: Props<Data>) => {
|
export const Table = <Data,>(props: Props<Data>) => {
|
||||||
const { rtl } = useRsi();
|
const { rtl } = useSpreadsheetImportInternal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDataGrid direction={rtl ? 'rtl' : 'ltr'} rowHeight={52} {...props} />
|
<StyledDataGrid direction={rtl ? 'rtl' : 'ltr'} rowHeight={52} {...props} />
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { useContext } from 'react';
|
|
||||||
import { SetRequired } from 'type-fest';
|
|
||||||
|
|
||||||
import { RsiContext } from '@/spreadsheet-import/components/core/Providers';
|
|
||||||
import { defaultRSIProps } from '@/spreadsheet-import/components/SpreadsheetImport';
|
|
||||||
import { RsiProps } from '@/spreadsheet-import/types';
|
|
||||||
|
|
||||||
export const useRsi = <T extends string>() =>
|
|
||||||
useContext<SetRequired<RsiProps<T>, keyof typeof defaultRSIProps>>(
|
|
||||||
RsiContext,
|
|
||||||
);
|
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { spreadsheetImportState } from '@/spreadsheet-import/states/spreadsheetImportState';
|
import { spreadsheetImportState } from '@/spreadsheet-import/states/spreadsheetImportState';
|
||||||
import { RsiProps } from '@/spreadsheet-import/types';
|
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
export function useSpreadsheetImport() {
|
export function useSpreadsheetImport<T extends string>() {
|
||||||
const setSpreadSheetImport = useSetRecoilState(spreadsheetImportState);
|
const setSpreadSheetImport = useSetRecoilState(spreadsheetImportState);
|
||||||
|
|
||||||
const openSpreadsheetImport = (
|
const openSpreadsheetImport = (
|
||||||
options: Omit<RsiProps<string>, 'isOpen' | 'onClose'>,
|
options: Omit<SpreadsheetOptions<T>, 'isOpen' | 'onClose'>,
|
||||||
) => {
|
) => {
|
||||||
setSpreadSheetImport({
|
setSpreadSheetImport({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { StepType } from '@/spreadsheet-import/components/steps/UploadFlow';
|
import { StepType } from '@/spreadsheet-import/steps/components/UploadFlow';
|
||||||
|
|
||||||
export const useRsiInitialStep = (initialStep?: StepType) => {
|
export const useSpreadsheetImportInitialStep = (initialStep?: StepType) => {
|
||||||
const steps = ['uploadStep', 'matchColumnsStep', 'validationStep'] as const;
|
const steps = ['uploadStep', 'matchColumnsStep', 'validationStep'] as const;
|
||||||
|
|
||||||
const initialStepNumber = useMemo(() => {
|
const initialStepNumber = useMemo(() => {
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { SetRequired } from 'type-fest';
|
||||||
|
|
||||||
|
import { RsiContext } from '@/spreadsheet-import/components/Providers';
|
||||||
|
import { defaultSpreadsheetImportProps } from '@/spreadsheet-import/provider/components/SpreadsheetImport';
|
||||||
|
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
|
export const useSpreadsheetImportInternal = <T extends string>() =>
|
||||||
|
useContext<
|
||||||
|
SetRequired<
|
||||||
|
SpreadsheetOptions<T>,
|
||||||
|
keyof typeof defaultSpreadsheetImportProps
|
||||||
|
>
|
||||||
|
>(RsiContext);
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
|
import { Steps } from '@/spreadsheet-import/steps/components/Steps';
|
||||||
|
import type { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
|
export const defaultSpreadsheetImportProps: Partial<SpreadsheetOptions<any>> = {
|
||||||
|
autoMapHeaders: true,
|
||||||
|
allowInvalidSubmit: true,
|
||||||
|
autoMapDistance: 2,
|
||||||
|
uploadStepHook: async (value) => value,
|
||||||
|
selectHeaderStepHook: async (headerValues, data) => ({ headerValues, data }),
|
||||||
|
matchColumnsStepHook: async (table) => table,
|
||||||
|
dateFormat: 'yyyy-mm-dd', // ISO 8601,
|
||||||
|
parseRaw: true,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const SpreadsheetImport = <T extends string>(
|
||||||
|
props: SpreadsheetOptions<T>,
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Providers values={props}>
|
||||||
|
<ModalWrapper isOpen={props.isOpen} onClose={props.onClose}>
|
||||||
|
<Steps />
|
||||||
|
</ModalWrapper>
|
||||||
|
</Providers>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SpreadsheetImport.defaultProps = defaultSpreadsheetImportProps;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { spreadsheetImportState } from '../states/spreadsheetImportState';
|
import { spreadsheetImportState } from '@/spreadsheet-import/states/spreadsheetImportState';
|
||||||
|
|
||||||
import { SpreadsheetImport } from './SpreadsheetImport';
|
import { SpreadsheetImport } from './SpreadsheetImport';
|
||||||
|
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
import { RsiProps } from '../types';
|
import { SpreadsheetOptions } from '../types';
|
||||||
|
|
||||||
export type SpreadsheetImportState<T extends string> = {
|
export type SpreadsheetImportState<T extends string> = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
options: Omit<RsiProps<T>, 'isOpen' | 'onClose'> | null;
|
options: Omit<SpreadsheetOptions<T>, 'isOpen' | 'onClose'> | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const spreadsheetImportState = atom<SpreadsheetImportState<string>>({
|
export const spreadsheetImportState = atom<SpreadsheetImportState<any>>({
|
||||||
key: 'spreadsheetImportState',
|
key: 'spreadsheetImportState',
|
||||||
default: {
|
default: {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/core/ContinueButton';
|
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
||||||
import { Heading } from '@/spreadsheet-import/components/core/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import type { Field, RawData } from '@/spreadsheet-import/types';
|
import type { Field, RawData } from '@/spreadsheet-import/types';
|
||||||
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
|
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
|
||||||
import { getMatchedColumns } from '@/spreadsheet-import/utils/getMatchedColumns';
|
import { getMatchedColumns } from '@/spreadsheet-import/utils/getMatchedColumns';
|
||||||
@ -114,7 +114,8 @@ export const MatchColumnsStep = <T extends string>({
|
|||||||
const { enqueueDialog } = useDialog();
|
const { enqueueDialog } = useDialog();
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const dataExample = data.slice(0, 2);
|
const dataExample = data.slice(0, 2);
|
||||||
const { fields, autoMapHeaders, autoMapDistance } = useRsi<T>();
|
const { fields, autoMapHeaders, autoMapDistance } =
|
||||||
|
useSpreadsheetImportInternal<T>();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [columns, setColumns] = useState<Columns<T>>(
|
const [columns, setColumns] = useState<Columns<T>>(
|
||||||
// Do not remove spread, it indexes empty array elements, otherwise map() skips over them
|
// Do not remove spread, it indexes empty array elements, otherwise map() skips over them
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { MatchColumnSelect } from '@/spreadsheet-import/components/core/MatchColumnSelect';
|
import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect';
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { SelectOption } from '@/spreadsheet-import/types';
|
import { SelectOption } from '@/spreadsheet-import/types';
|
||||||
import { getFieldOptions } from '@/spreadsheet-import/utils/getFieldOptions';
|
import { getFieldOptions } from '@/spreadsheet-import/utils/getFieldOptions';
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ export const SubMatchingSelect = <T extends string>({
|
|||||||
column,
|
column,
|
||||||
onSubChange,
|
onSubChange,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
const { fields } = useRsi<T>();
|
const { fields } = useSpreadsheetImportInternal<T>();
|
||||||
const options = getFieldOptions(fields, column.value) as SelectOption[];
|
const options = getFieldOptions(fields, column.value) as SelectOption[];
|
||||||
const value = options.find((opt) => opt.value === option.value);
|
const value = options.find((opt) => opt.value === option.value);
|
||||||
|
|
||||||
@ -8,8 +8,8 @@ import {
|
|||||||
} from '@chakra-ui/accordion';
|
} from '@chakra-ui/accordion';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { MatchColumnSelect } from '@/spreadsheet-import/components/core/MatchColumnSelect';
|
import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect';
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import type { Fields } from '@/spreadsheet-import/types';
|
import type { Fields } from '@/spreadsheet-import/types';
|
||||||
import { IconChevronDown, IconForbid } from '@/ui/icon';
|
import { IconChevronDown, IconForbid } from '@/ui/icon';
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export const TemplateColumn = <T extends string>({
|
|||||||
onChange,
|
onChange,
|
||||||
onSubChange,
|
onSubChange,
|
||||||
}: TemplateColumnProps<T>) => {
|
}: TemplateColumnProps<T>) => {
|
||||||
const { fields } = useRsi<T>();
|
const { fields } = useSpreadsheetImportInternal<T>();
|
||||||
const column = columns[columnIndex];
|
const column = columns[columnIndex];
|
||||||
const isIgnored = column.type === ColumnType.ignored;
|
const isIgnored = column.type === ColumnType.ignored;
|
||||||
const isSelect = 'matchedOptions' in column;
|
const isSelect = 'matchedOptions' in column;
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/core/ContinueButton';
|
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
||||||
import { Heading } from '@/spreadsheet-import/components/core/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
import type { RawData } from '@/spreadsheet-import/types';
|
import type { RawData } from '@/spreadsheet-import/types';
|
||||||
import { Modal } from '@/ui/modal/components/Modal';
|
import { Modal } from '@/ui/modal/components/Modal';
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { Table } from '@/spreadsheet-import/components/core/Table';
|
import { Table } from '@/spreadsheet-import/components/Table';
|
||||||
import type { RawData } from '@/spreadsheet-import/types';
|
import type { RawData } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
import { generateSelectionColumns } from './SelectColumn';
|
import { generateSelectionColumns } from './SelectColumn';
|
||||||
@ -1,13 +1,12 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { Heading } from '@/spreadsheet-import/components/core/Heading';
|
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
||||||
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
import { Radio } from '@/ui/input/radio/components/Radio';
|
import { Radio } from '@/ui/input/radio/components/Radio';
|
||||||
import { RadioGroup } from '@/ui/input/radio/components/RadioGroup';
|
import { RadioGroup } from '@/ui/input/radio/components/RadioGroup';
|
||||||
import { Modal } from '@/ui/modal/components/Modal';
|
import { Modal } from '@/ui/modal/components/Modal';
|
||||||
|
|
||||||
import { ContinueButton } from '../../core/ContinueButton';
|
|
||||||
|
|
||||||
const Content = styled(Modal.Content)`
|
const Content = styled(Modal.Content)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInitialStep } from '@/spreadsheet-import/hooks/useSpreadsheetImportInitialStep';
|
||||||
import { useRsiInitialStep } from '@/spreadsheet-import/hooks/useRsiInitialStep';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { Modal } from '@/ui/modal/components/Modal';
|
import { Modal } from '@/ui/modal/components/Modal';
|
||||||
import { StepBar } from '@/ui/step-bar/components/StepBar';
|
import { StepBar } from '@/ui/step-bar/components/StepBar';
|
||||||
import { useStepBar } from '@/ui/step-bar/hooks/useStepBar';
|
import { useStepBar } from '@/ui/step-bar/hooks/useStepBar';
|
||||||
@ -24,9 +24,11 @@ const stepTitles = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const Steps = () => {
|
export const Steps = () => {
|
||||||
const { initialStepState } = useRsi();
|
const { initialStepState } = useSpreadsheetImportInternal();
|
||||||
|
|
||||||
const { steps, initialStep } = useRsiInitialStep(initialStepState?.type);
|
const { steps, initialStep } = useSpreadsheetImportInitialStep(
|
||||||
|
initialStepState?.type,
|
||||||
|
);
|
||||||
|
|
||||||
const { nextStep, activeStep } = useStepBar({
|
const { nextStep, activeStep } = useStepBar({
|
||||||
initialStep,
|
initialStep,
|
||||||
@ -3,7 +3,7 @@ import { useTheme } from '@emotion/react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import type XLSX from 'xlsx-ugnis';
|
import type XLSX from 'xlsx-ugnis';
|
||||||
|
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import type { RawData } from '@/spreadsheet-import/types';
|
import type { RawData } from '@/spreadsheet-import/types';
|
||||||
import { exceedsMaxRecords } from '@/spreadsheet-import/utils/exceedsMaxRecords';
|
import { exceedsMaxRecords } from '@/spreadsheet-import/utils/exceedsMaxRecords';
|
||||||
import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
|
import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
|
||||||
@ -29,6 +29,7 @@ export enum StepType {
|
|||||||
selectHeader = 'selectHeader',
|
selectHeader = 'selectHeader',
|
||||||
matchColumns = 'matchColumns',
|
matchColumns = 'matchColumns',
|
||||||
validateData = 'validateData',
|
validateData = 'validateData',
|
||||||
|
loading = 'loading',
|
||||||
}
|
}
|
||||||
export type StepState =
|
export type StepState =
|
||||||
| {
|
| {
|
||||||
@ -50,6 +51,9 @@ export type StepState =
|
|||||||
| {
|
| {
|
||||||
type: StepType.validateData;
|
type: StepType.validateData;
|
||||||
data: any[];
|
data: any[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: StepType.loading;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -58,7 +62,7 @@ interface Props {
|
|||||||
|
|
||||||
export const UploadFlow = ({ nextStep }: Props) => {
|
export const UploadFlow = ({ nextStep }: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { initialStepState } = useRsi();
|
const { initialStepState } = useSpreadsheetImportInternal();
|
||||||
const [state, setState] = useState<StepState>(
|
const [state, setState] = useState<StepState>(
|
||||||
initialStepState || { type: StepType.upload },
|
initialStepState || { type: StepType.upload },
|
||||||
);
|
);
|
||||||
@ -68,7 +72,7 @@ export const UploadFlow = ({ nextStep }: Props) => {
|
|||||||
uploadStepHook,
|
uploadStepHook,
|
||||||
selectHeaderStepHook,
|
selectHeaderStepHook,
|
||||||
matchColumnsStepHook,
|
matchColumnsStepHook,
|
||||||
} = useRsi();
|
} = useSpreadsheetImportInternal();
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
const errorToast = useCallback(
|
const errorToast = useCallback(
|
||||||
@ -191,7 +195,18 @@ export const UploadFlow = ({ nextStep }: Props) => {
|
|||||||
if (!uploadedFile) {
|
if (!uploadedFile) {
|
||||||
throw new Error('File not found');
|
throw new Error('File not found');
|
||||||
}
|
}
|
||||||
return <ValidationStep initialData={state.data} file={uploadedFile} />;
|
return (
|
||||||
|
<ValidationStep
|
||||||
|
initialData={state.data}
|
||||||
|
file={uploadedFile}
|
||||||
|
onSubmitStart={() =>
|
||||||
|
setState({
|
||||||
|
type: StepType.loading,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case StepType.loading:
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<ProgressBarContainer>
|
<ProgressBarContainer>
|
||||||
@ -3,7 +3,7 @@ import { useDropzone } from 'react-dropzone';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import * as XLSX from 'xlsx-ugnis';
|
import * as XLSX from 'xlsx-ugnis';
|
||||||
|
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { readFileAsync } from '@/spreadsheet-import/utils/readFilesAsync';
|
import { readFileAsync } from '@/spreadsheet-import/utils/readFilesAsync';
|
||||||
import { MainButton } from '@/ui/button/components/MainButton';
|
import { MainButton } from '@/ui/button/components/MainButton';
|
||||||
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
|
||||||
@ -83,7 +83,7 @@ type DropZoneProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => {
|
export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => {
|
||||||
const { maxFileSize, dateFormat, parseRaw } = useRsi();
|
const { maxFileSize, dateFormat, parseRaw } = useSpreadsheetImportInternal();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { Table } from '@/spreadsheet-import/components/core/Table';
|
import { Table } from '@/spreadsheet-import/components/Table';
|
||||||
import type { Fields } from '@/spreadsheet-import/types';
|
import type { Fields } from '@/spreadsheet-import/types';
|
||||||
import { generateExampleRow } from '@/spreadsheet-import/utils/generateExampleRow';
|
import { generateExampleRow } from '@/spreadsheet-import/utils/generateExampleRow';
|
||||||
|
|
||||||
@ -2,10 +2,10 @@ import { useCallback, useMemo, useState } from 'react';
|
|||||||
import type { RowsChangeData } from 'react-data-grid';
|
import type { RowsChangeData } from 'react-data-grid';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/core/ContinueButton';
|
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
||||||
import { Heading } from '@/spreadsheet-import/components/core/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
import { Table } from '@/spreadsheet-import/components/core/Table';
|
import { Table } from '@/spreadsheet-import/components/Table';
|
||||||
import { useRsi } from '@/spreadsheet-import/hooks/useRsi';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import type { Data } from '@/spreadsheet-import/types';
|
import type { Data } from '@/spreadsheet-import/types';
|
||||||
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
|
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
|
||||||
import { Button, ButtonVariant } from '@/ui/button/components/Button';
|
import { Button, ButtonVariant } from '@/ui/button/components/Button';
|
||||||
@ -56,14 +56,17 @@ const NoRowsContainer = styled.div`
|
|||||||
type Props<T extends string> = {
|
type Props<T extends string> = {
|
||||||
initialData: Data<T>[];
|
initialData: Data<T>[];
|
||||||
file: File;
|
file: File;
|
||||||
|
onSubmitStart?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ValidationStep = <T extends string>({
|
export const ValidationStep = <T extends string>({
|
||||||
initialData,
|
initialData,
|
||||||
file,
|
file,
|
||||||
|
onSubmitStart,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
const { enqueueDialog } = useDialog();
|
const { enqueueDialog } = useDialog();
|
||||||
const { fields, onClose, onSubmit, rowHook, tableHook } = useRsi<T>();
|
const { fields, onClose, onSubmit, rowHook, tableHook } =
|
||||||
|
useSpreadsheetImportInternal<T>();
|
||||||
|
|
||||||
const [data, setData] = useState<(Data<T> & Meta)[]>(
|
const [data, setData] = useState<(Data<T> & Meta)[]>(
|
||||||
useMemo(
|
useMemo(
|
||||||
@ -146,7 +149,8 @@ export const ValidationStep = <T extends string>({
|
|||||||
},
|
},
|
||||||
{ validData: [] as Data<T>[], invalidData: [] as Data<T>[], all: data },
|
{ validData: [] as Data<T>[], invalidData: [] as Data<T>[], all: data },
|
||||||
);
|
);
|
||||||
onSubmit(calculatedData, file);
|
onSubmitStart?.();
|
||||||
|
await onSubmit(calculatedData, file);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
const onContinue = () => {
|
const onContinue = () => {
|
||||||
@ -2,7 +2,7 @@ import { Column, useRowSelection } from 'react-data-grid';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { MatchColumnSelect } from '@/spreadsheet-import/components/core/MatchColumnSelect';
|
import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect';
|
||||||
import type { Data, Fields } from '@/spreadsheet-import/types';
|
import type { Data, Fields } from '@/spreadsheet-import/types';
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
@ -118,14 +118,13 @@ export const generateColumns = <T extends string>(
|
|||||||
),
|
),
|
||||||
editable: column.fieldType.type !== 'checkbox',
|
editable: column.fieldType.type !== 'checkbox',
|
||||||
editor: ({ row, onRowChange, onClose }) => {
|
editor: ({ row, onRowChange, onClose }) => {
|
||||||
|
const columnKey = column.key as keyof (Data<T> & Meta);
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
switch (column.fieldType.type) {
|
switch (column.fieldType.type) {
|
||||||
case 'select': {
|
case 'select': {
|
||||||
const value = column.fieldType.options.find(
|
const value = column.fieldType.options.find(
|
||||||
(option) =>
|
(option) => option.value === (row[columnKey] as string),
|
||||||
option.value ===
|
|
||||||
(row[column.key as keyof (Data<T> & Meta)] as string),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
component = (
|
component = (
|
||||||
@ -139,7 +138,7 @@ export const generateColumns = <T extends string>(
|
|||||||
: value
|
: value
|
||||||
}
|
}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
onRowChange({ ...row, [column.key]: value?.value }, true);
|
onRowChange({ ...row, [columnKey]: value?.value }, true);
|
||||||
}}
|
}}
|
||||||
options={column.fieldType.options}
|
options={column.fieldType.options}
|
||||||
/>
|
/>
|
||||||
@ -149,9 +148,9 @@ export const generateColumns = <T extends string>(
|
|||||||
default:
|
default:
|
||||||
component = (
|
component = (
|
||||||
<TextInput
|
<TextInput
|
||||||
value={row[column.key] as string}
|
value={row[columnKey] as string}
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
onRowChange({ ...row, [column.key]: value });
|
onRowChange({ ...row, [columnKey]: value });
|
||||||
}}
|
}}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onBlur={() => onClose(true)}
|
onBlur={() => onClose(true)}
|
||||||
@ -165,23 +164,24 @@ export const generateColumns = <T extends string>(
|
|||||||
editOnClick: true,
|
editOnClick: true,
|
||||||
},
|
},
|
||||||
formatter: ({ row, onRowChange }) => {
|
formatter: ({ row, onRowChange }) => {
|
||||||
|
const columnKey = column.key as keyof (Data<T> & Meta);
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
switch (column.fieldType.type) {
|
switch (column.fieldType.type) {
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
component = (
|
component = (
|
||||||
<ToggleContainer
|
<ToggleContainer
|
||||||
id={`${column.key}-${row.__index}`}
|
id={`${columnKey}-${row.__index}`}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toggle
|
<Toggle
|
||||||
value={row[column.key] as boolean}
|
value={row[columnKey] as boolean}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
onRowChange({
|
onRowChange({
|
||||||
...row,
|
...row,
|
||||||
[column.key]: !row[column.key as T],
|
[columnKey]: !row[columnKey],
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -190,30 +190,30 @@ export const generateColumns = <T extends string>(
|
|||||||
break;
|
break;
|
||||||
case 'select':
|
case 'select':
|
||||||
component = (
|
component = (
|
||||||
<DefaultContainer id={`${column.key}-${row.__index}`}>
|
<DefaultContainer id={`${columnKey}-${row.__index}`}>
|
||||||
{column.fieldType.options.find(
|
{column.fieldType.options.find(
|
||||||
(option) => option.value === row[column.key as T],
|
(option) => option.value === row[columnKey as T],
|
||||||
)?.label || null}
|
)?.label || null}
|
||||||
</DefaultContainer>
|
</DefaultContainer>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
component = (
|
component = (
|
||||||
<DefaultContainer id={`${column.key}-${row.__index}`}>
|
<DefaultContainer id={`${columnKey}-${row.__index}`}>
|
||||||
{row[column.key as T]}
|
{row[columnKey]}
|
||||||
</DefaultContainer>
|
</DefaultContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.__errors?.[column.key]) {
|
if (row.__errors?.[columnKey]) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{component}
|
{component}
|
||||||
{createPortal(
|
{createPortal(
|
||||||
<AppTooltip
|
<AppTooltip
|
||||||
anchorSelect={`#${column.key}-${row.__index}`}
|
anchorSelect={`#${columnKey}-${row.__index}`}
|
||||||
place="top"
|
place="top"
|
||||||
content={row.__errors?.[column.key]?.message}
|
content={row.__errors?.[columnKey]?.message}
|
||||||
/>,
|
/>,
|
||||||
document.body,
|
document.body,
|
||||||
)}
|
)}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Meta } from '@storybook/react';
|
import { Meta } from '@storybook/react';
|
||||||
|
|
||||||
import { ModalWrapper } from '@/spreadsheet-import/components/core/ModalWrapper';
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
import { Providers } from '@/spreadsheet-import/components/core/Providers';
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
import { MatchColumnsStep } from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
import { MatchColumnsStep } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
||||||
|
|
||||||
const meta: Meta<typeof MatchColumnsStep> = {
|
const meta: Meta<typeof MatchColumnsStep> = {
|
||||||
@ -58,7 +58,7 @@ const mockData = [
|
|||||||
|
|
||||||
export function Default() {
|
export function Default() {
|
||||||
return (
|
return (
|
||||||
<Providers rsiValues={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<MatchColumnsStep
|
<MatchColumnsStep
|
||||||
headerValues={mockData[0] as string[]}
|
headerValues={mockData[0] as string[]}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Meta } from '@storybook/react';
|
import { Meta } from '@storybook/react';
|
||||||
|
|
||||||
import { ModalWrapper } from '@/spreadsheet-import/components/core/ModalWrapper';
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
import { Providers } from '@/spreadsheet-import/components/core/Providers';
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
import { SelectHeaderStep } from '@/spreadsheet-import/components/steps/SelectHeaderStep/SelectHeaderStep';
|
import { SelectHeaderStep } from '@/spreadsheet-import/steps/components/SelectHeaderStep/SelectHeaderStep';
|
||||||
import {
|
import {
|
||||||
headerSelectionTableFields,
|
headerSelectionTableFields,
|
||||||
mockRsiValues,
|
mockRsiValues,
|
||||||
@ -20,7 +20,7 @@ export default meta;
|
|||||||
|
|
||||||
export function Default() {
|
export function Default() {
|
||||||
return (
|
return (
|
||||||
<Providers rsiValues={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<SelectHeaderStep
|
<SelectHeaderStep
|
||||||
data={headerSelectionTableFields}
|
data={headerSelectionTableFields}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Meta } from '@storybook/react';
|
import { Meta } from '@storybook/react';
|
||||||
|
|
||||||
import { ModalWrapper } from '@/spreadsheet-import/components/core/ModalWrapper';
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
import { Providers } from '@/spreadsheet-import/components/core/Providers';
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
import { SelectSheetStep } from '@/spreadsheet-import/components/steps/SelectSheetStep/SelectSheetStep';
|
import { SelectSheetStep } from '@/spreadsheet-import/steps/components/SelectSheetStep/SelectSheetStep';
|
||||||
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
||||||
|
|
||||||
const meta: Meta<typeof SelectSheetStep> = {
|
const meta: Meta<typeof SelectSheetStep> = {
|
||||||
@ -19,7 +19,7 @@ const sheetNames = ['Sheet1', 'Sheet2', 'Sheet3'];
|
|||||||
|
|
||||||
export function Default() {
|
export function Default() {
|
||||||
return (
|
return (
|
||||||
<Providers rsiValues={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<SelectSheetStep
|
<SelectSheetStep
|
||||||
sheetNames={sheetNames}
|
sheetNames={sheetNames}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Meta } from '@storybook/react';
|
import { Meta } from '@storybook/react';
|
||||||
|
|
||||||
import { ModalWrapper } from '@/spreadsheet-import/components/core/ModalWrapper';
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
import { Providers } from '@/spreadsheet-import/components/core/Providers';
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
import { UploadStep } from '@/spreadsheet-import/components/steps/UploadStep/UploadStep';
|
import { UploadStep } from '@/spreadsheet-import/steps/components/UploadStep/UploadStep';
|
||||||
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
import { mockRsiValues } from '@/spreadsheet-import/tests/mockRsiValues';
|
||||||
|
|
||||||
const meta: Meta<typeof UploadStep> = {
|
const meta: Meta<typeof UploadStep> = {
|
||||||
@ -17,7 +17,7 @@ export default meta;
|
|||||||
|
|
||||||
export function Default() {
|
export function Default() {
|
||||||
return (
|
return (
|
||||||
<Providers rsiValues={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<UploadStep onContinue={() => Promise.resolve()} />
|
<UploadStep onContinue={() => Promise.resolve()} />
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Meta } from '@storybook/react';
|
import { Meta } from '@storybook/react';
|
||||||
|
|
||||||
import { ModalWrapper } from '@/spreadsheet-import/components/core/ModalWrapper';
|
import { ModalWrapper } from '@/spreadsheet-import/components/ModalWrapper';
|
||||||
import { Providers } from '@/spreadsheet-import/components/core/Providers';
|
import { Providers } from '@/spreadsheet-import/components/Providers';
|
||||||
import { ValidationStep } from '@/spreadsheet-import/components/steps/ValidationStep/ValidationStep';
|
import { ValidationStep } from '@/spreadsheet-import/steps/components/ValidationStep/ValidationStep';
|
||||||
import {
|
import {
|
||||||
editableTableInitialData,
|
editableTableInitialData,
|
||||||
mockRsiValues,
|
mockRsiValues,
|
||||||
@ -22,7 +22,7 @@ const file = new File([''], 'file.csv');
|
|||||||
|
|
||||||
export function Default() {
|
export function Default() {
|
||||||
return (
|
return (
|
||||||
<Providers rsiValues={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<ValidationStep initialData={editableTableInitialData} file={file} />
|
<ValidationStep initialData={editableTableInitialData} file={file} />
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { defaultRSIProps } from '@/spreadsheet-import/components/SpreadsheetImport';
|
import { defaultSpreadsheetImportProps } from '@/spreadsheet-import/provider/components/SpreadsheetImport';
|
||||||
import type { RsiProps } from '@/spreadsheet-import/types';
|
import type { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
@ -87,13 +87,14 @@ const fields = [
|
|||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const mockComponentBehaviourForTypes = <T extends string>(props: RsiProps<T>) =>
|
const mockComponentBehaviourForTypes = <T extends string>(
|
||||||
props;
|
props: SpreadsheetOptions<T>,
|
||||||
|
) => props;
|
||||||
|
|
||||||
export const mockRsiValues = mockComponentBehaviourForTypes({
|
export const mockRsiValues = mockComponentBehaviourForTypes({
|
||||||
...defaultRSIProps,
|
...defaultSpreadsheetImportProps,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
onSubmit: (data) => {
|
onSubmit: async (data) => {
|
||||||
console.log(data.all.map((value) => value));
|
console.log(data.all.map((value) => value));
|
||||||
},
|
},
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { ReadonlyDeep } from 'type-fest';
|
import { ReadonlyDeep } from 'type-fest';
|
||||||
|
|
||||||
import { Columns } from '../components/steps/MatchColumnsStep/MatchColumnsStep';
|
import { Columns } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import { StepState } from '../components/steps/UploadFlow';
|
import { StepState } from '@/spreadsheet-import/steps/components/UploadFlow';
|
||||||
import { Meta } from '../components/steps/ValidationStep/types';
|
import { Meta } from '@/spreadsheet-import/steps/components/ValidationStep/types';
|
||||||
|
|
||||||
export type RsiProps<T extends string> = {
|
export type SpreadsheetOptions<Keys extends string> = {
|
||||||
// Is modal visible.
|
// Is modal visible.
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
// callback when RSI is closed before final submit
|
// callback when RSI is closed before final submit
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
// Field description for requested data
|
// Field description for requested data
|
||||||
fields: Fields<T>;
|
fields: Fields<Keys>;
|
||||||
// Runs after file upload step, receives and returns raw sheet data
|
// Runs after file upload step, receives and returns raw sheet data
|
||||||
uploadStepHook?: (data: RawData[]) => Promise<RawData[]>;
|
uploadStepHook?: (data: RawData[]) => Promise<RawData[]>;
|
||||||
// Runs after header selection step, receives and returns raw sheet data
|
// Runs after header selection step, receives and returns raw sheet data
|
||||||
@ -20,16 +20,16 @@ export type RsiProps<T extends string> = {
|
|||||||
) => Promise<{ headerValues: RawData; data: RawData[] }>;
|
) => Promise<{ headerValues: RawData; data: RawData[] }>;
|
||||||
// Runs once before validation step, used for data mutations and if you want to change how columns were matched
|
// Runs once before validation step, used for data mutations and if you want to change how columns were matched
|
||||||
matchColumnsStepHook?: (
|
matchColumnsStepHook?: (
|
||||||
table: Data<T>[],
|
table: Data<Keys>[],
|
||||||
rawData: RawData[],
|
rawData: RawData[],
|
||||||
columns: Columns<T>,
|
columns: Columns<Keys>,
|
||||||
) => Promise<Data<T>[]>;
|
) => Promise<Data<Keys>[]>;
|
||||||
// Runs after column matching and on entry change
|
// Runs after column matching and on entry change
|
||||||
rowHook?: RowHook<T>;
|
rowHook?: RowHook<Keys>;
|
||||||
// Runs after column matching and on entry change
|
// Runs after column matching and on entry change
|
||||||
tableHook?: TableHook<T>;
|
tableHook?: TableHook<Keys>;
|
||||||
// Function called after user finishes the flow
|
// Function called after user finishes the flow
|
||||||
onSubmit: (data: Result<T>, file: File) => void;
|
onSubmit: (data: Result<Keys>, file: File) => Promise<void>;
|
||||||
// Allows submitting with errors. Default: true
|
// Allows submitting with errors. Default: true
|
||||||
allowInvalidSubmit?: boolean;
|
allowInvalidSubmit?: boolean;
|
||||||
// Theme configuration passed to underlying Chakra-UI
|
// Theme configuration passed to underlying Chakra-UI
|
||||||
@ -110,7 +110,8 @@ export type Input = {
|
|||||||
export type Validation =
|
export type Validation =
|
||||||
| RequiredValidation
|
| RequiredValidation
|
||||||
| UniqueValidation
|
| UniqueValidation
|
||||||
| RegexValidation;
|
| RegexValidation
|
||||||
|
| FunctionValidation;
|
||||||
|
|
||||||
export type RequiredValidation = {
|
export type RequiredValidation = {
|
||||||
rule: 'required';
|
rule: 'required';
|
||||||
@ -133,6 +134,13 @@ export type RegexValidation = {
|
|||||||
level?: ErrorLevel;
|
level?: ErrorLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FunctionValidation = {
|
||||||
|
rule: 'function';
|
||||||
|
isValid: (value: string) => boolean;
|
||||||
|
errorMessage: string;
|
||||||
|
level?: ErrorLevel;
|
||||||
|
};
|
||||||
|
|
||||||
export type RowHook<T extends string> = (
|
export type RowHook<T extends string> = (
|
||||||
row: Data<T>,
|
row: Data<T>,
|
||||||
addError: (fieldKey: T, error: Info) => void,
|
addError: (fieldKey: T, error: Info) => void,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { v4 } from 'uuid';
|
|||||||
import type {
|
import type {
|
||||||
Errors,
|
Errors,
|
||||||
Meta,
|
Meta,
|
||||||
} from '@/spreadsheet-import/components/steps/ValidationStep/types';
|
} from '@/spreadsheet-import/steps/components/ValidationStep/types';
|
||||||
import type {
|
import type {
|
||||||
Data,
|
Data,
|
||||||
Fields,
|
Fields,
|
||||||
@ -93,8 +93,9 @@ export const addErrorsAndRunHooks = <T extends string>(
|
|||||||
case 'regex': {
|
case 'regex': {
|
||||||
const regex = new RegExp(validation.value, validation.flags);
|
const regex = new RegExp(validation.value, validation.flags);
|
||||||
data.forEach((entry, index) => {
|
data.forEach((entry, index) => {
|
||||||
const value = entry[field.key]?.toString() ?? '';
|
const value = entry[field.key]?.toString();
|
||||||
if (!value.match(regex)) {
|
|
||||||
|
if (value && !value.match(regex)) {
|
||||||
errors[index] = {
|
errors[index] = {
|
||||||
...errors[index],
|
...errors[index],
|
||||||
[field.key]: {
|
[field.key]: {
|
||||||
@ -108,6 +109,22 @@ export const addErrorsAndRunHooks = <T extends string>(
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'function': {
|
||||||
|
data.forEach((entry, index) => {
|
||||||
|
const value = entry[field.key]?.toString();
|
||||||
|
|
||||||
|
if (value && !validation.isValid(value)) {
|
||||||
|
errors[index] = {
|
||||||
|
...errors[index],
|
||||||
|
[field.key]: {
|
||||||
|
level: validation.level || 'error',
|
||||||
|
message: validation.errorMessage || 'Field is invalid',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { Columns } from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
import type { Columns } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import type { Fields } from '@/spreadsheet-import/types';
|
import type { Fields } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
export const findUnmatchedRequiredFields = <T extends string>(
|
export const findUnmatchedRequiredFields = <T extends string>(
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type {
|
|||||||
Column,
|
Column,
|
||||||
Columns,
|
Columns,
|
||||||
MatchColumnsProps,
|
MatchColumnsProps,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import type { Field, Fields } from '@/spreadsheet-import/types';
|
import type { Field, Fields } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
import { findMatch } from './findMatch';
|
import { findMatch } from './findMatch';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Columns,
|
Columns,
|
||||||
ColumnType,
|
ColumnType,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import type { Data, Fields, RawData } from '@/spreadsheet-import/types';
|
import type { Data, Fields, RawData } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
import { normalizeCheckboxValue } from './normalizeCheckboxValue';
|
import { normalizeCheckboxValue } from './normalizeCheckboxValue';
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
ColumnType,
|
ColumnType,
|
||||||
MatchColumnsProps,
|
MatchColumnsProps,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import type { Field } from '@/spreadsheet-import/types';
|
import type { Field } from '@/spreadsheet-import/types';
|
||||||
|
|
||||||
import { uniqueEntries } from './uniqueEntries';
|
import { uniqueEntries } from './uniqueEntries';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
ColumnType,
|
ColumnType,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
|
|
||||||
export const setIgnoreColumn = <T extends string>({
|
export const setIgnoreColumn = <T extends string>({
|
||||||
header,
|
header,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
MatchedOptions,
|
MatchedOptions,
|
||||||
MatchedSelectColumn,
|
MatchedSelectColumn,
|
||||||
MatchedSelectOptionsColumn,
|
MatchedSelectOptionsColumn,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
|
|
||||||
export const setSubColumn = <T>(
|
export const setSubColumn = <T>(
|
||||||
oldColumn: MatchedSelectColumn<T> | MatchedSelectOptionsColumn<T>,
|
oldColumn: MatchedSelectColumn<T> | MatchedSelectOptionsColumn<T>,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import uniqBy from 'lodash/uniqBy';
|
|||||||
import type {
|
import type {
|
||||||
MatchColumnsProps,
|
MatchColumnsProps,
|
||||||
MatchedOptions,
|
MatchedOptions,
|
||||||
} from '@/spreadsheet-import/components/steps/MatchColumnsStep/MatchColumnsStep';
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
|
|
||||||
export const uniqueEntries = <T extends string>(
|
export const uniqueEntries = <T extends string>(
|
||||||
data: MatchColumnsProps<T>['data'],
|
data: MatchColumnsProps<T>['data'],
|
||||||
|
|||||||
@ -19,7 +19,11 @@ export function DialogProvider({ children }: React.PropsWithChildren) {
|
|||||||
<>
|
<>
|
||||||
{children}
|
{children}
|
||||||
{dialogState.queue.map((dialog) => (
|
{dialogState.queue.map((dialog) => (
|
||||||
<Dialog {...dialog} onClose={() => handleDialogClose(dialog.id)} />
|
<Dialog
|
||||||
|
key={dialog.id}
|
||||||
|
{...dialog}
|
||||||
|
onClose={() => handleDialogClose(dialog.id)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export {
|
|||||||
IconBrandGithub,
|
IconBrandGithub,
|
||||||
IconBrandGoogle,
|
IconBrandGoogle,
|
||||||
IconBrandLinkedin,
|
IconBrandLinkedin,
|
||||||
|
IconBrandTwitter,
|
||||||
IconBrandX,
|
IconBrandX,
|
||||||
IconBriefcase,
|
IconBriefcase,
|
||||||
IconBuildingSkyscraper,
|
IconBuildingSkyscraper,
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const Container = styled.div<{ labelPosition?: LabelPosition }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
type RadioInputProps = {
|
type RadioInputProps = {
|
||||||
radioSize?: RadioSize;
|
'radio-size'?: RadioSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RadioInput = styled(motion.input)<RadioInputProps>`
|
const RadioInput = styled(motion.input)<RadioInputProps>`
|
||||||
@ -60,13 +60,13 @@ const RadioInput = styled(motion.input)<RadioInputProps>`
|
|||||||
background-color: ${({ theme }) => theme.grayScale.gray0};
|
background-color: ${({ theme }) => theme.grayScale.gray0};
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
content: '';
|
content: '';
|
||||||
height: ${({ radioSize }) =>
|
height: ${({ 'radio-size': radioSize }) =>
|
||||||
radioSize === RadioSize.Large ? '8px' : '6px'};
|
radioSize === RadioSize.Large ? '8px' : '6px'};
|
||||||
left: 50%;
|
left: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: ${({ radioSize }) =>
|
width: ${({ 'radio-size': radioSize }) =>
|
||||||
radioSize === RadioSize.Large ? '8px' : '6px'};
|
radioSize === RadioSize.Large ? '8px' : '6px'};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,10 +74,10 @@ const RadioInput = styled(motion.input)<RadioInputProps>`
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.12;
|
opacity: 0.12;
|
||||||
}
|
}
|
||||||
height: ${({ radioSize }) =>
|
height: ${({ 'radio-size': radioSize }) =>
|
||||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||||
position: relative;
|
position: relative;
|
||||||
width: ${({ radioSize }) =>
|
width: ${({ 'radio-size': radioSize }) =>
|
||||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ export function Radio({
|
|||||||
data-testid="input-radio"
|
data-testid="input-radio"
|
||||||
checked={checked}
|
checked={checked}
|
||||||
value={value}
|
value={value}
|
||||||
radioSize={size}
|
radio-size={size}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
initial={{ scale: 0.95 }}
|
initial={{ scale: 0.95 }}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const Container = styled.div<{ isLast: boolean }>`
|
|||||||
flex-grow: ${({ isLast }) => (isLast ? '0' : '1')};
|
flex-grow: ${({ isLast }) => (isLast ? '0' : '1')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StepCircle = styled(motion.div)<{ isCurrent: boolean }>`
|
const StepCircle = styled(motion.div)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
@ -40,7 +40,7 @@ const StepLabel = styled.span<{ isActive: boolean }>`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StepLine = styled(motion.div)<{ isActive: boolean }>`
|
const StepLine = styled(motion.div)`
|
||||||
height: 2px;
|
height: 2px;
|
||||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||||
@ -92,7 +92,6 @@ export const Step = ({
|
|||||||
return (
|
return (
|
||||||
<Container isLast={isLast}>
|
<Container isLast={isLast}>
|
||||||
<StepCircle
|
<StepCircle
|
||||||
isCurrent={isActive}
|
|
||||||
variants={variantsCircle}
|
variants={variantsCircle}
|
||||||
animate={isActive ? 'active' : 'inactive'}
|
animate={isActive ? 'active' : 'inactive'}
|
||||||
>
|
>
|
||||||
@ -107,7 +106,6 @@ export const Step = ({
|
|||||||
<StepLabel isActive={isActive}>{label}</StepLabel>
|
<StepLabel isActive={isActive}>{label}</StepLabel>
|
||||||
{!isLast && (
|
{!isLast && (
|
||||||
<StepLine
|
<StepLine
|
||||||
isActive={isActive}
|
|
||||||
variants={variantsLine}
|
variants={variantsLine}
|
||||||
animate={isActive ? 'active' : 'inactive'}
|
animate={isActive ? 'active' : 'inactive'}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -99,6 +99,7 @@ type OwnProps<SortField> = {
|
|||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
|
onImport?: () => void;
|
||||||
updateEntityMutation: any;
|
updateEntityMutation: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ export function EntityTable<SortField>({
|
|||||||
onColumnsChange,
|
onColumnsChange,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
|
onImport,
|
||||||
updateEntityMutation,
|
updateEntityMutation,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||||
@ -136,6 +138,7 @@ export function EntityTable<SortField>({
|
|||||||
onColumnsChange={onColumnsChange}
|
onColumnsChange={onColumnsChange}
|
||||||
onSortsUpdate={onSortsUpdate}
|
onSortsUpdate={onSortsUpdate}
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
|
onImport={onImport}
|
||||||
/>
|
/>
|
||||||
<StyledTableWrapper>
|
<StyledTableWrapper>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState';
|
||||||
|
|
||||||
|
export function useUpsertEntityTableItems() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
<T extends { id: string }>(entities: T[]) => {
|
||||||
|
// Create a map of new entities for quick lookup.
|
||||||
|
const newEntityMap = new Map(
|
||||||
|
entities.map((entity) => [entity.id, entity]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter out entities that are already the same in the state.
|
||||||
|
const entitiesToUpdate = entities.filter((entity) => {
|
||||||
|
const currentEntity = snapshot
|
||||||
|
.getLoadable(tableEntitiesFamilyState(entity.id))
|
||||||
|
.valueMaybe();
|
||||||
|
|
||||||
|
return (
|
||||||
|
!currentEntity ||
|
||||||
|
JSON.stringify(currentEntity) !==
|
||||||
|
JSON.stringify(newEntityMap.get(entity.id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Batch set state for the filtered entities.
|
||||||
|
for (const entity of entitiesToUpdate) {
|
||||||
|
set(tableEntitiesFamilyState(entity.id), entity);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
18
front/src/modules/ui/table/hooks/useUpsertTableRowIds.ts
Normal file
18
front/src/modules/ui/table/hooks/useUpsertTableRowIds.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||||
|
|
||||||
|
export function useUpsertTableRowIds() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(rowIds: string[]) => {
|
||||||
|
const currentRowIds = snapshot
|
||||||
|
.getLoadable(tableRowIdsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const uniqueRowIds = Array.from(new Set([...rowIds, ...currentRowIds]));
|
||||||
|
set(tableRowIdsState, uniqueRowIds);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -9,7 +9,6 @@ import { useTheme } from '@emotion/react';
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
|
|
||||||
import { IconButton } from '@/ui/button/components/IconButton';
|
import { IconButton } from '@/ui/button/components/IconButton';
|
||||||
import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader';
|
||||||
import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput';
|
import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput';
|
||||||
@ -51,6 +50,7 @@ import { TableOptionsDropdownSection } from './TableOptionsDropdownSection';
|
|||||||
type TableOptionsDropdownButtonProps = {
|
type TableOptionsDropdownButtonProps = {
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
|
onImport?: () => void;
|
||||||
HotkeyScope: TableOptionsHotkeyScope;
|
HotkeyScope: TableOptionsHotkeyScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,12 +61,11 @@ enum Option {
|
|||||||
export const TableOptionsDropdownButton = ({
|
export const TableOptionsDropdownButton = ({
|
||||||
onColumnsChange,
|
onColumnsChange,
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
|
onImport,
|
||||||
HotkeyScope,
|
HotkeyScope,
|
||||||
}: TableOptionsDropdownButtonProps) => {
|
}: TableOptionsDropdownButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { openSpreadsheetImport } = useSpreadsheetImport();
|
|
||||||
|
|
||||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
|
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
@ -94,16 +93,6 @@ export const TableOptionsDropdownButton = ({
|
|||||||
setHotkeyScopeAndMemorizePreviousScope,
|
setHotkeyScopeAndMemorizePreviousScope,
|
||||||
} = usePreviousHotkeyScope();
|
} = usePreviousHotkeyScope();
|
||||||
|
|
||||||
function handleImport() {
|
|
||||||
openSpreadsheetImport({
|
|
||||||
onSubmit: (datam, file) => {
|
|
||||||
console.log('datam', datam);
|
|
||||||
console.log('file', file);
|
|
||||||
},
|
|
||||||
fields: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleColumnVisibilityChange = useCallback(
|
const handleColumnVisibilityChange = useCallback(
|
||||||
(columnId: string, nextIsVisible: boolean) => {
|
(columnId: string, nextIsVisible: boolean) => {
|
||||||
const nextColumns = columns.map((column) =>
|
const nextColumns = columns.map((column) =>
|
||||||
@ -245,8 +234,8 @@ export const TableOptionsDropdownButton = ({
|
|||||||
<IconTag size={theme.icon.size.md} />
|
<IconTag size={theme.icon.size.md} />
|
||||||
Properties
|
Properties
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{false && (
|
{onImport && (
|
||||||
<DropdownMenuItem onClick={handleImport}>
|
<DropdownMenuItem onClick={onImport}>
|
||||||
<IconFileImport size={theme.icon.size.md} />
|
<IconFileImport size={theme.icon.size.md} />
|
||||||
Import
|
Import
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@ -26,6 +26,7 @@ type OwnProps<SortField> = {
|
|||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
|
onImport?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TableHeader<SortField>({
|
export function TableHeader<SortField>({
|
||||||
@ -34,6 +35,7 @@ export function TableHeader<SortField>({
|
|||||||
onColumnsChange,
|
onColumnsChange,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
|
onImport,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
const [sorts, setSorts] = useRecoilScopedState<SelectedSortType<SortField>[]>(
|
const [sorts, setSorts] = useRecoilScopedState<SelectedSortType<SortField>[]>(
|
||||||
sortScopedState,
|
sortScopedState,
|
||||||
@ -82,6 +84,7 @@ export function TableHeader<SortField>({
|
|||||||
isPrimaryButton
|
isPrimaryButton
|
||||||
/>
|
/>
|
||||||
<TableOptionsDropdownButton
|
<TableOptionsDropdownButton
|
||||||
|
onImport={onImport}
|
||||||
onColumnsChange={onColumnsChange}
|
onColumnsChange={onColumnsChange}
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
HotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
HotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { v4 } from 'uuid';
|
|||||||
|
|
||||||
import { CompanyTable } from '@/companies/table/components/CompanyTable';
|
import { CompanyTable } from '@/companies/table/components/CompanyTable';
|
||||||
import { SEARCH_COMPANY_QUERY } from '@/search/graphql/queries/searchCompanyQuery';
|
import { SEARCH_COMPANY_QUERY } from '@/search/graphql/queries/searchCompanyQuery';
|
||||||
|
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
|
||||||
import { IconBuildingSkyscraper } from '@/ui/icon';
|
import { IconBuildingSkyscraper } from '@/ui/icon';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||||
import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar';
|
import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar';
|
||||||
@ -64,21 +65,23 @@ export function Companies() {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithTopBarContainer
|
<SpreadsheetImportProvider>
|
||||||
title="Companies"
|
<WithTopBarContainer
|
||||||
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
title="Companies"
|
||||||
onAddButtonClick={handleAddButtonClick}
|
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
||||||
>
|
onAddButtonClick={handleAddButtonClick}
|
||||||
<RecoilScope
|
|
||||||
scopeId="companies"
|
|
||||||
SpecificContext={TableRecoilScopeContext}
|
|
||||||
>
|
>
|
||||||
<StyledTableContainer>
|
<RecoilScope
|
||||||
<CompanyTable />
|
scopeId="companies"
|
||||||
</StyledTableContainer>
|
SpecificContext={TableRecoilScopeContext}
|
||||||
<EntityTableActionBar />
|
>
|
||||||
<EntityTableContextMenu />
|
<StyledTableContainer>
|
||||||
</RecoilScope>
|
<CompanyTable />
|
||||||
</WithTopBarContainer>
|
</StyledTableContainer>
|
||||||
|
<EntityTableActionBar />
|
||||||
|
<EntityTableContextMenu />
|
||||||
|
</RecoilScope>
|
||||||
|
</WithTopBarContainer>
|
||||||
|
</SpreadsheetImportProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { PeopleTable } from '@/people/table/components/PeopleTable';
|
import { PeopleTable } from '@/people/table/components/PeopleTable';
|
||||||
|
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
|
||||||
import { IconUser } from '@/ui/icon';
|
import { IconUser } from '@/ui/icon';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||||
import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar';
|
import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar';
|
||||||
@ -56,18 +57,20 @@ export function People() {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecoilScope scopeId="people" SpecificContext={TableRecoilScopeContext}>
|
<SpreadsheetImportProvider>
|
||||||
<WithTopBarContainer
|
<RecoilScope scopeId="people" SpecificContext={TableRecoilScopeContext}>
|
||||||
title="People"
|
<WithTopBarContainer
|
||||||
icon={<IconUser size={theme.icon.size.sm} />}
|
title="People"
|
||||||
onAddButtonClick={handleAddButtonClick}
|
icon={<IconUser size={theme.icon.size.sm} />}
|
||||||
>
|
onAddButtonClick={handleAddButtonClick}
|
||||||
<StyledTableContainer>
|
>
|
||||||
<PeopleTable />
|
<StyledTableContainer>
|
||||||
</StyledTableContainer>
|
<PeopleTable />
|
||||||
<EntityTableActionBar />
|
</StyledTableContainer>
|
||||||
<EntityTableContextMenu />
|
<EntityTableActionBar />
|
||||||
</WithTopBarContainer>
|
<EntityTableContextMenu />
|
||||||
</RecoilScope>
|
</WithTopBarContainer>
|
||||||
|
</RecoilScope>
|
||||||
|
</SpreadsheetImportProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8083,10 +8083,10 @@ clsx@1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
|
|
||||||
clsx@^1.1.1:
|
clsx@^2.0.0:
|
||||||
version "1.2.1"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
|
||||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
|
||||||
|
|
||||||
cmdk@^0.2.0:
|
cmdk@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
@ -15937,12 +15937,12 @@ react-colorful@^5.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b"
|
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b"
|
||||||
integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==
|
integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==
|
||||||
|
|
||||||
react-data-grid@7.0.0-beta.13:
|
react-data-grid@^7.0.0-beta.36:
|
||||||
version "7.0.0-beta.13"
|
version "7.0.0-beta.36"
|
||||||
resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-7.0.0-beta.13.tgz#c0728258cbbb033af611eed50ed124744a76ee76"
|
resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-7.0.0-beta.36.tgz#d607b8f35fc1b90cfae078e9ae89ccbe29279f9f"
|
||||||
integrity sha512-vhBdkC2KqAawmmzYTcNlfhfjcYFQsinNr5pPTUG6/3DzLfYWo1S6nl48wgPWgyD9uDclV3H5NWvKSSwQTTsYMQ==
|
integrity sha512-HU+qVusA9UQU1bQsqKjkyRS4beL95Plfv5BoJ2Qc/wU1HsqxAM5K7NVqrmPQhYKUO+GxdkJkl2aDYSTgXk81Ww==
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx "^1.1.1"
|
clsx "^2.0.0"
|
||||||
|
|
||||||
react-datepicker@^4.11.0:
|
react-datepicker@^4.11.0:
|
||||||
version "4.16.0"
|
version "4.16.0"
|
||||||
|
|||||||
4
infra/dev/yarn.lock
Normal file
4
infra/dev/yarn.lock
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ import {
|
|||||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||||
import { AppAbility } from 'src/ability/ability.factory';
|
import { AppAbility } from 'src/ability/ability.factory';
|
||||||
import { FindUniqueCompanyArgs } from 'src/core/@generated/company/find-unique-company.args';
|
import { FindUniqueCompanyArgs } from 'src/core/@generated/company/find-unique-company.args';
|
||||||
|
import { CreateManyCompanyArgs } from 'src/core/@generated/company/create-many-company.args';
|
||||||
|
|
||||||
import { CompanyService } from './company.service';
|
import { CompanyService } from './company.service';
|
||||||
|
|
||||||
@ -123,4 +124,22 @@ export class CompanyResolver {
|
|||||||
select: prismaSelect.value,
|
select: prismaSelect.value,
|
||||||
} as Prisma.CompanyCreateArgs);
|
} as Prisma.CompanyCreateArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => AffectedRows, {
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
@UseGuards(AbilityGuard)
|
||||||
|
@CheckAbilities(CreateCompanyAbilityHandler)
|
||||||
|
async createManyCompany(
|
||||||
|
@Args() args: CreateManyCompanyArgs,
|
||||||
|
@AuthWorkspace() workspace: Workspace,
|
||||||
|
): Promise<Prisma.BatchPayload> {
|
||||||
|
return this.companyService.createMany({
|
||||||
|
data: args.data.map((company) => ({
|
||||||
|
...company,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
})),
|
||||||
|
skipDuplicates: args.skipDuplicates,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import { AppAbility } from 'src/ability/ability.factory';
|
|||||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||||
|
import { CreateManyPersonArgs } from 'src/core/@generated/person/create-many-person.args';
|
||||||
|
|
||||||
import { PersonService } from './person.service';
|
import { PersonService } from './person.service';
|
||||||
|
|
||||||
@ -165,6 +166,24 @@ export class PersonResolver {
|
|||||||
} as Prisma.PersonCreateArgs);
|
} as Prisma.PersonCreateArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => AffectedRows, {
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
@UseGuards(AbilityGuard)
|
||||||
|
@CheckAbilities(CreatePersonAbilityHandler)
|
||||||
|
async createManyPerson(
|
||||||
|
@Args() args: CreateManyPersonArgs,
|
||||||
|
@AuthWorkspace() workspace: Workspace,
|
||||||
|
): Promise<Prisma.BatchPayload> {
|
||||||
|
return this.personService.createMany({
|
||||||
|
data: args.data.map((person) => ({
|
||||||
|
...person,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
})),
|
||||||
|
skipDuplicates: args.skipDuplicates,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Mutation(() => String)
|
@Mutation(() => String)
|
||||||
@UseGuards(AbilityGuard)
|
@UseGuards(AbilityGuard)
|
||||||
@CheckAbilities(UpdatePersonAbilityHandler)
|
@CheckAbilities(UpdatePersonAbilityHandler)
|
||||||
|
|||||||
Reference in New Issue
Block a user