From c293bd6746e0324d912422594c3eb56e2c31d534 Mon Sep 17 00:00:00 2001 From: Amabelle Trachtenberg Date: Sat, 9 Dec 2023 20:29:09 +0200 Subject: [PATCH 01/94] chore: change texts to use i18n - part c#161 (#284) --- src/locale/en.json | 4 +- src/locale/he.json | 4 +- src/pages/TimelinePage.tsx | 19 ++--- src/pages/components/DateSelector.tsx | 6 +- .../components/DisplayGapsPercentage.tsx | 7 +- .../FilterPositionsByStartTimeSelector.tsx | 6 +- src/pages/gaps/index.tsx | 73 ++++++++++--------- 7 files changed, 65 insertions(+), 54 deletions(-) diff --git a/src/locale/en.json b/src/locale/en.json index 523136bbf..a5aa7618e 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -94,5 +94,7 @@ "bug_type_message":"Please enter the type of request", "bug_type_bug":"Bug", "bug_type_feature":"Feature Request", - "funding_paragraph": "The Databus project is developed by the Hasadna for public knowledge, by the contributions of volunteers, and based upon" + "funding_paragraph": "The Databus project is developed by the Hasadna for public knowledge, by the contributions of volunteers, and based upon", + "all_rides_completed": "All/Almost the rides are completed", + "missing_rides": "Of the rides are missing" } diff --git a/src/locale/he.json b/src/locale/he.json index 54b79e856..3de57e238 100644 --- a/src/locale/he.json +++ b/src/locale/he.json @@ -95,5 +95,7 @@ "bug_type_bug": "באג", "bug_type_feature": "בקשה לפיתוח", "funding_paragraph": "דאטאבוס פותח בסדנא לידע ציבורי, בעבודת מתנדבים, ומבוסס על", - "halufa_ride": "נסיעה חלופית מס" + "halufa_ride": "נסיעה חלופית מס", + "all_rides_completed": "כמעט / כל הנסיעות בוצעו", + "missing_rides": "מהנסיעות חסרות" } diff --git a/src/pages/TimelinePage.tsx b/src/pages/TimelinePage.tsx index 0123f4cc2..af6dca842 100644 --- a/src/pages/TimelinePage.tsx +++ b/src/pages/TimelinePage.tsx @@ -11,7 +11,7 @@ import { } from 'src/api/gtfsService' import RouteSelector from 'src/pages/components/RouteSelector' import { Label } from 'src/pages/components/Label' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import StopSelector from 'src/pages/components/StopSelector' import { Spin } from 'antd' import { getSiriStopHitTimesAsync } from 'src/api/siriService' @@ -28,6 +28,7 @@ const StyledTimelineBoard = styled(TimelineBoard)` ` const TimelinePage = () => { + const { t } = useTranslation() const { search, setSearch } = useContext(SearchContext) const { operatorId, lineNumber, timestamp, routes, routeKey } = search const [state, setState] = useState({}) @@ -133,7 +134,7 @@ const TimelinePage = () => { {/* choose date */} - { {/* choose operator */} - { {/* choose line */} - { {routesIsLoading && ( - )} {!routesIsLoading && routes && (routes.length === 0 ? ( - {TEXTS.line_not_found} + {t('line_not_found')} ) : ( { {stopsIsLoading && ( - )} @@ -208,7 +209,7 @@ const TimelinePage = () => { {hitsIsLoading && ( - )} @@ -224,7 +225,7 @@ const TimelinePage = () => { siriTimes={siriHitTimes} /> ) : ( - {TEXTS.hits_not_found} + {t('hits_not_found')} ))} ) diff --git a/src/pages/components/DateSelector.tsx b/src/pages/components/DateSelector.tsx index 0edec6d1c..af5f1ec46 100644 --- a/src/pages/components/DateSelector.tsx +++ b/src/pages/components/DateSelector.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' import { DatePicker } from '@mui/x-date-pickers/DatePicker' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { DataAndTimeSelectorProps } from './utils/dateAndTime' import { DateValidationError } from '@mui/x-date-pickers' import styled from 'styled-components' @@ -11,6 +11,8 @@ const Error = styled.div` export function DateSelector({ time, onChange, customLabel }: DataAndTimeSelectorProps) { const [error, setError] = useState(null) + const { t } = useTranslation() + return ( <> onChange(ts!)} format="DD/MM/YYYY" - label={customLabel || TEXTS.choose_date} + label={customLabel || t('choose_date')} disableFuture onError={(err) => setError(err)} /> diff --git a/src/pages/components/DisplayGapsPercentage.tsx b/src/pages/components/DisplayGapsPercentage.tsx index c96abff42..7ddfea097 100644 --- a/src/pages/components/DisplayGapsPercentage.tsx +++ b/src/pages/components/DisplayGapsPercentage.tsx @@ -1,4 +1,4 @@ -import { TEXTS } from '../../resources/texts' +import { useTranslation } from 'react-i18next' import './DisplayGapsPercentage.scss' import { Row } from './Row' @@ -39,13 +39,14 @@ function DisplayGapsPercentage({ decentPercentage: number terriblePercentage: number }) { + const { t } = useTranslation() 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}` + ? t('all_rides_completed') + : `${Math.floor(gapsPercentage)}% ${t('missing_rides')}` return ( diff --git a/src/pages/components/FilterPositionsByStartTimeSelector.tsx b/src/pages/components/FilterPositionsByStartTimeSelector.tsx index d065b3c6b..6df8f0d79 100644 --- a/src/pages/components/FilterPositionsByStartTimeSelector.tsx +++ b/src/pages/components/FilterPositionsByStartTimeSelector.tsx @@ -1,4 +1,4 @@ -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { Autocomplete, TextField } from '@mui/material' type FilterPositionsByStartTimeSelectorProps = { @@ -18,6 +18,8 @@ export function FilterPositionsByStartTimeSelector({ const valueFinned = options.find((option) => option.value === startTime) const value = valueFinned ? valueFinned : null + const { t } = useTranslation() + return ( setStartTime(value ? value.value : '0')} id="start-time-select" options={options} - renderInput={(params) => } + renderInput={(params) => } getOptionLabel={(option) => option.label} /> ) diff --git a/src/pages/gaps/index.tsx b/src/pages/gaps/index.tsx index 6c4af4b60..ccac575a1 100644 --- a/src/pages/gaps/index.tsx +++ b/src/pages/gaps/index.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from 'react' import { PageContainer } from '../components/PageContainer' import { Row } from '../components/Row' import { Label } from '../components/Label' -import { TEXTS } from '../../resources/texts' +import { useTranslation } from 'react-i18next' import OperatorSelector from '../components/OperatorSelector' import LineNumberSelector from '../components/LineSelector' import { SearchContext } from '../../model/pageState' @@ -21,32 +21,6 @@ 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) -} - -function formatStatus(all: GapsList, gap: Gap) { - if (!gap.siriTime) { - return TEXTS.ride_missing - } - if (gap.gtfsTime) { - return TEXTS.ride_as_planned - } - const hasTwinRide = all.some((g) => g.gtfsTime && g.siriTime && g.siriTime.isSame(gap.siriTime)) - if (hasTwinRide) { - return TEXTS.ride_duped - } - 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; ` @@ -56,6 +30,7 @@ const TitleCell = styled(Cell)` ` const GapsPage = () => { + const { t } = useTranslation() const { search, setSearch } = useContext(SearchContext) const { operatorId, lineNumber, timestamp, routes, routeKey } = search const [gaps, setGaps] = useState() @@ -64,6 +39,32 @@ const GapsPage = () => { const [gapsIsLoading, setGapsIsLoading] = useState(false) const [onlyGapped, setOnlyGapped] = useSessionStorage('onlyGapped', false) + function formatTime(time: Moment) { + return time.format(t('time_format')) + } + + function formatStatus(all: GapsList, gap: Gap) { + if (!gap.siriTime) { + return t('ride_missing') + } + if (gap.gtfsTime) { + return t('ride_as_planned') + } + const hasTwinRide = all.some((g) => g.gtfsTime && g.siriTime && g.siriTime.isSame(gap.siriTime)) + if (hasTwinRide) { + return t('ride_duped') + } + return t('ride_extra') + } + + function getGapsPercentage(gaps: GapsList | undefined): number | undefined { + const ridesInTime = gaps?.filter((gap) => formatStatus([], gap) === t('ride_as_planned')) + if (!gaps || !ridesInTime) return undefined + const ridesInTimePercentage = (ridesInTime?.length / gaps?.length) * 100 + const allRidesPercentage = 100 + return allRidesPercentage - ridesInTimePercentage + } + useEffect(() => { if (operatorId && routes && routeKey && timestamp) { const selectedRoute = routes.find((route) => route.key === routeKey) @@ -102,7 +103,7 @@ const GapsPage = () => { {/* choose date */} - { {/* choose operator */} - { {/* choose line */} - { {routesIsLoading && ( - )} {!routesIsLoading && routes && (routes.length === 0 ? ( - {TEXTS.line_not_found} + {t('line_not_found')} ) : ( { {gapsIsLoading && ( - )} @@ -167,7 +168,7 @@ const GapsPage = () => { control={ setOnlyGapped(e.target.checked)} /> } - label={TEXTS.checkbox_only_gaps} + label={t('checkbox_only_gaps')} /> { terriblePercentage={20} /> - {TEXTS.planned_time} - {TEXTS.planned_status} + {t('planned_time')} + {t('planned_status')} {gaps ?.filter((gap) => gap.gtfsTime || gap.siriTime) From ec095a59cd9122911611108f317d6187a7a60b57 Mon Sep 17 00:00:00 2001 From: Moshe Date: Sat, 9 Dec 2023 22:20:42 +0200 Subject: [PATCH 02/94] chore: change page preloader to same as starting (#285) --- src/routes/index.tsx | 4 ++-- src/shared/Preloader.tsx | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/shared/Preloader.tsx diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 5ff9328d7..be9e64fa2 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -10,7 +10,6 @@ const SingleLineMapPage = lazy(() => import('../pages/singleLineMap')) const About = lazy(() => import('../pages/About')) const Profile = lazy(() => import('../pages/Profile')) const BugReportForm = lazy(() => import('../pages/BugReportForm ')) -import CircularProgress from '@mui/material/CircularProgress' import { RadarChartOutlined, @@ -23,6 +22,7 @@ import { BarChartOutlined, LineChartOutlined, } from '@ant-design/icons' +import Preloader from 'src/shared/Preloader' export const usePages = () => { const { t } = useTranslation() @@ -92,7 +92,7 @@ const RoutesList = () => { const RedirectToDashboard = () => const routes = pages.filter((r) => r.element) return ( - }> + }> {routes.map(({ path, element }) => ( diff --git a/src/shared/Preloader.tsx b/src/shared/Preloader.tsx new file mode 100644 index 000000000..cf3e7382a --- /dev/null +++ b/src/shared/Preloader.tsx @@ -0,0 +1,8 @@ +const Preloader = () => ( + <> +
🚌
+
+ +) + +export default Preloader From 0023217bebac2894053a91f2f1e66ddf01d7e307 Mon Sep 17 00:00:00 2001 From: Moshe Date: Sun, 10 Dec 2023 17:39:30 +0200 Subject: [PATCH 03/94] feat: add explanation at the top of dashboard page (#287) --- src/pages/dashboard/DashboardPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/dashboard/DashboardPage.tsx b/src/pages/dashboard/DashboardPage.tsx index e9e9fa6af..2c4d1ba9d 100644 --- a/src/pages/dashboard/DashboardPage.tsx +++ b/src/pages/dashboard/DashboardPage.tsx @@ -9,7 +9,7 @@ import moment from 'moment' import './DashboardPage.scss' import { PageContainer } from '../components/PageContainer' import { TEXTS } from 'src/resources/texts' -import { Typography } from 'antd' +import { Alert, Typography } from 'antd' import Grid from '@mui/material/Unstable_Grid2' // Grid version 2 // Components @@ -30,6 +30,7 @@ const DashboardPage = () => { return ( ביצועי תחבורה ציבורית + Date: Sun, 10 Dec 2023 20:07:50 +0200 Subject: [PATCH 04/94] feat: add widget component (#283) * New Widget Component --------- Co-authored-by: yadenmezi --- src/pages/Profile.tsx | 5 +++-- src/pages/components/Widget.stories.tsx | 5 +++-- src/pages/dashboard/AllLineschart/AllLinesChart.tsx | 5 +++-- src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx | 5 +++-- src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx | 5 +++-- src/pages/gapsPatterns/GapsPatternsPage.tsx | 5 +++-- src/shared/Widget.tsx | 5 +++++ 7 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 src/shared/Widget.tsx diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 6560f5723..fe0646984 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -21,6 +21,7 @@ import RouteSelector from './components/RouteSelector' //API // import { /*getGtfsRidesList,*/ getRidesAsync } from 'src/api/profileService' import { getRoutesAsync } from '../api/gtfsService' +import Widget from 'src/shared/Widget' // time inputs // import { DateSelector } from './components/DateSelector' @@ -92,7 +93,7 @@ const LineProfileComponent = ({ search }: { search: PageSearchState }) => { return ( -
+

{t('profile_page')}



@@ -143,7 +144,7 @@ const LineProfileComponent = ({ search }: { search: PageSearchState }) => {
- +
) } diff --git a/src/pages/components/Widget.stories.tsx b/src/pages/components/Widget.stories.tsx index 756524289..064e47a00 100644 --- a/src/pages/components/Widget.stories.tsx +++ b/src/pages/components/Widget.stories.tsx @@ -1,15 +1,16 @@ import type { Meta, StoryObj } from '@storybook/react' import { BusToolTip } from './MapLayers/BusToolTip' import '../../shared/shared.css' +import Widget from 'src/shared/Widget' const meta = { title: 'Components/Widget', component: () => (
-
+ שלום חברים!
ככה נראה widget util -
+
), parameters: { diff --git a/src/pages/dashboard/AllLineschart/AllLinesChart.tsx b/src/pages/dashboard/AllLineschart/AllLinesChart.tsx index 801d19fa4..c669894d8 100644 --- a/src/pages/dashboard/AllLineschart/AllLinesChart.tsx +++ b/src/pages/dashboard/AllLineschart/AllLinesChart.tsx @@ -6,6 +6,7 @@ import OperatorHbarChart from './OperatorHbarChart/OperatorHbarChart' import { GroupByRes, useGroupBy } from 'src/api/groupByService' import { FC } from 'react' import { Moment } from 'moment/moment' +import Widget from 'src/shared/Widget' const convertToChartCompatibleStruct = (arr: GroupByRes[]) => { return arr.map((item: GroupByRes) => ({ @@ -29,7 +30,7 @@ export const AllLinesChart: FC = ({ startDate, endDate } }) return ( -
+

{TEXTS.dashboard_page_title} = ({ startDate, endDate } ) : ( )} -

+ ) } diff --git a/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx b/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx index 976622f3e..483b15ac6 100644 --- a/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx +++ b/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx @@ -4,6 +4,7 @@ import { Skeleton, Radio, RadioChangeEvent } from 'antd' import ArrivalByTimeChart from './ArrivalByTimeChart' import { GroupByRes, useGroupBy } from 'src/api/groupByService' import { Moment } from 'moment/moment' +import Widget from 'src/shared/Widget' const convertToGraphCompatibleStruct = (arr: GroupByRes[]) => { return arr.map((item: GroupByRes) => ({ @@ -33,7 +34,7 @@ const DayTimeChart: FC = ({ startDate, endDate, operatorId }) }) return ( -
+

{groupByHour ? TEXTS.dashboard_page_graph_title_hour : TEXTS.dashboard_page_graph_title_day}

@@ -52,7 +53,7 @@ const DayTimeChart: FC = ({ startDate, endDate, operatorId }) operatorId={operatorId} /> )} -
+ ) } diff --git a/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx b/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx index 87059d698..55f8e529e 100644 --- a/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx +++ b/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx @@ -4,6 +4,7 @@ import LinesHbarChart from './LineHbarChart/LinesHbarChart' import { TEXTS } from 'src/resources/texts' import { FC } from 'react' import { Moment } from 'moment/moment' +import Widget from 'src/shared/Widget' interface WorstLinesChartProps { startDate: Moment @@ -35,7 +36,7 @@ export const WorstLinesChart: FC = ({ startDate, endDate, } return ( -
+

{TEXTS.worst_lines_page_title}

{lineDataLoading ? ( @@ -45,7 +46,7 @@ export const WorstLinesChart: FC = ({ startDate, endDate, operators_whitelist={['אלקטרה אפיקים', 'דן', 'מטרופולין', 'קווים', 'אגד']} /> )} -
+ ) } diff --git a/src/pages/gapsPatterns/GapsPatternsPage.tsx b/src/pages/gapsPatterns/GapsPatternsPage.tsx index 6ff0173cf..27778c80a 100644 --- a/src/pages/gapsPatterns/GapsPatternsPage.tsx +++ b/src/pages/gapsPatterns/GapsPatternsPage.tsx @@ -34,6 +34,7 @@ import { DateSelector } from '../components/DateSelector' import { INPUT_SIZE } from 'src/resources/sizes' const { Title } = Typography import { useTranslation } from 'react-i18next' +import Widget from 'src/shared/Widget' // Define prop types for the component interface BusLineStatisticsProps { lineRef: number @@ -71,7 +72,7 @@ function GapsByHour({ lineRef, operatorRef, fromDate, toDate }: BusLineStatistic return ( lineRef > 0 && ( -
+ {t('dashboard_page_graph_title')} {isLoading && lineRef ? ( @@ -137,7 +138,7 @@ function GapsByHour({ lineRef, operatorRef, fromDate, toDate }: BusLineStatistic )} -
+ ) ) } diff --git a/src/shared/Widget.tsx b/src/shared/Widget.tsx new file mode 100644 index 000000000..5ce1393c2 --- /dev/null +++ b/src/shared/Widget.tsx @@ -0,0 +1,5 @@ +const Widget = (props: { children: React.ReactNode }) => { + return
{props.children}
+} + +export default Widget From b5b8a0deeb991966b24afebb722f5925c79b376a Mon Sep 17 00:00:00 2001 From: Amabelle Trachtenberg Date: Mon, 11 Dec 2023 13:07:07 +0200 Subject: [PATCH 05/94] chore: change texts to use i18n part d#161 (#286) --- src/locale/en.json | 14 ++++++++++- src/locale/he.json | 14 ++++++++++- src/pages/Profile.tsx | 5 ++-- src/pages/components/LineSelector.tsx | 7 +++--- src/pages/components/MapLayers/BusToolTip.tsx | 25 ++++++++++--------- src/pages/components/MinuteSelector.tsx | 5 ++-- src/pages/components/OperatorSelector.tsx | 5 ++-- src/pages/components/RouteSelector.tsx | 6 ++--- src/pages/components/StopSelector.tsx | 6 +++-- src/pages/components/TimeSelector.tsx | 5 ++-- 10 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/locale/en.json b/src/locale/en.json index a5aa7618e..f565208d2 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -96,5 +96,17 @@ "bug_type_feature":"Feature Request", "funding_paragraph": "The Databus project is developed by the Hasadna for public knowledge, by the contributions of volunteers, and based upon", "all_rides_completed": "All/Almost the rides are completed", - "missing_rides": "Of the rides are missing" + "missing_rides": "Of the rides are missing", + "line": "line", + "from": "from", + "destination": "destination", + "velocity": "velocity", + "kmh": "kmh", + "sample_time": "sample time", + "vehicle_ref": "vehicle plate", + "drive_direction": "drive direction", + "bearing": "bearing", + "coords": "coords", + "hide_document": "hide document", + "show_document": "show document" } diff --git a/src/locale/he.json b/src/locale/he.json index 3de57e238..4f1ed3c13 100644 --- a/src/locale/he.json +++ b/src/locale/he.json @@ -97,5 +97,17 @@ "funding_paragraph": "דאטאבוס פותח בסדנא לידע ציבורי, בעבודת מתנדבים, ומבוסס על", "halufa_ride": "נסיעה חלופית מס", "all_rides_completed": "כמעט / כל הנסיעות בוצעו", - "missing_rides": "מהנסיעות חסרות" + "missing_rides": "מהנסיעות חסרות", + "line": "קו", + "from": "מוצא", + "destination": "יעד", + "velocity": "מהירות", + "kmh": "קמ״ש", + "sample_time": "זמן דגימה", + "vehicle_ref": "לוחית רישוי", + "drive_direction": "כיוון נסיעה:", + "bearing": "מעלות", + "coords": "נ.צ.", + "hide_document": "הסתר מידע לגיקים", + "show_document": "הצג מידע לגיקים" } diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index fe0646984..de273ad9e 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -9,7 +9,6 @@ import { Label } from './components/Label' import { NotFound } from './components/NotFound' import { PageContainer } from './components/PageContainer' -import { TEXTS } from 'src/resources/texts' import { useTranslation } from 'react-i18next' // import GapsPage from './GapsPage' // import SingleLineMapPage from './SingleLineMapPage' @@ -39,7 +38,7 @@ const Profile = () => { const GeneralDetailsAboutLine = () => { const { search, setSearch } = useContext(SearchContext) const { operatorId, lineNumber, routes, routeKey } = search - + const { t } = useTranslation() return ( <> @@ -63,7 +62,7 @@ const GeneralDetailsAboutLine = () => { {routes && (routes.length === 0 ? ( - {TEXTS.line_not_found} + {t('line_not_found')} ) : ( { const [value, setValue] = useState(lineNumber) const debouncedSetLineNumber = useCallback(debounce(setLineNumber, 200), [setLineNumber]) + const { t } = useTranslation() useLayoutEffect(() => { setValue(lineNumber) @@ -32,7 +33,7 @@ const LineSelector = ({ lineNumber, setLineNumber }: LineSelectorProps) => { return ( { @@ -43,7 +44,7 @@ const LineSelector = ({ lineNumber, setLineNumber }: LineSelectorProps) => { shrink: true, }} InputProps={{ - placeholder: TEXTS.line_placeholder, + placeholder: t('line_placeholder'), endAdornment: , }} /> diff --git a/src/pages/components/MapLayers/BusToolTip.tsx b/src/pages/components/MapLayers/BusToolTip.tsx index 72d9fdcb5..7d9d121b1 100644 --- a/src/pages/components/MapLayers/BusToolTip.tsx +++ b/src/pages/components/MapLayers/BusToolTip.tsx @@ -6,7 +6,7 @@ import './BusToolTip.scss' import { getSiriRideWithRelated } from 'src/api/siriService' import { SiriRideWithRelatedPydanticModel } from 'open-bus-stride-client/openapi/models/SiriRideWithRelatedPydanticModel' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { Spin } from 'antd' import cn from 'classnames' @@ -16,6 +16,7 @@ export function BusToolTip({ position, icon }: BusToolTipProps) { const [siriRide, setSiriRide] = useState() const [isLoading, setIsLoading] = useState(false) const [showJson, setShowJson] = useState(false) + const { t } = useTranslation() useEffect(() => { setIsLoading(true) @@ -31,32 +32,32 @@ export function BusToolTip({ position, icon }: BusToolTipProps) {
{isLoading ? ( <> - {TEXTS.loading_routes} + {t('loading_routes')} ) : ( <>

- {TEXTS.line} :{siriRide && siriRide!.gtfsRouteRouteShortName} + {t('line')} :{siriRide && siriRide!.gtfsRouteRouteShortName}

bus icon
  • - {TEXTS.from} : + {t('from')} : {siriRide && siriRide!.gtfsRouteRouteLongName?.split('<->')[0]}
  • - {TEXTS.destination} : + {t('destination')} : {siriRide && siriRide!.gtfsRouteRouteLongName?.split('<->')[1]}
  • - {TEXTS.velocity} :{`${position.point?.velocity} ${TEXTS.kmh}`} + {t('velocity')} :{`${position.point?.velocity} ${t('kmh')}`}
  • - {TEXTS.sample_time} : + {t('sample_time')} : {moment(position.point!.recorded_at_time as string, moment.ISO_8601) .tz('Israel') @@ -64,27 +65,27 @@ export function BusToolTip({ position, icon }: BusToolTipProps) {
  • - {TEXTS.vehicle_ref} :{position.point?.siri_ride__vehicle_ref} + {t('vehicle_ref')} :{position.point?.siri_ride__vehicle_ref}
{/*maybe option to add info like this in extend card for now I put this condition */} {window.screen.height > 1100 && ( <>

- {TEXTS.drive_direction} : + {t('drive_direction')} : - ( {position.point?.bearing} {TEXTS.bearing}) + ( {position.point?.bearing} {t('bearing')})

- {TEXTS.coords} :{position.loc.join(' , ')} + {t('coords')} :{position.loc.join(' , ')}

)} )} {showJson && (
diff --git a/src/pages/components/MinuteSelector.tsx b/src/pages/components/MinuteSelector.tsx
index 30c5ac13b..db1fc29bd 100644
--- a/src/pages/components/MinuteSelector.tsx
+++ b/src/pages/components/MinuteSelector.tsx
@@ -1,4 +1,4 @@
-import { TEXTS } from 'src/resources/texts'
+import { useTranslation } from 'react-i18next'
 import { TextField } from '@mui/material'
 import ClearButton from './ClearButton'
 import './Selector.scss'
@@ -10,6 +10,7 @@ type MinuteSelectorProps = {
 }
 
 const MinuteSelector = ({ num, setNum }: MinuteSelectorProps) => {
+  const { t } = useTranslation()
   const handleClearInput = () => {
     setNum(1) // 1 minute this is the wanted default value
   }
@@ -21,7 +22,7 @@ const MinuteSelector = ({ num, setNum }: MinuteSelectorProps) => {
   return (
      setNum(+e.target.value)}
diff --git a/src/pages/components/OperatorSelector.tsx b/src/pages/components/OperatorSelector.tsx
index 4d9b7d279..7324f4735 100644
--- a/src/pages/components/OperatorSelector.tsx
+++ b/src/pages/components/OperatorSelector.tsx
@@ -1,5 +1,5 @@
 import { useEffect, useState } from 'react'
-import { TEXTS } from 'src/resources/texts'
+import { useTranslation } from 'react-i18next'
 import { Operator, RELEVANT_OPERATORS } from 'src/model/operator'
 import { Autocomplete, TextField } from '@mui/material'
 
@@ -14,6 +14,7 @@ const OperatorSelector = ({
   setOperatorId,
   onlyMajorOperators = false,
 }: OperatorSelectorProps) => {
+  const { t } = useTranslation()
   const [operators, setOperators] = useState([])
   useEffect(() => {
     const majorOperatorsIds = ['3', '5', '15', '18', '25']
@@ -37,7 +38,7 @@ const OperatorSelector = ({
       onChange={(e, value) => setOperatorId(value ? value.id : '')}
       id="operator-select"
       options={operators}
-      renderInput={(params) => }
+      renderInput={(params) => }
       getOptionLabel={(option) => option.name}
     />
   )
diff --git a/src/pages/components/RouteSelector.tsx b/src/pages/components/RouteSelector.tsx
index 612b476eb..3e6bcd1d0 100644
--- a/src/pages/components/RouteSelector.tsx
+++ b/src/pages/components/RouteSelector.tsx
@@ -1,4 +1,4 @@
-import { formatted, TEXTS } from 'src/resources/texts'
+import { formatted } from 'src/resources/texts'
 import { BusRoute } from 'src/model/busRoute'
 import { Autocomplete, TextField } from '@mui/material'
 import { useEffect } from 'react'
@@ -12,7 +12,7 @@ type RouteSelectorProps = {
 }
 
 const getRouteTitle = (route: BusRoute, t: TFunction<'translation', undefined>) =>
-  `${route.fromName} ${TEXTS.direction_arrow} ${route.toName}  ${
+  `${route.fromName} ${t('direction_arrow')} ${route.toName}  ${
     route.routeAlternative === '#' || route.routeAlternative === '0'
       ? ''
       : `(${t('halufa_ride')} ${route.routeAlternative})`
@@ -43,7 +43,7 @@ const RouteSelector = ({ routes, routeKey, setRouteKey }: RouteSelectorProps) =>
       id="route-select"
       options={routes}
       renderInput={(params) => (
-        
+        
       )}
       getOptionLabel={(route) => getRouteTitle(route, t)}
     />
diff --git a/src/pages/components/StopSelector.tsx b/src/pages/components/StopSelector.tsx
index 6133d41a6..c6f6ab80b 100644
--- a/src/pages/components/StopSelector.tsx
+++ b/src/pages/components/StopSelector.tsx
@@ -1,6 +1,7 @@
-import { formatted, TEXTS } from 'src/resources/texts'
+import { formatted } from 'src/resources/texts'
 import { BusStop } from 'src/model/busStop'
 import { Autocomplete, TextField } from '@mui/material'
+import { useTranslation } from 'react-i18next'
 
 type StopSelectorProps = {
   stops: BusStop[]
@@ -11,6 +12,7 @@ type StopSelectorProps = {
 const StopSelector = ({ stops, stopKey, setStopKey }: StopSelectorProps) => {
   const valueFinned = stops.find((stop) => stop.key === stopKey)
   const value = valueFinned ? valueFinned : null
+  const { t } = useTranslation()
 
   return (
      {
       id="stop-select"
       options={stops}
       renderInput={(params) => (
-        
+        
       )}
       getOptionLabel={(stop) => stop.name}
     />
diff --git a/src/pages/components/TimeSelector.tsx b/src/pages/components/TimeSelector.tsx
index 4811dc27a..054e0d51d 100644
--- a/src/pages/components/TimeSelector.tsx
+++ b/src/pages/components/TimeSelector.tsx
@@ -1,12 +1,13 @@
-import { TEXTS } from 'src/resources/texts'
+import { useTranslation } from 'react-i18next'
 import { TimePicker, renderTimeViewClock } from '@mui/x-date-pickers'
 import { DataAndTimeSelectorProps } from './utils/dateAndTime'
 
 export function TimeSelector({ time, onChange }: DataAndTimeSelectorProps) {
+  const { t } = useTranslation()
   return (
      onChange(ts!)}
       ampm={false}

From 146b281de106edd949f0a418da0e038ab51dcd6e Mon Sep 17 00:00:00 2001
From: Moshe 
Date: Tue, 12 Dec 2023 15:12:14 +0200
Subject: [PATCH 06/94] feat: improve design of about page (#290)

---
 src/pages/about/About.scss               |  8 ++++
 src/pages/{About.tsx => about/index.tsx} | 47 +++++++++---------------
 src/routes/index.tsx                     |  2 +-
 3 files changed, 27 insertions(+), 30 deletions(-)
 create mode 100644 src/pages/about/About.scss
 rename src/pages/{About.tsx => about/index.tsx} (87%)

diff --git a/src/pages/about/About.scss b/src/pages/about/About.scss
new file mode 100644
index 000000000..69b8ce24e
--- /dev/null
+++ b/src/pages/about/About.scss
@@ -0,0 +1,8 @@
+ul {
+    list-style: none;
+    padding: 0;
+}
+
+li img {
+    width: 1.9em;
+}
\ No newline at end of file
diff --git a/src/pages/About.tsx b/src/pages/about/index.tsx
similarity index 87%
rename from src/pages/About.tsx
rename to src/pages/about/index.tsx
index 96eda97f1..76d430977 100644
--- a/src/pages/About.tsx
+++ b/src/pages/about/index.tsx
@@ -1,11 +1,16 @@
 import styled from 'styled-components'
-import SlackIcon from '../resources/slack-icon.svg'
+import SlackIcon from '../../resources/slack-icon.svg'
 import { useTranslation } from 'react-i18next'
+import Widget from 'src/shared/Widget'
+import { Typography } from 'antd'
 
+import './About.scss'
+const { Title } = Typography
 const About = () => {
   return (
     
       
+ קצת עלינו @@ -21,14 +26,14 @@ const WhatIsWebsite = () => { const { t } = useTranslation() return ( - +

{t('what_is_website')}

{t('what_is_website_paragraph')}

  • {t('planning_information')}
  • {t('performance_information')}
-
+ ) } @@ -36,10 +41,10 @@ const DiscoveredMistake = () => { const { t } = useTranslation() return ( - +

{t('discovered_mistake')}

{t('discovered_mistake_paragraph')}

-
+ ) } @@ -47,7 +52,7 @@ const Privacy = () => { const { t } = useTranslation() return ( - +

{t('privacy')}

באתר מוטמע שירות{' '} @@ -57,7 +62,7 @@ const Privacy = () => { קראו כאן על מדיניות הפרטיות של השירות.

-
+ ) } @@ -65,7 +70,7 @@ const License = () => { const { t } = useTranslation() return ( - +

{t('license')}

כל המידע המוצג באתר מבוסס על נתונים המפורסמים במקורות המידע הממשלתיים. השימוש במידע כפוף ל @@ -73,7 +78,7 @@ const License = () => { של Creative Commons.

-
+ ) } @@ -81,7 +86,7 @@ const Questions = () => { const { t } = useTranslation() return ( - +

{t('questions')}

  • @@ -101,7 +106,7 @@ const Questions = () => {
-
+ ) } @@ -109,7 +114,7 @@ const Funding = () => { const { t } = useTranslation() return ( - +

{t('funding')}

@@ -127,7 +132,7 @@ const Funding = () => { - + ) } @@ -144,20 +149,4 @@ const AboutStyle = styled.div` } ` -const ParagraphStyle = styled.div` - & h2 { - font-size: 1.5em; - } - & p { - font-size: 1.15em; - } - & ul { - list-style: none; - padding: 0; - } - & img { - width: 5%; - } -` - export default About diff --git a/src/routes/index.tsx b/src/routes/index.tsx index be9e64fa2..ffaf117e1 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -7,7 +7,7 @@ const GapsPage = lazy(() => import('../pages/gaps')) const GapsPatternsPage = lazy(() => import('../pages/gapsPatterns')) const RealtimeMapPage = lazy(() => import('../pages/realtimeMap')) const SingleLineMapPage = lazy(() => import('../pages/singleLineMap')) -const About = lazy(() => import('../pages/About')) +const About = lazy(() => import('../pages/about')) const Profile = lazy(() => import('../pages/Profile')) const BugReportForm = lazy(() => import('../pages/BugReportForm ')) From 2bbd7054dd55e383e4494b09bacca2a9ab11f554 Mon Sep 17 00:00:00 2001 From: giladfuchs Date: Wed, 13 Dec 2023 18:19:17 +0200 Subject: [PATCH 07/94] add for key at getLocations operatorRef lineRef for getting new time from server --- src/api/useVehicleLocations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/useVehicleLocations.ts b/src/api/useVehicleLocations.ts index 98508c9eb..18c812621 100644 --- a/src/api/useVehicleLocations.ts +++ b/src/api/useVehicleLocations.ts @@ -139,7 +139,7 @@ function getLocations({ operatorRef?: number onUpdate: (locations: VehicleLocation[] | { finished: true }) => void // the observer will be called every time with all the locations that were loaded }) { - const key = `${formatTime(from)}-${formatTime(to)}` + const key = `${formatTime(from)}-${formatTime(to)}-${operatorRef}-${lineRef}` if (!loadedLocations.has(key)) { loadedLocations.set(key, new LocationObservable({ from, to, lineRef, operatorRef })) } From a9caac978ce4cdb03c65fc2189b7e6da03c7e97d Mon Sep 17 00:00:00 2001 From: NoamGaash Date: Thu, 14 Dec 2023 14:32:54 +0200 Subject: [PATCH 08/94] feat: easter language toggle (#289) --- README.md | 17 +- src/App.tsx | 10 +- src/layout/sidebar/menu/Menu.tsx | 11 +- src/locale/en.json | 1 + src/locale/he.json | 1 + src/pages/EasterEgg/EasterEgg.tsx | 240 +------------------------ src/pages/EasterEgg/Envelope.tsx | 233 ++++++++++++++++++++++++ src/pages/EasterEgg/LanguageToggle.tsx | 24 +++ 8 files changed, 289 insertions(+), 248 deletions(-) create mode 100644 src/pages/EasterEgg/Envelope.tsx create mode 100644 src/pages/EasterEgg/LanguageToggle.tsx diff --git a/README.md b/README.md index 506e3d619..6c7cf39ef 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,28 @@ # Open bus ranking app ## Welcome! -This is the official repository of the open bus (תחב"צ פתוחה) project - also known as "ShameBus". +This is the official repository of the open bus (תחב"צ פתוחה / דאטאבוס) project - also known as "ShameBus". [link to the project](https://open-bus-map-search.hasadna.org.il/dashboard) -- While in the site, type "storybook" (in lowercase) to see a secret 😉 Please feel free to submit pull requests and contribute to the project (see the "contribution" section). ## View video (Hebrew language): [![video (hebrew) about the project](https://img.youtube.com/vi/6H6jkJCVhgk/0.jpg)](https://www.youtube.com/watch?v=6H6jkJCVhgk) +# Easter eggs +We've hidden a couple of fun surprises in our web app, just for you. Discovering them is as easy as typing a few magic words on your keyboard. + +## How to Find the Easter Eggs +1. Open our [web app](https://open-bus-map-search.hasadna.org.il/dashboard) +2. **Unleash the Magic Words:** + To reveal the hidden gems, use your keyboard to type the following commands: + + - **Type "storybook":** + Watch the magic unfold as you type "storybook" on your keyboard. You might just stumble upon our Storybook, a treasure trove of UI components showcasing the beauty and functionality of our app. + - **Type "english":** + Feel like switching up the language? Type "english" and see the language toggle in action. Our app is multilingual, and you can experience it by triggering this secret command. + + ## deployments [![Netlify Status](https://api.netlify.com/api/v1/badges/d3ef62c2-b5bb-48ac-8299-71e5bd22b211/deploy-status)](https://app.netlify.com/sites/open-bus/deploys) diff --git a/src/App.tsx b/src/App.tsx index 26bbb1fa6..ad28d96a7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,10 +18,10 @@ import { heIL as heILmui } from '@mui/x-date-pickers/locales' import { ThemeProvider, createTheme } from '@mui/material' import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment' import { LocalizationProvider } from '@mui/x-date-pickers' - -import { usePages } from './routes' import { EasterEgg } from './pages/EasterEgg/EasterEgg' +import { usePages } from './routes' import MainLayout from './layout' +import { Envelope } from './pages/EasterEgg/Envelope' const theme = createTheme( { @@ -105,7 +105,11 @@ const App = () => { const RoutedApp = () => ( - + + + + + ) export default RoutedApp diff --git a/src/layout/sidebar/menu/Menu.tsx b/src/layout/sidebar/menu/Menu.tsx index 83f28f343..d55ce3a73 100644 --- a/src/layout/sidebar/menu/Menu.tsx +++ b/src/layout/sidebar/menu/Menu.tsx @@ -6,6 +6,7 @@ import { usePages } from 'src/routes' import type { MenuProps } from 'antd' import { Menu } from 'antd' +import { LanguageToggle } from 'src/pages/EasterEgg/LanguageToggle' type MenuItem = Required['items'][number] function getItem( @@ -23,18 +24,12 @@ function getItem( } const MainMenu = () => { - const { t, i18n } = useTranslation() + const { t } = useTranslation() const pages = usePages() const items: MenuItem[] = pages.map((itm) => { return getItem({t(itm.label)}, itm.path, itm.icon) }) - const [currentLanguage, setCurrentLanguage] = useState('en') - const handleChangeLanguage = () => { - const newLanguage = currentLanguage === 'en' ? 'he' : 'en' - setCurrentLanguage(newLanguage) - i18n.changeLanguage(newLanguage) - } const location = useLocation() const [current, setCurrent] = useState( location.pathname === '/' || location.pathname === '' ? '/dashboard' : location.pathname, @@ -60,7 +55,7 @@ const MainMenu = () => { mode="inline" items={items} /> - {null && } + {} ) } diff --git a/src/locale/en.json b/src/locale/en.json index f565208d2..6ab7696c8 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -70,6 +70,7 @@ "migdal_company": "\"A tower in the community\"", "and_smaller_donors": "And other small contributions from my friends and fans of the workshop.", "github_link": "Go to GitHub", + "Change Language":"שנה שפה", "bug_title": "Title/Summary", "bug_title_message": "Please enter a title/summary!", "bug_description": "Description", diff --git a/src/locale/he.json b/src/locale/he.json index 4f1ed3c13..70a969540 100644 --- a/src/locale/he.json +++ b/src/locale/he.json @@ -70,6 +70,7 @@ "and_smaller_donors": "ותרומות קטנות נוספות של ידידי ואוהדי הסדנא.", "gaps_patterns_page_title": "דפוסי נסיעות שלא יצאו", "github_link": "למעבר אל GitHub", + "Change Language":"Change Language", "bug_title": "כותרת/סיכום", "bug_title_message": "אנא הזן כותרת/סיכום!", "bug_description": "תיאור", diff --git a/src/pages/EasterEgg/EasterEgg.tsx b/src/pages/EasterEgg/EasterEgg.tsx index 366109a85..8d1ea84b7 100644 --- a/src/pages/EasterEgg/EasterEgg.tsx +++ b/src/pages/EasterEgg/EasterEgg.tsx @@ -1,233 +1,9 @@ -import { useState } from 'react' -import styled from 'styled-components' +import { createContext, useState } from 'react' import useKonami from 'use-konami' -const colors = { - primaryColor300: '#7ec1ff', // the prev color '#e95f55', - primaryColor400: '#1890ff', // the prev color '#e15349' , - primaryColor500: '#317fc8', // the prev color '#cb5a5e', - primaryColor600: '#136fc5', // the prev color '#cf4a43', -} -const EnvelopeWrapper = styled.div` - .letter-image { - position: absolute; - top: 50%; - left: 50%; - width: 200px; - height: 200px; - -webkit-transform: translate(-50%, -50%); - -moz-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - cursor: pointer; - direction: ltr; - z-index: 999; - opacity: 1; - animation: fadeIn 1s ease-in-out; - - &.fade-out { - opacity: 0; - animation: fadeOut 1s ease-in-out; - } - } - - @keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - - @keyframes fadeOut { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } - } - - .animated-mail { - position: absolute; - height: 150px; - width: 200px; - -webkit-transition: 0.4s; - -moz-transition: 0.4s; - transition: 0.4s; - - .body { - position: absolute; - bottom: 0; - width: 0; - height: 0; - border-style: solid; - border-width: 0 0 100px 200px; - border-color: transparent transparent ${colors.primaryColor300} transparent; - z-index: 2; - } - - .top-fold { - position: absolute; - top: 50px; - width: 0; - height: 0; - border-style: solid; - border-width: 50px 100px 0 100px; - -webkit-transform-origin: 50% 0%; - -webkit-transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; - -moz-transform-origin: 50% 0%; - -moz-transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; - transform-origin: 50% 0%; - transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; - border-color: ${colors.primaryColor600} transparent transparent transparent; - z-index: 2; - } - - .back-fold { - position: absolute; - bottom: 0; - width: 200px; - height: 100px; - background: ${colors.primaryColor600}; - z-index: 0; - } - - .left-fold { - position: absolute; - bottom: 0; - width: 0; - height: 0; - border-style: solid; - border-width: 50px 0 50px 100px; - border-color: transparent transparent transparent ${colors.primaryColor400}; - z-index: 2; - } - - .letter { - left: 20px; - bottom: 0px; - position: absolute; - width: 160px; - height: 60px; - background: white; - z-index: 1; - overflow: hidden; - -webkit-transition: 0.4s 0.2s; - -moz-transition: 0.4s 0.2s; - transition: 0.4s 0.2s; - - .letter-border { - height: 10px; - width: 100%; - background: repeating-linear-gradient( - -45deg, - ${colors.primaryColor500}, - ${colors.primaryColor500} 8px, - transparent 8px, - transparent 18px - ); - } - - .letter-title { - margin-top: 10px; - margin-left: 5px; - height: 10px; - width: 40%; - background: ${colors.primaryColor500}; - } - .letter-context { - margin-top: 10px; - margin-left: 5px; - height: 10px; - width: 20%; - background: ${colors.primaryColor500}; - } - .letter-favicon { - display: flex; - justify-content: center; - } - .letter-stamp { - margin-top: 30px; - margin-left: 120px; - border-radius: 100%; - height: 30px; - width: 30px; - background: ${colors.primaryColor500}; - opacity: 0.3; - } - } - } - - .shadow { - position: absolute; - top: 200px; - left: 50%; - width: 400px; - height: 30px; - transition: 0.4s; - transform: translateX(-50%); - -webkit-transition: 0.4s; - -webkit-transform: translateX(-50%); - -moz-transition: 0.4s; - -moz-transform: translateX(-50%); - - border-radius: 100%; - background: radial-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); - } - - .letter-image:hover { - .animated-mail { - transform: translateY(50px); - -webkit-transform: translateY(50px); - -moz-transform: translateY(50px); - } - - .animated-mail .top-fold { - transition: transform 0.4s, z-index 0.2s; - transform: rotateX(180deg); - -webkit-transition: transform 0.4s, z-index 0.2s; - -webkit-transform: rotateX(180deg); - -moz-transition: transform 0.4s, z-index 0.2s; - -moz-transform: rotateX(180deg); - z-index: 0; - } - - .animated-mail .letter { - height: 180px; - } - - .shadow { - width: 250px; - } - } -` -const Envelope = ({ fade }: { fade: boolean }) => ( - -

-
-
-
-
-
-
-
- busFavicon -
-
-
-
-
-
-
-
-
-
-
- -) +export const FadeContext = createContext(false) -export function EasterEgg() { +export function EasterEgg({ children, code }: { children?: React.ReactNode; code: string }) { const [show, setShow] = useState(false) const [fade, setFade] = useState(false) useKonami({ @@ -241,13 +17,7 @@ export function EasterEgg() { setFade(false) }, 10000) }, - sequence: 'storybook'.split(''), + sequence: code.split(''), }) - return ( - show && ( - - - - ) - ) + return show && {children} } diff --git a/src/pages/EasterEgg/Envelope.tsx b/src/pages/EasterEgg/Envelope.tsx new file mode 100644 index 000000000..dca0f799b --- /dev/null +++ b/src/pages/EasterEgg/Envelope.tsx @@ -0,0 +1,233 @@ +import { useContext } from 'react' +import styled from 'styled-components' +import { FadeContext } from './EasterEgg' + +const colors = { + primaryColor300: '#7ec1ff', + primaryColor400: '#1890ff', + primaryColor500: '#317fc8', + primaryColor600: '#136fc5', +} + +const EnvelopeWrapper = styled.div` + .letter-image { + position: absolute; + top: 50%; + left: 50%; + width: 200px; + height: 200px; + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + cursor: pointer; + direction: ltr; + z-index: 999; + opacity: 1; + animation: fadeIn 1s ease-in-out; + + &.fade-out { + opacity: 0; + animation: fadeOut 1s ease-in-out; + } + } + + @keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + @keyframes fadeOut { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + + .animated-mail { + position: absolute; + height: 150px; + width: 200px; + -webkit-transition: 0.4s; + -moz-transition: 0.4s; + transition: 0.4s; + + .body { + position: absolute; + bottom: 0; + width: 0; + height: 0; + border-style: solid; + border-width: 0 0 100px 200px; + border-color: transparent transparent ${colors.primaryColor300} transparent; + z-index: 2; + } + + .top-fold { + position: absolute; + top: 50px; + width: 0; + height: 0; + border-style: solid; + border-width: 50px 100px 0 100px; + -webkit-transform-origin: 50% 0%; + -webkit-transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; + -moz-transform-origin: 50% 0%; + -moz-transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; + transform-origin: 50% 0%; + transition: transform 0.4s 0.4s, z-index 0.2s 0.4s; + border-color: ${colors.primaryColor600} transparent transparent transparent; + z-index: 2; + } + + .back-fold { + position: absolute; + bottom: 0; + width: 200px; + height: 100px; + background: ${colors.primaryColor600}; + z-index: 0; + } + + .left-fold { + position: absolute; + bottom: 0; + width: 0; + height: 0; + border-style: solid; + border-width: 50px 0 50px 100px; + border-color: transparent transparent transparent ${colors.primaryColor400}; + z-index: 2; + } + + .letter { + left: 20px; + bottom: 0px; + position: absolute; + width: 160px; + height: 60px; + background: white; + z-index: 1; + overflow: hidden; + -webkit-transition: 0.4s 0.2s; + -moz-transition: 0.4s 0.2s; + transition: 0.4s 0.2s; + + .letter-border { + height: 10px; + width: 100%; + background: repeating-linear-gradient( + -45deg, + ${colors.primaryColor500}, + ${colors.primaryColor500} 8px, + transparent 8px, + transparent 18px + ); + } + + .letter-title { + margin-top: 10px; + margin-left: 5px; + height: 10px; + width: 40%; + background: ${colors.primaryColor500}; + } + .letter-context { + margin-top: 10px; + margin-left: 5px; + height: 10px; + width: 20%; + background: ${colors.primaryColor500}; + } + .letter-favicon { + display: flex; + justify-content: center; + } + .letter-stamp { + margin-top: 30px; + margin-left: 120px; + border-radius: 100%; + height: 30px; + width: 30px; + background: ${colors.primaryColor500}; + opacity: 0.3; + } + } + } + + .shadow { + position: absolute; + top: 200px; + left: 50%; + width: 400px; + height: 30px; + transition: 0.4s; + transform: translateX(-50%); + -webkit-transition: 0.4s; + -webkit-transform: translateX(-50%); + -moz-transition: 0.4s; + -moz-transform: translateX(-50%); + + border-radius: 100%; + background: radial-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); + } + + .letter-image:hover { + .animated-mail { + transform: translateY(50px); + -webkit-transform: translateY(50px); + -moz-transform: translateY(50px); + } + + .animated-mail .top-fold { + transition: transform 0.4s, z-index 0.2s; + transform: rotateX(180deg); + -webkit-transition: transform 0.4s, z-index 0.2s; + -webkit-transform: rotateX(180deg); + -moz-transition: transform 0.4s, z-index 0.2s; + -moz-transform: rotateX(180deg); + z-index: 0; + } + + .animated-mail .letter { + height: 180px; + } + + .shadow { + width: 250px; + } + } +` + +export const Envelope = () => { + const fade = useContext(FadeContext) + return ( + +
+
+
+
+
+
+
+
+ busFavicon +
+
+
+
+
+
+
+
+
+
+
+
+ ) +} diff --git a/src/pages/EasterEgg/LanguageToggle.tsx b/src/pages/EasterEgg/LanguageToggle.tsx new file mode 100644 index 000000000..a629dc9b5 --- /dev/null +++ b/src/pages/EasterEgg/LanguageToggle.tsx @@ -0,0 +1,24 @@ +import { useReducer } from 'react' +import { useTranslation } from 'react-i18next' +import { EasterEgg } from './EasterEgg' +import { Button } from '@mui/material' + +export const LanguageToggle = () => { + const { t, i18n } = useTranslation() + const [, handleChangeLanguage] = useReducer((state: string) => { + const newLanguage = { he: 'en', en: 'he' }[state] + i18n.changeLanguage(newLanguage) + return newLanguage! + }, 'he') + + return ( + + + + ) +} From 5d3dc4c2e7de5b7f5eaebc69f749f67f4d7626cc Mon Sep 17 00:00:00 2001 From: eyalY <106626717+eyalyehia@users.noreply.github.com> Date: Thu, 14 Dec 2023 21:04:09 +0200 Subject: [PATCH 09/94] #232 (#298) --- .../HbarChart/HbarChart.stories.tsx | 52 +++++++++++++++++++ .../LineHbarChart/HbarChart/HbarChart.tsx | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.stories.tsx diff --git a/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.stories.tsx b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.stories.tsx new file mode 100644 index 000000000..14d3a2363 --- /dev/null +++ b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { HbarChart, Entry } from './HbarChart' + +const meta = { + title: 'Components/MapLayers/HbarChart', + component: HbarChart, + tags: ['map', 'tooltip', 'autodocs'], +} satisfies Meta + +export default meta + +type Story = StoryObj + +const operatorsPT: Entry[] = [ + { + name: 'כפיר', + total: 1883, + actual: 0, + color: '#5840c0', + }, + { + name: 'Unknown', + total: 2318, + actual: 0, + color: '#2a443e', + }, + { + name: 'כרמלית', + total: 998, + actual: 0, + color: '#5cbcec', + }, + { + name: 'כבל אקספרס', + total: 12824, + actual: 0, + color: '#0d2b58', + }, + { + name: 'מועצה אזורית גולן', + total: 1992, + actual: 1506, + color: '#957476', + }, +] + +export const Chart: Story = { + args: { + entries: operatorsPT, + complement: false, + }, +} diff --git a/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx index 54e71a35f..b283a22f0 100644 --- a/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx +++ b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx @@ -3,7 +3,7 @@ import { TEXTS } from 'src/resources/texts' import './HbarChart.scss' import { Tooltip } from '@mui/material' -type Entry = { name: string; total: number; actual: number; color?: string } +export type Entry = { name: string; total: number; actual: number; color?: string } const numberFormatter = new Intl.NumberFormat('he-IL') export function HbarChart({ From 9393b6f2648e042bda3a79d0e4d4b155e5ffd017 Mon Sep 17 00:00:00 2001 From: NoamGaash Date: Thu, 14 Dec 2023 22:28:36 +0200 Subject: [PATCH 10/94] refactor: use modern routing + get some data for line profile page (#291) --- src/App.tsx | 118 ++-------------------- src/layout/index.tsx | 19 +++- src/layout/sidebar/SideBar.tsx | 5 +- src/layout/sidebar/menu/Menu.tsx | 5 +- src/locale/he.json | 6 +- src/pages/Profile.tsx | 92 +++++------------ src/routes/MainRoute.tsx | 99 ++++++++++++++++++ src/routes/index.tsx | 167 ++++++++++++++++--------------- 8 files changed, 246 insertions(+), 265 deletions(-) create mode 100644 src/routes/MainRoute.tsx diff --git a/src/App.tsx b/src/App.tsx index ad28d96a7..2ba787bd1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,115 +1,15 @@ -import { useCallback, useEffect } from 'react' import 'antd/dist/antd.min.css' import './App.scss' -import { ConfigProvider } from 'antd' import 'leaflet/dist/leaflet.css' -import heIL from 'antd/es/locale/he_IL' -import { BrowserRouter as Router, useSearchParams } from 'react-router-dom' -import { PageSearchState, SearchContext } from './model/pageState' -import moment from 'moment' -import { useSessionStorage } from 'usehooks-ts' -import { useLocation } from 'react-router-dom' -import ReactGA from 'react-ga4' -import { CacheProvider } from '@emotion/react' -import createCache from '@emotion/cache' -import rtlPlugin from 'stylis-plugin-rtl' import 'moment/locale/he' -import { heIL as heILmui } from '@mui/x-date-pickers/locales' -import { ThemeProvider, createTheme } from '@mui/material' -import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment' -import { LocalizationProvider } from '@mui/x-date-pickers' -import { EasterEgg } from './pages/EasterEgg/EasterEgg' -import { usePages } from './routes' -import MainLayout from './layout' -import { Envelope } from './pages/EasterEgg/Envelope' - -const theme = createTheme( - { - direction: 'rtl', - palette: { - primary: { - main: '#5f5bff', - }, - }, - }, - heILmui, -) - -// Create rtl cache -const cacheRtl = createCache({ - key: 'muirtl', - stylisPlugins: [rtlPlugin], -}) - -const App = () => { - const location = useLocation() - - const [searchParams, setSearchParams] = useSearchParams() - const operatorId = searchParams.get('operatorId') - const lineNumber = searchParams.get('lineNumber') - const routeKey = searchParams.get('routeKey') - const timestamp = searchParams.get('timestamp') - const pages = usePages() - - useEffect(() => { - ReactGA.send({ hitType: 'pageview', page: location.pathname + location.search }) - }, [location]) - - const [search, setSearch] = useSessionStorage('search', { - timestamp: +timestamp! || moment().valueOf(), - operatorId: operatorId || '', - lineNumber: lineNumber || '', - routeKey: routeKey || '', - }) - - useEffect(() => { - const page = pages.find((page) => page.path === location.pathname) - if (page?.searchParamsRequired) { - const params = new URLSearchParams({ timestamp: search.timestamp.toString() }) - - if (search.operatorId) { - params.set('operatorId', search.operatorId) - } - if (search.lineNumber) { - params.set('lineNumber', search.lineNumber) - } - if (search.routeKey) { - params.set('routeKey', search.routeKey) - } - setSearchParams(params) - } - }, [search.lineNumber, search.operatorId, search.routeKey, search.timestamp, location.pathname]) - - const safeSetSearch = useCallback((mutate: (prevState: PageSearchState) => PageSearchState) => { - setSearch((current: PageSearchState) => { - const newSearch = mutate(current) - return newSearch - }) - }, []) - - return ( - - - - - - - - - - - - ) -} - -const RoutedApp = () => ( - - - - - - - - +import router from './routes' +import { RouterProvider } from 'react-router-dom' +import { Suspense } from 'react' +import Preloader from './shared/Preloader' + +export const RoutedApp = () => ( + }> + + ) export default RoutedApp diff --git a/src/layout/index.tsx b/src/layout/index.tsx index f6556670b..9ce785664 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -3,7 +3,11 @@ import MainHeader from './header/Header' import SideBar from './sidebar/SideBar' import styled from 'styled-components' import LayoutContext from './LayoutContext' -import RoutesList from '../routes' +import { Outlet } from 'react-router-dom' +import { Suspense } from 'react' +import Preloader from 'src/shared/Preloader' +import { EasterEgg } from 'src/pages/EasterEgg/EasterEgg' +import { Envelope } from 'src/pages/EasterEgg/Envelope' const { Content } = Layout @@ -19,7 +23,7 @@ const StyledBody = styled.div` min-height: 360px; ` -function MainLayout() { +export function MainLayout() { return ( @@ -28,7 +32,14 @@ function MainLayout() { - + }> + + + + + + + @@ -36,5 +47,3 @@ function MainLayout() { ) } - -export default MainLayout diff --git a/src/layout/sidebar/SideBar.tsx b/src/layout/sidebar/SideBar.tsx index 5d2a30715..4486ccff6 100644 --- a/src/layout/sidebar/SideBar.tsx +++ b/src/layout/sidebar/SideBar.tsx @@ -5,7 +5,7 @@ import { useContext, useState } from 'react' import { LayoutContextInterface, LayoutCtx } from '../LayoutContext' import GitHubLink from './GitHubLink/GitHubLink' import { Link } from 'react-router-dom' -import { usePages } from 'src/routes' +import { PAGES } from 'src/routes' const { Sider } = Layout const Logo = () => ( @@ -18,7 +18,6 @@ const CollapsedLogo = () =>

🚌

export default function SideBar() { const { drawerOpen, setDrawerOpen } = useContext(LayoutCtx) const [collapsed, setCollapsed] = useState(false) - const pages = usePages() return ( <> setCollapsed(value)} className="hideOnMobile"> - + {collapsed ? : }
diff --git a/src/layout/sidebar/menu/Menu.tsx b/src/layout/sidebar/menu/Menu.tsx index d55ce3a73..4dbbe2f17 100644 --- a/src/layout/sidebar/menu/Menu.tsx +++ b/src/layout/sidebar/menu/Menu.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react' import { Link, useLocation } from 'react-router-dom' import './menu.scss' import { useTranslation } from 'react-i18next' -import { usePages } from 'src/routes' +import { PAGES } from 'src/routes' import type { MenuProps } from 'antd' import { Menu } from 'antd' @@ -25,8 +25,7 @@ function getItem( const MainMenu = () => { const { t } = useTranslation() - const pages = usePages() - const items: MenuItem[] = pages.map((itm) => { + const items: MenuItem[] = PAGES.map((itm) => { return getItem({t(itm.label)}, itm.path, itm.icon) }) diff --git a/src/locale/he.json b/src/locale/he.json index 70a969540..221a83f3d 100644 --- a/src/locale/he.json +++ b/src/locale/he.json @@ -110,5 +110,9 @@ "bearing": "מעלות", "coords": "נ.צ.", "hide_document": "הסתר מידע לגיקים", - "show_document": "הצג מידע לגיקים" + "show_document": "הצג מידע לגיקים", + "lineProfile": { + "title": "פרופיל קו", + "notFound":"לא הצלחנו למצוא את הקו שחיפשת :(" + } } diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index de273ad9e..95e1c9953 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -1,31 +1,15 @@ import styled from 'styled-components' -import React from 'react' -// import { useContext, useState } from 'react' -import { useContext } from 'react' import Grid from '@mui/material/Unstable_Grid2' // Grid version 2 -import moment from 'moment' import { Label } from './components/Label' import { NotFound } from './components/NotFound' import { PageContainer } from './components/PageContainer' import { useTranslation } from 'react-i18next' -// import GapsPage from './GapsPage' -// import SingleLineMapPage from './SingleLineMapPage' -import { PageSearchState, SearchContext } from '../model/pageState' -import LineNumberSelector from './components/LineSelector' -import OperatorSelector from './components/OperatorSelector' -import RouteSelector from './components/RouteSelector' //API -// import { /*getGtfsRidesList,*/ getRidesAsync } from 'src/api/profileService' -import { getRoutesAsync } from '../api/gtfsService' import Widget from 'src/shared/Widget' - -// time inputs -// import { DateSelector } from './components/DateSelector' -// import { TimeSelector } from './components/TimeSelector' -// import { useDate } from './components/DateTimePicker' +import { useLoaderData } from 'react-router-dom' const Profile = () => { return ( @@ -36,71 +20,47 @@ const Profile = () => { } const GeneralDetailsAboutLine = () => { - const { search, setSearch } = useContext(SearchContext) - const { operatorId, lineNumber, routes, routeKey } = search - const { t } = useTranslation() return ( <> - {/* choose operator */} - - setSearch((current) => ({ ...current, operatorId: id }))} - /> - - - {/* choose line number */} - - setSearch((current) => ({ ...current, lineNumber: number }))} - /> - - - {/* choose route*/} - - {routes && - (routes.length === 0 ? ( - {t('line_not_found')} - ) : ( - setSearch((current) => ({ ...current, routeKey: key }))} - /> - ))} - - - + ) } -const LineProfileComponent = ({ search }: { search: PageSearchState }) => { +const LineProfileComponent = () => { const { t } = useTranslation() - - // const resp = getRidesAsync(search.operatorId, search.lineNumber, search.routeKey, new Date()) - // const resp = getGtfsRidesList(new Date(), '3', '271', '1') - const resp = getRoutesAsync( - moment(search.timestamp), - moment(search.timestamp), - search.operatorId, - search.lineNumber, - ) + const route = useLoaderData() as { + // TODO: find better type definition + agency_name: string + route_short_name: string + route_long_name: string + message?: string + } + console.log('route', route) + + if (route.message) + return ( + + +

{t('lineProfile.notFound')}

+
{route.message}
+
+
+ ) return ( -

{t('profile_page')}

- +

{t('lineProfile.title')}

+

- +

- +
-
{resp.toString()}
+
{JSON.stringify(route, null, 2)}