Skip to content

Commit

Permalink
Feat/table view for issues (#1648)
Browse files Browse the repository at this point in the history
* [feat] add table view for issues
* create data table ui component
* create re usable table component and used them for Data table UI component
* refactor team members componen so that it can show card and table component
* add a swtich button to change issue view

* fix ts lint error and refactor team member table view

* * add footer to team table view

* * fix lint issue

* address review comment

* address review comment

* fix ui related issue

* fix: Background color

---------

Co-authored-by: Ruslan Konviser <[email protected]>
Co-authored-by: Badal Khatri <[email protected]>
  • Loading branch information
3 people authored Oct 26, 2023
1 parent 01cd3fb commit 53d03c5
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 178 deletions.
6 changes: 6 additions & 0 deletions apps/web/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,9 @@ export const APPLICATION_LANGUAGES_CODE = [
'ru',
'es'
];

export enum IssuesView {
CARDS = 'CARDS',
TABLE = 'TABLE',
BLOCKS = 'BLOCKS'
}
139 changes: 139 additions & 0 deletions apps/web/components/ui/data-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"

import {
Table,
TableHeader,
TableRow,
TableHead,
TableCell,
TableBody,
TableFooter,
} from './table';

interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[],
footerRows?: React.ReactNode[],
isError?: boolean;
noResultsMessage?: {
heading: string;
content: string;
}
}

function DataTable<TData, TValue>({
columns,
data,
footerRows,
}: DataTableProps<TData, TValue>) {

const [rowSelection, setRowSelection] = React.useState({})
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [sorting, setSorting] = React.useState<SortingState>([])

const table = useReactTable({
data,
columns,
state: {
sorting,
columnVisibility,
rowSelection,
columnFilters,
},
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
})

return (
<Table className='border-transparent bg-light--theme-light dark:bg-dark--theme-light mt-8 w-full'>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody className="divide-y divide-gray-200">
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className='my-4'
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}
className="rounded-[16px] my-4">

{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
{
footerRows && footerRows?.length > 0 && (
<TableFooter className='bg-gray-50 dark:bg-gray-800'>
{footerRows.map((row, index) => (
<TableRow key={`footer-row-${index}}`}>{row}</TableRow>
))}
</TableFooter>
)
}

</Table>
);
}

export default DataTable;
115 changes: 115 additions & 0 deletions apps/web/components/ui/table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import * as React from "react"

import { clsxm } from '@app/utils';


const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={clsxm("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"

const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={clsxm("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"

const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={clsxm("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"

const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={clsxm("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"

const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={clsxm(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"

const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={clsxm(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"

const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={clsxm("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"

const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={clsxm("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"

export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}
107 changes: 107 additions & 0 deletions apps/web/lib/features/team-member-cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useTeamMemberCard, useTMCardTaskEdit, useCollaborative } from "@app/hooks";
import { OT_Member } from "@app/interfaces";
import { clsxm } from "@app/utils";
import { InputField } from "lib/components";
import { TaskTimes } from "./task/task-times";
import { TaskEstimateInfo } from "./team/user-team-card/task-estimate";
import { TaskInfo } from "./team/user-team-card/task-info";
import { UserInfo } from "./team/user-team-card/user-info";
import { UserTeamCardMenu } from "./team/user-team-card/user-team-card-menu";
import React from "react";
import get from "lodash/get";


export function TaskCell({ row }: { row: any }) {
const member = row.original as OT_Member;
const memberInfo = useTeamMemberCard(member);
const taskEdition = useTMCardTaskEdit(memberInfo?.memberTask);
const publicTeam = false;

return (
<TaskInfo
edition={taskEdition}
memberInfo={memberInfo}
className="2xl:w-80 3xl:w-[32rem] w-1/5 lg:px-4 px-2"
publicTeam={publicTeam}
/>
);
}

export function UserInfoCell({ cell }: { cell: any}) {
const row = get(cell, 'row', {});
const member = row.original as OT_Member;
const publicTeam = get(cell, 'column.columnDef.meta.publicTeam', false);
const memberInfo = useTeamMemberCard(member);

return (
<UserInfo
memberInfo={memberInfo}
className="2xl:w-[20.625rem] w-1/4"
publicTeam={publicTeam}
/>
);
}

export function WorkedOnTaskCell({ row }: { row: any }) {
const member = row.original as OT_Member;
const memberInfo = useTeamMemberCard(member);

return (
<TaskTimes
activeAuthTask={true}
memberInfo={memberInfo}
task={memberInfo?.memberTask}
isAuthUser={memberInfo?.isAuthUser}
className="2xl:w-32 3xl:w-[8rem] w-1/5 flex flex-col gap-y-[1.125rem] justify-center"
/>
);
}

export function TaskEstimateInfoCell({ row }: { row: any }) {
const member = row.original as OT_Member;
const memberInfo = useTeamMemberCard(member);
const taskEdition = useTMCardTaskEdit(memberInfo?.memberTask);

return (
<TaskEstimateInfo
memberInfo={memberInfo}
edition={taskEdition}
activeAuthTask={true}
className="lg:px-3 2xl:w-52 3xl:w-64 w-1/5"
/>
);
}

export function ActionMenuCell ({ cell }: { cell: any}) {
const row = get(cell, 'row', {});
const member = row.original as OT_Member;
const active = get(cell, 'column.columnDef.meta.active', false);
const memberInfo = useTeamMemberCard(member);

const { collaborativeSelect, user_selected, onUserSelect } = useCollaborative(
memberInfo.memberUser
);
const taskEdition = useTMCardTaskEdit(memberInfo.memberTask);

return (
<>
{(!collaborativeSelect || active) && (
<UserTeamCardMenu memberInfo={memberInfo} edition={taskEdition} />
)}

{collaborativeSelect && !active && (
<InputField
type="checkbox"
checked={user_selected()}
className={clsxm(
'border-none w-4 h-4 mr-1 accent-primary-light',
'border-2 border-primary-light',
'2xl:w-[2rem] w-1/5'
)}
noWrapper={true}
onChange={onUserSelect}
/>
)}
</>
);
}
Loading

0 comments on commit 53d03c5

Please sign in to comment.