Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: nginx health check & selector loader. #155

Merged
merged 3 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions packages/ui/src/components/busSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useCallback, useMemo, memo, useEffect, useRef } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useQuery, useIsFetching } from '@tanstack/react-query'
import { useNavigate, generatePath } from 'react-router-dom'
import { latLng, latLngBounds } from 'leaflet'
import styled from 'styled-components'
Expand Down Expand Up @@ -43,6 +43,8 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
const [routeName, setRouteName] = useState<RouteName>()
const { dispatch, agency, route, direction, stop } = useGlobals()
const vehiclesDispatch = useVehiclesDispatch()
const vehicleFetches = useIsFetching({ queryKey: ['vehicles'] })
const routeVehiclesLoaded = useRef(false)
const stops = useMemo(() => {
if (direction && route) {
return route.stops.filter(({ id }) => direction.stops.includes(id))
Expand All @@ -57,7 +59,7 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
const {
data: routes,
error: routesError,
isLoading: isRoutesLoading
isFetching: isRoutesFetching
} = useQuery({
queryKey: ['routes', agency?.id],
queryFn: () => getAllRoutes(agency?.id),
Expand All @@ -67,7 +69,7 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
const {
data: routeConfig,
error: routeError,
isLoading: isRouteLoading
isFetching: isRouteFetching
} = useQuery({
/**
* Use two attributes from a route to prevent collisions across agencies.
Expand Down Expand Up @@ -156,7 +158,9 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
}
}, [navigate, dispatch, vehiclesDispatch, agency, route, direction])
const error = getFirstDataError([routesError, routeError])
const isLoading = isRoutesLoading || isRouteLoading
const isFetching = isRoutesFetching || isRouteFetching
const isPageLoading =
isFetching || Boolean(!routeVehiclesLoaded.current && vehicleFetches)

/**
* Update page title and description.
Expand Down Expand Up @@ -286,8 +290,23 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
}
}, [dispatch, homeStop, agencies, agency, routeName, routes, route, direction, stop])

/**
* These effects are so that the loader only
* renders for the first load of vehicles
* when the route changes.
*/
useEffect(() => {
routeVehiclesLoaded.current = false
}, [route])

useEffect(() => {
if (!vehicleFetches && !routeVehiclesLoaded.current) {
routeVehiclesLoaded.current = true
}
}, [vehicleFetches])

return (
<Page title="Bus Selector">
<Page title="Bus Selector" loading={isPageLoading}>
{error instanceof Error && <SelectorError err={error} />}
<Form
onSubmit={evt => {
Expand All @@ -297,26 +316,26 @@ const BusSelector = memo(function BusSelector({ agencies }: BusSelectorProps) {
agencies={agencies}
selected={agency}
onSelect={onSelectAgency}
isDisabled={isLoading}
isDisabled={isFetching}
/>
<Routes
routes={routes}
selected={routeName}
onSelect={onSelectRoute}
isDisabled={isLoading || !agency}
isDisabled={isFetching || !agency}
/>
<Directions
directions={route?.directions}
selected={direction}
onSelect={onSelectDirection}
isDisabled={isLoading || !agency || !route}
isDisabled={isFetching || !agency || !route}
/>
<Stops
stops={stops}
selected={stop}
onClear={onClearStop}
onSelect={onSelectStop}
isDisabled={isLoading || !agency || !route || !direction}
isDisabled={isFetching || !agency || !route || !direction}
/>
</Form>
</Page>
Expand Down
12 changes: 12 additions & 0 deletions packages/ui/src/components/dots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Loading } from '@busmap/components/loading'
import { PB90T, PB20T } from '@busmap/components/colors'

import { useTheme } from '@module/settings/contexts/theme.js'

const Dots = () => {
const { mode } = useTheme()

return <Loading indent={2} color={mode === 'dark' ? PB90T : PB20T} />
}

export { Dots }
8 changes: 2 additions & 6 deletions packages/ui/src/components/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import styled from 'styled-components'
import { Loading as Dots } from '@busmap/components/loading'
import { PB20T, PB90T } from '@busmap/components/colors'

import { useTheme } from '../modules/settings/contexts/theme.js'
import { Dots } from './dots.js'

import type { FC } from 'react'

Expand All @@ -24,12 +22,10 @@ const Text = styled.p`
font-size: 1rem;
`
const Loading: FC<LoadingProps> = ({ text, useIcon = true }) => {
const { mode } = useTheme()

return (
<Text>
<span>
{text} {useIcon && <Dots indent={2} color={mode === 'dark' ? PB90T : PB20T} />}
{text} {useIcon && <Dots />}
</span>
</Text>
)
Expand Down
10 changes: 8 additions & 2 deletions packages/ui/src/components/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import styled from 'styled-components'

import { Dots } from './dots.js'

import type { FC, ReactNode } from 'react'

interface PageProps {
title: string
className?: string
loading?: boolean
children: ReactNode
}

Expand All @@ -23,10 +26,13 @@ const Section = styled.section`
line-height: 1.25;
}
`
const Page: FC<PageProps> = ({ title, children, className }) => {
const Page: FC<PageProps> = ({ title, children, className, loading = false }) => {
return (
<Section className={className}>
<h2>{title}</h2>
<h2>
{title}
{loading && <Dots />}
</h2>
{children}
</Section>
)
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { toast } from '@busmap/components/toast'
import { Button } from '@busmap/components/button'
import { SignOut } from '@busmap/components/icons/signOut'
import { PB80T, PB20T, PB90T } from '@busmap/components/colors'
import { Loading } from '@busmap/components/loading'

import { logout } from '@core/api/authn.js'
import { useGlobals } from '@core/globals.js'
import { useStorageDispatch } from '@core/contexts/storage.js'
import { useTheme } from '@module/settings/contexts/theme.js'

import { Page } from './page.js'
import { Dots } from './dots.js'

import type { FC } from 'react'
import type { Mode } from '@busmap/common/types/settings'
Expand Down Expand Up @@ -151,7 +151,7 @@ const Profile: FC = () => {
<div>
{loading ? (
<p>
Signing you out <Loading indent={2} color={mode === 'dark' ? PB90T : PB20T} />
Signing you out <Dots />
</p>
) : (
<SignOutBtn $mode={mode} disabled={loading} onClick={onClickSignOut}>
Expand Down
7 changes: 2 additions & 5 deletions packages/ui/src/components/signIn.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import { toast } from '@busmap/components/toast'
import { Loading } from '@busmap/components/loading'
import { PB20T, PB90T } from '@busmap/components/colors'

import { login } from '@core/api/authn.js'
import { useGlobals } from '@core/globals.js'
import { useStorageDispatch } from '@core/contexts/storage.js'
import { useTheme } from '@module/settings/contexts/theme.js'
import { MAX_USER_FAVORITES } from '@module/favorites/common.js'
import { get as getFavorites } from '@module/favorites/api/get.js'

import { Page } from './page.js'
import { Dots } from './dots.js'

import type { FC } from 'react'
import type { RiderFavoriteItem } from '@busmap/common/types/favorites'
Expand All @@ -22,7 +20,6 @@ const Note = styled.em`
const SignIn: FC = () => {
const ref = useRef<HTMLDivElement>(null)
const { dispatch } = useGlobals()
const { mode } = useTheme()
const [riderFavorites, setRiderFavorites] = useState<RiderFavoriteItem[]>()
const [loading, setLoading] = useState(false)
const storageDispatch = useStorageDispatch()
Expand Down Expand Up @@ -81,7 +78,7 @@ const SignIn: FC = () => {
<Page title="Sign In">
{loading ? (
<p>
Signing you in <Loading indent={2} color={mode === 'dark' ? PB90T : PB20T} />
Signing you in <Dots />
</p>
) : (
<>
Expand Down
2 changes: 0 additions & 2 deletions packages/web/default.dev.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
include /etc/nginx/conf.d/core/compression.conf;

location / {
resolver 8.8.8.8 valid=30s;

proxy_pass http://dev_server;
proxy_http_version 1.1;
proxy_set_header Host $host;
Expand Down
14 changes: 8 additions & 6 deletions packages/web/templates/default.conf.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Configuration to stage a production build locally.
# Known differences:
# - TODO (when they arise)
# Configuration to stage a production build.

server {
# Redirect server configuration
Expand All @@ -14,6 +12,7 @@ server {

server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name ${SERVER_NAME};
root /var/www/${HOST_NAME};
Expand All @@ -22,10 +21,13 @@ server {
include /etc/nginx/conf.d/core/ssl.conf;
include /etc/nginx/conf.d/core/compression.conf;

location / {
resolver 8.8.8.8 valid=30s;
location = /healthcheck {
add_header Content-Type "application/json";
return 200 '{"status": "OK"}';
}

location ~ /(authn|restbus|rider|favorite) {
location / {
location ~ /(authn|restbus|rider|favorite|health) {
access_log /var/log/nginx/api.log api;
proxy_pass http://api_server;
# Allow keepalive to work with the upstream
Expand Down