diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 00000000..fd1adc4a --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,20 @@ +name: 'Lint PR' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 00000000..6c0e13a1 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,106 @@ +name: Preview +on: + pull_request: + +env: + ASSET_URL: https://s3.amazonaws.com/noam-gaash.co.il/${{ github.run_id }}/open-bus/${{ github.sha }} + should_run: ${{ secrets.AWS_SECRET_ACCESS_KEY}} +jobs: + should_run: + runs-on: ubuntu-latest + outputs: + should_run: ${{ steps.set_should_run.outputs.should_run }} + steps: + - name: Set should_run + id: set_should_run + if: env.should_run + run: echo "::set-output name=should_run::true" + + build: + runs-on: ubuntu-latest + needs: [should_run] + if: ${{ needs.should_run.outputs.should_run == 'true' }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'yarn' + - name: Run install + uses: borales/actions-yarn@v4 + with: + cmd: install + - name: Build + run: yarn build + - name: upload artifact + uses: actions/upload-artifact@v2 + with: + name: dist + path: dist + build-storybook: + runs-on: ubuntu-latest + needs: [should_run] + if: ${{ needs.should_run.outputs.should_run == 'true' }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'yarn' + - name: Run install + uses: borales/actions-yarn@v4 + with: + cmd: install + - name: Build Storybook + run: yarn build-storybook -o dist/storybook + - name: upload artifact + uses: actions/upload-artifact@v2 + with: + name: dist + path: dist + deploy-to-s3: + needs: [build, build-storybook] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Download artifact + uses: actions/download-artifact@v2 + with: + name: dist + path: dist + - uses: shallwefootball/s3-upload-action@master + name: Upload trace to S3 + if: always() + id: s3-trace + continue-on-error: true + with: + aws_key_id: ${{ secrets.AWS_KEY_ID }} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}} + aws_bucket: noam-gaash.co.il + source_dir: dist + destination_dir: ${{ github.run_id }}/open-bus/${{ github.sha }} + - name: find comment + uses: peter-evans/find-comment@v1 + if: github.event_name == 'pull_request' + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'Preview' + - name: update comment + uses: peter-evans/create-or-update-comment@v1 + if: steps.fc.outputs.comment-id + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace + body: | + Preview: https://s3.amazonaws.com/noam-gaash.co.il/${{ github.run_id }}/open-bus/${{ github.sha }}/index.html + Preview Storybook: https://s3.amazonaws.com/noam-gaash.co.il/${{ github.run_id }}/open-bus/${{ github.sha }}/storybook/index.html + - name: create comment + uses: peter-evans/create-or-update-comment@v1 + if: steps.fc.outputs.comment-id == '' + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + Preview: https://s3.amazonaws.com/noam-gaash.co.il/${{ github.run_id }}/open-bus/${{ github.sha }}/index.html + Preview Storybook: https://s3.amazonaws.com/noam-gaash.co.il/${{ github.run_id }}/open-bus/${{ github.sha }}/storybook/index.html diff --git a/README.md b/README.md index 6c7cf39e..21e77725 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ We've hidden a couple of fun surprises in our web app, just for you. Discovering 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. + - **Type "geek":** + To get some experimental charts with some additional data and aggregation ## deployments diff --git a/package.json b/package.json index 17e34685..440335ca 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@mui/x-date-pickers": "^6.12.1", "@types/leaflet.markercluster": "^1.5.2", "@vitejs/plugin-react-swc": "^3.4.0", - "antd": "^4.22.5", + "antd": "^5.12.2", "axios": "^1.6.0", "classnames": "^2.3.2", "dayjs": "^1.11.9", diff --git a/src/App.tsx b/src/App.tsx index 2ba787bd..dae52c0b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,3 @@ -import 'antd/dist/antd.min.css' import './App.scss' import 'leaflet/dist/leaflet.css' import 'moment/locale/he' diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 9ce78566..81336db7 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -3,7 +3,7 @@ import MainHeader from './header/Header' import SideBar from './sidebar/SideBar' import styled from 'styled-components' import LayoutContext from './LayoutContext' -import { Outlet } from 'react-router-dom' +import { Link, Outlet } from 'react-router-dom' import { Suspense } from 'react' import Preloader from 'src/shared/Preloader' import { EasterEgg } from 'src/pages/EasterEgg/EasterEgg' @@ -39,6 +39,11 @@ export function MainLayout() { + + + + + diff --git a/src/locale/en.json b/src/locale/en.json index 6ab7696c..7811470a 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -43,12 +43,13 @@ "from_date": "dated", "to_date": "Until", "watch_locations_in_range": "View bus locations within a range of", - "minutes": "דקות", - "minutes_5": "5 דקות", + "minutes": "minutes", + "minutes_5": "5 minutes", "show_x_bus_locations": "Bus locations", "from_time_x_to_time_y": "From XXX to YYY", "choose_start_time": "Select a start time", - "group_by_hour_tooltip_content": "Kibbutz by hour", + "loading_times_tooltip_content": "Loading bus times. Current times available to use", + "group_by_hour_tooltip_content": "Group by hour", "start": "start", "end": "end", "about_title": "About", @@ -109,5 +110,11 @@ "bearing": "bearing", "coords": "coords", "hide_document": "hide document", - "show_document": "show document" + "show_document": "show document", + "dashboard_page_graph_title_hour": "Percentage of rides out, by hour", + "dashboard_page_graph_title_day": "Percentage of rides out, by day", + "group_by_day_tooltip_content": "Group by day", + "order_by_hour": "by hour", + "order_by_severity": "by severity", + "choose_dates": "Dates" } diff --git a/src/locale/he.json b/src/locale/he.json index 221a83f3..5a5cbf23 100644 --- a/src/locale/he.json +++ b/src/locale/he.json @@ -47,6 +47,7 @@ "show_x_bus_locations": "מיקומי אוטובוסים", "from_time_x_to_time_y": "משעה XXX עד שעה YYY", "choose_start_time": "בחירת שעת התחלה", + "loading_times_tooltip_content": "שעות נוספות בטעינה. ניתן להשתמש בשעות הזמינות כבר עכשיו", "group_by_hour_tooltip_content": "קיבוץ לפי שעה", "start": "התחלה", "end": "סיום", @@ -114,5 +115,17 @@ "lineProfile": { "title": "פרופיל קו", "notFound":"לא הצלחנו למצוא את הקו שחיפשת :(" - } + }, + "errorPage": { + "title": "אופס, משהו השתבש", + "text": "אנא נסה שוב מאוחר יותר", + "text2": "אם הבעיה נמשכת, אנא פנה אלינו בכתובת:", + "button": "חזרה לדף הבית" + }, + "dashboard_page_graph_title_hour": "אחוזי יציאה מסך הנסיעות לפי שעה", + "dashboard_page_graph_title_day": "אחוזי יציאה מסך הנסיעות לפי יום", + "group_by_day_tooltip_content": "קיבוץ לפי יום", + "order_by_hour": "לפי שעה", + "order_by_severity": "לפי חומרה", + "choose_dates": "תאריכים" } diff --git a/src/locale/utils.ts b/src/locale/utils.ts new file mode 100644 index 00000000..be550b65 --- /dev/null +++ b/src/locale/utils.ts @@ -0,0 +1,2 @@ +const PLACEHOLDER = 'XXX' +export const formatted = (text: string, value: string) => text.replace(PLACEHOLDER, value) diff --git a/src/pages/DataResearch/DataResearch.scss b/src/pages/DataResearch/DataResearch.scss new file mode 100644 index 00000000..5e970cb4 --- /dev/null +++ b/src/pages/DataResearch/DataResearch.scss @@ -0,0 +1,3 @@ +.recharts-tooltip-wrapper { + z-index: 1; +} diff --git a/src/pages/DataResearch/DataResearch.tsx b/src/pages/DataResearch/DataResearch.tsx new file mode 100644 index 00000000..5e545b37 --- /dev/null +++ b/src/pages/DataResearch/DataResearch.tsx @@ -0,0 +1,229 @@ +import React, { useMemo } from 'react' +import { useGroupBy } from 'src/api/groupByService' +import Widget from 'src/shared/Widget' +import { useDate } from '../components/DateTimePicker' +import moment from 'moment' +import { Grid } from '@mui/material' +import { DateSelector } from '../components/DateSelector' +import { useTranslation } from 'react-i18next' +import { Skeleton } from 'antd' +import { + Area, + Tooltip, + AreaChart, + CartesianGrid, + ResponsiveContainer, + XAxis, + YAxis, +} from 'recharts' +import { PageContainer } from '../components/PageContainer' +import { getColorName } from '../dashboard/AllLineschart/OperatorHbarChart/OperatorHbarChart' + +import './DataResearch.scss' + +const now = moment() +const unique: (value: string, index: number, self: string[]) => boolean = (value, index, self) => + self.indexOf(value) === index + +export const DataResearch = () => { + return ( + + +

