Builds query to user page

This commit is contained in:
Kalle 2022-08-30 23:15:46 +03:00
parent 0efff0c907
commit f4234b0aa0
7 changed files with 145 additions and 2 deletions

View File

@ -0,0 +1,36 @@
with "BuildWithWeapon" as (
select
"id",
"title",
"description",
"modes",
"headGearSplId",
"clothesGearSplId",
"shoesGearSplId",
"updatedAt",
json_group_array("BuildWeapon"."weaponSplId") as "weapons"
from
"Build"
left join "BuildWeapon" on "BuildWeapon"."buildId" = "Build"."id"
where
"Build"."ownerId" = @userId
group by
"Build"."id"
)
select
"BuildWithWeapon".*,
json_group_array(
json_object(
'ability',
"BuildAbility"."ability",
'gearType',
"BuildAbility"."gearType",
'slotIndex',
"BuildAbility"."slotIndex"
)
) as "abilities"
from
"BuildWithWeapon"
left join "BuildAbility" on "BuildAbility"."buildId" = "BuildWithWeapon"."id"
group by
"BuildWithWeapon"."id";

View File

@ -0,0 +1,6 @@
select
count(*)
from
"Build"
where
"ownerId" = @userId;

View File

@ -1,13 +1,19 @@
import { sql } from "~/db/sql";
import type { Build, BuildAbility, BuildWeapon } from "~/db/types";
import type { Ability, ModeShort } from "~/modules/in-game-lists";
import createBuildSql from "./createBuild.sql";
import createBuildWeaponSql from "./createBuildWeapon.sql";
import createBuildAbilitySql from "./createBuildAbility.sql";
import countByUserIdSql from "./countByUserId.sql";
import buildsByUserIdSql from "./buildsByUserId.sql";
import invariant from "tiny-invariant";
const createBuildStm = sql.prepare(createBuildSql);
const createBuildWeaponStm = sql.prepare(createBuildWeaponSql);
const createBuildAbilityStm = sql.prepare(createBuildAbilitySql);
const countByUserIdStm = sql.prepare(countByUserIdSql);
const buildsByUserIdStm = sql.prepare(buildsByUserIdSql);
interface CreateArgs {
ownerId: Build["ownerId"];
@ -51,3 +57,67 @@ export const create = sql.transaction((build: CreateArgs) => {
});
}
});
export function countByUserId(userId: Build["ownerId"]) {
return (countByUserIdStm.get({ userId })?.count ?? 0) as number;
}
type BuildsByUserRow = Pick<
Build,
| "id"
| "title"
| "description"
| "modes"
| "headGearSplId"
| "clothesGearSplId"
| "shoesGearSplId"
| "updatedAt"
> & { weapons: string; abilities: string };
export function buildsByUserId(userId: Build["ownerId"]) {
const rows = buildsByUserIdStm.all({ userId }) as Array<BuildsByUserRow>;
return rows.map(augmentBuild);
}
function augmentBuild<T>(
row: T & { modes: Build["modes"]; weapons: string; abilities: string }
) {
const modes = row.modes ? (row.modes.split(",") as ModeShort[]) : null;
const weapons = JSON.parse(row.weapons) as Array<BuildWeapon["weaponSplId"]>;
const abilities = JSON.parse(row.abilities) as Array<
Pick<BuildAbility, "ability" | "gearType" | "slotIndex">
>;
return {
...row,
modes,
weapons,
abilities: dbAbilitiesToArrayOfArrays(abilities),
};
}
const gearOrder: Array<BuildAbility["gearType"]> = ["HEAD", "CLOTHES", "SHOES"];
function dbAbilitiesToArrayOfArrays(
abilities: Array<Pick<BuildAbility, "ability" | "gearType" | "slotIndex">>
): [
head: [main: Ability, s1: Ability, s2: Ability, s3: Ability],
clothes: [main: Ability, s1: Ability, s2: Ability, s3: Ability],
shoes: [main: Ability, s1: Ability, s2: Ability, s3: Ability]
] {
const sorted = abilities
.slice()
.sort((a, b) => {
if (a.gearType === b.gearType) return a.slotIndex - b.slotIndex;
return gearOrder.indexOf(a.gearType) - gearOrder.indexOf(b.gearType);
})
.map((a) => a.ability);
invariant(sorted.length === 12, "expected 12 abilities");
return [
[sorted[0]!, sorted[1]!, sorted[2]!, sorted[3]!],
[sorted[4]!, sorted[5]!, sorted[6]!, sorted[7]!],
[sorted[8]!, sorted[9]!, sorted[10]!, sorted[11]!],
];
}

View File

@ -65,6 +65,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
: undefined,
badges: db.badges.countsByUserId(user.id),
results: db.calendarEvents.findResultsByUserId(user.id),
buildsCount: db.builds.countByUserId(user.id),
});
};
@ -91,6 +92,11 @@ export default function UserPageLayout() {
{t("results")}
</SubNavLink>
)}
{(isOwnPage || data.buildsCount > 0) && (
<SubNavLink to="builds" data-cy="builds-page-link">
{t("pages.builds")} ({data.buildsCount})
</SubNavLink>
)}
</SubNav>
<Outlet />
</>

View File

@ -0,0 +1,23 @@
import { json, type LoaderArgs } from "@remix-run/node";
// import { useLoaderData } from "@remix-run/react";
import { Main } from "~/components/Main";
import { db } from "~/db";
import { notFoundIfFalsy } from "~/utils/remix";
import { userParamsSchema } from "../u.$identifier";
export const loader = ({ params }: LoaderArgs) => {
const { identifier } = userParamsSchema.parse(params);
const user = notFoundIfFalsy(db.users.findByIdentifier(identifier));
const builds = db.builds.buildsByUserId(user.id);
return json({ builds });
};
export default function UserBuildsPage() {
// const data = useLoaderData<typeof loader>();
// console.log({ data });
return <Main>hey</Main>;
}

View File

@ -5,6 +5,7 @@
"pages.contributors": "Contributors",
"pages.calendar": "Calendar",
"pages.faq": "FAQ",
"pages.builds": "Builds",
"header.profile": "Profile",
"header.logout": "Log out",

View File

@ -1,3 +1,4 @@
/* eslint-disable no-console */
import fs from "node:fs";
import path from "node:path";
import invariant from "tiny-invariant";
@ -32,7 +33,7 @@ async function main() {
.replace("Path_Wst_", "");
const weaponId = weapons.find(
(w) => w.internalName === weaponInternalName
(w: any) => w.internalName === weaponInternalName
).id;
invariant(typeof weaponId === "number", weaponInternalName + " has no id");
@ -64,7 +65,7 @@ async function main() {
invariant(internalName);
const gearId = gear.find(
(g) => g.internalName === internalName && g.type === type
(g: any) => g.internalName === internalName && g.type === type
)?.id;
invariant(typeof gearId === "number", internalName + " has no id");