sendou.ink/app/components/BuildCard.tsx
Kalle e97fcd4e99 Remove Cypress tests
Planned to be replaced with Playwright maybe?
Just removing in the meanwhile so they don't confuse people.
Or that people won't accidentally develop new.
2022-10-30 02:15:15 +03:00

230 lines
6.1 KiB
TypeScript

import clsx from "clsx";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import type { Build, BuildWeapon, GearType, User } from "~/db/types";
import { useIsMounted } from "~/hooks/useIsMounted";
import { useUser } from "~/modules/auth";
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,
navIconUrl,
userBuildsPage,
} 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";
interface BuildProps {
build: Pick<
Build,
| "id"
| "title"
| "description"
| "clothesGearSplId"
| "headGearSplId"
| "shoesGearSplId"
| "updatedAt"
> & {
abilities: BuildAbilitiesTuple;
modes: ModeShort[] | null;
weapons: Array<BuildWeapon["weaponSplId"]>;
};
owner?: Pick<User, "discordId" | "discordDiscriminator" | "discordName">;
canEdit?: boolean;
}
export function BuildCard({ build, owner, canEdit = false }: BuildProps) {
const user = useUser();
const { t } = useTranslation(["weapons", "builds", "common"]);
const { i18n } = useTranslation();
const isMounted = useIsMounted();
const {
id,
title,
description,
clothesGearSplId,
headGearSplId,
shoesGearSplId,
updatedAt,
abilities,
modes,
weapons,
} = build;
return (
<div className="build">
<div className="stack xxs">
<div className="build__top-row">
<h2 className="build__title">{title}</h2>
{modes && modes.length > 0 && (
<div className="build__modes">
{modes.map((mode) => (
<Image
key={mode}
alt=""
path={modeImageUrl(mode)}
width={18}
height={18}
/>
))}
</div>
)}
</div>
<div className="build__date-author-row">
{owner && (
<>
<Link to={userBuildsPage(owner)}>{discordFullName(owner)}</Link>
<div></div>
</>
)}
<time className={clsx({ invisible: !isMounted })}>
{isMounted
? databaseTimestampToDate(updatedAt).toLocaleDateString(
i18n.language,
{
day: "numeric",
month: "numeric",
year: "numeric",
}
)
: "t"}
</time>
</div>
</div>
<div className="build__weapons">
{weapons.map((weaponSplId) => (
<div key={weaponSplId} className="build__weapon">
<Image
path={mainWeaponImageUrl(weaponSplId)}
alt={t(`weapons:MAIN_${weaponSplId}` as any)}
title={t(`weapons:MAIN_${weaponSplId}` as any)}
height={36}
width={36}
/>
</div>
))}
{weapons.length === 1 && (
<div className="build__weapon-text">
{t(`weapons:MAIN_${weapons[0]!}` as any)}
</div>
)}
</div>
<div className="build__gear-abilities">
<AbilitiesRowWithGear
gearType="HEAD"
abilities={abilities[0]}
gearId={headGearSplId}
/>
<AbilitiesRowWithGear
gearType="CLOTHES"
abilities={abilities[1]}
gearId={clothesGearSplId}
/>
<AbilitiesRowWithGear
gearType="SHOES"
abilities={abilities[2]}
gearId={shoesGearSplId}
/>
</div>
<div className="build__bottom-row">
<Link
to={analyzerPage({
weaponId: weapons[0]!,
abilities: abilities.flat(),
})}
>
<Image
alt={t("common:pages.analyzer")}
className="build__icon"
path={navIconUrl("analyzer")}
/>
</Link>
{description && (
<Popover
buttonChildren={<InfoIcon className="build__icon" />}
triggerClassName="minimal tiny build__small-text"
>
{description}
</Popover>
)}
{canEdit && (
<>
<LinkButton
className="build__small-text"
variant="minimal"
tiny
to={`new?buildId=${id}&userId=${user!.id}`}
>
<EditIcon className="build__icon" />
</LinkButton>
<FormWithConfirm
dialogHeading={t("builds:deleteConfirm", { title })}
fields={[["buildToDeleteId", id]]}
>
<Button
className="build__small-text"
variant="minimal-destructive"
tiny
type="submit"
>
<TrashIcon className="build__icon" />
</Button>
</FormWithConfirm>
</>
)}
</div>
</div>
);
}
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 (
<>
<Image
height={64}
width={64}
alt={translatedGearName}
title={translatedGearName}
path={gearImageUrl(gearType, gearId)}
className="build__gear"
/>
{abilities.map((ability, i) => (
<Ability
key={i}
ability={ability}
size={i === 0 ? "MAIN" : "SUB"}
readonly
/>
))}
</>
);
}