Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QA feedback on the chart and card names on truncations #2219

Merged
merged 5 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions platform/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
1 change: 1 addition & 0 deletions platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"globals": "^15.9.0",
"prettier": "^3.3.3",
"typescript": "5.6.3",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid TypeScript version

The specified TypeScript version 5.6.3 is invalid. The latest stable version as of April 2024 is 5.3.3.

Apply this diff to fix the version:

-    "typescript": "5.6.3",
+    "typescript": "5.3.3",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"typescript": "5.6.3",
"typescript": "5.3.3",

🛠️ Refactor suggestion

Add missing TypeScript type definitions

Since you're adding TypeScript support, you'll need type definitions for your existing dependencies. Several key packages in your dependencies require separate @types/* packages.

Add these essential type definitions:

     "typescript": "5.3.3",
+    "@types/react": "^18.2.0",
+    "@types/react-dom": "^18.2.0",
+    "@types/node": "^20.0.0",
+    "@types/mapbox-gl": "^2.7.19",
+    "@types/pako": "^2.0.3",
+    "@types/papaparse": "^5.3.14",
+    "@types/underscore": "^1.11.15",
     "webpack": "^5.74.0"

This will provide proper type checking and better IDE support for your core dependencies.

Committable suggestion skipped: line range outside the PR's diff.

"webpack": "^5.74.0"
},
"engines": {
Expand Down
79 changes: 66 additions & 13 deletions platform/src/common/components/AQNumberCard/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState, useEffect, useMemo } from 'react';
import React, { useState, useEffect, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { IconMap } from './IconMap';
import CustomTooltip from '../Tooltip';
import { useWindowSize } from '@/lib/windowSize';
import { setOpenModal, setModalType } from '@/lib/store/services/downloadModal';
import { fetchRecentMeasurementsData } from '@/lib/store/services/deviceRegistry/RecentMeasurementsSlice';
import PropTypes from 'prop-types';

// Constants
const AIR_QUALITY_LEVELS = [
Expand All @@ -25,8 +26,8 @@ const AIR_QUALITY_LEVELS = [
];

const MAX_CARDS = 4;
const TRUNCATE_LENGTH = 12;

// SkeletonCard Component
const SkeletonCard = () => (
<div className="w-full border border-gray-200 bg-white rounded-xl px-4 py-6">
<div className="flex flex-col justify-between h-[168px]">
Expand All @@ -51,6 +52,7 @@ const SkeletonCard = () => (
</div>
);

// SiteCard Component
const SiteCard = ({ site, onOpenModal, windowWidth, pollutantType }) => {
const measurements = useSelector(
(state) => state.recentMeasurements.measurements,
Expand All @@ -77,11 +79,37 @@ const SiteCard = ({ site, onOpenModal, windowWidth, pollutantType }) => {
}, [reading]);

const AirQualityIcon = IconMap[status];
const truncatedName = !site.name
? '---'
: site.name.length > TRUNCATE_LENGTH
? `${site.name.slice(0, TRUNCATE_LENGTH)}...`
: site.name;

// Ref and state for detecting text truncation
const nameRef = useRef(null);
const [isTruncated, setIsTruncated] = useState(false);

useEffect(() => {
const checkTruncation = () => {
const el = nameRef.current;
if (el) {
setIsTruncated(el.scrollWidth > el.clientWidth);
}
};

// Initial check
checkTruncation();

// Re-check on window resize
window.addEventListener('resize', checkTruncation);
return () => {
window.removeEventListener('resize', checkTruncation);
};
}, [site.name, windowWidth]);

const siteNameElement = (
<div
ref={nameRef}
className="text-gray-800 text-lg font-medium capitalize text-left w-full max-w-[185px] md:max-w-full lg:max-w-[185px] overflow-hidden text-ellipsis whitespace-nowrap"
>
{site.name || '---'}
</div>
);

return (
<button
Expand All @@ -91,12 +119,16 @@ const SiteCard = ({ site, onOpenModal, windowWidth, pollutantType }) => {
<div className="relative w-full flex flex-col justify-between bg-white border border-gray-200 rounded-xl px-4 py-6 h-[200px] shadow-sm hover:shadow-md transition-shadow duration-200 ease-in-out cursor-pointer">
<div className="flex justify-between items-center mb-4">
<div>
<div
className="text-gray-800 text-lg font-medium capitalize text-left max-w-full"
title={site.name || 'No Location Data'}
>
{truncatedName}
</div>
{isTruncated ? (
<CustomTooltip
tooltipsText={site.name || 'No Location Data'}
position="top"
>
{siteNameElement}
</CustomTooltip>
) : (
siteNameElement
)}
<div className="text-base text-left text-slate-400 capitalize">
{site.country || '---'}
</div>
Expand Down Expand Up @@ -139,6 +171,18 @@ const SiteCard = ({ site, onOpenModal, windowWidth, pollutantType }) => {
);
};

SiteCard.propTypes = {
site: PropTypes.shape({
_id: PropTypes.string.isRequired,
name: PropTypes.string,
country: PropTypes.string,
}).isRequired,
onOpenModal: PropTypes.func.isRequired,
windowWidth: PropTypes.number.isRequired,
pollutantType: PropTypes.string.isRequired,
};

// AddLocationCard Component
const AddLocationCard = ({ onOpenModal }) => (
<button
onClick={() => onOpenModal('addLocation')}
Expand All @@ -149,6 +193,11 @@ const AddLocationCard = ({ onOpenModal }) => (
</button>
);

AddLocationCard.propTypes = {
onOpenModal: PropTypes.func.isRequired,
};

// AQNumberCard Component
const AQNumberCard = ({ className = '' }) => {
const dispatch = useDispatch();
const { width: windowWidth } = useWindowSize();
Expand Down Expand Up @@ -238,4 +287,8 @@ const AQNumberCard = ({ className = '' }) => {
);
};

AQNumberCard.propTypes = {
className: PropTypes.string,
};

export default React.memo(AQNumberCard);
24 changes: 2 additions & 22 deletions platform/src/common/components/Charts/ChartContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ const ChartContainer = ({
const [loadingFormat, setLoadingFormat] = useState(null);
const [downloadComplete, setDownloadComplete] = useState(null);

// State to control skeleton loader display
const [showSkeleton, setShowSkeleton] = useState(chartLoading);

// Handle click outside for dropdown
useEffect(() => {
const handleClickOutside = (event) => {
Expand All @@ -53,28 +50,11 @@ const ChartContainer = ({
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);

// TEMPORARY: Handle loading issue, remove later
useEffect(() => {
let timer;

if (chartLoading) {
setShowSkeleton(true);
} else {
timer = setTimeout(() => {
setShowSkeleton(false);
}, 4000);
}

return () => {
if (timer) {
clearTimeout(timer);
}
};
}, [chartLoading]);
// Update skeleton loader based on loading state
const showSkeleton = chartLoading;

/**
* Exports the chart in the specified format.
*
* @param {string} format - The format to export the chart (png, jpg, pdf).
*/
const exportChart = useCallback(async (format) => {
Expand Down
47 changes: 27 additions & 20 deletions platform/src/common/components/Charts/components/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ export const truncate = (str) => {
return str.length > 20 ? str.substr(0, 20 - 1) + '...' : str;
};

const truncate2 = (value) => {
return value.length > 10 ? `${value.substring(0, 10)}...` : value;
};

/**
* @param {Number} value
* @returns {Object}
Expand Down Expand Up @@ -210,12 +206,13 @@ const CustomDot = (props) => {
* Customized legend component
* @param {Object} props
*/
const renderCustomizedLegend = (props) => {
const { payload } = props;
const renderCustomizedLegend = ({ payload }) => {
// Determine if truncation is needed based on the number of locations
const shouldTruncate = payload.length > 3;

// Sort the payload array from darkest to lightest color
const sortedPayload = React.useMemo(() => {
return payload.sort((a, b) => {
return [...payload].sort((a, b) => {
const colorToGrayscale = (color) => {
if (color) {
const hex = color.replace('#', '');
Expand All @@ -231,24 +228,34 @@ const renderCustomizedLegend = (props) => {
}, [payload]);

return (
<div className="py-4 flex flex-wrap gap-2 justify-end w-full">
<div className="relative flex flex-wrap justify-end gap-2 w-full p-2">
{sortedPayload.map((entry, index) => (
<div
key={index}
style={{
color: '#485972',
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
}}
className="tooltip tooltip-top gap-1 w-auto text-[12px] leading-[14px] font-normal outline-none text-[#485972]"
data-tip={entry.value}
className="flex items-center gap-1 text-xs text-gray-700 whitespace-nowrap relative"
>
<div
className="w-2 h-2 m-0 p-0 rounded-full inline-block"
<span
className="w-2 h-2 rounded-full"
style={{ backgroundColor: entry.color }}
></div>
<p className="m-0 p-0">{truncate2(entry.value)}</p>
></span>

{/* Only truncate and add tooltip if shouldTruncate is true */}
<span
className={`${shouldTruncate ? 'truncate max-w-[100px] group' : ''}`}
title={shouldTruncate ? entry.value : null}
>
{entry.value}
</span>

{/* Tooltip appears only if truncation is applied */}
{shouldTruncate && (
<div className="absolute bottom-full mb-1 hidden group-hover:flex flex-col items-center">
<span className="text-xs text-white bg-gray-700 px-2 py-1 rounded-md shadow-md">
{entry.value}
</span>
<span className="w-2 h-2 bg-gray-700 rotate-45 transform -translate-y-1/2"></span>
</div>
)}
</div>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import jsPDF from 'jspdf';
import 'jspdf-autotable';
import { saveAs } from 'file-saver';
import CustomToast from '../../../Toast/CustomToast';
import { startOfDay, endOfDay } from 'date-fns';

/**
* Header component for the Download Data modal.
Expand Down Expand Up @@ -110,13 +109,6 @@ const DataDownload = ({ onClose }) => {
setFormData((prevData) => ({ ...prevData, [id]: option }));
}, []);

const toLocalISOString = (date) => {
const offsetDate = new Date(
date.getTime() - date.getTimezoneOffset() * 60000,
);
return offsetDate.toISOString().slice(0, 19); // Remove milliseconds and 'Z'
};

/**
* Handles the submission of the form.
* Prepares data and calls the exportDataApi to download the data.
Expand All @@ -126,7 +118,7 @@ const DataDownload = ({ onClose }) => {
async (e) => {
e.preventDefault();
setDownloadLoading(true);
setFormError(''); // Reset previous errors
setFormError('');

// Validate form data
if (
Expand All @@ -149,17 +141,16 @@ const DataDownload = ({ onClose }) => {

// Prepare data for API
const apiData = {
startDateTime:
toLocalISOString(startOfDay(formData.duration.name.start)) + ':00',
endDateTime:
toLocalISOString(endOfDay(formData.duration.name.end)) + ':59',
startDateTime: new Date(formData.duration.name.start).toISOString(),
endDateTime: new Date(formData.duration.name.end).toISOString(),
sites: selectedSites.map((site) => site._id),
network: formData.network.name,
datatype: formData.dataType.name.toLowerCase(),
pollutants: [formData.pollutant.name.toLowerCase().replace('.', '_')],
resolution: formData.frequency.name.toLowerCase(),
downloadType: formData.fileType.name.toLowerCase(),
outputFormat: 'airqo-standard',
minimum: true,
};

try {
Expand Down
4 changes: 2 additions & 2 deletions platform/src/common/components/TopBar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SettingsIcon from '@/icons/SideBar/SettingsIcon';
import UserIcon from '@/icons/Topbar/userIcon';
import ChartIcon from '@/icons/Topbar/chartIcon';
import CustomDropdown from '../Dropdowns/CustomDropdown';
import TopBarSearch from '../search/TopBarSearch';
// import TopBarSearch from '../search/TopBarSearch';
import { setOpenModal, setModalType } from '@/lib/store/services/downloadModal';
import {
setToggleDrawer,
Expand Down Expand Up @@ -217,7 +217,7 @@ const TopBar = ({ topbarTitle, noBorderBottom, showSearch = false }) => {
handleOpenModal('search');
}}
>
<TopBarSearch customWidth="md:max-w-[192px]" />
{/* <TopBarSearch customWidth="md:max-w-[192px]" /> */}
</button>
</div>
)}
Expand Down
2 changes: 1 addition & 1 deletion platform/src/pages/Home/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const Home = () => {
}, [cardCheckList, steps]);

// console.info('checkListData', checkListData);
console.info('cardCheckList', cardCheckList);
// console.info('cardCheckList', cardCheckList);

return (
<Layout noBorderBottom pageTitle="Home" topbarTitle="Home">
Expand Down
Loading
Loading