From 2100cc0412defbf312e800aa929234e9e9e32a8b Mon Sep 17 00:00:00 2001 From: Sudheer Salavadi Date: Fri, 5 Jul 2024 11:05:28 +0530 Subject: [PATCH] List view time stamp improvements Following functions needs to reviewed - Rename & Delete functionality - Drag & Drop in gird view - Table Sorting --- .../MetricListItem/MetricListItem.tsx | 39 +++++-- .../MetricViewHeader/MetricViewHeader.tsx | 76 ++++++------- .../components/MetricsList/ListView.tsx | 103 +++++++++++++----- 3 files changed, 138 insertions(+), 80 deletions(-) diff --git a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx index 31286dc01..8d6690172 100644 --- a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx +++ b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx @@ -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 = ({ 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 = ({ ); 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 = ({ ); case 'lastModified': - return parseDate(metric.lastModified).calendar(); // Use moment to return relative time + const date = parseDate(metric.lastModified); + return formatDate(date); case 'options': return ( <> diff --git a/frontend/app/components/Dashboard/components/MetricViewHeader/MetricViewHeader.tsx b/frontend/app/components/Dashboard/components/MetricViewHeader/MetricViewHeader.tsx index 8e042c302..b36eb68c9 100644 --- a/frontend/app/components/Dashboard/components/MetricViewHeader/MetricViewHeader.tsx +++ b/frontend/app/components/Dashboard/components/MetricViewHeader/MetricViewHeader.tsx @@ -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 (
- +
-
- +
+
@@ -39,11 +43,11 @@ function MetricViewHeader({siteId}: { siteId: string }) {
metricStore.updateKey('sort', {by: value.value})} - plain={true} - className='ml-4' - /> - metricStore.updateKey('filter', {...filter, showMine: !filter.showMine}) + metricStore.updateKey('filter', { ...filter, showMine: !filter.showMine }) } />
- {/**/} 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 (
- - +
List
, value: 'list' }, { label:
- +
Grid
, value: 'grid' diff --git a/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx b/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx index 22d189e6e..30dff5d47 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx @@ -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) => { 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({ 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, + sorter: SorterResult | SorterResult[] + ) => { + const sortResult = sorter as SorterResult; + 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) => { 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) => ( { e.stopPropagation(); - toggleSelection && toggleSelection(parseInt(metric.metricId)); + toggleSelection && toggleSelection(metric.metricId); }} renderColumn="title" /> @@ -57,9 +104,9 @@ const ListView: React.FC = (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) => ( = (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) => ( = (props: Props) => { dataIndex: 'visibility', key: 'visibility', width: '10%', - render: (text: any, metric: any) => ( + render: (text: string, metric: Metric) => ( = (props: Props) => { /> ), }, - { title: '', key: 'options', className: 'text-right', - width: '5%', - align: 'right', - render: (text: any, metric: any) => ( + render: (text: string, metric: Metric) => ( = (props: Props) => { }, ]; - const data = list.map((metric: any) => ({ - ...metric, - key: metric.metricId, - })); - return ( 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) => { } : undefined } - pagination={false} + pagination={{ + current: pagination.current, + pageSize: pagination.pageSize, + total: sortedData.length, + }} /> ); };