Ctrl K
ComunidadGuías…

src/components/dives/dive-hero.tsx

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"use client";

import type { ReactNode } from "react";
import type { Dive } from "@/lib/api/dives";
import { MapBackdrop } from "@/components/map/map-backdrop";
import { UnplacedBackdrop } from "@/components/ui/backdrop-card";
import { MapHero, type MapHeroFigure } from "@/components/ui/map-hero";
import { diveFacts } from "@/components/dives/dive-facts";
import { diveChips } from "@/components/dives/dive-chips";
import { DiveTitle } from "@/components/dives/dive-title";
import {
  diveMapLocations,
  hasMapPosition,
} from "@/components/dives/dive-map-locations";
import { DiveIcon } from "@/components/logo";
import { useUnits } from "@/hooks/useUnits";
import { formatDurationHoursMinutes } from "@/lib/date-time";
import { formatDepth, formatTemperature, formatVisibility } from "@/lib/units";
import type { ReturnTarget } from "@/lib/return-to";

/**
 * The dive page's heading: the dive's card drawn the width of the window, with
 * its figures in whole units as the card rounds them - its dive time, its
 * maximum depth, the water's temperature, the visibility and how many species
 * were seen, each but the dive time only where the dive records it.
 */
export function DiveHero({
  dive,
  back,
  actions,
}: {
  dive: Dive;
  back: ReturnTarget;
  actions?: ReactNode;
}) {
  const units = useUnits();
  const locations = diveMapLocations(dive);
  const isPlaced = hasMapPosition(locations);
  const facts = diveFacts(dive);

  const wholeDepth = (meters: number) =>
    formatDepth(meters, units, { decimals: 0 });
  const figures: MapHeroFigure[] = [
    {
      label: "Dive time",
      value: formatDurationHoursMinutes(dive.submerged_time),
    },
  ];
  if (dive.max_depth != null) {
    figures.push({ label: "Max depth", value: wholeDepth(dive.max_depth) });
  }
  // `!= null` throughout, since 0 °C is a reading.
  if (dive.bottom_temperature != null) {
    figures.push({
      label: "Water temp",
      value: formatTemperature(dive.bottom_temperature, units, {
        decimals: 0,
      }),
    });
  }
  if (dive.visibility != null) {
    figures.push({
      label: "Visibility",
      value: formatVisibility(dive.visibility, units),
    });
  }
  // A dive lists each species once, so its sightings are its species.
  const speciesCount = dive.sightings?.length ?? 0;
  if (speciesCount > 0) {
    figures.push({ label: "Species seen", value: speciesCount });
  }

  return (
    <MapHero
      backHref={back.href}
      backLabel={back.label}
      icon={DiveIcon}
      actions={actions}
      title={
        <DiveTitle
          diveNumber={dive.dive_number}
          sites={dive.dive_sites}
          course={dive.course_uuid != null}
          rating={dive.rating}
        />
      }
      subtitle={facts}
      overline={diveChips(dive)}
      figures={figures}
      mapCredit={isPlaced}
      // The map's water for a dive with no position, or where this instance
      // draws no map, as its card draws one.
      backdrop={({ map, covered }) => (
        <MapBackdrop
          locations={locations}
          subject={`the location of dive #${dive.dive_number}`}
          {...map}
          water={
            <UnplacedBackdrop
              coveredBottom={covered.bottom}
              coveredTop={covered.top}
              hero
              icon={DiveIcon}
            />
          }
        />
      )}
    />
  );
}