From 8c10719124d56133485cc4b67132e0187a5c16d3 Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Mon, 21 Sep 2026 20:47:52 -0400 Subject: [PATCH] feat(banking): currency emblem image with CurrencyAmount display wrapper - GameRules currencySymbol switches from text to a media upload - new CurrencyAmount/CurrencySymbol components; render sites across banking, base management, dashboard, market, campaigns and profiles are wired to show the emblem inline with amounts - migration 20260921_140000 (text column out, media fk in); registers the five pending migrations in the migrations index --- public/images/assets/icons/credits.png | Bin 0 -> 25488 bytes src/collections/game/GameRules.ts | 6 ++- .../frontend/banking/AccountCard.tsx | 11 +++-- .../frontend/banking/AccountDetail.tsx | 11 +++-- .../frontend/banking/BankingOverview.tsx | 5 +- .../frontend/banking/CurrencyAmount.tsx | 23 ++++++++++ .../frontend/banking/CurrencySymbol.tsx | 43 ++++++++++++++++++ .../frontend/banking/WalletHero.tsx | 11 +++-- .../baseManagement/EmploymentCard.tsx | 5 +- .../frontend/baseManagement/StaffRoster.tsx | 15 +++--- .../frontend/dashboard/OpenNegotiations.tsx | 5 +- .../frontend/dashboard/WalletWidget.tsx | 5 +- .../frontend/intelligence/CampaignCard.tsx | 4 +- .../frontend/market/ListingCard.tsx | 7 +-- .../frontend/market/MakeOfferDialog.tsx | 15 +++--- .../market/NegotiationHistoryDialog.tsx | 5 +- .../frontend/market/NegotiationPanel.tsx | 5 +- .../frontend/personnel/NpcProfile.tsx | 4 +- src/lib/banking/format.ts | 11 ++--- src/lib/banking/index.ts | 7 ++- ...260921_140000_add_currency_symbol_image.ts | 17 +++++++ src/migrations/index.ts | 32 ++++++++++++- 22 files changed, 185 insertions(+), 62 deletions(-) create mode 100644 public/images/assets/icons/credits.png create mode 100644 src/components/frontend/banking/CurrencyAmount.tsx create mode 100644 src/components/frontend/banking/CurrencySymbol.tsx create mode 100644 src/migrations/20260921_140000_add_currency_symbol_image.ts diff --git a/public/images/assets/icons/credits.png b/public/images/assets/icons/credits.png new file mode 100644 index 0000000000000000000000000000000000000000..b99cfe6c75d32d6893950fa07451a354e7fca460 GIT binary patch literal 25488 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 XrW=_J=UoLT00000NkvXXu0mjfZRKD> literal 0 HcmV?d00001 diff --git a/src/collections/game/GameRules.ts b/src/collections/game/GameRules.ts index d829f47..70dba61 100644 --- a/src/collections/game/GameRules.ts +++ b/src/collections/game/GameRules.ts @@ -37,9 +37,11 @@ export const GameRules: GlobalConfig = { }, { name: "currencySymbol", - type: "text", + type: "upload", + relationTo: "media", admin: { - description: 'Optional symbol prefixed to amounts, e.g. "¢" or "$".', + description: + "Optional emblem shown inline before amounts. It renders scaled to the surrounding text height (unlike a text symbol).", }, }, { diff --git a/src/components/frontend/banking/AccountCard.tsx b/src/components/frontend/banking/AccountCard.tsx index ccd7325..42038ec 100644 --- a/src/components/frontend/banking/AccountCard.tsx +++ b/src/components/frontend/banking/AccountCard.tsx @@ -11,7 +11,8 @@ import { ItemTitle, } from "@/components/ui/item"; import { LandmarkIcon, UsersIcon, WalletIcon } from "lucide-react"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { type AccountStatus, accountStatusBase, accountStatusClass } from "@/lib/banking/ui"; const accountTypeMeta = { @@ -85,10 +86,10 @@ export function AccountCard({
- {formatAmount( - account.balance ?? 0, - currencyConfig.resourceName ? currencyConfig : currency, - )} +
diff --git a/src/components/frontend/banking/AccountDetail.tsx b/src/components/frontend/banking/AccountDetail.tsx index 21195d3..ea00c0b 100644 --- a/src/components/frontend/banking/AccountDetail.tsx +++ b/src/components/frontend/banking/AccountDetail.tsx @@ -4,7 +4,8 @@ import Link from "next/link"; import { ArrowLeftIcon } from "lucide-react"; import type { BankAccount, LedgerEntry } from "@/payload-types"; import { Item, ItemContent, ItemHeader, ItemTitle } from "@/components/ui/item"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { type AccountStatus, accountStatusBase, accountStatusClass } from "@/lib/banking/ui"; import { accountTypeInfo } from "@/components/frontend/banking/AccountCard"; import { BankTransactionDialog } from "@/components/frontend/banking/BankTransactionDialog"; @@ -72,10 +73,10 @@ export function AccountDetail({ Balance

- {formatAmount( - account.balance ?? 0, - currencyConfig.resourceName ? currencyConfig : currency, - )} +

diff --git a/src/components/frontend/banking/BankingOverview.tsx b/src/components/frontend/banking/BankingOverview.tsx index ac30f55..0b6f689 100644 --- a/src/components/frontend/banking/BankingOverview.tsx +++ b/src/components/frontend/banking/BankingOverview.tsx @@ -8,7 +8,8 @@ import { AccountCard } from "@/components/frontend/banking/AccountCard"; import { CreateAccountDialog } from "@/components/frontend/banking/CreateAccountDialog"; import { WalletHero } from "@/components/frontend/banking/WalletHero"; import { ActivityFeed } from "@/components/frontend/banking/ActivityFeed"; -import { currencyLabel, formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import { currencyLabel, type CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { usePageCommands } from "@/components/command-palette/CommandPaletteContext"; interface BankingOverviewProps { @@ -126,7 +127,7 @@ export function BankingOverview({ {isManager && (

UNIT FUNDS  - {formatAmount(unitFundsTotal, currencyConfig)} +

)} diff --git a/src/components/frontend/banking/CurrencyAmount.tsx b/src/components/frontend/banking/CurrencyAmount.tsx new file mode 100644 index 0000000..4a8c7b0 --- /dev/null +++ b/src/components/frontend/banking/CurrencyAmount.tsx @@ -0,0 +1,23 @@ +import { formatAmount } from "@/lib/banking/format"; +import type { CurrencyDisplay } from "@/lib/banking/format"; +import { CurrencySymbol } from "./CurrencySymbol"; + +/** + * Renders the currency emblem inline with the formatted amount. Delegates + * symbol rendering to and formatting to formatAmount. + * Renders nothing when currency is null/undefined/number (no emblem to show). + */ +export function CurrencyAmount({ + value, + currency, +}: { + readonly value: number; + readonly currency?: CurrencyDisplay | null; +}) { + return ( + <> + + {formatAmount(value, currency)} + + ); +} diff --git a/src/components/frontend/banking/CurrencySymbol.tsx b/src/components/frontend/banking/CurrencySymbol.tsx new file mode 100644 index 0000000..1d80af2 --- /dev/null +++ b/src/components/frontend/banking/CurrencySymbol.tsx @@ -0,0 +1,43 @@ +import type { CurrencyDisplay } from "@/lib/banking/format"; +import { cn } from "@/lib/utils"; +import { isCurrencyConfig } from "@/lib/banking/format"; + +/** + * Renders the currency emblem (an uploaded media image) inline with the text it + * sits beside. The image height is tied to the surrounding font size and capped + * just below the line height, so it scales with the text and never floats larger + * than the numbers next to it. Renders nothing when no emblem is configured. + */ +export function CurrencySymbol({ + currency, + className, + ariaHidden, +}: { + readonly currency?: CurrencyDisplay | null; + readonly className?: string; + readonly ariaHidden?: boolean; +}) { + if (!currency || typeof currency !== "object") return null; + const config = isCurrencyConfig(currency) ? currency : null; + const symbolUrl = config?.symbol; + if (!symbolUrl) return null; + + const alt = + config?.name?.trim() || + config?.singular?.trim() || + config?.plural?.trim() || + "currency symbol"; + + return ( + {alt} + ); +} diff --git a/src/components/frontend/banking/WalletHero.tsx b/src/components/frontend/banking/WalletHero.tsx index 51ebd0c..2d7398f 100644 --- a/src/components/frontend/banking/WalletHero.tsx +++ b/src/components/frontend/banking/WalletHero.tsx @@ -6,7 +6,8 @@ import Link from "next/link"; import { ChevronRightIcon, WalletIcon } from "lucide-react"; import type { BankAccount, Resource } from "@/payload-types"; import { Button } from "@/components/ui/button"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { type AccountStatus, accountStatusBase, @@ -120,10 +121,10 @@ export function WalletHero({

- {formatAmount( - account.balance ?? 0, - currencyConfig.resourceName ? currencyConfig : currency, - )} +

LAST ACTIVITY {lastActivity} diff --git a/src/components/frontend/baseManagement/EmploymentCard.tsx b/src/components/frontend/baseManagement/EmploymentCard.tsx index 309ef2c..2a51e6c 100644 --- a/src/components/frontend/baseManagement/EmploymentCard.tsx +++ b/src/components/frontend/baseManagement/EmploymentCard.tsx @@ -1,7 +1,8 @@ "use client"; import { BadgeCheckIcon } from "lucide-react"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { HireStaffDialog } from "@/components/frontend/baseManagement/HireStaffDialog"; import type { LaborClassificationOption, StructureStaffingOption } from "@/lib/base"; @@ -51,7 +52,7 @@ export function EmploymentCard({ {employment.positionType} · {employment.structureName}

- {formatAmount(employment.salary, currencyConfig)}/tick + /tick

) : canManage && hiringEnabled ? ( diff --git a/src/components/frontend/baseManagement/StaffRoster.tsx b/src/components/frontend/baseManagement/StaffRoster.tsx index 9743ec9..9408beb 100644 --- a/src/components/frontend/baseManagement/StaffRoster.tsx +++ b/src/components/frontend/baseManagement/StaffRoster.tsx @@ -17,7 +17,8 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; -import { currencyLabel, formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import { currencyLabel, type CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import type { LaborClassificationOption, StructureStaffingOption } from "@/lib/base"; import { HireStaffDialog } from "@/components/frontend/baseManagement/HireStaffDialog"; @@ -239,7 +240,7 @@ export function StaffRoster({ {structure.maintenanceCost > 0 && (

- Maintenance: {formatAmount(structure.maintenanceCost, currencyConfig)}/tick + Maintenance: /tick

)} @@ -262,7 +263,7 @@ export function StaffRoster({
- {formatAmount(member.salary, currencyConfig)}/tick + /tick {canManage && (
diff --git a/src/components/frontend/dashboard/OpenNegotiations.tsx b/src/components/frontend/dashboard/OpenNegotiations.tsx index 72b5c5c..9f1e7e4 100644 --- a/src/components/frontend/dashboard/OpenNegotiations.tsx +++ b/src/components/frontend/dashboard/OpenNegotiations.tsx @@ -2,7 +2,8 @@ import Link from "next/link"; import type { MarketListing, MarketNegotiation } from "@/payload-types"; import { Item, ItemContent, ItemHeader, ItemTitle } from "@/components/ui/item"; import { ChevronRightIcon, HandshakeIcon } from "lucide-react"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; interface OpenNegotiationsProps { readonly negotiations: MarketNegotiation[]; @@ -36,7 +37,7 @@ export function OpenNegotiations({ // NegotiationPanel on the market page). if (negotiations.length === 0) return null; - const fmt = (n: number) => formatAmount(n, currencyConfig); + const fmt = (n: number) => ; return ( diff --git a/src/components/frontend/dashboard/WalletWidget.tsx b/src/components/frontend/dashboard/WalletWidget.tsx index 8d96b25..4ffcb81 100644 --- a/src/components/frontend/dashboard/WalletWidget.tsx +++ b/src/components/frontend/dashboard/WalletWidget.tsx @@ -2,7 +2,8 @@ import Link from "next/link"; import type { BankAccount } from "@/payload-types"; import { Item, ItemContent, ItemHeader, ItemTitle } from "@/components/ui/item"; import { WalletIcon } from "lucide-react"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { MyWalletCard } from "@/components/frontend/banking/MyWalletCard"; interface WalletWidgetProps { @@ -44,7 +45,7 @@ export function WalletWidget({ account, currencyConfig }: WalletWidgetProps) { - {formatAmount(account.balance ?? 0, currencyConfig)} + diff --git a/src/components/frontend/intelligence/CampaignCard.tsx b/src/components/frontend/intelligence/CampaignCard.tsx index 24ba1b4..baccc56 100644 --- a/src/components/frontend/intelligence/CampaignCard.tsx +++ b/src/components/frontend/intelligence/CampaignCard.tsx @@ -43,8 +43,8 @@ const officialStatusLabels: Record = { const statusTextColors: Record = { concept: "text-muted-foreground", - inProgress: "text-blue-400", - completed: "text-emerald-400", + inProgress: "text-emerald-400", + completed: "text-blue-400", }; export function CampaignCard({ campaign, size = "lg" }: CampaignCardProps) { diff --git a/src/components/frontend/market/ListingCard.tsx b/src/components/frontend/market/ListingCard.tsx index 4420726..2621a7c 100644 --- a/src/components/frontend/market/ListingCard.tsx +++ b/src/components/frontend/market/ListingCard.tsx @@ -18,7 +18,8 @@ import { } from "@/components/ui/item"; import { getAssetTypeLabel } from "@/lib/locker"; import { getRarityLabel, isNegotiable } from "@/lib/market"; -import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import type { CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { PackageIcon, ShoppingCartIcon, XIcon } from "lucide-react"; import { buyMarketListing, cancelMarketListing, resetEconomyState } from "@/app/(frontend)/logistics/market/actions"; import { Countdown } from "@/components/frontend/market/Countdown"; @@ -156,7 +157,7 @@ export function ListingCard({
- {formatAmount(listing.price ?? 0, currencyConfig)} / unit + / unit {stock > 1 && ( @@ -164,7 +165,7 @@ export function ListingCard({ {selectedQty}/{stock} ·{" "} )} - total {formatAmount(selectedTotal, currencyConfig)} + total
{error &&

{error}

} diff --git a/src/components/frontend/market/MakeOfferDialog.tsx b/src/components/frontend/market/MakeOfferDialog.tsx index 4b36db7..ef8764c 100644 --- a/src/components/frontend/market/MakeOfferDialog.tsx +++ b/src/components/frontend/market/MakeOfferDialog.tsx @@ -17,6 +17,7 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { currencyLabel, formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { acceptMarketOffer, cancelMarketOffer, @@ -239,7 +240,7 @@ export function MakeOfferDialog({ listing, currentUserId, currencyConfig }: Make Negotiate: {assetName} {isVendor ? "Haggle with the vendor." : "Send an offer to the seller."} Asking{" "} - {fmt(listing.price ?? 0)} per unit. + per unit. @@ -282,7 +283,7 @@ export function MakeOfferDialog({ listing, currentUserId, currencyConfig }: Make )}

{stock}× in stock · total{" "} - {fmt((Number(amount) || 0) * clampedQty)} +

{error &&

{error}

} @@ -291,7 +292,7 @@ export function MakeOfferDialog({ listing, currentUserId, currencyConfig }: Make {outcome?.kind === "pending" && (

- Offer of {fmt(negotiation?.amount ?? 0)} each for{" "} + Offer of each for{" "} {negotiation?.quantity ?? stock}× sent. Waiting for the{" "} {isVendor ? "vendor" : "seller"} to respond.

@@ -303,10 +304,10 @@ export function MakeOfferDialog({ listing, currentUserId, currencyConfig }: Make

The {isVendor ? "vendor" : "seller"} counteroffers{" "} - {fmt(negotiation?.amount ?? 0)}{" "} + {" "} each for {negotiation?.quantity ?? stock}×, for a total of{" "} - {fmt((negotiation?.amount ?? 0) * (negotiation?.quantity ?? stock))} + .

@@ -330,10 +331,10 @@ export function MakeOfferDialog({ listing, currentUserId, currencyConfig }: Make Deal closed

- Sold at {fmt(outcome.price)} each for{" "} + Sold at each for{" "} {negotiation?.quantity ?? stock}×, for a total of{" "} - {fmt(outcome.price * (negotiation?.quantity ?? stock))} + . The items are in your locker.

diff --git a/src/components/frontend/market/NegotiationHistoryDialog.tsx b/src/components/frontend/market/NegotiationHistoryDialog.tsx index 9abd337..8d3e728 100644 --- a/src/components/frontend/market/NegotiationHistoryDialog.tsx +++ b/src/components/frontend/market/NegotiationHistoryDialog.tsx @@ -14,7 +14,8 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { currencyLabel, formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import { currencyLabel, type CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { buildChatBubbles } from "@/lib/market/chatBubbles"; import { ChatBubbleList } from "@/components/frontend/market/ChatBubbleList"; import { ChevronDownIcon, HistoryIcon } from "lucide-react"; @@ -79,7 +80,7 @@ function HistoryRow({ }) { const [expanded, setExpanded] = useState(false); const listing = listingOf(negotiation); - const fmt = (n: number) => formatAmount(n, currencyConfig); + const fmt = (n: number) => ; const buyerLabel = role === "buyer" ? "You" : counterpartyLabel(negotiation, "buyer"); const sellerLabel = role === "seller" ? "You" : counterpartyLabel(negotiation, "seller"); diff --git a/src/components/frontend/market/NegotiationPanel.tsx b/src/components/frontend/market/NegotiationPanel.tsx index 5324fb7..9d2736a 100644 --- a/src/components/frontend/market/NegotiationPanel.tsx +++ b/src/components/frontend/market/NegotiationPanel.tsx @@ -7,6 +7,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { formatAmount, type CurrencyConfig } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; import { acceptMarketOffer, cancelMarketOffer, @@ -131,7 +132,7 @@ function NegotiationRow({
- {fmt(negotiation.amount ?? 0)} + {negotiation.quantity ? `${negotiation.quantity}× · ` : ""} {theirProposal @@ -143,7 +144,7 @@ function NegotiationRow({ <> {" · total "} - {fmt((negotiation.amount ?? 0) * negotiation.quantity)} + ) : null} diff --git a/src/components/frontend/personnel/NpcProfile.tsx b/src/components/frontend/personnel/NpcProfile.tsx index 1b08d5f..794b361 100644 --- a/src/components/frontend/personnel/NpcProfile.tsx +++ b/src/components/frontend/personnel/NpcProfile.tsx @@ -14,7 +14,7 @@ import { CardTitle, } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; -import { formatAmount } from "@/lib/banking/format"; +import { CurrencyAmount } from "@/components/frontend/banking/CurrencyAmount"; interface NpcProfileProps { readonly npc: GameNpc; @@ -183,7 +183,7 @@ export function NpcProfile({ npc, listings }: NpcProfileProps) {

- {formatAmount(listing.price, listing.currency)} +
diff --git a/src/lib/banking/format.ts b/src/lib/banking/format.ts index 571af70..93197a0 100644 --- a/src/lib/banking/format.ts +++ b/src/lib/banking/format.ts @@ -14,9 +14,9 @@ export interface CurrencyConfig { readonly resourceName?: string | null; } -type CurrencyDisplay = CurrencyLike | CurrencyConfig | number | null; +export type CurrencyDisplay = CurrencyLike | CurrencyConfig | number | null; -function isCurrencyConfig(currency: CurrencyLike | CurrencyConfig): currency is CurrencyConfig { +export function isCurrencyConfig(currency: CurrencyLike | CurrencyConfig): currency is CurrencyConfig { return ( "symbol" in currency || "singular" in currency || @@ -47,12 +47,7 @@ export function currencyLabel(currency?: CurrencyDisplay, amount = 2): string { export function formatAmount(amount: number, currency?: CurrencyDisplay): string { const n = amount.toLocaleString(); const label = currencyLabel(currency, amount); - const symbol = - currency && typeof currency === "object" && isCurrencyConfig(currency) - ? currency.symbol?.trim() - : undefined; - const formatted = symbol ? `${symbol}${n}` : n; - return label ? `${formatted} ${label}` : formatted; + return label ? `${n} ${label}` : n; } export function formatDate(date: string | Date): string { diff --git a/src/lib/banking/index.ts b/src/lib/banking/index.ts index fcc9092..e0cee12 100644 --- a/src/lib/banking/index.ts +++ b/src/lib/banking/index.ts @@ -51,10 +51,15 @@ export async function getMainCurrencyName(payload: PayloadType): Promise= 1, so guard the object case before reading .url. + const symbolMedia = + typeof rules.currencySymbol === "object" && rules.currencySymbol !== null + ? rules.currencySymbol + : null; return { name: rules.currencyName?.trim() || null, - symbol: rules.currencySymbol?.trim() || null, + symbol: symbolMedia?.url ?? null, singular: rules.currencySingular?.trim() || null, plural: rules.currencyPlural?.trim() || null, denomination: rules.currencyDenomination?.trim() || null, diff --git a/src/migrations/20260921_140000_add_currency_symbol_image.ts b/src/migrations/20260921_140000_add_currency_symbol_image.ts new file mode 100644 index 0000000..7479774 --- /dev/null +++ b/src/migrations/20260921_140000_add_currency_symbol_image.ts @@ -0,0 +1,17 @@ +import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' + +export async function up({ db, payload, req }: MigrateUpArgs): Promise { + await db.execute(sql` + ALTER TABLE "game_rules" DROP COLUMN "currency_symbol"; + ALTER TABLE "game_rules" ADD COLUMN "currency_symbol_id" integer; + ALTER TABLE "game_rules" ADD CONSTRAINT "game_rules_currency_symbol_id_media_id_fk" FOREIGN KEY ("currency_symbol_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action; + CREATE INDEX "game_rules_currency_symbol_idx" ON "game_rules" USING btree ("currency_symbol_id");`) +} + +export async function down({ db, payload, req }: MigrateDownArgs): Promise { + await db.execute(sql` + ALTER TABLE "game_rules" DROP CONSTRAINT "game_rules_currency_symbol_id_media_id_fk"; + DROP INDEX "game_rules_currency_symbol_idx"; + ALTER TABLE "game_rules" DROP COLUMN "currency_symbol_id"; + ALTER TABLE "game_rules" ADD COLUMN "currency_symbol" varchar;`) +} diff --git a/src/migrations/index.ts b/src/migrations/index.ts index 51e72b1..b0af3aa 100644 --- a/src/migrations/index.ts +++ b/src/migrations/index.ts @@ -77,6 +77,11 @@ import * as migration_20260918_194842_add_operation_allocation from './20260918_ import * as migration_20260918_222549_add_operation_readiness from './20260918_222549_add_operation_readiness'; import * as migration_20260920_044743_add_zone_pressure from './20260920_044743_add_zone_pressure'; import * as migration_20260921_113443_add_supply_boxes from './20260921_113443_add_supply_boxes'; +import * as migration_20260921_120000_add_world_map_flag from './20260921_120000_add_world_map_flag'; +import * as migration_20260921_123000_add_map_intel_fields from './20260921_123000_add_map_intel_fields'; +import * as migration_20260921_130500_weather_freeform from './20260921_130500_weather_freeform'; +import * as migration_20260921_134000_add_node_icons from './20260921_134000_add_node_icons'; +import * as migration_20260921_140000_add_currency_symbol_image from './20260921_140000_add_currency_symbol_image'; export const migrations = [ { @@ -472,6 +477,31 @@ export const migrations = [ { up: migration_20260921_113443_add_supply_boxes.up, down: migration_20260921_113443_add_supply_boxes.down, - name: '20260921_113443_add_supply_boxes' + name: '20260921_113443_add_supply_boxes', + }, + { + up: migration_20260921_120000_add_world_map_flag.up, + down: migration_20260921_120000_add_world_map_flag.down, + name: '20260921_120000_add_world_map_flag', + }, + { + up: migration_20260921_123000_add_map_intel_fields.up, + down: migration_20260921_123000_add_map_intel_fields.down, + name: '20260921_123000_add_map_intel_fields', + }, + { + up: migration_20260921_130500_weather_freeform.up, + down: migration_20260921_130500_weather_freeform.down, + name: '20260921_130500_weather_freeform', + }, + { + up: migration_20260921_134000_add_node_icons.up, + down: migration_20260921_134000_add_node_icons.down, + name: '20260921_134000_add_node_icons', + }, + { + up: migration_20260921_140000_add_currency_symbol_image.up, + down: migration_20260921_140000_add_currency_symbol_image.down, + name: '20260921_140000_add_currency_symbol_image', }, ];