diff --git a/.prettierignore b/.prettierignore index fbe17aa6..f74b9a7d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ -**/generated/* \ No newline at end of file +**/generated/* +README.md diff --git a/README.md b/README.md index 14cb2b9f..9eb4b2d6 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ This app is created by the volunteers of [Public Knowledge Workshop](https://www - `yarn start` ### testing the project: -- running the tests - `yarn playwright test` -- running the tests with UI - `yarn playwright test --ui` +- running the tests - `yarn test` +- running the tests with UI - `yarn test:ui` - additional helpful flags - https://playwright.dev/docs/test-cli ### useful resources: diff --git a/package.json b/package.json index cba2caff..4ab80625 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "sass": "^1.58.3", "styled-components": "^5.3.5", "stylis-plugin-rtl": "^2.1.1", - "typescript": "^4.7.4", + "typescript": "^5.2.2", "underscore.string": "^3.3.6", "usehooks-ts": "^2.9.1", "vite": "^4.4.9", @@ -45,8 +45,8 @@ "start": "vite", "build": "tsc && vite build", "serve": "vite preview", - "test": "echo \"no default test defined. to run playwright test, use `npm run test:playwright`\"", - "test:playwright": "playwright test", + "test": "playwright test", + "test:ui": "playwright test --ui", "lint": "eslint . --max-warnings 0", "lint:fix": "eslint . --fix", "format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc" diff --git a/src/pages/GapsPage.tsx b/src/pages/GapsPage.tsx index a80f9e45..09486685 100644 --- a/src/pages/GapsPage.tsx +++ b/src/pages/GapsPage.tsx @@ -19,6 +19,7 @@ import { DateSelector } from './components/DateSelector' import { FormControlLabel, Switch } from '@mui/material' import Grid from '@mui/material/Unstable_Grid2' // Grid version 2 import { INPUT_SIZE } from 'src/resources/sizes' +import DisplayGapsPercentage from './components/DisplayGapsPercentage' function formatTime(time: Moment) { return time.format(TEXTS.time_format) @@ -38,6 +39,14 @@ function formatStatus(all: GapsList, gap: Gap) { return TEXTS.ride_extra } +function getGapsPercentage(gaps: GapsList | undefined): number | undefined { + const ridesInTime = gaps?.filter((gap) => formatStatus([], gap) === TEXTS.ride_as_planned) + if (!gaps || !ridesInTime) return undefined + const ridesInTimePercentage = (ridesInTime?.length / gaps?.length) * 100 + const allRidesPercentage = 100 + return allRidesPercentage - ridesInTimePercentage +} + const Cell = styled.div` width: 120px; ` @@ -81,6 +90,8 @@ const GapsPage = () => { .finally(() => setRoutesIsLoading(false)) }, [operatorId, lineNumber, timestamp, setSearch]) + const gapsPercentage = getGapsPercentage(gaps) + return ( @@ -153,6 +164,11 @@ const GapsPage = () => { } label={TEXTS.checkbox_only_gaps} /> + {TEXTS.planned_time} {TEXTS.planned_status} diff --git a/src/pages/components/DisplayGapsPercentage.scss b/src/pages/components/DisplayGapsPercentage.scss new file mode 100644 index 00000000..40303fbe --- /dev/null +++ b/src/pages/components/DisplayGapsPercentage.scss @@ -0,0 +1,15 @@ +@import '../../resources/variables'; + +.gaps-percentage-displayed { + font-weight: bold; + width: 200px; + &-great-result { + color: $great-result; + } + &-decent-result { + color: $decent-result; + } + &-terrible-result { + color: $terrible-result; + } +} diff --git a/src/pages/components/DisplayGapsPercentage.tsx b/src/pages/components/DisplayGapsPercentage.tsx new file mode 100644 index 00000000..d3c88f08 --- /dev/null +++ b/src/pages/components/DisplayGapsPercentage.tsx @@ -0,0 +1,58 @@ +import React from 'react' + +import { TEXTS } from '../../resources/texts' +import './DisplayGapsPercentage.scss' +import { Row } from './Row' + +function getStatus(percentage: number, decentPercentage: number, terriblePercentage: number) { + const moreThanOneHundredPercent = 101 + const statuses = [ + { + min: -Infinity, + status: 'invalid', + }, + { + min: 0, + status: 'great', + }, + { + min: decentPercentage, + status: 'decent', + }, + { + min: terriblePercentage, + status: 'terrible', + }, + { + min: moreThanOneHundredPercent, + status: 'invalid', + }, + ] + return statuses.findLast((status: { min: number; status: string }) => status.min <= percentage) + ?.status +} + +function DisplayGapsPercentage({ + gapsPercentage, + decentPercentage, + terriblePercentage, +}: { + gapsPercentage: number | undefined + decentPercentage: number + terriblePercentage: number +}) { + if (!gapsPercentage && gapsPercentage != 0) return <> + const status = getStatus(gapsPercentage, decentPercentage, terriblePercentage) + const stylesClass = `gaps-percentage-displayed-${status}-result` + const text = + status === 'great' + ? TEXTS.all_rides_completed + : `${Math.floor(gapsPercentage)}% ${TEXTS.missing_rides}` + + return ( + +
{text}
+
+ ) +} +export default DisplayGapsPercentage diff --git a/src/pages/components/utils/tooltip/Tooltip.scss b/src/pages/components/utils/tooltip/Tooltip.scss deleted file mode 100644 index 79163c72..00000000 --- a/src/pages/components/utils/tooltip/Tooltip.scss +++ /dev/null @@ -1,64 +0,0 @@ -.tooltip { - position: relative; - display: inline-block; - - &:hover .generic-tooltip { - opacity: 1; - } - - .tooltip-trigger { - position: relative; - display: inline-block; - } - - .tooltip-icon { - position: relative; - display: inline-flex; - justify-content: center; - align-items: center; - width: 20px; - height: 20px; - border-radius: 50%; - background-color: #888; - color: #fff; - font-size: 14px; - cursor: pointer; - vertical-align: middle; - margin-right: 10px; - } - - .tooltip-icon:hover { - background-color: #333; - } - - .generic-tooltip { - position: absolute; - top: 50%; - left: -5px; - transform: translate(-100%, -50%); - padding: 8px; - background-color: #333; - color: #fff; - font-size: 14px; - border-radius: 4px; - opacity: 0; - transition: opacity 0.3s; - z-index: 1; - width: 500px; - } - - - .generic-tooltip::after { - content: ''; - top: 50%; - left: 50%; - display: block; - width: 10px; - height: 10px; - background: #333; - opacity: .7; - right: 0; - transform: translate(50%, 0%) rotate(45deg); - position: absolute; - } -} \ No newline at end of file diff --git a/src/pages/components/utils/tooltip/Tooltip.tsx b/src/pages/components/utils/tooltip/Tooltip.tsx deleted file mode 100644 index e511f558..00000000 --- a/src/pages/components/utils/tooltip/Tooltip.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import './Tooltip.scss' - -interface TooltipProps { - text: React.ReactNode - children: React.ReactNode -} - -const Tooltip = ({ text, children }: TooltipProps) => { - return ( -
-
{children}
-
{text}
-
- ) -} - -export default Tooltip diff --git a/src/pages/dashboard/DashboardPage.scss b/src/pages/dashboard/DashboardPage.scss index bd186fd3..4b398587 100644 --- a/src/pages/dashboard/DashboardPage.scss +++ b/src/pages/dashboard/DashboardPage.scss @@ -39,5 +39,25 @@ height: 400px; overflow-y: scroll; } + + .tooltip-icon { + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: #888; + color: #fff; + font-size: 14px; + cursor: pointer; + vertical-align: middle; + margin-right: 10px; + } + + .tooltip-icon:hover { + background-color: #333; + } } } \ No newline at end of file diff --git a/src/pages/dashboard/DashboardPage.tsx b/src/pages/dashboard/DashboardPage.tsx index 8279b599..8db99cef 100644 --- a/src/pages/dashboard/DashboardPage.tsx +++ b/src/pages/dashboard/DashboardPage.tsx @@ -1,7 +1,6 @@ import React, { Fragment, useCallback, useState } from 'react' import { useGroupBy } from 'src/api/groupByService' import { PageContainer } from '../components/PageContainer' -import Tooltip from '../components/utils/tooltip/Tooltip' import OperatorHbarChart from './OperatorHbarChart/OperatorHbarChart' import './DashboardPage.scss' import { TEXTS } from 'src/resources/texts' @@ -9,7 +8,7 @@ import ArrivalByTimeChart from './ArrivalByTimeChart/ArrivalByTimeChart' import { DatePicker } from '@mui/x-date-pickers/DatePicker' import moment, { Moment } from 'moment' import LinesHbarChart from './LineHbarChart/LinesHbarChart' -import { FormControlLabel, Switch } from '@mui/material' +import { FormControlLabel, Switch, Tooltip } from '@mui/material' import { Label } from 'src/pages/components/Label' import OperatorSelector from 'src/pages/components/OperatorSelector' @@ -99,7 +98,10 @@ const DashboardPage = () => {

{TEXTS.dashboard_page_title} - + i

diff --git a/src/resources/texts.tsx b/src/resources/texts.tsx index aa53bc73..fac06e31 100644 --- a/src/resources/texts.tsx +++ b/src/resources/texts.tsx @@ -85,6 +85,8 @@ export const TEXTS = { innovation_authority: 'רשות החדשנות', migdal_company: '“מגדל בקהילה“', and_smaller_donors: 'ותרומות קטנות נוספות של ידידי ואוהדי הסדנא.', + all_rides_completed: 'כמעט / כל הנסיעות בוצעו', + missing_rides: 'מהנסיעות חסרות', } export const formatted = (text: string, value: string) => text.replace(PLACEHOLDER, value) diff --git a/src/resources/variables.scss b/src/resources/variables.scss index 341a5880..0c7dc5c6 100644 --- a/src/resources/variables.scss +++ b/src/resources/variables.scss @@ -4,3 +4,6 @@ $color-active: rgba(95, 91, 255); $mobile-max-width: 768px; +$great-result: green; +$decent-result: orange; +$terrible-result: red;