mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-27 01:05:08 +03:00
Merge pull request #29 from twentyhq/cbo-add-table
Add simple styled table
This commit is contained in:
commit
c6a60824b3
@ -1,84 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
aws-ecr: circleci/aws-ecr@8.2.1
|
||||
aws-ecs: circleci/aws-ecs@03.2.0
|
||||
slack: circleci/slack@4.12.0
|
||||
node: circleci/node@5.0.3
|
||||
|
||||
jobs:
|
||||
tests-front:
|
||||
executor: node/default
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: cd front && npm install
|
||||
name: install node dependencies
|
||||
- run:
|
||||
command: cd front && npm run test
|
||||
name: tests
|
||||
|
||||
workflows:
|
||||
build-and-deploy:
|
||||
jobs:
|
||||
- tests-front
|
||||
- aws-ecr/build-and-push-image:
|
||||
name: build-image
|
||||
filters:
|
||||
branches:
|
||||
only: main
|
||||
requires:
|
||||
- tests-front
|
||||
dockerfile: ./infra/prod/twenty/Dockerfile
|
||||
registry-id: AWS_ACCOUNT_ID
|
||||
aws-access-key-id: AWS_ACCESS_KEY_ID
|
||||
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
|
||||
region: $AWS_REGION
|
||||
repo: $AWS_ECR_REPO
|
||||
tag: $CIRCLE_SHA1
|
||||
extra-build-args: >
|
||||
--build-arg REACT_APP_API_URL=$REACT_APP_API_URL
|
||||
|
||||
- aws-ecs/deploy-service-update:
|
||||
name: deploy-canary
|
||||
requires:
|
||||
- build-image
|
||||
family: $AWS_ECS_CONTAINER_NAME_CANARY
|
||||
cluster: $AWS_ECS_CLUSTER
|
||||
container-image-name-updates: "container=$AWS_ECS_CONTAINER_NAME_CANARY,tag=${CIRCLE_SHA1}"
|
||||
- slack/on-hold:
|
||||
name: slack-notification
|
||||
context: slack-secrets
|
||||
requires:
|
||||
- deploy-canary
|
||||
- hold:
|
||||
type: approval
|
||||
requires:
|
||||
- slack-notification
|
||||
- aws-ecs/deploy-service-update:
|
||||
name: deploy-default
|
||||
requires:
|
||||
- hold
|
||||
family: $AWS_ECS_CONTAINER_NAME_DEFAULT
|
||||
cluster: $AWS_ECS_CLUSTER
|
||||
container-image-name-updates: "container=$AWS_ECS_CONTAINER_NAME_DEFAULT,tag=${CIRCLE_SHA1}"
|
||||
post-steps:
|
||||
- slack/notify:
|
||||
event: pass
|
||||
template: basic_success_1
|
||||
- slack/notify:
|
||||
event: fail
|
||||
template: basic_fail_1
|
||||
- aws-ecr/build-and-push-image:
|
||||
name: build-image-latest
|
||||
requires:
|
||||
- deploy-default
|
||||
dockerfile: ./infra/prod/twenty/Dockerfile
|
||||
registry-id: AWS_ACCOUNT_ID
|
||||
aws-access-key-id: AWS_ACCESS_KEY_ID
|
||||
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
|
||||
region: $AWS_REGION
|
||||
repo: $AWS_ECR_REPO
|
||||
tag: latest
|
||||
extra-build-args: >
|
||||
--build-arg REACT_APP_API_URL=$REACT_APP_API_URL
|
@ -1,46 +1,33 @@
|
||||
module.exports = {
|
||||
webpackFinal: (config) => {
|
||||
webpackFinal: config => {
|
||||
config.module.rules.push({
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
use: [{
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
presets: [
|
||||
require('@babel/preset-typescript').default,
|
||||
[require('@babel/preset-react').default, { runtime: 'automatic' }],
|
||||
require('@babel/preset-env').default,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
config.resolve.extensions.push('.ts', '.tsx')
|
||||
|
||||
presets: [require('@babel/preset-typescript').default, [require('@babel/preset-react').default, {
|
||||
runtime: 'automatic'
|
||||
}], require('@babel/preset-env').default]
|
||||
}
|
||||
}]
|
||||
});
|
||||
config.resolve.extensions.push('.ts', '.tsx');
|
||||
config.module.rules.push({
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto',
|
||||
})
|
||||
|
||||
config.resolve.extensions.push('.mjs')
|
||||
|
||||
return config
|
||||
type: 'javascript/auto'
|
||||
});
|
||||
config.resolve.extensions.push('.mjs');
|
||||
return config;
|
||||
},
|
||||
stories: [
|
||||
"../src/**/*.stories.mdx",
|
||||
"../src/**/*.stories.@(js|jsx|ts|tsx)"
|
||||
],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/preset-create-react-app"
|
||||
],
|
||||
framework: "@storybook/react",
|
||||
core: {
|
||||
builder: "@storybook/builder-webpack5"
|
||||
}
|
||||
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions", "@storybook/preset-create-react-app", '@storybook/addon-mdx-gfm'],
|
||||
framework: {
|
||||
name: '@storybook/react-webpack5',
|
||||
options: {}
|
||||
},
|
||||
docs: {
|
||||
autodocs: true
|
||||
}
|
||||
};
|
48628
front/package-lock.json
generated
48628
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"@types/node": "^16.18.4",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
@ -18,7 +19,6 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
@ -26,8 +26,8 @@
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public",
|
||||
"storybook": "storybook dev -p 6006 -s public",
|
||||
"build-storybook": "storybook build -s public",
|
||||
"coverage": "react-scripts test --coverage --watchAll"
|
||||
},
|
||||
"eslintConfig": {
|
||||
@ -67,19 +67,19 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^6.5.14",
|
||||
"@storybook/addon-essentials": "^6.5.14",
|
||||
"@storybook/addon-interactions": "^6.5.14",
|
||||
"@storybook/addon-links": "^6.5.14",
|
||||
"@storybook/builder-webpack5": "^6.5.14",
|
||||
"@storybook/manager-webpack5": "^6.5.14",
|
||||
"@storybook/node-logger": "^6.5.14",
|
||||
"@storybook/preset-create-react-app": "^4.1.2",
|
||||
"@storybook/react": "^6.5.14",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@storybook/addon-actions": "^7.0.2",
|
||||
"@storybook/addon-essentials": "^7.0.2",
|
||||
"@storybook/addon-interactions": "^7.0.2",
|
||||
"@storybook/addon-links": "^7.0.2",
|
||||
"@storybook/node-logger": "^7.0.2",
|
||||
"@storybook/preset-create-react-app": "^7.0.2",
|
||||
"@storybook/react": "^7.0.2",
|
||||
"@storybook/react-webpack5": "^7.0.2",
|
||||
"@storybook/testing-library": "^0.1.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"babel-plugin-named-exports-order": "^0.0.2",
|
||||
@ -91,9 +91,10 @@
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-storybook": "^0.6.7",
|
||||
"eslint-plugin-storybook": "^0.6.11",
|
||||
"prettier": "^2.8.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"storybook": "^7.0.2",
|
||||
"typescript": "^4.9.3",
|
||||
"webpack": "^5.75.0"
|
||||
}
|
||||
|
99
front/src/components/table/Table.tsx
Normal file
99
front/src/components/table/Table.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import TableHeader from './TableHeader';
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type OwnProps = {
|
||||
data: Array<any>;
|
||||
columns: Array<ColumnDef<any, any>>;
|
||||
viewName: string;
|
||||
viewIcon?: IconProp;
|
||||
};
|
||||
|
||||
const StyledTable = styled.table`
|
||||
min-width: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #f5f5f5;
|
||||
border-spacing: 0;
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #f5f5f5;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
padding: 11px 0 11px 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
function Table({ data, columns, viewName, viewIcon }: OwnProps) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableHeader viewName={viewName} viewIcon={viewIcon} />
|
||||
<StyledTable>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<td key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
{table
|
||||
.getFooterGroups()
|
||||
.flatMap((group) => group.headers)
|
||||
.filter((header) => !!header.column.columnDef.footer).length > 0 && (
|
||||
<tfoot>
|
||||
{table.getFooterGroups().map((footerGroup) => (
|
||||
<tr key={footerGroup.id}>
|
||||
{footerGroup.headers.map((header) => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.footer,
|
||||
header.getContext(),
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tfoot>
|
||||
)}
|
||||
</StyledTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Table;
|
28
front/src/components/table/TableHeader.tsx
Normal file
28
front/src/components/table/TableHeader.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
|
||||
type OwnProps = {
|
||||
viewName: string;
|
||||
viewIcon?: IconProp;
|
||||
};
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: ${(props) => props.theme.text60};
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
function TableHeader({ viewName, viewIcon }: OwnProps) {
|
||||
return (
|
||||
<StyledTitle>
|
||||
{viewIcon && <FontAwesomeIcon icon={viewIcon} />}
|
||||
{viewName}
|
||||
</StyledTitle>
|
||||
);
|
||||
}
|
||||
|
||||
export default TableHeader;
|
5
front/src/interfaces/company.interface.ts
Normal file
5
front/src/interfaces/company.interface.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Company {
|
||||
id: number;
|
||||
name: string;
|
||||
logo: string;
|
||||
}
|
5
front/src/interfaces/pipe.interface.ts
Normal file
5
front/src/interfaces/pipe.interface.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Pipe {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
@ -1,10 +1,125 @@
|
||||
import { faUser } from '@fortawesome/free-regular-svg-icons';
|
||||
import {
|
||||
faBuilding,
|
||||
faCalendar,
|
||||
faEnvelope,
|
||||
faRectangleList,
|
||||
faUser,
|
||||
} from '@fortawesome/free-regular-svg-icons';
|
||||
import { faList, faMapPin, faPhone } from '@fortawesome/free-solid-svg-icons';
|
||||
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
|
||||
import Table from '../../components/table/Table';
|
||||
import { Company } from '../../interfaces/company.interface';
|
||||
import { Pipe } from '../../interfaces/pipe.interface';
|
||||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import styled from '@emotion/styled';
|
||||
import TableHeader from '../../components/table/TableHeader';
|
||||
|
||||
type People = {
|
||||
fullName: string;
|
||||
email: string;
|
||||
company: Company;
|
||||
phone: string;
|
||||
creationDate: string;
|
||||
pipe: Pipe;
|
||||
city: string;
|
||||
};
|
||||
|
||||
const StyledPeopleContainer = styled.div`
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
|
||||
table {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const defaultData: Array<People> = [
|
||||
{
|
||||
fullName: 'Alexandre Prot',
|
||||
email: 'alexandre@qonto.com',
|
||||
company: { id: 1, name: 'Qonto', logo: 'https://qonto.eu/logo.png' },
|
||||
phone: '06 12 34 56 78',
|
||||
creationDate: 'Feb 23, 2018',
|
||||
pipe: { id: 1, name: 'Sales Pipeline', icon: 'faUser' },
|
||||
city: 'Paris',
|
||||
},
|
||||
{
|
||||
fullName: 'Alexandre Prot',
|
||||
email: 'alexandre@qonto.com',
|
||||
company: { id: 1, name: 'Qonto', logo: 'https://qonto.eu/logo.png' },
|
||||
phone: '06 12 34 56 78',
|
||||
creationDate: 'Feb 23, 2018',
|
||||
pipe: { id: 1, name: 'Sales Pipeline', icon: 'faUser' },
|
||||
city: 'Paris',
|
||||
},
|
||||
{
|
||||
fullName: 'Alexandre Prot',
|
||||
email: 'alexandre@qonto.com',
|
||||
company: { id: 1, name: 'Qonto', logo: 'https://qonto.eu/logo.png' },
|
||||
phone: '06 12 34 56 78',
|
||||
creationDate: 'Feb 23, 2018',
|
||||
pipe: { id: 1, name: 'Sales Pipeline', icon: 'faUser' },
|
||||
city: 'Paris',
|
||||
},
|
||||
{
|
||||
fullName: 'Alexandre Prot',
|
||||
email: 'alexandre@qonto.com',
|
||||
company: { id: 1, name: 'Qonto', logo: 'https://qonto.eu/logo.png' },
|
||||
phone: '06 12 34 56 78',
|
||||
creationDate: 'Feb 23, 2018',
|
||||
pipe: { id: 1, name: 'Sales Pipeline', icon: 'faUser' },
|
||||
city: 'Paris',
|
||||
},
|
||||
{
|
||||
fullName: 'Alexandre Prot',
|
||||
email: 'alexandre@qonto.com',
|
||||
company: { id: 1, name: 'Qonto', logo: 'https://qonto.eu/logo.png' },
|
||||
phone: '06 12 34 56 78',
|
||||
creationDate: 'Feb 23, 2018',
|
||||
pipe: { id: 1, name: 'Sales Pipeline', icon: 'faUser' },
|
||||
city: 'Paris',
|
||||
},
|
||||
];
|
||||
|
||||
const columnHelper = createColumnHelper<People>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor('fullName', {
|
||||
header: () => <TableHeader viewName="People" viewIcon={faUser} />,
|
||||
}),
|
||||
columnHelper.accessor('email', {
|
||||
header: () => <TableHeader viewName="Email" viewIcon={faEnvelope} />,
|
||||
}),
|
||||
columnHelper.accessor('company', {
|
||||
cell: (props) => <span>{props.row.original.company.name}</span>,
|
||||
header: () => <TableHeader viewName="Company" viewIcon={faBuilding} />,
|
||||
}),
|
||||
columnHelper.accessor('phone', {
|
||||
header: () => <TableHeader viewName="Phone" viewIcon={faPhone} />,
|
||||
}),
|
||||
columnHelper.accessor('creationDate', {
|
||||
header: () => <TableHeader viewName="Creation" viewIcon={faCalendar} />,
|
||||
}),
|
||||
columnHelper.accessor('pipe', {
|
||||
cell: (props) => <span>{props.row.original.pipe.name}</span>,
|
||||
header: () => <TableHeader viewName="Pipe" viewIcon={faRectangleList} />,
|
||||
}),
|
||||
columnHelper.accessor('city', {
|
||||
header: () => <TableHeader viewName="City" viewIcon={faMapPin} />,
|
||||
}),
|
||||
];
|
||||
|
||||
function People() {
|
||||
return (
|
||||
<WithTopBarContainer title="People" icon={faUser}>
|
||||
<></>
|
||||
<StyledPeopleContainer>
|
||||
<Table
|
||||
data={defaultData}
|
||||
columns={columns}
|
||||
viewName="All People"
|
||||
viewIcon={faList}
|
||||
/>
|
||||
</StyledPeopleContainer>
|
||||
</WithTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user