From 23c09234d952dd2eebac245b12cd96f3a6964f0a Mon Sep 17 00:00:00 2001 From: Marek Hanzal Date: Fri, 31 Jan 2025 13:14:19 +0100 Subject: [PATCH] Controlled demand in construction --- .../map/$mapId/building/$buildingId/view.tsx | 27 ++++- .../Construction/Requirement/Item.tsx | 104 +++++++++++++++++- .../Requirement/RequirementPanel.tsx | 10 ++ 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/apps/pico/src/@routes/$locale/apps/derivean/map/$mapId/building/$buildingId/view.tsx b/apps/pico/src/@routes/$locale/apps/derivean/map/$mapId/building/$buildingId/view.tsx index 3f74a70e..b82b2725 100644 --- a/apps/pico/src/@routes/$locale/apps/derivean/map/$mapId/building/$buildingId/view.tsx +++ b/apps/pico/src/@routes/$locale/apps/derivean/map/$mapId/building/$buildingId/view.tsx @@ -9,10 +9,13 @@ export const Route = createFileRoute( "/$locale/apps/derivean/map/$mapId/building/$buildingId/view", )({ async loader({ - context: { queryClient, kysely }, + context: { queryClient, kysely, session }, params: { mapId, buildingId }, }) { + const user = await session(); + return { + user, requirement: await queryClient.ensureQueryData({ queryKey: [ "GameMap", @@ -88,6 +91,19 @@ export const Route = createFileRoute( .limit(1) .as("available"); }, + (eb) => { + return eb + .selectFrom("Demand as d") + .select((eb) => { + return Kysely.jsonObject({ + id: eb.ref("d.id"), + priority: eb.ref("d.priority"), + }).as("demand"); + }) + .whereRef("d.resourceId", "=", "br.resourceId") + .where("d.buildingId", "=", buildingId) + .as("demand"); + }, ]) .orderBy("r.name", "asc"), output: z.object({ @@ -103,6 +119,12 @@ export const Route = createFileRoute( name: z.string().min(1), }), ).nullish(), + demand: withJsonSchema( + z.object({ + id: z.string().min(1), + priority: z.number(), + }), + ).nullish(), passive: withBoolSchema(), }), }); @@ -115,10 +137,11 @@ export const Route = createFileRoute( const { building } = useLoaderData({ from: "/$locale/apps/derivean/map/$mapId/building/$buildingId", }); - const { requirement } = Route.useLoaderData(); + const { user, requirement } = Route.useLoaderData(); return building.constructionId ? diff --git a/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/Item.tsx b/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/Item.tsx index 4f748e3c..6882350f 100644 --- a/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/Item.tsx +++ b/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/Item.tsx @@ -1,18 +1,76 @@ -import { Icon, Progress } from "@use-pico/client"; +import { useMutation } from "@tanstack/react-query"; +import { + ArrowDownIcon, + ArrowUpIcon, + Badge, + Button, + Icon, + Progress, + Tx, + useInvalidator, +} from "@use-pico/client"; import { toHumanNumber, tvc } from "@use-pico/common"; import type { FC } from "react"; +import { kysely } from "~/app/derivean/db/kysely"; import type { RequirementPanel } from "~/app/derivean/game/GameMap2/Construction/Requirement/RequirementPanel"; import { DemandIcon } from "~/app/derivean/icon/DemandIcon"; import { PackageIcon } from "~/app/derivean/icon/PackageIcon"; export namespace Item { export interface Props { + mapId: string; + userId: string; requirement: RequirementPanel.Requirement; } } -export const Item: FC = ({ requirement }) => { +export const Item: FC = ({ mapId, userId, requirement }) => { + const invalidator = useInvalidator([["GameMap"]]); const available = requirement.available || 0; + const priorityUpMutation = useMutation({ + async mutationFn({ demandId }: { demandId: string }) { + return kysely.transaction().execute(async (tx) => { + const { priority } = await tx + .selectFrom("Demand as d") + .select((eb) => eb.fn.max("d.priority").as("priority")) + .where("d.mapId", "=", mapId) + .where("d.userId", "=", userId) + .where("d.id", "!=", demandId) + .executeTakeFirstOrThrow(); + + return tx + .updateTable("Demand") + .set({ priority: priority + 1 }) + .where("id", "=", demandId) + .execute(); + }); + }, + async onSuccess() { + await invalidator(); + }, + }); + const priorityDownMutation = useMutation({ + async mutationFn({ demandId }: { demandId: string }) { + return kysely.transaction().execute(async (tx) => { + const { priority } = await tx + .selectFrom("Demand as d") + .select((eb) => eb.fn.min("d.priority").as("priority")) + .where("d.mapId", "=", mapId) + .where("d.userId", "=", userId) + .where("d.id", "!=", demandId) + .executeTakeFirstOrThrow(); + + return tx + .updateTable("Demand") + .set({ priority: priority - 1 }) + .where("id", "=", demandId) + .execute(); + }); + }, + async onSuccess() { + await invalidator(); + }, + }); return (
= ({ requirement }) => { available >= requirement.amount ? ["bg-green-500"] : undefined, }} /> + + {requirement.demand ? +
+ + + {requirement.demand.priority} + + +
+ : null}
); }; diff --git a/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/RequirementPanel.tsx b/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/RequirementPanel.tsx index 9ffe03a9..326c40b4 100644 --- a/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/RequirementPanel.tsx +++ b/apps/pico/src/app/derivean/game/GameMap2/Construction/Requirement/RequirementPanel.tsx @@ -16,6 +16,11 @@ export namespace RequirementPanel { id: string; } + export interface Demand { + id: string; + priority: number; + } + export interface Requirement { id: string; name: string; @@ -23,16 +28,19 @@ export namespace RequirementPanel { transport: number; supply?: Supply | null; available?: number | null; + demand?: Demand | null; passive: boolean; } export interface Props extends Panel.PropsEx { + userId: string; building: Building; requirement: Requirement[]; } } export const RequirementPanel: FC = ({ + userId, building, requirement, ...props @@ -61,6 +69,8 @@ export const RequirementPanel: FC = ({ return ( );