mirror of
https://github.com/lingble/twenty.git
synced 2025-11-01 21:27:58 +00:00
In place edit company info (#90)
* Add update company functionality * Fix padding in cells with chips * Add icons to table headers
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
**/**/.env
|
**/**/.env
|
||||||
**/**/.npmrc
|
**/**/.npmrc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
node_modules
|
||||||
@@ -48,6 +48,7 @@ describe('mapCompany', () => {
|
|||||||
expect(company.name).toBe('ACME');
|
expect(company.name).toBe('ACME');
|
||||||
expect(company.domain_name).toBe('exmaple.com');
|
expect(company.domain_name).toBe('exmaple.com');
|
||||||
expect(company.creationDate).toEqual(now);
|
expect(company.creationDate).toEqual(now);
|
||||||
|
expect(company.accountOwner).toBeUndefined();
|
||||||
expect(company.employees).toBe(10);
|
expect(company.employees).toBe(10);
|
||||||
expect(company.address).toBe(
|
expect(company.address).toBe(
|
||||||
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
||||||
@@ -76,11 +77,9 @@ describe('mapCompany', () => {
|
|||||||
expect(company.name).toBe('ACME');
|
expect(company.name).toBe('ACME');
|
||||||
expect(company.domain_name).toBe('exmaple.com');
|
expect(company.domain_name).toBe('exmaple.com');
|
||||||
expect(company.created_at).toEqual(now.toUTCString());
|
expect(company.created_at).toEqual(now.toUTCString());
|
||||||
expect(company.account_owner?.id).toBe(
|
expect(company.account_owner_id).toBe(
|
||||||
'522d4ec4-c46b-4360-a0a7-df8df170be81',
|
'522d4ec4-c46b-4360-a0a7-df8df170be81',
|
||||||
);
|
);
|
||||||
expect(company.account_owner?.email).toBe('john@example.com');
|
|
||||||
expect(company.account_owner?.displayName).toBe('John Doe');
|
|
||||||
expect(company.employees).toBe(10);
|
expect(company.employees).toBe(10);
|
||||||
expect(company.address).toBe(
|
expect(company.address).toBe(
|
||||||
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
||||||
@@ -103,6 +102,7 @@ describe('mapCompany', () => {
|
|||||||
expect(company.name).toBe('ACME');
|
expect(company.name).toBe('ACME');
|
||||||
expect(company.domain_name).toBe('exmaple.com');
|
expect(company.domain_name).toBe('exmaple.com');
|
||||||
expect(company.created_at).toEqual(now.toUTCString());
|
expect(company.created_at).toEqual(now.toUTCString());
|
||||||
|
expect(company.account_owner_id).toBeUndefined();
|
||||||
expect(company.employees).toBe(10);
|
expect(company.employees).toBe(10);
|
||||||
expect(company.address).toBe(
|
expect(company.address).toBe(
|
||||||
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
'1 Infinite Loop, 95014 Cupertino, California, USA',
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ export type GraphqlQueryCompany = {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GraphqlMutationCompany = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
domain_name: string;
|
||||||
|
account_owner_id?: string;
|
||||||
|
employees: number;
|
||||||
|
address: string;
|
||||||
|
created_at: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const mapCompany = (company: GraphqlQueryCompany): Company => ({
|
export const mapCompany = (company: GraphqlQueryCompany): Company => ({
|
||||||
...company,
|
...company,
|
||||||
name: company.name,
|
name: company.name,
|
||||||
@@ -51,16 +61,10 @@ export const mapCompany = (company: GraphqlQueryCompany): Company => ({
|
|||||||
opportunities: [{ name: 'Sales Pipeline', icon: '' }],
|
opportunities: [{ name: 'Sales Pipeline', icon: '' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapGqlCompany = (company: Company): GraphqlQueryCompany => ({
|
export const mapGqlCompany = (company: Company): GraphqlMutationCompany => ({
|
||||||
...company,
|
...company,
|
||||||
name: company.name,
|
name: company.name,
|
||||||
domain_name: company.domain_name,
|
domain_name: company.domain_name,
|
||||||
created_at: company.creationDate.toUTCString(),
|
created_at: company.creationDate.toUTCString(),
|
||||||
account_owner: company.accountOwner
|
account_owner_id: company.accountOwner?.id,
|
||||||
? {
|
|
||||||
id: company.accountOwner.id,
|
|
||||||
email: company.accountOwner.email,
|
|
||||||
displayName: `${company.accountOwner.first_name} ${company.accountOwner.last_name}`,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { createColumnHelper } from '@tanstack/react-table';
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { Company } from '../../interfaces/company.interface';
|
import { Company } from '../../interfaces/company.interface';
|
||||||
import { OrderByFields } from '../../services/companies';
|
import { OrderByFields, updateCompany } from '../../services/companies';
|
||||||
import ColumnHead from '../../components/table/ColumnHead';
|
import ColumnHead from '../../components/table/ColumnHead';
|
||||||
import HorizontalyAlignedContainer from '../../layout/containers/HorizontalyAlignedContainer';
|
import HorizontalyAlignedContainer from '../../layout/containers/HorizontalyAlignedContainer';
|
||||||
import Checkbox from '../../components/form/Checkbox';
|
import Checkbox from '../../components/form/Checkbox';
|
||||||
import CompanyChip from '../../components/chips/CompanyChip';
|
import CompanyChip from '../../components/chips/CompanyChip';
|
||||||
import EditableCell from '../../components/table/EditableCell';
|
import EditableCell from '../../components/table/EditableCell';
|
||||||
import PipeChip from '../../components/chips/PipeChip';
|
import PipeChip from '../../components/chips/PipeChip';
|
||||||
import { faCalendar } from '@fortawesome/pro-regular-svg-icons';
|
import {
|
||||||
|
faBuildings,
|
||||||
|
faCalendar,
|
||||||
|
faLinkSimple,
|
||||||
|
faMapPin,
|
||||||
|
faRectangleHistory,
|
||||||
|
faSigma,
|
||||||
|
faUser,
|
||||||
|
} from '@fortawesome/pro-regular-svg-icons';
|
||||||
import ClickableCell from '../../components/table/ClickableCell';
|
import ClickableCell from '../../components/table/ClickableCell';
|
||||||
import PersonChip from '../../components/chips/PersonChip';
|
import PersonChip from '../../components/chips/PersonChip';
|
||||||
import { SortType } from '../../components/table/table-header/interface';
|
import { SortType } from '../../components/table/table-header/interface';
|
||||||
@@ -28,7 +36,7 @@ export const sortsAvailable = [
|
|||||||
const columnHelper = createColumnHelper<Company>();
|
const columnHelper = createColumnHelper<Company>();
|
||||||
export const companiesColumns = [
|
export const companiesColumns = [
|
||||||
columnHelper.accessor('name', {
|
columnHelper.accessor('name', {
|
||||||
header: () => <ColumnHead viewName="Name" />,
|
header: () => <ColumnHead viewName="Name" viewIcon={faBuildings} />,
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<HorizontalyAlignedContainer>
|
<HorizontalyAlignedContainer>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -43,52 +51,54 @@ export const companiesColumns = [
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('employees', {
|
columnHelper.accessor('employees', {
|
||||||
header: () => <ColumnHead viewName="Employees" />,
|
header: () => <ColumnHead viewName="Employees" viewIcon={faSigma} />,
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
content={props.row.original.employees.toFixed(0)}
|
content={props.row.original.employees.toFixed(0)}
|
||||||
changeHandler={(value) => {
|
changeHandler={(value) => {
|
||||||
const company = props.row.original;
|
const company = props.row.original;
|
||||||
company.employees = parseInt(value);
|
company.employees = parseInt(value);
|
||||||
// TODO: update company
|
updateCompany(company).catch((error) => console.error(error));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('domain_name', {
|
columnHelper.accessor('domain_name', {
|
||||||
header: () => <ColumnHead viewName="URL" />,
|
header: () => <ColumnHead viewName="URL" viewIcon={faLinkSimple} />,
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
content={props.row.original.domain_name}
|
content={props.row.original.domain_name}
|
||||||
changeHandler={(value) => {
|
changeHandler={(value) => {
|
||||||
const company = props.row.original;
|
const company = props.row.original;
|
||||||
company.domain_name = value;
|
company.domain_name = value;
|
||||||
// TODO: update company
|
updateCompany(company).catch((error) => console.error(error));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('address', {
|
columnHelper.accessor('address', {
|
||||||
header: () => <ColumnHead viewName="Address" />,
|
header: () => <ColumnHead viewName="Address" viewIcon={faMapPin} />,
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<EditableCell
|
<EditableCell
|
||||||
content={props.row.original.address}
|
content={props.row.original.address}
|
||||||
changeHandler={(value) => {
|
changeHandler={(value) => {
|
||||||
const company = props.row.original;
|
const company = props.row.original;
|
||||||
company.address = value;
|
company.address = value;
|
||||||
// TODO: update company
|
updateCompany(company).catch((error) => console.error(error));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('opportunities', {
|
columnHelper.accessor('opportunities', {
|
||||||
header: () => <ColumnHead viewName="Opportunities" />,
|
header: () => (
|
||||||
|
<ColumnHead viewName="Opportunities" viewIcon={faRectangleHistory} />
|
||||||
|
),
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<HorizontalyAlignedContainer>
|
<ClickableCell href="#">
|
||||||
{props.row.original.opportunities.map((opportunity) => (
|
{props.row.original.opportunities.map((opportunity) => (
|
||||||
<PipeChip name={opportunity.name} picture={opportunity.icon} />
|
<PipeChip name={opportunity.name} picture={opportunity.icon} />
|
||||||
))}
|
))}
|
||||||
</HorizontalyAlignedContainer>
|
</ClickableCell>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('creationDate', {
|
columnHelper.accessor('creationDate', {
|
||||||
@@ -104,9 +114,9 @@ export const companiesColumns = [
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('accountOwner', {
|
columnHelper.accessor('accountOwner', {
|
||||||
header: () => <ColumnHead viewName="Account Owner" />,
|
header: () => <ColumnHead viewName="Account Owner" viewIcon={faUser} />,
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<HorizontalyAlignedContainer>
|
<ClickableCell href="#">
|
||||||
<>
|
<>
|
||||||
{props.row.original.accountOwner && (
|
{props.row.original.accountOwner && (
|
||||||
<PersonChip
|
<PersonChip
|
||||||
@@ -116,7 +126,7 @@ export const companiesColumns = [
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</HorizontalyAlignedContainer>
|
</ClickableCell>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export * from './select';
|
export * from './select';
|
||||||
|
export * from './update';
|
||||||
|
|||||||
50
front/src/services/companies/update.ts
Normal file
50
front/src/services/companies/update.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { FetchResult, gql } from '@apollo/client';
|
||||||
|
import { Company, mapGqlCompany } from '../../interfaces/company.interface';
|
||||||
|
import { apiClient } from '../../apollo';
|
||||||
|
|
||||||
|
export const UPDATE_COMPANY = gql`
|
||||||
|
mutation UpdateCompany(
|
||||||
|
$id: uuid
|
||||||
|
$name: String
|
||||||
|
$domain_name: String
|
||||||
|
$account_owner_id: uuid
|
||||||
|
$address: String
|
||||||
|
$employees: Int
|
||||||
|
) {
|
||||||
|
update_companies(
|
||||||
|
where: { id: { _eq: $id } }
|
||||||
|
_set: {
|
||||||
|
account_owner_id: $account_owner_id
|
||||||
|
address: $address
|
||||||
|
domain_name: $domain_name
|
||||||
|
employees: $employees
|
||||||
|
name: $name
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
affected_rows
|
||||||
|
returning {
|
||||||
|
account_owner {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
address
|
||||||
|
created_at
|
||||||
|
domain_name
|
||||||
|
employees
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export async function updateCompany(
|
||||||
|
company: Company,
|
||||||
|
): Promise<FetchResult<Company>> {
|
||||||
|
const result = await apiClient.mutate({
|
||||||
|
mutation: UPDATE_COMPANY,
|
||||||
|
variables: mapGqlCompany(company),
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user