List view time stamp improvements
Following functions needs to reviewed - Rename & Delete functionality - Drag & Drop in gird view - Table Sorting
This commit is contained in:
parent
03fe6c499c
commit
2100cc0412
3 changed files with 138 additions and 80 deletions
|
|
@ -9,7 +9,6 @@ import cn from 'classnames';
|
|||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { toast } from 'react-toastify';
|
||||
import moment from 'moment'; // Import moment
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
metric: any;
|
||||
|
|
@ -83,7 +82,7 @@ const MetricListItem: React.FC<Props> = ({
|
|||
try {
|
||||
console.log('Renaming metric:', metric);
|
||||
console.log('New name:', newName);
|
||||
metric.name = newName; // Directly update the name property
|
||||
metric.name = newName;
|
||||
|
||||
// Add a toJson method if it doesn't exist
|
||||
if (typeof metric.toJson !== 'function') {
|
||||
|
|
@ -138,15 +137,38 @@ const MetricListItem: React.FC<Props> = ({
|
|||
);
|
||||
|
||||
const parseDate = (dateString: string) => {
|
||||
// Try to parse as ISO 8601 string first
|
||||
let date = moment(dateString, moment.ISO_8601);
|
||||
if (!date.isValid()) {
|
||||
// Try to parse as a number (timestamp)
|
||||
date = moment(Number(dateString));
|
||||
let date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) {
|
||||
date = new Date(parseInt(dateString, 10));
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
const now = new Date();
|
||||
const diffTime = Math.abs(now.getTime() - date.getTime());
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
const formatTime = (date: Date) => {
|
||||
let hours = date.getHours();
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12; // the hour '0' should be '12'
|
||||
return `${hours}:${minutes} ${ampm}`;
|
||||
};
|
||||
|
||||
if (diffDays <= 1) {
|
||||
return `Today at ${formatTime(date)}`;
|
||||
} else if (diffDays <= 2) {
|
||||
return `Yesterday at ${formatTime(date)}`;
|
||||
} else if (diffDays <= 3) {
|
||||
return `${diffDays} days ago at ${formatTime(date)}`;
|
||||
} else {
|
||||
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()} at ${formatTime(date)}`;
|
||||
}
|
||||
};
|
||||
|
||||
switch (renderColumn) {
|
||||
case 'title':
|
||||
return (
|
||||
|
|
@ -170,7 +192,8 @@ const MetricListItem: React.FC<Props> = ({
|
|||
</div>
|
||||
);
|
||||
case 'lastModified':
|
||||
return parseDate(metric.lastModified).calendar(); // Use moment to return relative time
|
||||
const date = parseDate(metric.lastModified);
|
||||
return formatDate(date);
|
||||
case 'options':
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,37 +1,41 @@
|
|||
import React from 'react';
|
||||
import {PageTitle, Toggler, Icon} from "UI";
|
||||
import {Segmented, Button} from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { PageTitle, Toggler, Icon } from "UI";
|
||||
import { Segmented, Button } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import MetricsSearch from '../MetricsSearch';
|
||||
import Select from 'Shared/Select';
|
||||
import {useStore} from 'App/mstore';
|
||||
import {observer, useObserver} from 'mobx-react-lite';
|
||||
import {DROPDOWN_OPTIONS} from 'App/constants/card';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer, useObserver } from 'mobx-react-lite';
|
||||
import { DROPDOWN_OPTIONS } from 'App/constants/card';
|
||||
import AddCardModal from 'Components/Dashboard/components/AddCardModal';
|
||||
import {useModal} from 'Components/Modal';
|
||||
import { useModal } from 'Components/Modal';
|
||||
import AddCardSelectionModal from "Components/Dashboard/components/AddCardSelectionModal";
|
||||
import NewDashboardModal from "Components/Dashboard/components/DashboardList/NewDashModal";
|
||||
|
||||
function MetricViewHeader({siteId}: { siteId: string }) {
|
||||
const {metricStore} = useStore();
|
||||
function MetricViewHeader({ siteId }: { siteId: string }) {
|
||||
const { metricStore } = useStore();
|
||||
const filter = metricStore.filter;
|
||||
const {showModal} = useModal();
|
||||
const { showModal } = useModal();
|
||||
const [showAddCardModal, setShowAddCardModal] = React.useState(false);
|
||||
|
||||
// Set the default sort order to 'desc'
|
||||
useEffect(() => {
|
||||
metricStore.updateKey('sort', { by: 'desc' });
|
||||
}, [metricStore]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='flex items-center justify-between px-6'>
|
||||
<div className='flex items-baseline mr-3'>
|
||||
<PageTitle title='Cards' className=''/>
|
||||
<PageTitle title='Cards' className='' />
|
||||
</div>
|
||||
<div className='ml-auto flex items-center'>
|
||||
<Button type='primary'
|
||||
// onClick={() => showModal(<AddCardModal siteId={siteId}/>, {right: true})}
|
||||
onClick={() => setShowAddCardModal(true)}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setShowAddCardModal(true)}
|
||||
icon={<PlusOutlined />}
|
||||
>Create Card</Button>
|
||||
<div className='ml-4 w-1/4' style={{minWidth: 300}}>
|
||||
<MetricsSearch/>
|
||||
<div className='ml-4 w-1/4' style={{ minWidth: 300 }}>
|
||||
<MetricsSearch />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,11 +43,11 @@ function MetricViewHeader({siteId}: { siteId: string }) {
|
|||
<div className='border-y px-6 py-1 mt-2 flex items-center w-full justify-between'>
|
||||
<div className='items-center flex gap-4'>
|
||||
<Select
|
||||
options={[{label: 'All Types', value: 'all'}, ...DROPDOWN_OPTIONS]}
|
||||
options={[{ label: 'All Types', value: 'all' }, ...DROPDOWN_OPTIONS]}
|
||||
name='type'
|
||||
defaultValue={filter.type}
|
||||
onChange={({value}) =>
|
||||
metricStore.updateKey('filter', {...filter, type: value.value})
|
||||
onChange={({ value }) =>
|
||||
metricStore.updateKey('filter', { ...filter, type: value.value })
|
||||
}
|
||||
plain={true}
|
||||
isSearchable={true}
|
||||
|
|
@ -52,37 +56,25 @@ function MetricViewHeader({siteId}: { siteId: string }) {
|
|||
<DashboardDropdown
|
||||
plain={false}
|
||||
onChange={(value: any) =>
|
||||
metricStore.updateKey('filter', {...filter, dashboard: value})
|
||||
metricStore.updateKey('filter', { ...filter, dashboard: value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2'>
|
||||
<ListViewToggler/>
|
||||
<div className='flex items-center gap-6'>
|
||||
<ListViewToggler />
|
||||
|
||||
<Select
|
||||
options={[
|
||||
{label: 'Newest', value: 'desc'},
|
||||
{label: 'Oldest', value: 'asc'}
|
||||
]}
|
||||
name='sort'
|
||||
defaultValue={metricStore.sort.by}
|
||||
onChange={({value}) => metricStore.updateKey('sort', {by: value.value})}
|
||||
plain={true}
|
||||
className='ml-4'
|
||||
/>
|
||||
<Toggler
|
||||
label='My Cards'
|
||||
checked={filter.showMine}
|
||||
name='test'
|
||||
className='font-medium mr-2'
|
||||
onChange={() =>
|
||||
metricStore.updateKey('filter', {...filter, showMine: !filter.showMine})
|
||||
metricStore.updateKey('filter', { ...filter, showMine: !filter.showMine })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/*<AddCardSelectionModal open={showAddCardModal}/>*/}
|
||||
<NewDashboardModal
|
||||
onClose={() => setShowAddCardModal(false)}
|
||||
open={showAddCardModal}
|
||||
|
|
@ -95,8 +87,8 @@ function MetricViewHeader({siteId}: { siteId: string }) {
|
|||
|
||||
export default observer(MetricViewHeader);
|
||||
|
||||
function DashboardDropdown({onChange, plain = false}: { plain?: boolean; onChange: any }) {
|
||||
const {dashboardStore, metricStore} = useStore();
|
||||
function DashboardDropdown({ onChange, plain = false }: { plain?: boolean; onChange: any }) {
|
||||
const { dashboardStore, metricStore } = useStore();
|
||||
const dashboardOptions = dashboardStore.dashboards.map((i: any) => ({
|
||||
key: i.id,
|
||||
label: i.name,
|
||||
|
|
@ -110,7 +102,7 @@ function DashboardDropdown({onChange, plain = false}: { plain?: boolean; onChang
|
|||
plain={plain}
|
||||
options={dashboardOptions}
|
||||
value={metricStore.filter.dashboard}
|
||||
onChange={({value}: any) => onChange(value)}
|
||||
onChange={({ value }: any) => onChange(value)}
|
||||
isMulti={true}
|
||||
color='black'
|
||||
/>
|
||||
|
|
@ -118,23 +110,23 @@ function DashboardDropdown({onChange, plain = false}: { plain?: boolean; onChang
|
|||
}
|
||||
|
||||
function ListViewToggler() {
|
||||
const {metricStore} = useStore();
|
||||
const { metricStore } = useStore();
|
||||
const listView = useObserver(() => metricStore.listView);
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<Segmented
|
||||
<Segmented
|
||||
size='small'
|
||||
options={[
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name={'list-alt'} color={'inherit'}/>
|
||||
<Icon name={'list-alt'} color={'inherit'} />
|
||||
<div>List</div>
|
||||
</div>,
|
||||
value: 'list'
|
||||
},
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name={'grid'} color={'inherit'}/>
|
||||
<Icon name={'grid'} color={'inherit'} />
|
||||
<div>Grid</div>
|
||||
</div>,
|
||||
value: 'grid'
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import React from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Checkbox, Table } from 'antd';
|
||||
import MetricListItem from '../MetricListItem';
|
||||
import classNames from 'classnames';
|
||||
import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface';
|
||||
|
||||
interface Metric {
|
||||
metricId: number;
|
||||
name: string;
|
||||
owner: string;
|
||||
lastModified: string;
|
||||
visibility: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
list: any;
|
||||
siteId: any;
|
||||
selectedList: any;
|
||||
toggleSelection?: (metricId: any) => void;
|
||||
list: Metric[];
|
||||
siteId: string;
|
||||
selectedList: number[];
|
||||
toggleSelection?: (metricId: number) => void;
|
||||
toggleAll?: (e: any) => void;
|
||||
disableSelection?: boolean;
|
||||
allSelected?: boolean;
|
||||
|
|
@ -16,6 +24,45 @@ interface Props {
|
|||
|
||||
const ListView: React.FC<Props> = (props: Props) => {
|
||||
const { siteId, list, selectedList, toggleSelection, disableSelection = false, allSelected = false, toggleAll } = props;
|
||||
const [sorter, setSorter] = useState<{ field: string; order: 'ascend' | 'descend' }>({
|
||||
field: 'lastModified',
|
||||
order: 'descend'
|
||||
});
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10 });
|
||||
|
||||
const sortedData = useMemo(() => {
|
||||
return [...list].sort((a, b) => {
|
||||
if (sorter.field === 'lastModified') {
|
||||
return sorter.order === 'ascend'
|
||||
? new Date(a.lastModified).getTime() - new Date(b.lastModified).getTime()
|
||||
: new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime();
|
||||
} else if (sorter.field === 'name') {
|
||||
return sorter.order === 'ascend' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
|
||||
} else if (sorter.field === 'owner') {
|
||||
return sorter.order === 'ascend' ? a.owner.localeCompare(b.owner) : b.owner.localeCompare(a.owner);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}, [list, sorter]);
|
||||
|
||||
const paginatedData = useMemo(() => {
|
||||
const start = (pagination.current! - 1) * pagination.pageSize!;
|
||||
const end = start + pagination.pageSize!;
|
||||
return sortedData.slice(start, end);
|
||||
}, [sortedData, pagination]);
|
||||
|
||||
const handleTableChange = (
|
||||
pagination: TablePaginationConfig,
|
||||
filters: Record<string, (string | number | boolean)[] | null>,
|
||||
sorter: SorterResult<Metric> | SorterResult<Metric>[]
|
||||
) => {
|
||||
const sortResult = sorter as SorterResult<Metric>;
|
||||
setSorter({
|
||||
field: sortResult.field as string,
|
||||
order: sortResult.order as 'ascend' | 'descend'
|
||||
});
|
||||
setPagination(pagination);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
|
@ -35,18 +82,18 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
dataIndex: 'name',
|
||||
key: 'title',
|
||||
className: 'cap-first',
|
||||
sorter: (a: any, b: any) => a.name.localeCompare(b.name),
|
||||
width: '40%',
|
||||
render: (text: any, metric: any) => (
|
||||
width:'35%',
|
||||
sorter: true,
|
||||
render: (text: string, metric: Metric) => (
|
||||
<MetricListItem
|
||||
key={metric.metricId}
|
||||
metric={metric}
|
||||
siteId={siteId}
|
||||
disableSelection={disableSelection}
|
||||
selected={selectedList.includes(parseInt(metric.metricId))}
|
||||
selected={selectedList.includes(metric.metricId)}
|
||||
toggleSelection={(e: any) => {
|
||||
e.stopPropagation();
|
||||
toggleSelection && toggleSelection(parseInt(metric.metricId));
|
||||
toggleSelection && toggleSelection(metric.metricId);
|
||||
}}
|
||||
renderColumn="title"
|
||||
/>
|
||||
|
|
@ -57,9 +104,9 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
dataIndex: 'owner',
|
||||
key: 'owner',
|
||||
className: 'capitalize',
|
||||
sorter: (a: any, b: any) => a.owner.localeCompare(b.owner),
|
||||
width: '25%',
|
||||
render: (text: any, metric: any) => (
|
||||
width:'25%',
|
||||
sorter: true,
|
||||
render: (text: string, metric: Metric) => (
|
||||
<MetricListItem
|
||||
key={metric.metricId}
|
||||
metric={metric}
|
||||
|
|
@ -72,9 +119,8 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
title: 'Last Modified',
|
||||
dataIndex: 'lastModified',
|
||||
key: 'lastModified',
|
||||
sorter: (a: any, b: any) => new Date(a.lastModified).getTime() - new Date(b.lastModified).getTime(),
|
||||
width: '20%',
|
||||
render: (text: any, metric: any) => (
|
||||
sorter: true,
|
||||
render: (text: string, metric: Metric) => (
|
||||
<MetricListItem
|
||||
key={metric.metricId}
|
||||
metric={metric}
|
||||
|
|
@ -88,7 +134,7 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
dataIndex: 'visibility',
|
||||
key: 'visibility',
|
||||
width: '10%',
|
||||
render: (text: any, metric: any) => (
|
||||
render: (text: string, metric: Metric) => (
|
||||
<MetricListItem
|
||||
key={metric.metricId}
|
||||
metric={metric}
|
||||
|
|
@ -97,14 +143,11 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
/>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: '',
|
||||
key: 'options',
|
||||
className: 'text-right',
|
||||
width: '5%',
|
||||
align: 'right',
|
||||
render: (text: any, metric: any) => (
|
||||
render: (text: string, metric: Metric) => (
|
||||
<MetricListItem
|
||||
key={metric.metricId}
|
||||
metric={metric}
|
||||
|
|
@ -115,20 +158,16 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
},
|
||||
];
|
||||
|
||||
const data = list.map((metric: any) => ({
|
||||
...metric,
|
||||
key: metric.metricId,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
dataSource={paginatedData}
|
||||
rowKey="metricId"
|
||||
onChange={handleTableChange}
|
||||
rowSelection={
|
||||
!disableSelection
|
||||
? {
|
||||
selectedRowKeys: selectedList.map((id: any) => id.toString()),
|
||||
selectedRowKeys: selectedList.map((id: number) => id.toString()),
|
||||
onChange: (selectedRowKeys) => {
|
||||
selectedRowKeys.forEach((key) => {
|
||||
toggleSelection && toggleSelection(parseInt(key));
|
||||
|
|
@ -137,7 +176,11 @@ const ListView: React.FC<Props> = (props: Props) => {
|
|||
}
|
||||
: undefined
|
||||
}
|
||||
pagination={false}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: sortedData.length,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue