import clsx from "clsx"; import { useTranslation } from "~/hooks/useTranslation"; import { Link } from "react-router-dom"; import type { Build, BuildWeapon, GearType, UserWithPlusTier, } from "~/db/types"; import { useIsMounted } from "~/hooks/useIsMounted"; import { useUser } from "~/features/auth/core"; import type { Ability as AbilityType, ModeShort, } from "~/modules/in-game-lists"; import type { BuildAbilitiesTuple } from "~/modules/in-game-lists/types"; import { databaseTimestampToDate } from "~/utils/dates"; import { discordFullName, gearTypeToInitial } from "~/utils/strings"; import { analyzerPage, gearImageUrl, mainWeaponImageUrl, modeImageUrl, mySlugify, navIconUrl, userBuildsPage, weaponBuildPage, } from "~/utils/urls"; import { Ability } from "./Ability"; import { Button, LinkButton } from "./Button"; import { FormWithConfirm } from "./FormWithConfirm"; import { TrashIcon } from "./icons/Trash"; import { EditIcon } from "./icons/Edit"; import { Image } from "./Image"; import { Popover } from "./Popover"; import { InfoIcon } from "./icons/Info"; import { LockIcon } from "./icons/Lock"; import type { BuildWeaponWithTop500Info } from "~/features/builds"; import { altWeaponIdToId } from "~/modules/in-game-lists/weapon-ids"; interface BuildProps { build: Pick< Build, | "id" | "title" | "description" | "clothesGearSplId" | "headGearSplId" | "shoesGearSplId" | "updatedAt" | "private" > & { abilities: BuildAbilitiesTuple; modes: ModeShort[] | null; weapons: Array<{ weaponSplId: BuildWeapon["weaponSplId"]; minRank: number | null; maxPower: number | null; }>; }; owner?: Pick< UserWithPlusTier, "discordId" | "discordName" | "discordDiscriminator" | "plusTier" >; canEdit?: boolean; } export function BuildCard({ build, owner, canEdit = false }: BuildProps) { const user = useUser(); const { t } = useTranslation(["weapons", "builds", "common", "game-misc"]); const { i18n } = useTranslation(); const isMounted = useIsMounted(); const { id, title, description, clothesGearSplId, headGearSplId, shoesGearSplId, updatedAt, abilities, modes, weapons, } = build; return (
{modes && modes.length > 0 && (
{modes.map((mode) => ( {t(`game-misc:MODE_LONG_${mode}` ))}
)}

{title}

{owner && ( <> {discordFullName(owner)}
)} {owner?.plusTier ? ( <> +{owner.plusTier}
) : null}
{build.private ? (
{" "} {t("common:build.private")}
) : null}
{weapons.map((weapon) => ( ))} {weapons.length === 1 && (
{t(`weapons:MAIN_${weapons[0]!.weaponSplId}` as any)}
)}
{t("common:pages.analyzer")} {description ? ( } triggerClassName="minimal tiny build__small-text" > {description} ) : null} {canEdit && ( <> )}
); } function RoundWeaponImage({ weapon }: { weapon: BuildWeaponWithTop500Info }) { const { weaponSplId, maxPower, minRank } = weapon; const normalizedWeaponSplId = altWeaponIdToId.get(weaponSplId) ?? weaponSplId; const { t } = useTranslation(["weapons"]); const slug = mySlugify( t(`weapons:MAIN_${normalizedWeaponSplId}`, { lng: "en" }), ); const isTop500 = typeof maxPower === "number" && typeof minRank === "number"; return (
{isTop500 ? ( ) : null} {t(`weapons:MAIN_${weaponSplId}`
); } function AbilitiesRowWithGear({ gearType, abilities, gearId, }: { gearType: GearType; abilities: AbilityType[]; gearId: number; }) { const { t } = useTranslation(["gear"]); const translatedGearName = t( `gear:${gearTypeToInitial(gearType)}_${gearId}` as any, ); return ( <> {translatedGearName} {abilities.map((ability, i) => ( ))} ); }