מחקרים

+

אם יש לכם רעיון מעניין למה קורים פה דברים, דברו איתנו בסלאק!

+
+ +
+ ) +} + +function StackedResearchSection() { + const [startDate, setStartDate] = useDate(now.clone().subtract(7, 'days')) + const [endDate, setEndDate] = useDate(now.clone().subtract(1, 'day')) + const [groupByHour, setGroupByHour] = React.useState(false) + const [graphData, loadingGraph] = useGroupBy({ + dateTo: endDate, + dateFrom: startDate, + groupBy: groupByHour ? 'operator_ref,gtfs_route_hour' : 'operator_ref,gtfs_route_date', + }) + + return ( + +

בעיות etl/gps/משהו גלובאלי אחר

+ + + + +
+ ) +} + +function StackedResearchInputs({ + startDate, + setStartDate, + endDate, + setEndDate, + groupByHour, + setGroupByHour, +}: { + startDate: moment.Moment + setStartDate: (date: moment.Moment) => void + endDate: moment.Moment + setEndDate: (date: moment.Moment) => void + groupByHour: boolean + setGroupByHour: (value: boolean) => void +}) { + const { t } = useTranslation() + return ( + <> + + + setStartDate(data)} + customLabel={t('start')} + /> + + + setEndDate(data)} + customLabel={t('end')} + /> + + + + + ) +} + +const StackedResearchChart = ({ + graphData, + isLoading, + title, + description, + field = 'total_actual_rides', +}: { + graphData: { + gtfs_route_date: string + gtfs_route_hour: string + operator_ref?: { + agency_name?: string + } + total_actual_rides: number + total_planned_rides: number + }[] + isLoading?: boolean + title?: string + description?: string + field?: 'total_actual_rides' | 'total_planned_rides' | 'total_missed_rides' +}) => { + const data = useMemo( + () => + graphData + .reduce((acc, curr) => { + const val = + field === 'total_missed_rides' + ? curr.total_planned_rides - curr.total_actual_rides + : curr[field] + const date = curr.gtfs_route_date ?? curr.gtfs_route_hour + const entry = acc.find((item) => item.date === date) + if (entry) { + if (val) entry[curr.operator_ref?.agency_name || 'Unknown'] = val + } else { + const newEntry = { + date: date, + [curr.operator_ref?.agency_name || 'Unknown']: val, + } + acc.push(newEntry) + } + return acc + }, [] as Record[]) + .sort((a, b) => { + if (a.date > b.date) return 1 + if (a.date < b.date) return -1 + return 0 + }), + [graphData], + ) + + const operators = graphData + .map((operator) => operator.operator_ref?.agency_name || 'Unknown') + .filter(unique) + + return ( + <> + {title &&

{title}

} + {description && ( +

+ מה רואים בגרף? +
+ {description} +

+ )} + {isLoading ? ( + + ) : ( + + + + + + {operators.map((operator) => ( + + ))} + + + + )} + + ) +} diff --git a/src/pages/ErrorPage.tsx b/src/pages/ErrorPage.tsx new file mode 100644 index 00000000..8b37566d --- /dev/null +++ b/src/pages/ErrorPage.tsx @@ -0,0 +1,27 @@ +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +const Centered = styled.div` + text-align: center; +` + +export const ErrorPage = () => { + const { t } = useTranslation() + return ( + +

{t('errorPage.title')}

+
+

{t('errorPage.text')}

+
+ error +
+

+ {t('errorPage.text2')}
+ + https://github.com/hasadna/open-bus-map-search/issues/new + +

+
+
+ ) +} diff --git a/src/pages/components/RouteSelector.tsx b/src/pages/components/RouteSelector.tsx index 3e6bcd1d..258c97ab 100644 --- a/src/pages/components/RouteSelector.tsx +++ b/src/pages/components/RouteSelector.tsx @@ -1,4 +1,4 @@ -import { formatted } from 'src/resources/texts' +import { formatted } from 'src/locale/utils' import { BusRoute } from 'src/model/busRoute' import { Autocomplete, TextField } from '@mui/material' import { useEffect } from 'react' diff --git a/src/pages/components/StopSelector.tsx b/src/pages/components/StopSelector.tsx index c6f6ab80..af89ec16 100644 --- a/src/pages/components/StopSelector.tsx +++ b/src/pages/components/StopSelector.tsx @@ -1,4 +1,4 @@ -import { formatted } from 'src/resources/texts' +import { formatted } from 'src/locale/utils' import { BusStop } from 'src/model/busStop' import { Autocomplete, TextField } from '@mui/material' import { useTranslation } from 'react-i18next' diff --git a/src/pages/dashboard/AllLineschart/AllLinesChart.tsx b/src/pages/dashboard/AllLineschart/AllLinesChart.tsx index c669894d..09af2960 100644 --- a/src/pages/dashboard/AllLineschart/AllLinesChart.tsx +++ b/src/pages/dashboard/AllLineschart/AllLinesChart.tsx @@ -1,7 +1,7 @@ import { Tooltip } from '@mui/material' import { Skeleton } from 'antd' import { Fragment } from 'react' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import OperatorHbarChart from './OperatorHbarChart/OperatorHbarChart' import { GroupByRes, useGroupBy } from 'src/api/groupByService' import { FC } from 'react' @@ -28,13 +28,13 @@ export const AllLinesChart: FC = ({ startDate, endDate } dateFrom: startDate, groupBy: 'operator_ref', }) - + const { t } = useTranslation() return (

- {TEXTS.dashboard_page_title} + {t('dashboard_page_title')} i diff --git a/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx b/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx index 483b15ac..5d4d6a15 100644 --- a/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx +++ b/src/pages/dashboard/ArrivalByTimeChart/DayTimeChart.tsx @@ -1,5 +1,5 @@ import React, { FC } from 'react' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { Skeleton, Radio, RadioChangeEvent } from 'antd' import ArrivalByTimeChart from './ArrivalByTimeChart' import { GroupByRes, useGroupBy } from 'src/api/groupByService' @@ -25,6 +25,7 @@ interface DayTimeChartProps { } const DayTimeChart: FC = ({ startDate, endDate, operatorId }) => { + const { t } = useTranslation() const [groupByHour, setGroupByHour] = React.useState(false) const [graphData, loadingGraph] = useGroupBy({ @@ -36,14 +37,14 @@ const DayTimeChart: FC = ({ startDate, endDate, operatorId }) return (

- {groupByHour ? TEXTS.dashboard_page_graph_title_hour : TEXTS.dashboard_page_graph_title_day} + {groupByHour ? t('dashboard_page_graph_title_hour') : t('dashboard_page_graph_title_day')}

setGroupByHour(e.target.value === 'byHour')} defaultValue="byDay"> - {TEXTS.group_by_day_tooltip_content} - {TEXTS.group_by_hour_tooltip_content} + {t('group_by_day_tooltip_content')} + {t('group_by_hour_tooltip_content')} {loadingGraph ? ( diff --git a/src/pages/dashboard/DashboardPage.tsx b/src/pages/dashboard/DashboardPage.tsx index 2c4d1ba9..03c32582 100644 --- a/src/pages/dashboard/DashboardPage.tsx +++ b/src/pages/dashboard/DashboardPage.tsx @@ -8,7 +8,7 @@ import moment from 'moment' // Styling import './DashboardPage.scss' import { PageContainer } from '../components/PageContainer' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { Alert, Typography } from 'antd' import Grid from '@mui/material/Unstable_Grid2' // Grid version 2 @@ -26,6 +26,7 @@ const DashboardPage = () => { const [startDate, setStartDate] = useDate(now.clone().subtract(7, 'days')) const [endDate, setEndDate] = useDate(now.clone().subtract(1, 'day')) const [operatorId, setOperatorId] = useState('') + const { t } = useTranslation() return ( @@ -42,14 +43,14 @@ const DashboardPage = () => { setStartDate(data)} - customLabel={TEXTS.start} + customLabel={t('start')} /> setEndDate(data)} - customLabel={TEXTS.end} + customLabel={t('end')} /> diff --git a/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx index b283a22f..47f5e85c 100644 --- a/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx +++ b/src/pages/dashboard/WorstLinesChart/LineHbarChart/HbarChart/HbarChart.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import './HbarChart.scss' import { Tooltip } from '@mui/material' @@ -13,6 +13,7 @@ export function HbarChart({ entries: Entry[] complement?: boolean }) { + const { t } = useTranslation() const percents = entries .map((o) => (o.actual / o.total) * 100) .map((p) => (complement ? Math.max(100 - p, 0) : p)) @@ -28,9 +29,9 @@ export function HbarChart({ placement={'top'} title={
- {TEXTS.rides_planned}: {numberFormatter.format(entry.total)} + {t('rides_planned')}: {numberFormatter.format(entry.total)}
- {TEXTS.rides_actual}: {numberFormatter.format(entry.actual)} + {t('rides_actual')}: {numberFormatter.format(entry.actual)}
} followCursor={true}> diff --git a/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx b/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx index 55f8e529..50cee322 100644 --- a/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx +++ b/src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx @@ -1,7 +1,7 @@ import { Skeleton } from 'antd' import { GroupByRes, useGroupBy } from 'src/api/groupByService' import LinesHbarChart from './LineHbarChart/LinesHbarChart' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { FC } from 'react' import { Moment } from 'moment/moment' import Widget from 'src/shared/Widget' @@ -19,6 +19,8 @@ export const WorstLinesChart: FC = ({ startDate, endDate, groupBy: 'operator_ref,line_ref', }) + const { t } = useTranslation() + const convertToWorstLineChartCompatibleStruct = (arr: GroupByRes[], operatorId: string) => { if (!arr || !arr.length) return [] return arr @@ -37,7 +39,7 @@ export const WorstLinesChart: FC = ({ startDate, endDate, return ( -

{TEXTS.worst_lines_page_title}

+

{t('worst_lines_page_title')}

{lineDataLoading ? ( ) : ( diff --git a/src/pages/gapsPatterns/GapsPatternsPage.tsx b/src/pages/gapsPatterns/GapsPatternsPage.tsx index 27778c80..8700bdc1 100644 --- a/src/pages/gapsPatterns/GapsPatternsPage.tsx +++ b/src/pages/gapsPatterns/GapsPatternsPage.tsx @@ -7,7 +7,6 @@ import { useDate } from '../components/DateTimePicker' import { PageContainer } from '../components/PageContainer' import { Row } from '../components/Row' import { Label } from '../components/Label' -import { TEXTS } from '../../resources/texts' import OperatorSelector from '../components/OperatorSelector' import LineNumberSelector from '../components/LineSelector' import { NotFound } from '../components/NotFound' @@ -85,8 +84,8 @@ function GapsByHour({ lineRef, operatorRef, fromDate, toDate }: BusLineStatistic setSortingMode(e.target.value as 'hour' | 'severity') } value={sortingMode}> - {TEXTS.order_by_hour} - {TEXTS.order_by_severity} + {t('order_by_hour')} + {t('order_by_severity')} { const { search, setSearch } = useContext(SearchContext) const { operatorId, lineNumber, routes, routeKey } = search const [routesIsLoading, setRoutesIsLoading] = useState(false) + const { t } = useTranslation() const loadSearchData = async () => { setRoutesIsLoading(true) @@ -174,7 +174,7 @@ const GapsPatternsPage = () => { - { setStartDate(data)} - customLabel={TEXTS.start} + customLabel={t('start')} /> @@ -197,13 +197,13 @@ const GapsPatternsPage = () => { setEndDate(data)} - customLabel={TEXTS.end} + customLabel={t('end')} /> - { /> - { {routesIsLoading && ( - )} {!routesIsLoading && routes && (routes.length === 0 ? ( - {TEXTS.line_not_found} + {t('line_not_found')} ) : ( <> { const pos = locations.map((location) => ({ @@ -92,14 +92,14 @@ export default function RealtimeMapPage() { return ( - {TEXTS.realtime_map_explanation.slice(0, 25)} + {t('realtime_map_explanation').slice(0, 25)} - {/* from date */} - {/*minutes*/} - - {/* Buttons */} - {/*TODO (another PR another issue) - 3) use text `TEXTS`. - */} {/* loaded info */}

{loaded} {`- `} - {TEXTS.show_x_bus_locations} {` `} - {TEXTS.from_time_x_to_time_y + {t('show_x_bus_locations')} {` `} + {t('from_time_x_to_time_y') .replace('XXX', moment(from).format('hh:mm A')) .replace('YYY', moment(to).format('hh:mm A'))}

diff --git a/src/pages/singleLineMap/index.tsx b/src/pages/singleLineMap/index.tsx index a2299eec..f029498a 100644 --- a/src/pages/singleLineMap/index.tsx +++ b/src/pages/singleLineMap/index.tsx @@ -8,7 +8,7 @@ import LineNumberSelector from 'src/pages/components/LineSelector' import OperatorSelector from 'src/pages/components/OperatorSelector' import RouteSelector from 'src/pages/components/RouteSelector' import { INPUT_SIZE } from 'src/resources/sizes' -import { TEXTS } from 'src/resources/texts' +import { useTranslation } from 'react-i18next' import { SearchContext } from '../../model/pageState' import { NotFound } from '../components/NotFound' import { Point } from '../realtimeMap' @@ -22,7 +22,7 @@ import getAgencyList, { Agency } from 'src/api/agencyList' import { VehicleLocation } from 'src/model/vehicleLocation' import { getColorByHashString } from '../dashboard/AllLineschart/OperatorHbarChart/utils' import { DateSelector } from '../components/DateSelector' -import { CircularProgress } from '@mui/material' +import { CircularProgress, Tooltip } from '@mui/material' import { FilterPositionsByStartTimeSelector } from '../components/FilterPositionsByStartTimeSelector' import { PageContainer } from '../components/PageContainer' import { busIcon, busIconPath } from '../components/utils/BusIcon' @@ -46,6 +46,7 @@ const SingleLineMapPage = () => { const [isExpanded, setIsExpanded] = useState(false) const toggleExpanded = useCallback(() => setIsExpanded((expanded) => !expanded), []) const [agencyList, setAgencyList] = useState([]) + const { t } = useTranslation() useEffect(() => { getAgencyList().then(setAgencyList).catch(console.log) @@ -120,7 +121,7 @@ const SingleLineMapPage = () => { {/* choose date*/} - { {/* choose operator */} - { {/* choose line number */} - { {routes && (routes.length === 0 ? ( - {TEXTS.line_not_found} + {t('line_not_found')} ) : ( void locationsIsLoading: boolean }) { + const { t } = useTranslation() const [startTime, setStartTime] = useState('00:00:00') const options = useMemo(() => { const options = positions @@ -251,9 +253,15 @@ function FilterPositionsByStartTime({ return ( <> - + + {locationsIsLoading && ( + + + + )} - {locationsIsLoading && } import('../pages/singleLineMap')) const About = lazy(() => import('../pages/about')) const Profile = lazy(() => import('../pages/Profile')) const BugReportForm = lazy(() => import('../pages/BugReportForm ')) +const DataResearch = lazy(() => + import('../pages/DataResearch/DataResearch').then((m) => ({ default: m.DataResearch })), +) import { RadarChartOutlined, @@ -22,6 +25,7 @@ import { LineChartOutlined, } from '@ant-design/icons' import { MainRoute } from './MainRoute' +import { ErrorPage } from 'src/pages/ErrorPage' export const PAGES = [ { @@ -90,12 +94,13 @@ const getRoutesList = () => { return ( }> {routes.map(({ path, element }) => ( - + ))} } + ErrorBoundary={ErrorPage} loader={async ({ params: { gtfsRideGtfsRouteId } }) => { const resp = await fetch( `https://open-bus-stride-api.hasadna.org.il/gtfs_routes/get?id=${gtfsRideGtfsRouteId}`, @@ -104,8 +109,8 @@ const getRoutesList = () => { return gtfs_route }} /> - , - } key="back" />, + } /> + } key="back" /> // ) diff --git a/src/shared/Widget.tsx b/src/shared/Widget.tsx index 5ce1393c..e319c227 100644 --- a/src/shared/Widget.tsx +++ b/src/shared/Widget.tsx @@ -1,5 +1,5 @@ const Widget = (props: { children: React.ReactNode }) => { - return
{props.children}
+ return
{props.children}
} export default Widget diff --git a/vite.config.ts b/vite.config.ts index ae259285..e4ff9d1b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,8 +1,11 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' +const ASSET_URL = process.env.ASSET_URL || '' + // https://vitejs.dev/config/ export default defineConfig({ + base: ASSET_URL, plugins: [react()], resolve: { alias: {