diff --git a/__tests__/components/__snapshots__/date-time-options.js.snap b/__tests__/components/__snapshots__/date-time-options.js.snap index 9e8214cdc..37415059c 100644 --- a/__tests__/components/__snapshots__/date-time-options.js.snap +++ b/__tests__/components/__snapshots__/date-time-options.js.snap @@ -28,8 +28,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -145,8 +151,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -262,8 +274,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -379,8 +397,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -496,8 +520,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -613,8 +643,14 @@ exports[`components > form > call-taker > date time options should correctly han > @@ -730,8 +766,14 @@ exports[`components > form > call-taker > date time options should render 1`] = > diff --git a/__tests__/components/date-time-options.js b/__tests__/components/date-time-options.js index d2737d38f..931e05de5 100644 --- a/__tests__/components/date-time-options.js +++ b/__tests__/components/date-time-options.js @@ -1,3 +1,4 @@ +import '../test-utils/mock-window-matchMedia' import '../test-utils/mock-window-url' import { getMockInitialState, diff --git a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap index a3a1b7bc5..65c9368a3 100644 --- a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap +++ b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap @@ -46,7 +46,7 @@ exports[`components > viewers > nearby view renders nothing on a blank page 1`] className="nearby-view base-color-bg" >
viewers > nearby view renders nothing on a blank page 1`] } >
    viewers > nearby view renders proper scooter dates 1`] = ` className="nearby-view base-color-bg" >
    viewers > nearby view renders proper scooter dates 1`] = ` } >
      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` >
      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = `
        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
      • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

        1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

          1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

              viewers > nearby view renders proper scooter dates 1`] = ` >

              viewers > nearby view renders proper scooter dates 1`] = ` >

              viewers > nearby view renders proper scooter dates 1`] = `

              viewers > nearby view renders proper scooter dates 1`] = `
                viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
              • viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                  viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                1. viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                    viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` >
                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = `
                        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                      • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                        1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                          1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                              viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                            1. viewers > nearby view renders proper scooter dates 1`] = ` title="988" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` >
                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = `
                                  viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                • viewers > nearby view renders proper scooter dates 1`] = ` title="67" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="73" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                      viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                    1. viewers > nearby view renders proper scooter dates 1`] = ` title="984" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                        viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = `

                                        viewers > nearby view renders proper scooter dates 1`] = ` >
                                        viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = `

                                        viewers > nearby view renders proper scooter dates 1`] = `
                                          viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                        • viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = `

                                            viewers > nearby view renders proper scooter dates 1`] = `
                                              viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                            • viewers > nearby view renders proper scooter dates 1`] = ` title="522" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                              1. viewers > nearby view renders proper scooter dates 1`] = ` title="67" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                1. viewers > nearby view renders proper scooter dates 1`] = ` title="522" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                    viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="73" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                      viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                    1. viewers > nearby view renders proper scooter dates 1`] = ` title="322" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                      1. viewers > nearby view renders proper scooter dates 1`] = ` title="322" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                          viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = `

                                                          viewers > nearby view renders proper scooter dates 1`] = ` >
                                                          viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = `

                                                          viewers > nearby view renders proper scooter dates 1`] = `
                                                            viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                                          • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                            1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                              1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` className="departure-times" >

                                                                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > {leadTimeInMinutes} мин до запланированного времени отправления + notifications: за {leadTimeInMinutes} мин до запланированного времени + отправления notificationsDisabled: "Уведомления: отключены" TripTools: copyLink: Скопировать ссылку @@ -693,17 +702,23 @@ components: storeTripHistory: Сохранять историю поездок updating: Обновляется UserSettings: - confirmDeletion: "Недавние поисковые запросы и\_(или) места сохранены. Отключение сохранения недавних мест и\_(или) поисковых запросов приведет к удалению этих элементов. Продолжить?" + confirmDeletion: "Недавние поисковые запросы и (или) места сохранены. Отключение + сохранения недавних мест и (или) поисковых запросов приведет к удалению этих + элементов. Продолжить?" favoriteStops: Избранные остановки myPreferences: Мои параметры mySavedPlaces: Мои сохраненные места (изменить) noFavoriteStops: Избранные остановки recentPlaces: Недавние места - recentSearchSummary: "Режим «{mode}». {from}\_— {to}" + recentSearchSummary: "Режим «{mode}». {from} — {to}" recentSearches: Недавние поисковые запросы - rememberSearches: "Сохранить недавние места\_/ поисковые запросы?" + rememberSearches: "Сохранить недавние места / поисковые запросы?" stopId: "Идентификатор остановки: {stopId}" - storageDisclaimer: "Все сохраненные параметры, места и настройки будут храниться в локальном хранилище браузера. TriMet не получит доступ к данным о вашем доме, месте работы или другим местоположениям. В любой момент вы можете отключить сохранение недавних мест\_/ поисковых запросов и очистить список местоположений дома/работы, а также избранных остановок.\n" + storageDisclaimer: "Все сохраненные параметры, места и настройки будут храниться + в локальном хранилище браузера. TriMet не получит доступ к данным о вашем доме, + месте работы или другим местоположениям. В любой момент вы можете отключить + сохранение недавних мест / поисковых запросов и очистить список местоположений + дома/работы, а также избранных остановок.\n" UserTripSettings: forgetOptions: Удалить варианты rememberOptions: Сохранить варианты поездки @@ -725,12 +740,12 @@ components: prompt: Куда вы хотите отправиться? config: accessModes: - bicycle: "Общественный транспорт\_+\_личный велосипед" - bicycle_rent: "Общественный транспорт\_+\_прокат велосипеда" + bicycle: "Общественный транспорт + личный велосипед" + bicycle_rent: "Общественный транспорт + прокат велосипеда" car_hail: Заказная поездка car_park: Парковочное место - micromobility: "Общественный транспорт\_+\_личный самокат" - micromobility_rent: "Общественный транспорт\_+\_прокат электросамоката" + micromobility: "Общественный транспорт + личный самокат" + micromobility_rent: "Общественный транспорт + прокат электросамоката" bicycleModes: bicycle: Личный велосипед bicycle_rent: Прокат велосипедов diff --git a/i18n/tl.yml b/i18n/tl.yml index 4d48060ef..ad174d969 100644 --- a/i18n/tl.yml +++ b/i18n/tl.yml @@ -45,7 +45,8 @@ actions: confirmDeletePlace: Gusto mo bang alisin ang lugar na ito? emailVerificationResent: Ipinadala ulit ang mensahe ng pag-verify sa email. genericError: "Nagka-error: {err}" - itineraryExistenceCheckFailed: Nagka-error sa pagtingin kung posible ang napili mong biyahe. + itineraryExistenceCheckFailed: Nagka-error sa pagtingin kung posible ang napili + mong biyahe. mustAcceptTermsToSavePlace: >- Pakitanggap ang Mga Tuntunin ng Paggamit (sa ilalim ng Aking Account) para mag-save ng mga lokasyon. @@ -106,13 +107,11 @@ common: submitting: Isinusumite… "yes": Oo itineraryDescriptions: - calories: "{calories, number} Cal" fareUnknown: Walang impormasyon sa pamasahe noItineraryToDisplay: Walang ipapakitang itinerary. relativeCo2: > {co2} {isMore, select, true {mas maraming} other {mas kaunting} } CO₂ kaysa magmaneho nang mag-isa - transfers: "{transfers, plural, =0 {} one {# transfer} other {# transfers}}" linkOpensNewWindow: (Magbubukas sa bagong window) modes: bicycle_rent: Bikeshare @@ -151,7 +150,6 @@ common: enterStartLocation: Ilagay ang lokasyon ng pagsisimula o {mapAction} sa mapa… tap: tap time: - departureArrivalTimes: "{startTime, time, short}—{endTime, time, short}" duration: aFewSeconds: a few seconds nDays: "{days, plural, =1 {one day} other {# days}}" @@ -204,7 +202,8 @@ components: ang pampublikong transportasyon sa pagpili mo ng mode. origin: pinagmulan planTripTooltip: Planuhin ang biyahe - validationMessage: "Ilarawan ang mga sumusunod na field para makapagplano ng biyahe: {issues}" + validationMessage: "Ilarawan ang mga sumusunod na field para makapagplano ng biyahe: + {issues}" BeforeSignInScreen: mainTitle: Sina-sign in ka message: > @@ -263,7 +262,6 @@ components: ariaLabel: Pag-navigate sa form ItinerarySummary: itineraryDetails: Mga detalye ng itinerary - minMaxFare: "{minTotalFare} - {maxTotalFare}" LocationSearch: enterLocation: Ilagay ang lokasyon setDestination: Itakda ang Patutunguhan @@ -396,7 +394,8 @@ components: invalidCode: Maglagay ng 6 na digit para sa code sa pag-validate. invalidPhone: Maglagay ng valid na numero ng telepono. phoneNumberSubmitted: Matagumpay na naisumite ang numero ng teleponong {phoneNumber}. - phoneNumberVerified: Matagumpay na na-verify ang numero ng teleponong {phoneNumber} . + phoneNumberVerified: Matagumpay na na-verify ang numero ng teleponong {phoneNumber} + . placeholder: Ilagay ang numero ng iyong telepono prompt: "Ilagay ang numero ng iyong telepono para sa mga SMS na notification:" requestNewCode: Humiling ng bagong code @@ -585,9 +584,11 @@ components: travelingAt: Bumibiyahe nang {milesPerHour} vehicleName: Sasakyan {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Tinitingnan kung may itinerary para sa bawat araw ng linggo... + checkingItineraryExistence: Tinitingnan kung may itinerary para sa bawat araw + ng linggo... tripDaysPrompt: Anong mga araw mo ginagawa ang biyaheng ito? - tripIsAvailableOnDaysIndicated: Available ang iyong biyahe sa mga araw ng linggo na nakasaad sa itaas. + tripIsAvailableOnDaysIndicated: Available ang iyong biyahe sa mga araw ng linggo + na nakasaad sa itaas. tripNamePrompt: "Pangalanan ang biyaheng ito:" tripNotAvailableOnDay: Hindi available ang biyahe sa {repeatedDay} unsavedChangesExistingTrip: >- @@ -632,7 +633,8 @@ components: unknownState: Hindi Alam ang Status ng Biyahe untogglePause: Ipagpatuloy inactive: - description: Ipagpatuloy ang pagsubaybay sa biyahe para makita ang updated na status + description: Ipagpatuloy ang pagsubaybay sa biyahe para makita ang updated na + status heading: Naka-pause ang pagsubaybay sa biyahe nextTripNotPossible: description: > @@ -651,7 +653,8 @@ components: description: Hinihintay na makalkula ang biyahe. heading: Hindi pa nakakalkula ang biyahe snoozed: - description: I-unsnooze ang pagsubaybay sa biyahe para makita ang updated na status. + description: I-unsnooze ang pagsubaybay sa biyahe para makita ang updated na + status. heading: Naka-snooze ang pagsubaybay sa biyahe ngayong araw upcoming: nextTripBegins: >- @@ -661,7 +664,8 @@ components: Magsisimula ang biyahe nang {tripStart, time, short}. (Magsisimula ang realtime na pagsubaybay nang {monitoringStart, time, short}.) tripStartIsDelayed: Naantala ang oras ng pagsisimula ng biyahe nang {duration}! - tripStartIsEarly: Nagsisimula na ang biyahe {duration} na mas maaga kaysa sa inaasahan! + tripStartIsEarly: Nagsisimula na ang biyahe {duration} na mas maaga kaysa sa + inaasahan! tripStartsSoonNoUpdates: >- Malapit nang magsimula ang biyahe (walang realtime na update na available). diff --git a/i18n/vi.yml b/i18n/vi.yml index 316b37876..b10292003 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -5,7 +5,8 @@ actions: callQuerySaveError: "Lỗi khi lưu trữ các truy vấn cuộc gọi: {err}" callSaveError: "Không thể lưu cuộc gọi: {err}" checkSessionError: "Lỗi khi thiết lập phiên ủy quyền: {err}" - couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy vấn. + couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy + vấn. fetchCallsError: "Lỗi khi tìm nạp cuộc gọi: {err}" queryFetchError: "Lỗi khi tìm nạp các truy vấn: {err}" fieldTrip: @@ -28,7 +29,8 @@ actions: Không thể lưu kế hoạch chuyến đi: Không thể lưu kế hoạch chuyến đi này do thiếu sức chứa trên một hoặc nhiều xe. Vui lòng lên kế hoạch lại chuyến đi của bạn. - maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết quả hợp lệ + maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết + quả hợp lệ saveItinerariesError: "Không lưu được hành trình: {err}" setDateError: "Lỗi khi cài đặt ngày:" setGroupSizeError: "Lỗi khi cài đặt kích thước nhóm:" @@ -40,7 +42,8 @@ actions: Để sử dụng địa điểm hiện tại của mình, quý vị hãy cho phép sử dụng vị trí trong trình duyệt và tải lại trang này. - geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt của bạn + geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt + của bạn unknownPositionError: Lỗi không xác định khi tìm vị trí userDeniedPermission: Người dùng từ chối cấp quyền map: @@ -52,7 +55,8 @@ actions: confirmDeletePlace: Bạn có muốn loại bỏ nơi này không? emailVerificationResent: Thông báo xác minh email đã được gửi lại. genericError: "Phát sinh lỗi: {err}" - itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là có thể. + itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là + có thể. mustAcceptTermsToSavePlace: >- Vui lòng chấp nhận Điều Khoản Sử Dụng (trong phần Tài Khoản Của Tôi) để lưu lại địa điểm. @@ -112,12 +116,10 @@ common: submitting: Đang gửi… "yes": Đúng itineraryDescriptions: - calories: "{calories, number} calo" fareUnknown: Không có thông tin giá vé noItineraryToDisplay: Không có hành trình để hiển thị. relativeCo2: | {co2} CO₂ {isMore, select, true {nhiều} other {ít} } hơn so với xe hơi - transfers: "{transfers, plural, =0 {} other {# chuyển}}" linkOpensNewWindow: (Mở khoảng thời gian mới) modes: bicycle_rent: Chia sẻ xe đạp @@ -156,7 +158,6 @@ common: enterStartLocation: Nhập vị trí bắt đầu hoặc {mapAction} vào bản đồ… tap: chạm time: - departureArrivalTimes: "{startTime, time, short}—{endTime, time, short}" duration: aFewSeconds: vài giây nDays: "{days} ngày" @@ -168,12 +169,14 @@ common: {} other {# giây}} components: A11yPrefs: - accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc định + accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc + định AccountSetupFinishPane: message: Bạn đã sẵn sàng để bắt đầu lên kế hoạch cho các chuyến đi của bạn. AddPlaceButton: addPlace: Thêm địa điểm - needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung gian + needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung + gian tooManyPlaces: Địa điểm trung gian tối đa đạt được AdvancedOptions: bannedRoutes: Chọn các tuyến đường bị cấm… @@ -259,14 +262,14 @@ components: editPlaceGeneric: Chỉnh sửa vị trí invalidAddress: Vui lòng cài đặt một vị trí cho nơi này. invalidName: Vui lòng nhập tên cho nơi này. - nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên khác. + nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên + khác. placeNotFound: Không tìm thấy địa điểm placeNotFoundDescription: Xin lỗi, địa điểm được yêu cầu không được tìm thấy. FormNavigationButtons: ariaLabel: Điều hướng hình thức ItinerarySummary: itineraryDetails: Chi tiết hành trình - minMaxFare: "{minTotalFare} - {maxTotalFare}" LocationSearch: enterLocation: Nhập vị trí setDestination: Chọn điểm đến @@ -335,8 +338,10 @@ components: description: Nội dung bạn yêu cầu không có sẵn. header: Không tìm thấy nội dung NotificationPrefsPane: - devicesRegistered: "{count, plural, one {# device} other {# devices}} đã đăng ký" - noDeviceForPush: Đăng ký thiết bị của quý vị bằng ứng dụng di động để nhận thông báo đẩy. + devicesRegistered: "{count, plural, one {# device} other {# devices}} đã đăng + ký" + noDeviceForPush: Đăng ký thiết bị của quý vị bằng ứng dụng di động để nhận thông + báo đẩy. notificationChannelPrompt: "Nhận thông báo về các chuyến đi đã lưu bằng:" OTP2ErrorRenderer: LOCATION_NOT_FOUND: @@ -579,16 +584,21 @@ components: travelingAt: di chuyển với tốc độ {milesPerHour} vehicleName: Phương tiện giao thông {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong tuần… + checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong + tuần… tripDaysPrompt: Bạn thực hiện chuyến đi này vào những ngày nào? - tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong tuần như đã nêu ở trên. + tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong + tuần như đã nêu ở trên. tripNamePrompt: "Vui lòng cung cấp tên cho chuyến đi này:" tripNotAvailableOnDay: Chuyến đi không có sẵn vào {repeatedDay} - unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những thay đổi sẽ bị mất. - unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó sẽ bị mất. + unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những + thay đổi sẽ bị mất. + unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó + sẽ bị mất. TripNotificationsPane: advancedSettings: Cài đặt nâng cao - altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến nghị + altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến + nghị delaysAboveThreshold: Có sự chậm trễ hoặc gián đoạn của hơn howToReceiveAlerts: > Để nhận thông báo cho các chuyến đi đã lưu của bạn, bật thông báo trong @@ -597,7 +607,8 @@ components: notificationsTurnedOff: Thông báo được tắt cho tài khoản của bạn. notifyViaChannelWhen: "Thông báo cho tôi qua {channel} khi:" oneHour: 1 tiếng - realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình của tôi + realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình + của tôi timeBefore: "{time} trước" TripStatus: alerts: "{alerts, plural, one {# cảnh báo!} other {# cảnh báo!}}" @@ -610,7 +621,8 @@ components: earlyHeading: >- Chuyến đi đang diễn ra và sẽ đến sớm hơn {formattedDuration} so với dự kiến! - noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực có sẵn). + noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực + có sẵn). onTimeHeading: Chuyến đi đang được tiến hành và đúng giờ. base: lastCheckedDefaultText: Thời gian được kiểm tra lần cuối không xác định @@ -653,7 +665,8 @@ components: tripStartIsEarly: >- Thời gian bắt đầu chuyến đi đang diễn ra sớm hơn {duration} so với dự kiến! - tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời gian thực). + tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời + gian thực). tripStartsSoonOnTime: Chuyến đi đang bắt đầu sớm và sắp đúng giờ. TripSummary: arriveAt: "Đến nơi " diff --git a/i18n/zh_Hans.yml b/i18n/zh_Hans.yml index 5d4b6a419..c3012c95a 100644 --- a/i18n/zh_Hans.yml +++ b/i18n/zh_Hans.yml @@ -99,12 +99,10 @@ common: submitting: 正在提交… "yes": 是 itineraryDescriptions: - calories: "{calories, number} 大卡" fareUnknown: 无票价信息 noItineraryToDisplay: 没有显示行程. relativeCo2: | {co2} {isMore, select, true {更多} other {更少} } CO₂ 比单独驾车 - transfers: "{transfers, plural, =0 {} other {# 换乘}}" linkOpensNewWindow: (打开新窗口) modes: bicycle_rent: 共享单车 @@ -143,7 +141,6 @@ common: enterStartLocation: 输入出发地点或{mapAction}地图… tap: 点击 time: - departureArrivalTimes: "{startTime, time, short}—{endTime, time, short}" duration: aFewSeconds: 几秒钟 nDays: "{days} 天" @@ -244,7 +241,6 @@ components: ariaLabel: 表格导航 ItinerarySummary: itineraryDetails: 行程详情 - minMaxFare: "{minTotalFare} - {maxTotalFare}" LocationSearch: enterLocation: 输入位置 setDestination: 设置目的地 diff --git a/i18n/zh_Hant.yml b/i18n/zh_Hant.yml index 4fbc7d8fd..b18d02dc0 100644 --- a/i18n/zh_Hant.yml +++ b/i18n/zh_Hant.yml @@ -99,12 +99,10 @@ common: submitting: 正在提交… "yes": 是 itineraryDescriptions: - calories: "{calories, number}卡" fareUnknown: 無票價資訊 noItineraryToDisplay: 沒有預定行程可顯示。 relativeCo2: | {co2}比單獨開車排放的CO₂{isMore, select, true {要多} other {少}} - transfers: "{transfers}次轉乘" linkOpensNewWindow: (開啟新視窗) modes: bicycle_rent: 自行車共享 @@ -143,7 +141,6 @@ common: enterStartLocation: 輸入開始位置或{mapAction} 地圖…… tap: 輕觸 time: - departureArrivalTimes: "{startTime, time, short}—{endTime, time, short}" duration: aFewSeconds: 幾秒鐘 nDays: "{days}天" @@ -244,7 +241,6 @@ components: ariaLabel: 表單導航 ItinerarySummary: itineraryDetails: 路線詳細資訊 - minMaxFare: "{minTotalFare} - {maxTotalFare}" LocationSearch: enterLocation: 輸入位置 setDestination: 設定目的地 diff --git a/index.css b/index.css index 697d3b1b6..c5a4df16f 100644 --- a/index.css +++ b/index.css @@ -1,5 +1,6 @@ @import url(node_modules/bootstrap/dist/css/bootstrap.min.css); - +@import url(react-sliding-pane/dist/react-sliding-pane.css); +@import url(lib/bike-rental.css); @import url(node_modules/maplibre-gl/dist/maplibre-gl.css); @import url(lib/components/admin/call-taker.css); @@ -12,190 +13,25 @@ @import url(lib/components/user/nav-login-button.css); @import url(lib/components/viewers/viewers.css); -@import url(lib/bike-rental.css); -@import url(react-sliding-pane/dist/react-sliding-pane.css); /* Hide IE/Edge clear button in text input fields. */ input[type="text"]::-ms-clear { display: none; } -/* New app menu */ -.app-menu-icon { - background: none; - border: none; - cursor: pointer; - display: flex; - flex-direction: column; - height: 15px; - justify-content: space-between; - padding: 0; - position: absolute; - top: 16px; - transition: all 1s ease; - width: 21px; - z-index: 10; -} - -@media only screen and (max-width: 768px) { - #locale-selector-wrapper { - display: none; - } - .app-menu-icon { - left: 15px; - } -} - -.app-menu-icon .menu-line { - border-bottom: 3px solid #ffffff; - display: block; - position: relative; - transition: all 0.5s ease; - width: 100%; -} - -.app-menu-icon[aria-expanded="true"] .menu-line.top { - transform: rotate(45deg); - top: 7px; -} -.app-menu-icon[aria-expanded="true"] .menu-line.bottom { - transform: rotate(-45deg); - bottom: 5px; -} -.app-menu-icon[aria-expanded="true"] .menu-line.middle { - display: none; -} - -.slide-pane { - transition: all 0.2s ease-in-out; -} - -.slide-pane_from_left { - margin: 52px auto 0 0; - /* Keep pane from overflowing on smaller screens */ - height: calc(100% - 52px); -} - -.slide-pane__content { - padding: 6px 0; -} - -.slide-pane__overlay { - z-index: 1000; -} - -.app-menu { - margin: 0; - padding: 0.5rem 0; -} - -.app-menu img, -.app-menu svg { - max-height: 1em; - margin: 0 2rem; - vertical-align: middle; - width: 1em; -} - -.app-menu a, -.app-menu button { - background: none; - border: none; - color: inherit; - cursor: pointer; - display: flex; - font-size: 20px; - padding: 0.5rem 0; - text-decoration: none; - width: 100%; -} - -/* Prevents hover styles from getting triggered on mobile */ -@media (hover: hover) { - .app-menu a:hover, - .app-menu button:hover { - color: #4c97f5; - } -} - -.app-menu a:focus, -.app-menu button:focus { - background-color: #ddd; - outline: 2px solid #4c97f5; - outline-offset: -2px; -} - -.app-menu button[aria-selected="true"], -.sort-option button[aria-selected="true"], -#locale-selector button[aria-selected="true"] { - font-weight: bold; -} - -.skip-nav-button { - color: initial; - position: fixed; - top: -30px; -} -.skip-nav-button:focus { - padding: 7px 24px; - top: 7px; - z-index: 100; -} - -.view-switcher { - display: none; -} - -@media (min-width: 768px) { - .view-switcher { - display: flex; - } -} - -.expand-menu-chevron { - flex-grow: 1; - text-align: end; -} - -.dropdown-header { - font-size: inherit; - line-height: normal; - color: inherit; - white-space: nowrap; -} - -.sub-menu-container { - border-top: 1px solid #cccccc; - margin-top: 0.5rem; - padding-left: 2rem; -} - -.app-menu .app-menu-divider { - border-bottom: 1px solid #ccc; - padding: 1rem 0; -} - -/* Header image or title */ -/* If an icon is used, visually-hide the title (but keep it visible to screen readers). */ -.with-icon div.navbar-title { - height: 0; - overflow: hidden; - width: 0; -} - /* Buttons */ button.header, button.step, .clear-button-formatting { background: transparent; - color: inherit; border: 0; + color: inherit; + margin: 0; + padding: 0; text-align: inherit; text-decoration: none; - padding: 0; - margin: 0; } .clear-button-formatting:active { @@ -217,9 +53,9 @@ button.step { } .overflow-ellipsis { - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } .map-container .leaflet-top { diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index 39291f813..e4d5ab1a2 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -509,6 +509,10 @@ export const fetchNearby = (position, radius, currentServiceWeek) => { export const findStopTimesForStop = (params) => function (dispatch, getState) { + // If the stop is already in the store, don't fetch it again, unless we are forcing a refetch + if (!params.forceFetch && getState().otp.transitIndex.stops[params.stopId]) + return + dispatch(fetchingStopTimesForStop(params)) const { date, stopId } = params const timeZone = getState().otp.config.homeTimezone diff --git a/lib/actions/ui.js b/lib/actions/ui.js index cdd0691da..a548a43bc 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -47,6 +47,9 @@ const viewRoute = createAction('SET_VIEWED_ROUTE') export const toggleAutoRefresh = createAction('TOGGLE_AUTO_REFRESH') const settingItineraryView = createAction('SET_ITINERARY_VIEW') export const setPopupContent = createAction('SET_POPUP_CONTENT') +export const setNetworkConnectionLost = createAction( + 'SET_NETWORK_CONNECTION_LOST' +) // This code-less action calls the reducer code // and thus resets the session timeout. diff --git a/lib/components/app/app.css b/lib/components/app/app.css index 87fe44222..697857947 100644 --- a/lib/components/app/app.css +++ b/lib/components/app/app.css @@ -1,8 +1,8 @@ /* application menu styling */ .app-menu-button { - border: none; background: none; + border: none; color: #fff; font-size: 24px; padding: 0px 0px; @@ -21,10 +21,10 @@ .app-menu .btn-default:active, .app-menu .open > .dropdown-toggle.btn-default:focus, .app-menu .open > .dropdown-toggle.btn-default { - color: #fff; background: none; - box-shadow: none; border: none; + box-shadow: none; + color: #fff; } .app-menu .btn-default:hover { @@ -47,36 +47,266 @@ } } +/* Navbar settings */ + +.navbar .navbar-header { + display: grid; + grid-template-columns: 300px auto 170px; +} + +.navbar .navbar-nav { + grid-column: 3; + margin: 0; +} + +.navbar .navbar-brand { + align-items: center; + display: flex; + gap: 25px; + grid-column: 1 / span 1; + width: 100%; +} + +.view-switcher { + align-items: center; + display: flex; + justify-content: center; +} + +@media (min-width: 992px) { + .view-switcher { + height: 100%; + left: 0; + position: absolute; + width: 100% + } +} + + +@media (max-width: 768px) { + .navbar .navbar-header { + grid-template-columns: 50px auto 70px; + width: auto !important; + + } + + .navbar .navbar-brand { + gap: 15px; + grid-column: 1 / span 2; + padding: 10px; + } + + .view-switcher { + display: none; +} + +} + +/* Mobile navbar */ +.mobile-navbar-container .navbar-header { + display: block; + grid-column: 1; + grid-row: 1; + justify-self: center; + width: 25px; +} + +.mobile-navbar-container .mobile-header { + grid-row: 1; + height: 100%; + justify-self: center; +} + +.mobile-navbar-container .locale-selector-and-login { + grid-row: 1; + height: 50px; + justify-self: flex-end; +} + +.mobile-navbar-container .container-fluid { + align-items: center; + display: grid; + grid-template-columns: 50px auto 70px; + grid-template-rows: 50px; + padding: 0; +} + +/* New app menu */ +.app-menu-icon { + background: none; + border: none; + cursor: pointer; + display: flex; + flex-direction: column; + height: 15px; + justify-content: space-between; + padding: 0; + transition: all 1s ease; + width: 21px; + z-index: 10; +} + +@media only screen and (max-width: 768px) { + #locale-selector-wrapper { + display: none; + } + .app-menu-icon { + left: 15px; + } +} + +.app-menu-icon .menu-line { + border-bottom: 3px solid #ffffff; + display: block; + position: relative; + transition: all 0.5s ease; + width: 100%; +} + +.app-menu-icon[aria-expanded="true"] .menu-line.top { + top: 7px; + transform: rotate(45deg); +} +.app-menu-icon[aria-expanded="true"] .menu-line.bottom { + bottom: 5px; + transform: rotate(-45deg); +} +.app-menu-icon[aria-expanded="true"] .menu-line.middle { + display: none; +} + +.slide-pane { + transition: all 0.2s ease-in-out; +} + +.slide-pane_from_left { + /* Keep pane from overflowing on smaller screens */ + height: calc(100% - 52px); + margin: 52px auto 0 0; +} + +.slide-pane__content { + padding: 6px 0; +} + +.slide-pane__overlay { + z-index: 1000; +} + +.app-menu { + margin: 0; + padding: 0.5rem 0; +} + +.app-menu img, +.app-menu svg { + margin: 0 2rem; + max-height: 1em; + vertical-align: middle; + width: 1em; +} + +.app-menu a, +.app-menu button { + background: none; + border: none; + color: inherit; + cursor: pointer; + display: flex; + font-size: 20px; + padding: 0.5rem 0; + text-decoration: none; + width: 100%; +} + +/* Prevents hover styles from getting triggered on mobile */ +@media (hover: hover) { + .app-menu a:hover, + .app-menu button:hover { + color: #4c97f5; + } +} + +.app-menu a:focus, +.app-menu button:focus { + background-color: #ddd; + outline: 2px solid #4c97f5; + outline-offset: -2px; +} + +.app-menu button[aria-selected="true"], +.sort-option button[aria-selected="true"], +#locale-selector button[aria-selected="true"] { + font-weight: bold; +} + +.skip-nav-button { + color: initial; + position: fixed; + top: -30px; +} +.skip-nav-button:focus { + padding: 7px 24px; + top: 7px; + z-index: 100; +} + +.expand-menu-chevron { + flex-grow: 1; + text-align: end; +} + +.dropdown-header { + color: inherit; + font-size: inherit; + line-height: normal; + white-space: nowrap; +} + +.sub-menu-container { + border-top: 1px solid #cccccc; + margin-top: 0.5rem; + padding-left: 2rem; +} + +.app-menu .app-menu-divider { + border-bottom: 1px solid #ccc; + padding: 1rem 0; +} + +/* Header image or title */ +/* If an icon is used, visually-hide the title (but keep it visible to screen readers). */ +.with-icon div.navbar-title { + height: 0; + overflow: hidden; + width: 0; +} + /* PrintLayout styles */ .otp.print-layout { - max-width: 640px; margin: 30px auto; + max-width: 640px; } .otp.print-layout > .header { - margin-bottom: 30px; border-bottom: 4px solid black; font-size: 36px; font-weight: 600; + margin-bottom: 30px; } .otp.print-layout > .map-container { border: 2px solid black; + box-sizing: border-box; height: 400px; margin-bottom: 30px; - box-sizing: border-box; } /* View Switcher Styling */ -.view-switcher { - align-items: center; - display: flex; - justify-content: center; -} .view-switcher a { - color: rgba(255, 255, 255, 0.85); border-radius: 15px; + color: rgba(255, 255, 255, 0.85); font-size: 14px; padding: 6px 12px; user-select: none; @@ -98,8 +328,8 @@ /* Full screen modal styling */ .fullscreen-modal { - width: 75vw; height: 60vh; + width: 75vw; } .fullscreen-modal .modal-content { height: 90vh; @@ -108,6 +338,9 @@ height: 100%; width: 100%; } +.otp .navbar { + z-index: 25; +} /** These changes kick in when the map is hidden. These rules ensure that the entire "sidebar" (which at this point fills the entire screen) scrolls as a single unit. diff --git a/lib/components/app/batch-routing-panel.tsx b/lib/components/app/batch-routing-panel.tsx index 6ca1f6da2..30001e1b3 100644 --- a/lib/components/app/batch-routing-panel.tsx +++ b/lib/components/app/batch-routing-panel.tsx @@ -25,6 +25,7 @@ import ViewerContainer from '../viewers/viewer-container' interface Props { activeSearch: any currentQuery: any + geocoderResultsOrder?: Array intl: IntlShape mainPanelContent: number mobile?: boolean @@ -89,7 +90,13 @@ class BatchRoutingPanel extends Component { } render() { - const { activeSearch, intl, mobile, showUserSettings } = this.props + const { + activeSearch, + geocoderResultsOrder, + intl, + mobile, + showUserSettings + } = this.props const { planTripClicked } = this.state const mapAction = mobile ? intl.formatMessage({ @@ -157,6 +164,7 @@ class BatchRoutingPanel extends Component {

                                                                  { showClearButton={!mobile} /> { const { mainPanelContent } = state.otp.ui const currentQuery = state.otp.currentQuery + const geocoderResultsOrder = state.otp.config?.geocoder?.geocoderResultsOrder return { activeSearch: getActiveSearch(state), currentQuery, + geocoderResultsOrder, mainPanelContent, showUserSettings } diff --git a/lib/components/app/desktop-nav.tsx b/lib/components/app/desktop-nav.tsx index f9a265b86..8ce6df8d9 100644 --- a/lib/components/app/desktop-nav.tsx +++ b/lib/components/app/desktop-nav.tsx @@ -13,14 +13,15 @@ import { DEFAULT_APP_TITLE } from '../../util/constants' import InvisibleA11yLabel from '../util/invisible-a11y-label' import NavLoginButtonAuth0 from '../user/nav-login-button-auth0' +import { NetworkConnectionBanner } from './network-connection-banner' import AppMenu, { Icon } from './app-menu' import LocaleSelector from './locale-selector' import NavbarItem from './nav-item' import ViewSwitcher from './view-switcher' const StyledNav = styled(Nav)` - /* Almost override bootstrap's margin-right: -15px */ - margin-right: -5px; + display: flex; + justify-content: end; /* Target only the svgs in the Navbar */ & > li > button > svg, & > li > span > button > span > svg { @@ -41,10 +42,6 @@ const StyledNav = styled(Nav)` padding: 15px; line-height: 20px; - @media (max-width: 768px) { - padding: 10px; - } - &:hover { background: rgba(0, 0, 0, 0.05); color: #ececec; @@ -63,6 +60,7 @@ const NavItemOnLargeScreens = styled(NavbarItem)` // Typscript TODO: otpConfig type export type Props = { locale: string + networkConnectionLost: boolean otpConfig: AppConfig popupTarget?: string setPopupContent: (url: string) => void @@ -83,6 +81,7 @@ export type Props = { */ const DesktopNav = ({ locale, + networkConnectionLost, otpConfig, popupTarget, setPopupContent @@ -99,18 +98,14 @@ const DesktopNav = ({ const BrandingElement = brandClickable ? 'a' : 'div' - const commonStyles = { marginLeft: 50 } - const brandingProps = brandClickable - ? { - href: '/#/', - style: { - ...commonStyles, - display: 'block', - position: 'relative', - zIndex: 10 - } - } - : { style: { ...commonStyles } } + const brandingProps = brandClickable && { + href: '/#/', + style: { + display: 'block', + position: 'relative', + zIndex: 10 + } + } const popupButtonText = popupTarget && intl.formatMessage({ @@ -142,7 +137,7 @@ const DesktopNav = ({ )} - + {popupTarget && ( @@ -166,6 +161,7 @@ const DesktopNav = ({ + ) } @@ -173,8 +169,10 @@ const DesktopNav = ({ // connect to the redux store const mapStateToProps = (state: AppReduxState) => { const { config: otpConfig } = state.otp + const { networkConnectionLost } = state.otp.ui.errors return { locale: state.otp.ui.locale, + networkConnectionLost, otpConfig, popupTarget: otpConfig.popups?.launchers?.toolbar } diff --git a/lib/components/app/nav-item.tsx b/lib/components/app/nav-item.tsx index d6cd2f9f9..9c4c92be8 100644 --- a/lib/components/app/nav-item.tsx +++ b/lib/components/app/nav-item.tsx @@ -19,10 +19,6 @@ export const NavbarButton = styled.button` padding: 15px; transition: all 0.1s ease-in-out; - @media (max-width: 768px) { - padding: 10px; - } - &:hover, &[aria-expanded='true'] { background: rgba(0, 0, 0, 0.05); diff --git a/lib/components/app/network-connection-banner.tsx b/lib/components/app/network-connection-banner.tsx new file mode 100644 index 000000000..a323520ac --- /dev/null +++ b/lib/components/app/network-connection-banner.tsx @@ -0,0 +1,83 @@ +import { CSSTransition, TransitionGroup } from 'react-transition-group' +import { FormattedMessage } from 'react-intl' +import React, { useRef } from 'react' +import styled from 'styled-components' + +import { RED_ON_WHITE } from '../util/colors' +import InvisibleA11yLabel from '../util/invisible-a11y-label' + +const containerClassname = 'network-connection-banner' +const timeout = 250 + +const TransitionStyles = styled.div` + .${containerClassname} { + background: ${RED_ON_WHITE}; + border-left: 1px solid #e7e7e7; + border-right: 1px solid #e7e7e7; + color: #fff; + font-weight: 600; + padding: 5px; + position: absolute; + text-align: center; + top: 50px; + width: 100%; + // When banner is fully loaded, set z-index higher than nav so we're not seeing the nav border. + z-index: 26; + + @media (max-width: 768px) { + border: 0; + } + } + .${containerClassname}-enter { + opacity: 0; + transform: translateY(-100%); + } + .${containerClassname}-enter-active { + opacity: 1; + transform: translateY(0); + transition: opacity ${timeout}ms ease-in; + } + .${containerClassname}-exit { + opacity: 1; + transform: translateY(0); + z-index: 20; + } + .${containerClassname}-exit-active { + opacity: 0; + transform: translateY(-100%); + transition: opacity ${timeout}ms ease-in, transform ${timeout}ms ease-in; + z-index: 20; + } +` + +export const NetworkConnectionBanner = ({ + networkConnectionLost +}: { + networkConnectionLost: boolean +}): JSX.Element => { + const connectionLostBannerRef = useRef(null) + return ( + + + {networkConnectionLost ? ( + + ) : ( + + )} + + + {networkConnectionLost && ( + +
                                                                  + +
                                                                  +
                                                                  + )} +
                                                                  +
                                                                  + ) +} diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index 2b2a44dfc..ee4c3c684 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -154,10 +154,13 @@ class ResponsiveWebapp extends Component { map, matchContentToUrl, parseUrlQueryString, - receivedPositionResponse + receivedPositionResponse, + setNetworkConnectionLost } = this.props // Add on back button press behavior. window.addEventListener('popstate', handleBackButtonPress) + window.addEventListener('online', () => setNetworkConnectionLost(false)) + window.addEventListener('offline', () => setNetworkConnectionLost(true)) // If a URL is detected without hash routing (e.g., http://localhost:9966?sessionId=test), // window.location.search will have a value. In this case, we need to redirect to the URL root with the @@ -180,10 +183,7 @@ class ResponsiveWebapp extends Component { navigator.geolocation.watchPosition( // On success (position) => { - // This object cloning is required to be allowed to read the position info twice - // on webkit browsers. - // See https://github.com/opentripplanner/otp-react-redux/pull/697 for details - receivedPositionResponse({ position: { ...position } }) + receivedPositionResponse({ position }) }, // On error (error) => { @@ -441,6 +441,7 @@ const mapStateToWrapperProps = (state) => { const mapWrapperDispatchToProps = { processSignIn: authActions.processSignIn, setLocale: uiActions.setLocale, + setNetworkConnectionLost: uiActions.setNetworkConnectionLost, showAccessTokenError: authActions.showAccessTokenError, showLoginError: authActions.showLoginError } diff --git a/lib/components/app/view-switcher.tsx b/lib/components/app/view-switcher.tsx index fc500f9b5..0bd47d5e8 100644 --- a/lib/components/app/view-switcher.tsx +++ b/lib/components/app/view-switcher.tsx @@ -3,14 +3,11 @@ import React from 'react' import Link from '../util/link' -type Props = { - sticky?: boolean -} /** * This component is a switcher between * the main views of the application. */ -const ViewSwitcher = ({ sticky }: Props) => { +const ViewSwitcher = (): JSX.Element => { const intl = useIntl() return (
                                                                  { className="view-switcher" id="view-switcher" role="group" - style={ - sticky - ? { - height: '100%', - left: 0, - position: 'absolute', - width: '100%' - } - : {} - } > diff --git a/lib/components/form/advanced-settings-panel.tsx b/lib/components/form/advanced-settings-panel.tsx index 0f0e99929..ccf3ea25d 100644 --- a/lib/components/form/advanced-settings-panel.tsx +++ b/lib/components/form/advanced-settings-panel.tsx @@ -133,15 +133,14 @@ const DtSelectorContainer = styled.div` ` const MobilityProfileContainer = styled.div` - margin-bottom: 12px; -` - -const UnderlinedLink = styled(Link)` - text-decoration: underline; + margin: 60px 0 60px 5px; ` const MobilityProfileDropdown = styled(DropdownSelector)` margin: 20px 0px; + label { + padding-left: 0; + } ` const AdvancedSettingsPanel = ({ diff --git a/lib/components/form/call-taker/advanced-options.js b/lib/components/form/call-taker/advanced-options.js index de97254ce..891db3c4b 100644 --- a/lib/components/form/call-taker/advanced-options.js +++ b/lib/components/form/call-taker/advanced-options.js @@ -224,7 +224,7 @@ class AdvancedOptions extends Component { }} > {/* Show the first mode setting */} - {applicableModeSettings.length >= 1 && ( + {applicableModeSettings?.length >= 1 && (
                                                                  {/* Show the remaining items after the first */} - {applicableModeSettings.length > 1 && + {applicableModeSettings?.length > 1 && applicableModeSettings .slice(1) .map((ms) => ( diff --git a/lib/components/form/call-taker/date-time-picker.tsx b/lib/components/form/call-taker/date-time-picker.tsx index 282c104cc..96292df86 100644 --- a/lib/components/form/call-taker/date-time-picker.tsx +++ b/lib/components/form/call-taker/date-time-picker.tsx @@ -1,5 +1,5 @@ import { connect } from 'react-redux' -import { format, toDate } from 'date-fns-tz' +import { format, OptionsWithTZ, toDate } from 'date-fns-tz' import { getCurrentTime } from '@opentripplanner/core-utils/lib/time' import { IntlShape, useIntl } from 'react-intl' import { isMatch, parse } from 'date-fns' @@ -7,6 +7,10 @@ import { OverlayTrigger, Tooltip } from 'react-bootstrap' import coreUtils from '@opentripplanner/core-utils' import React, { useEffect, useRef, useState } from 'react' +import { AppReduxState, FilterType, SortType } from '../../../util/state-types' +import { DepartArriveTypeMap, DepartArriveValue } from '../date-time-modal' +import { updateItineraryFilter } from '../../../actions/narrative' + const { getCurrentDate, OTP_API_DATE_FORMAT, OTP_API_TIME_FORMAT } = coreUtils.time @@ -56,7 +60,7 @@ const SUPPORTED_TIME_FORMATS = [ 'HH:mm' ] -const safeFormat = (date: Date | '', time: string, options: any) => { +const safeFormat = (date: Date | '', time: string, options?: OptionsWithTZ) => { if (date === '') return '' try { return format(date, time, options) @@ -68,8 +72,9 @@ const safeFormat = (date: Date | '', time: string, options: any) => { type Props = { date?: string - departArrive?: string + departArrive?: DepartArriveValue homeTimezone: string + importedUpdateItineraryFilter: (payload: FilterType) => void onKeyDown: () => void setQueryParam: ({ date, @@ -80,6 +85,8 @@ type Props = { departArrive: string time: string }) => void + sort: SortType + syncSortWithDepartArrive?: boolean time?: string timeFormat: string } @@ -101,12 +108,15 @@ const DateTimeOptions = ({ date: initialDate, departArrive: initialDepartArrive, homeTimezone, + importedUpdateItineraryFilter, onKeyDown, setQueryParam, + sort, + syncSortWithDepartArrive, time: initialTime, timeFormat }: Props) => { - const [departArrive, setDepartArrive] = useState( + const [departArrive, setDepartArrive] = useState( initialDate || initialTime ? 'DEPART' : 'NOW' ) const [date, setDate] = useState(initialDate) @@ -187,6 +197,18 @@ const DateTimeOptions = ({ }) }) } + + if ( + syncSortWithDepartArrive && + DepartArriveTypeMap[departArrive] !== sort.type + ) { + importedUpdateItineraryFilter({ + sort: { + ...sort, + type: DepartArriveTypeMap[departArrive] + } + }) + } }, [dateTime, departArrive, homeTimezone, setQueryParam]) // Handler for updating the time and date fields when NOW is selected @@ -209,8 +231,8 @@ const DateTimeOptions = ({ return ( <>