From 618a92b6603d29f0e037a3a6a352d424f0c7a668 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 20:57:11 -0700 Subject: [PATCH 01/12] feat: half-baked multi-select, needs styling --- bun.lockb | Bin 248502 -> 256038 bytes package.json | 2 + src/components/cards/recipe-card.tsx | 58 ++++++-- src/components/dialogs/bulk-action-dialog.tsx | 99 ++++++++++++++ src/components/filter-btn.tsx | 13 +- src/components/ui/toggle-group.tsx | 59 +++++++++ src/components/ui/toggle.tsx | 45 +++++++ src/contexts/multi-select-context.tsx | 83 ++++++++++++ src/pages/_app.tsx | 27 ++-- src/pages/cooking.tsx | 125 ++++++++++++++---- 10 files changed, 458 insertions(+), 53 deletions(-) create mode 100644 src/components/dialogs/bulk-action-dialog.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/contexts/multi-select-context.tsx diff --git a/bun.lockb b/bun.lockb index bc210180a57cab4fd86515ec2e0241b91ef31155..8a25b3a83e266f7022c1c5b7c50a29b4d6bffb41 100755 GIT binary patch delta 43957 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 QZ5!-zr`jE~D^vde0C1jJ { * @memberof Props */ setPromptOpen?: Dispatch>; + + index: number; + allRecipes: T[]; } export const RecipeCard = ({ @@ -50,8 +54,14 @@ export const RecipeCard = ({ setObject, setPromptOpen, show, + index, + allRecipes, }: Props) => { const { activePlayer, patchPlayer } = usePlayers(); + const { isMultiSelectMode, selectedItems, toggleItem, addItems } = + useMultiSelect(); + const lastSelectedIndex = useRef(null); + let colorClass = ""; switch (status) { case 1: @@ -110,6 +120,34 @@ export const RecipeCard = ({ await patchPlayer(patch); } + const isSelected = selectedItems.has(recipe.itemID.toString()); + + const handleClick = (e: React.MouseEvent) => { + if (isMultiSelectMode) { + if (e.shiftKey && lastSelectedIndex.current !== null) { + // Select range of items + const start = Math.min(lastSelectedIndex.current, index); + const end = Math.max(lastSelectedIndex.current, index); + const itemsToSelect = allRecipes + .slice(start, end + 1) + .map((r) => r.itemID.toString()); + addItems(itemsToSelect); + } else { + // Single item selection + toggleItem(recipe.itemID.toString()); + lastSelectedIndex.current = index; + } + return; + } + + if (recipe.minVersion === "1.6.0" && !show && status < 1) { + setPromptOpen?.(true); + return; + } + setObject(recipe); + setIsOpen(true); + }; + return ( @@ -117,17 +155,13 @@ export const RecipeCard = ({ className={cn( "relative flex select-none items-center justify-between rounded-lg border px-5 py-4 text-neutral-950 shadow-sm transition-colors hover:cursor-pointer dark:text-neutral-50", colorClass, + isMultiSelectMode && isSelected && "ring-2 ring-blue-500", )} - onClick={() => { - if (recipe.minVersion === "1.6.0" && !show && status < 1) { - setPromptOpen?.(true); - return; - } - setObject(recipe); - setIsOpen(true); - }} + onClick={handleClick} > - {recipe.minVersion === "1.6.0" && } + {recipe.minVersion === "1.6.0" && ( + + )}

({

- + {!isMultiSelectMode && ( + + )} diff --git a/src/components/dialogs/bulk-action-dialog.tsx b/src/components/dialogs/bulk-action-dialog.tsx new file mode 100644 index 00000000..48cde013 --- /dev/null +++ b/src/components/dialogs/bulk-action-dialog.tsx @@ -0,0 +1,99 @@ +import { usePlayers } from "@/contexts/players-context"; +import { useMultiSelect } from "@/contexts/multi-select-context"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +interface Props { + open: boolean; + setOpen: (open: boolean) => void; + type: "cooking" | "crafting" | "shipping" | "museum"; +} + +export const BulkActionDialog = ({ open, setOpen, type }: Props) => { + const { selectedItems, clearSelection } = useMultiSelect(); + const { activePlayer, patchPlayer } = usePlayers(); + + const handleBulkAction = async (status: number | null) => { + if (!activePlayer) return; + + const patch: any = {}; + const items = Array.from(selectedItems); + + switch (type) { + case "cooking": + patch.cooking = { + recipes: Object.fromEntries(items.map((id) => [id, status])), + }; + break; + case "crafting": + patch.crafting = { + recipes: Object.fromEntries(items.map((id) => [id, status])), + }; + break; + case "shipping": + patch.shipping = { + shipped: Object.fromEntries(items.map((id) => [id, status])), + }; + break; + case "museum": + patch.museum = { + donated: Object.fromEntries(items.map((id) => [id, status])), + }; + break; + } + + await patchPlayer(patch); + clearSelection(); + setOpen(false); + }; + + return ( + + + + Bulk Action + + {selectedItems.size} items selected + + +
+
+ + + +
+
+ + + +
+
+ ); +}; diff --git a/src/components/filter-btn.tsx b/src/components/filter-btn.tsx index 3782f163..0785b052 100644 --- a/src/components/filter-btn.tsx +++ b/src/components/filter-btn.tsx @@ -25,6 +25,7 @@ interface ButtonProps { target: string; _filter: string; setFilter: Dispatch>; + className?: string; } interface DataItem { @@ -51,6 +52,7 @@ export const FilterButton = ({ target, _filter, setFilter, + className, }: ButtonProps) => { const { activePlayer } = useContext(PlayersContext); @@ -66,8 +68,9 @@ export const FilterButton = ({ return ( + {isMultiSelectMode && ( + + )} - + + {/* Search Bar Row */} +
+ setSearch(v)} placeholder="Search Recipes" @@ -221,7 +289,7 @@ export default function Cooking() { ); } else return true; // all recipes }) - .map((f) => ( + .map((f, index, filteredRecipes) => ( ))}
@@ -243,6 +313,11 @@ export default function Cooking() { setOpen={setPromptOpen} toggleShow={toggleShow} /> + ); From c9ad28da6aef99fb8cc455496bca3c76ffff25ce Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 21:10:55 -0700 Subject: [PATCH 02/12] feat: multi-select on crafting --- src/pages/cooking.tsx | 13 ++-- src/pages/crafting.tsx | 131 +++++++++++++++++++++++++++++++++-------- 2 files changed, 113 insertions(+), 31 deletions(-) diff --git a/src/pages/cooking.tsx b/src/pages/cooking.tsx index a8e238ea..4ec400fc 100644 --- a/src/pages/cooking.tsx +++ b/src/pages/cooking.tsx @@ -188,7 +188,9 @@ export default function Cooking() { variant="outline" type="single" value={_filter} - onValueChange={(val) => val && setFilter(val)} + onValueChange={(val) => + setFilter(val === _filter ? "all" : val) + } className="gap-2" > @@ -239,17 +241,18 @@ export default function Cooking() { : "Select Multiple"} {isMultiSelectMode && ( - + )} diff --git a/src/pages/crafting.tsx b/src/pages/crafting.tsx index c134d7fd..eaec035c 100644 --- a/src/pages/crafting.tsx +++ b/src/pages/crafting.tsx @@ -10,6 +10,7 @@ import type { CraftingRecipe } from "@/types/recipe"; import { usePlayers } from "@/contexts/players-context"; import { usePreferences } from "@/contexts/preferences-context"; import { useEffect, useMemo, useState } from "react"; +import { useMultiSelect } from "@/contexts/multi-select-context"; import { AchievementCard } from "@/components/cards/achievement-card"; import { RecipeCard } from "@/components/cards/recipe-card"; @@ -23,6 +24,11 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Command, CommandInput } from "@/components/ui/command"; +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; +import { BulkActionDialog } from "@/components/dialogs/bulk-action-dialog"; const semverGte = require("semver/functions/gte"); @@ -32,6 +38,12 @@ const reqs: Record = { "Craft Master": Object.keys(recipes).length, }; +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // unknown or not completed + "1": "border-yellow-900 bg-yellow-500/20", // known, but not completed + "2": "border-green-900 bg-green-500/20", // completed +}; + export default function Crafting() { const [open, setIsOpen] = useState(false); const [recipe, setRecipe] = useState(null); @@ -48,6 +60,14 @@ export default function Crafting() { const { activePlayer } = usePlayers(); const { show, toggleShow } = usePreferences(); + const { + isMultiSelectMode, + toggleMultiSelectMode, + selectedItems, + clearSelection, + } = useMultiSelect(); + + const [bulkActionOpen, setBulkActionOpen] = useState(false); useEffect(() => { if (activePlayer) { @@ -174,31 +194,83 @@ export default function Crafting() {

All Recipes

- {/* Filters */} -
-
- - - + {/* Filters and Actions Row */} +
+ + setFilter(val === _filter ? "all" : val) + } + className="gap-2" + > + + + + Unknown ( + {reqs["Craft Master"] - (knownCount + craftedCount)}) + + + + + Known ({knownCount}) + + + + Crafted ({craftedCount}) + + +
+ + {isMultiSelectMode && ( + + )}
- +
+ {/* Search Bar Row */} +
+ setSearch(v)} placeholder="Search Recipes" @@ -232,8 +304,8 @@ export default function Crafting() { ); } else return true; // all recipes }) - .map((f) => ( - ( + key={f.itemID} recipe={f} status={ @@ -243,6 +315,8 @@ export default function Crafting() { setObject={setRecipe} setPromptOpen={setPromptOpen} show={show} + index={index} + allRecipes={filteredRecipes as CraftingRecipe[]} /> ))}
@@ -254,6 +328,11 @@ export default function Crafting() { setOpen={setPromptOpen} toggleShow={toggleShow} /> + ); From 405fb7432e8d3ff6501af8460d5ca993247c5a29 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 21:35:03 -0700 Subject: [PATCH 03/12] feat: multi-select on fishing --- src/components/cards/boolean-card.tsx | 9 + src/components/dialogs/bulk-action-dialog.tsx | 52 ++- src/components/filter-btn.tsx | 2 +- src/components/sheets/fish-sheet.tsx | 371 ++++++++---------- src/pages/fishing.tsx | 179 ++++++--- 5 files changed, 352 insertions(+), 261 deletions(-) diff --git a/src/components/cards/boolean-card.tsx b/src/components/cards/boolean-card.tsx index 7741bfdb..45aed75f 100644 --- a/src/components/cards/boolean-card.tsx +++ b/src/components/cards/boolean-card.tsx @@ -5,6 +5,7 @@ import type { ItemData, MuseumItem } from "@/types/items"; import { Dispatch, SetStateAction } from "react"; import { usePlayers } from "@/contexts/players-context"; +import { useMultiSelect } from "@/contexts/multi-select-context"; import { NewItemBadge } from "@/components/new-item-badge"; import { @@ -66,6 +67,7 @@ export const BooleanCard = ({ handleStatusChange, }: BooleanCardProps) => { const { activePlayer, patchPlayer } = usePlayers(); + const { isMultiSelectMode, selectedItems, toggleItem } = useMultiSelect(); // let itemType = "O"; //Todo add item types to object data files, and use them here to hotswap data source // let dataSource = objects; let iconURL: string; @@ -137,6 +139,8 @@ export const BooleanCard = ({ await patchPlayer(patch); } + const isSelected = selectedItems.has(item.itemID.toString()); + return ( @@ -146,8 +150,13 @@ export const BooleanCard = ({ completed ? "border-green-900 bg-green-500/20 hover:bg-green-500/30 dark:bg-green-500/10 hover:dark:bg-green-500/20" : "border-neutral-200 bg-white hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800", + isMultiSelectMode && isSelected && "ring-primary ring-2", )} onClick={() => { + if (isMultiSelectMode) { + toggleItem(item.itemID.toString()); + return; + } if (minVersion === "1.6.0" && !show && !completed) { setPromptOpen?.(true); return; diff --git a/src/components/dialogs/bulk-action-dialog.tsx b/src/components/dialogs/bulk-action-dialog.tsx index 48cde013..fd5f99dd 100644 --- a/src/components/dialogs/bulk-action-dialog.tsx +++ b/src/components/dialogs/bulk-action-dialog.tsx @@ -14,13 +14,32 @@ interface Props { open: boolean; setOpen: (open: boolean) => void; type: "cooking" | "crafting" | "shipping" | "museum"; + onBulkAction?: ( + status: number | null, + selectedItems: Set, + close: () => void, + ) => void; } -export const BulkActionDialog = ({ open, setOpen, type }: Props) => { +export const BulkActionDialog = ({ + open, + setOpen, + type, + onBulkAction, +}: Props) => { const { selectedItems, clearSelection } = useMultiSelect(); const { activePlayer, patchPlayer } = usePlayers(); + const close = () => { + clearSelection(); + setOpen(false); + }; + const handleBulkAction = async (status: number | null) => { + if (onBulkAction) { + onBulkAction(status, selectedItems, close); + return; + } if (!activePlayer) return; const patch: any = {}; @@ -50,10 +69,37 @@ export const BulkActionDialog = ({ open, setOpen, type }: Props) => { } await patchPlayer(patch); - clearSelection(); - setOpen(false); + close(); }; + if (type === "shipping") { + return ( + + + + Bulk Action + +
+ + +
+
+
+ ); + } + return ( diff --git a/src/components/filter-btn.tsx b/src/components/filter-btn.tsx index 0785b052..8bf3508e 100644 --- a/src/components/filter-btn.tsx +++ b/src/components/filter-btn.tsx @@ -99,7 +99,7 @@ export const FilterSearch = ({
diff --git a/src/components/sheets/fish-sheet.tsx b/src/components/sheets/fish-sheet.tsx index ad54db9b..19d3f970 100644 --- a/src/components/sheets/fish-sheet.tsx +++ b/src/components/sheets/fish-sheet.tsx @@ -28,6 +28,7 @@ import { SheetHeader, SheetTitle, } from "../ui/sheet"; +import { useMultiSelect } from "@/contexts/multi-select-context"; interface Props { open: boolean; @@ -37,6 +38,7 @@ interface Props { export const FishSheet = ({ open, setIsOpen, fish }: Props) => { const { activePlayer, patchPlayer } = useContext(PlayersContext); + const { selectedItems, clearSelection } = useMultiSelect(); const isDesktop = useMediaQuery("(min-width: 768px)"); const fishCaught = useMemo(() => { @@ -62,8 +64,8 @@ export const FishSheet = ({ open, setIsOpen, fish }: Props) => { async function handleStatusChange(status: number) { if (!activePlayer || !fish) return; - if (status === 2) fishCaught.add(fish.itemID); - if (status === 0) fishCaught.delete(fish.itemID); + if (status === 2) fishCaught.add(fish.itemID.toString()); + if (status === 0) fishCaught.delete(fish.itemID.toString()); const patch = { fishing: { @@ -75,6 +77,159 @@ export const FishSheet = ({ open, setIsOpen, fish }: Props) => { setIsOpen(false); } + async function handleBulkStatusChange(status: number) { + if (!activePlayer || selectedItems.size === 0) return; + + const newFishCaught = new Set(fishCaught); + selectedItems.forEach((itemId) => { + if (status === 2) newFishCaught.add(itemId.toString()); + if (status === 0) newFishCaught.delete(itemId.toString()); + }); + + const patch = { + fishing: { + fishCaught: Array.from(newFishCaught), + }, + }; + + await patchPlayer(patch); + clearSelection(); + setIsOpen(false); + } + + const content = ( + <> +
+
+
+ {selectedItems.size > 0 ? ( + <> + + + + ) : ( + <> + {fishCaught.has(fish?.itemID ?? 0) ? ( + + ) : ( + + )} + + )} + {!activePlayer && } + {name && ( + + )} +
+
+ {fish && ( + <> +
+

Location

+ +
    + {fish.locations.map((location) => ( +
  • + {location} +
  • + ))} +
+
+ {!fish.trapFish && ( + <> +
+

Season

+ +
    + {fish.seasons.map((season) => ( +
  • + {season} +
  • + ))} +
+
+
+

Time

+ +

+ {fish.time} +

+
+
+

Weather

+ +

+ {fish.weather} +

+
+
+

Difficulty

+ +

+ {fish.difficulty} +

+
+ + )} + + )} +
+ + ); + if (isDesktop) { return ( @@ -95,111 +250,7 @@ export const FishSheet = ({ open, setIsOpen, fish }: Props) => { {description ? description : "No Description Found"} - {fish && ( -
-
-
- {fishCaught.has(fish.itemID) ? ( - - ) : ( - - )} - {!activePlayer && } - {name && ( - - )} -
-
-
-

Location

- -
    - {fish.locations.map((location) => ( -
  • - {location} -
  • - ))} -
-
- {!fish.trapFish && ( - <> -
-

Season

- -
    - {fish.seasons.map((season) => ( -
  • - {season} -
  • - ))} -
-
-
-

Time

- -

- {fish.time} -

-
-
-

Weather

- -

- {fish.weather} -

-
-
-

Difficulty

- -

- {fish.difficulty} -

-
- - )} -
- )} + {content}
); @@ -225,111 +276,7 @@ export const FishSheet = ({ open, setIsOpen, fish }: Props) => { {description ? description : "No Description Found"} - {fish && ( -
-
-
- {fishCaught.has(fish.itemID) ? ( - - ) : ( - - )} - {!activePlayer && } - {name && ( - - )} -
-
-
-

Location

- -
    - {fish.locations.map((location) => ( -
  • - {location} -
  • - ))} -
-
- {!fish.trapFish && ( - <> -
-

Season

- -
    - {fish.seasons.map((season) => ( -
  • - {season} -
  • - ))} -
-
-
-

Time

- -

- {fish.time} -

-
-
-

Weather

- -

- {fish.weather} -

-
-
-

Difficulty

- -

- {fish.difficulty} -

-
- - )} -
- )} +
{content}
diff --git a/src/pages/fishing.tsx b/src/pages/fishing.tsx index 8786b79f..3aea9bb4 100644 --- a/src/pages/fishing.tsx +++ b/src/pages/fishing.tsx @@ -22,6 +22,12 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Command, CommandInput } from "@/components/ui/command"; +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; +import { useMultiSelect } from "@/contexts/multi-select-context"; +import { BulkActionDialog } from "@/components/dialogs/bulk-action-dialog"; import { IconClock, IconCloud } from "@tabler/icons-react"; @@ -72,6 +78,11 @@ const seasons = [ }, ]; +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // incomplete + "2": "border-green-900 bg-green-500/20", // completed +}; + export default function Fishing() { const [open, setIsOpen] = useState(false); const [fish, setFish] = useState(null); @@ -87,9 +98,17 @@ export default function Fishing() { const [gameVersion, setGameVersion] = useState("1.6.0"); - const { activePlayer } = usePlayers(); + const { activePlayer, patchPlayer } = usePlayers(); const { show, toggleShow } = usePreferences(); + const { + isMultiSelectMode, + toggleMultiSelectMode, + selectedItems, + clearSelection, + } = useMultiSelect(); + const [bulkActionOpen, setBulkActionOpen] = useState(false); + useEffect(() => { if (activePlayer) { setFishCaught(new Set(activePlayer?.fishing?.fishCaught ?? [])); @@ -127,6 +146,24 @@ export default function Fishing() { return { completed, additionalDescription }; }; + // Custom bulk action handler for fishing + const handleFishingBulkAction = async ( + status: number | null, + selectedItems: Set, + close: () => void, + ) => { + if (!activePlayer) return; + const current = new Set(activePlayer.fishing?.fishCaught ?? []); + selectedItems.forEach((id) => { + if (status === 2) current.add(id); + if (status === 0) current.delete(id); + }); + await patchPlayer({ + fishing: { fishCaught: Array.from(current) }, + }); + close(); + }; + return ( <> @@ -193,50 +230,96 @@ export default function Fishing() {

All Fish

- {/* Filters */} -
-
- +
+ + setFilter(val === _filter ? "all" : val) + } + className="gap-2" + > + + + + Uncaught ({reqs["Master Angler"] - fishCaught.size}) + + + + + + Caught ({fishCaught.size}) + + + +
+
+ - + + {isMultiSelectMode && ( + + )}
-
-
- - -
-
- - setSearch(v)} - placeholder="Search Fish" - /> - -
-
+
+ {/* Search Bar Row */} +
+ + setSearch(v)} + placeholder="Search Fish" + /> +
{/* Fish Cards */}
@@ -249,9 +332,9 @@ export default function Fishing() { }) .filter((f) => { if (_filter === "0") { - return !fishCaught.has(f.itemID); // incompleted + return !fishCaught.has(f.itemID.toString()); // uncaught } else if (_filter === "2") { - return fishCaught.has(f.itemID); // completed + return fishCaught.has(f.itemID.toString()); // caught } else return true; // all }) .filter((f) => { @@ -280,7 +363,7 @@ export default function Fishing() { + ); From cf2c38137ffb07e6a39978fd7a129840aa4a1057 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 21:36:41 -0700 Subject: [PATCH 04/12] fix: random validation error on poly/monoculture --- src/pages/shipping.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/shipping.tsx b/src/pages/shipping.tsx index ba1e6cbf..f026d1d7 100644 --- a/src/pages/shipping.tsx +++ b/src/pages/shipping.tsx @@ -102,13 +102,19 @@ export default function Shipping() { if (semverGte(gameVersion, "1.6.0") && key === "372") return; // Clam and Smoked Fish is excluded in 1.6 // Polyculture calculation - if (shipping_items[key as keyof typeof shipping_items].polyculture) { + if ( + shipping_items[key as keyof typeof shipping_items] && + shipping_items[key as keyof typeof shipping_items].polyculture + ) { if ((activePlayer.shipping?.shipped[key] ?? 0) >= 15) polycultureCount++; } // Monoculture calculation - if (shipping_items[key as keyof typeof shipping_items].monoculture) { + if ( + shipping_items[key as keyof typeof shipping_items] && + shipping_items[key as keyof typeof shipping_items].monoculture + ) { if ((activePlayer.shipping?.shipped[key] ?? 0) >= 300) monocultureAchieved = true; } From 74e445c77a777455b8b2f204f0dffc55b4c3e9eb Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 21:44:32 -0700 Subject: [PATCH 05/12] feat: filter update on shipping --- src/pages/shipping.tsx | 128 +++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 43 deletions(-) diff --git a/src/pages/shipping.tsx b/src/pages/shipping.tsx index f026d1d7..bccda6c4 100644 --- a/src/pages/shipping.tsx +++ b/src/pages/shipping.tsx @@ -22,6 +22,8 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Command, CommandInput } from "@/components/ui/command"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; import { IconClock } from "@tabler/icons-react"; @@ -56,9 +58,15 @@ const seasons = [ }, ]; +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // unshipped + "1": "border-green-900 bg-green-500/20", // shipped + "2": "border-blue-900 bg-blue-500/20", // polyculture +}; + export default function Shipping() { const [search, setSearch] = useState(""); - const [_filter, setFilter] = useState("all"); + const [filter, setFilter] = useState("all"); const [_seasonFilter, setSeasonFilter] = useState("all"); const [showPrompt, setPromptOpen] = useState(false); @@ -152,6 +160,20 @@ export default function Shipping() { return { completed, additionalDescription }; }; + // Calculate completedCount based on filtered items + const completedCount = Object.values(typedShippingItems) + .filter((i) => { + // Clam is excluded in 1.6, so we won't show it + if (i.itemID === "372") return !semverGte(gameVersion, "1.6.0"); + return true; + }) + .filter((i) => semverGte(gameVersion, i.minVersion)) + .filter((i) => { + if (_seasonFilter === "all") return true; + return i.seasons.includes(_seasonFilter); + }) + .filter((i) => i.itemID in basicShipped).length; + return ( <> @@ -218,31 +240,52 @@ export default function Shipping() {

All Items

- {/* Filters */} -
-
- - - + {/* Filters and Actions Row */} +
+
+ + setFilter(val === filter ? "all" : val) + } + className="gap-2" + > + + + + Unshipped ({reqs["Full Shipment"] - basicShippedCount}) + + + + + + Polyculture ({reqs["Polyculture"] - polycultureCount}) + + + + + + Completed ({completedCount}) + + +
- - setSearch(v)} - placeholder="Search Items" - /> -
+ {/* Search Bar Row */} +
+ + setSearch(v)} + placeholder="Search Items" + /> + +
{/* Items */}
{Object.values(typedShippingItems) @@ -276,24 +322,20 @@ export default function Shipping() { return name.toLowerCase().includes(search.toLowerCase()); }) .filter((i) => { - if (_filter === "0") { - // Item not shipped + if (filter === "0") { + // Unshipped return !(i.itemID in basicShipped); - } else if (_filter === "1") { - // Polyculture crops that need completing + } else if (filter === "1") { + // Polyculture in progress return ( i.itemID in shipping_items && shipping_items[i.itemID as keyof typeof shipping_items] .polyculture && (!basicShipped[i.itemID] || basicShipped[i.itemID]! < 15) ); - } else if (_filter === "2") { - // Shipped/Completed (we won't check for monoculture here) - return i.itemID in basicShipped && - shipping_items[i.itemID as keyof typeof shipping_items] - .polyculture - ? basicShipped[i.itemID]! >= 15 - : basicShipped[i.itemID]! >= 1; + } else if (filter === "2") { + // Completed (all shipped) + return i.itemID in basicShipped; } else return true; // all recipes }) .filter((i) => { From e7a1bdee13322f38d2113b3ea1fdd7ccf4950dac Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 21:54:39 -0700 Subject: [PATCH 06/12] feat: multi-select on museum --- src/components/dialogs/bulk-action-dialog.tsx | 28 +++ src/pages/museum.tsx | 228 +++++++++++++++--- 2 files changed, 229 insertions(+), 27 deletions(-) diff --git a/src/components/dialogs/bulk-action-dialog.tsx b/src/components/dialogs/bulk-action-dialog.tsx index fd5f99dd..31d3e657 100644 --- a/src/components/dialogs/bulk-action-dialog.tsx +++ b/src/components/dialogs/bulk-action-dialog.tsx @@ -100,6 +100,34 @@ export const BulkActionDialog = ({ ); } + if (type === "museum") { + return ( + + + + Bulk Action + +
+ + +
+
+
+ ); + } + return ( diff --git a/src/pages/museum.tsx b/src/pages/museum.tsx index 131d43bc..02322473 100644 --- a/src/pages/museum.tsx +++ b/src/pages/museum.tsx @@ -19,6 +19,12 @@ import { } from "@/components/ui/accordion"; import { usePlayers } from "@/contexts/players-context"; import { usePreferences } from "@/contexts/preferences-context"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; +import { useMultiSelect } from "@/contexts/multi-select-context"; +import { BulkActionDialog } from "@/components/dialogs/bulk-action-dialog"; +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; const reqs: Record = { "A Complete Collection": Object.values(museum).flatMap((item) => @@ -27,6 +33,11 @@ const reqs: Record = { "Treasure Trove": 40, }; +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // not donated + "2": "border-green-900 bg-green-500/20", // donated +}; + export default function Museum() { const [open, setIsOpen] = useState(false); const [museumArtifact, setMuseumArtifact] = useState(null); @@ -48,13 +59,22 @@ export default function Museum() { [activePlayer], ); + const { + isMultiSelectMode, + toggleMultiSelectMode, + selectedItems, + clearSelection, + } = useMultiSelect(); + const [bulkActionOpen, setBulkActionOpen] = useState(false); + const [bulkType, setBulkType] = useState<"artifact" | "mineral">("artifact"); + const getAchievementProgress = (name: string) => { let completed = false; let additionalDescription = ""; if (!activePlayer || !activePlayer.museum) return { completed, additionalDescription }; - + const collection = museumArtifactCollected.size + museumMineralCollected.size; @@ -75,6 +95,39 @@ export default function Museum() { Object.values(museum.minerals).length - museumMineralCollected.size, }; + // Calculate donatedCount for artifacts based on filtered items + const donatedArtifactCount = Object.values(museum.artifacts).filter((f) => + museumArtifactCollected.has(f.itemID), + ).length; + + // Custom bulk action handler for museum + const { patchPlayer } = usePlayers(); + const handleMuseumBulkAction = async ( + status: number | null, + selectedItems: Set, + close: () => void, + ) => { + if (!activePlayer) return; + const patch: any = { museum: {} }; + if (bulkType === "artifact") { + const current = new Set(activePlayer.museum?.artifacts ?? []); + selectedItems.forEach((id) => { + if (status === 2) current.add(id); + if (status === 0) current.delete(id); + }); + patch.museum.artifacts = Array.from(current); + } else { + const current = new Set(activePlayer.museum?.minerals ?? []); + selectedItems.forEach((id) => { + if (status === 2) current.add(id); + if (status === 0) current.delete(id); + }); + patch.museum.minerals = Array.from(current); + } + await patchPlayer(patch); + close(); + }; + return ( <> @@ -145,19 +198,80 @@ export default function Museum() {
-
- - +
+
+ + setArtifactFilter( + val === _artifactFilter ? "all" : val, + ) + } + className="gap-2" + > + + + + Not Donated ({remainingDonations.artifacts}) + + + + + + Donated ({donatedArtifactCount}) + + + +
+
+ + {isMultiSelectMode && ( + + )} +
{Object.values(museum.artifacts) @@ -190,19 +304,73 @@ export default function Museum() {

All Minerals

-
- - +
+
+ + setMineralFilter(val === _mineralFilter ? "all" : val) + } + className="gap-2" + > + + + + Not Donated ({remainingDonations.minerals}) + + + + + + Donated ({museumMineralCollected.size}) + + + +
+
+ + {isMultiSelectMode && ( + + )} +
{Object.values(museum.minerals) @@ -237,6 +405,12 @@ export default function Museum() { setOpen={setPromptOpen} toggleShow={toggleShow} /> + ); From d40c13380fbd78cd11ccefab29b60572a6e2ae93 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 22:01:08 -0700 Subject: [PATCH 07/12] feat: multi-select on walnuts --- src/components/cards/dialog-card.tsx | 10 ++ src/components/dialogs/bulk-action-dialog.tsx | 8 +- src/pages/island/walnuts.tsx | 159 ++++++++++++++---- 3 files changed, 144 insertions(+), 33 deletions(-) diff --git a/src/components/cards/dialog-card.tsx b/src/components/cards/dialog-card.tsx index 96644e6b..08e57558 100644 --- a/src/components/cards/dialog-card.tsx +++ b/src/components/cards/dialog-card.tsx @@ -28,6 +28,7 @@ import { import { Stardrop } from "@/lib/parsers/general"; import { ChevronRightIcon } from "@radix-ui/react-icons"; +import { useMultiSelect } from "@/contexts/multi-select-context"; interface Props { title: string; @@ -65,6 +66,7 @@ export const DialogCard = ({ }: Props) => { const { activePlayer, patchPlayer } = usePlayers(); const [open, setOpen] = useState(false); + const { isMultiSelectMode, selectedItems, toggleItem } = useMultiSelect(); const minVersion = _type === "power" ? powersData[_id].minVersion : "1.5.0"; @@ -72,6 +74,10 @@ export const DialogCard = ({ ? "border-green-900 bg-green-500/20 hover:bg-green-500/30 dark:bg-green-500/10 hover:dark:bg-green-500/20" : "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950 hover:bg-neutral-100 dark:hover:bg-neutral-800"; + if (isMultiSelectMode && selectedItems.has(_id)) { + checkedClass += " ring-2 ring-primary"; + } + async function handleStatusChange(status: boolean) { if (!activePlayer) return; @@ -156,6 +162,10 @@ export const DialogCard = ({ checkedClass, )} onClick={(e) => { + if (isMultiSelectMode) { + toggleItem(_id); + return; + } if (minVersion === "1.6.0" && !show && !completed) { e.preventDefault(); setPromptOpen?.(true); diff --git a/src/components/dialogs/bulk-action-dialog.tsx b/src/components/dialogs/bulk-action-dialog.tsx index 31d3e657..a191138f 100644 --- a/src/components/dialogs/bulk-action-dialog.tsx +++ b/src/components/dialogs/bulk-action-dialog.tsx @@ -113,14 +113,18 @@ export const BulkActionDialog = ({ onClick={() => handleBulkAction(2)} disabled={selectedItems.size === 0} > - Set All Selected as Donated + {onBulkAction && window.location.pathname.includes("walnuts") + ? "Set All Selected as Found" + : "Set All Selected as Donated"}
diff --git a/src/pages/island/walnuts.tsx b/src/pages/island/walnuts.tsx index 91a8a3a5..4c8c4410 100644 --- a/src/pages/island/walnuts.tsx +++ b/src/pages/island/walnuts.tsx @@ -14,6 +14,12 @@ import { FilterButton, FilterSearch } from "@/components/filter-btn"; import { Command, CommandInput } from "@/components/ui/command"; import { IconMapPin } from "@tabler/icons-react"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; +import { useMultiSelect } from "@/contexts/multi-select-context"; +import { BulkActionDialog } from "@/components/dialogs/bulk-action-dialog"; +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; const inter = Inter({ subsets: ["latin"] }); @@ -51,8 +57,14 @@ const type = [ label: "Volcano", }, ]; + +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // unfound + "2": "border-green-900 bg-green-500/20", // found +}; + export default function IslandWalnuts() { - const { activePlayer } = usePlayers(); + const { activePlayer, patchPlayer } = usePlayers(); const [walnutsFound, setWalnutsFound] = useState>(new Set()); const [_filter, setFilter] = useState("all"); @@ -60,6 +72,14 @@ export default function IslandWalnuts() { const [search, setSearch] = useState(""); + const { + isMultiSelectMode, + toggleMultiSelectMode, + selectedItems, + clearSelection, + } = useMultiSelect(); + const [bulkActionOpen, setBulkActionOpen] = useState(false); + useEffect(() => { if (activePlayer && activePlayer.walnuts?.found) { // take the walnut IDs in walnutFound and add them to a set @@ -87,6 +107,24 @@ export default function IslandWalnuts() { }); }, [walnutsFound, _filter]); + // Custom bulk action handler for walnuts + const handleWalnutBulkAction = async ( + status: number | null, + selectedItems: Set, + close: () => void, + ) => { + if (!activePlayer) return; + const current = { ...activePlayer.walnuts?.found }; + selectedItems.forEach((id) => { + if (status === 2) current[id] = walnuts[id].count; + if (status === 0) current[id] = 0; + }); + await patchPlayer({ + walnuts: { found: current }, + }); + close(); + }; + return ( <> @@ -116,33 +154,54 @@ export default function IslandWalnuts() {

Golden Walnut Tracker

-
-
- +
+ + setFilter(val === _filter ? "all" : val) + } + className="gap-2" + > + + + + Unfound ( + {130 - + Object.entries(activePlayer?.walnuts?.found ?? {}).reduce( + (a, b) => a + b[1], + 0, + )} + ) + + + + + + Found ( + {Object.entries(activePlayer?.walnuts?.found ?? {}).reduce( (a, b) => a + b[1], 0, - ) ?? 0 - })`} - setFilter={setFilter} - /> - a + b[1], - 0, - ) ?? 0 - })`} - setFilter={setFilter} - /> + )} + ) + + +
-
+
- - setSearch(v)} - placeholder="Search Walnuts" - /> - + + {isMultiSelectMode && ( + + )}
+ {/* Search Bar Row */} +
+ + setSearch(v)} + placeholder="Search Walnuts" + /> + +
{displayedWalnuts .filter((f) => { @@ -194,6 +285,12 @@ export default function IslandWalnuts() { })}
+ ); From 97c6daeb3f59322cdb74f1c1446ed3a122b12a09 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 22:03:50 -0700 Subject: [PATCH 08/12] feat: filter update on relationships --- src/pages/relationships.tsx | 64 ++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/pages/relationships.tsx b/src/pages/relationships.tsx index a94dd1eb..03ca090d 100644 --- a/src/pages/relationships.tsx +++ b/src/pages/relationships.tsx @@ -20,6 +20,8 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Command, CommandInput } from "@/components/ui/command"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { cn } from "@/lib/utils"; import { HeartIcon, HomeIcon, UsersIcon } from "@heroicons/react/24/solid"; import { IconBabyCarriage, IconAdjustments } from "@tabler/icons-react"; @@ -40,6 +42,11 @@ const sort_filters = [ { value: "hearts", label: "Hearts" }, ]; +const bubbleColors: Record = { + "0": "border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950", // incomplete + "2": "border-green-900 bg-green-500/20", // completed +}; + export default function Relationships() { const { activePlayer } = usePlayers(); @@ -269,19 +276,35 @@ export default function Relationships() { All Villagers
-
- - +
+ + setFilter(val === _filter ? "all" : val) + } + className="gap-2" + > + + + Incomplete + + + + Completed + +
- - setSearch(v)} - placeholder="Search Villagers" - /> -
+ {/* Search Bar Row */} +
+ + setSearch(v)} + placeholder="Search Villagers" + /> + +
{Object.values(villagers) .filter((v) => { From 0664d587c73e3a790cd3317dfeb0b7cef350f89b Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 22:06:01 -0700 Subject: [PATCH 09/12] feat: search on museum page --- src/pages/museum.tsx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/pages/museum.tsx b/src/pages/museum.tsx index 02322473..bc8ecbb3 100644 --- a/src/pages/museum.tsx +++ b/src/pages/museum.tsx @@ -2,6 +2,7 @@ import Head from "next/head"; import achievements from "@/data/achievements.json"; import museum from "@/data/museum.json"; +import objects from "@/data/objects.json"; import { MuseumItem } from "@/types/items"; import { useState, useMemo } from "react"; @@ -25,6 +26,7 @@ import { useMultiSelect } from "@/contexts/multi-select-context"; import { BulkActionDialog } from "@/components/dialogs/bulk-action-dialog"; import { Button } from "@/components/ui/button"; import { X } from "lucide-react"; +import { Command, CommandInput } from "@/components/ui/command"; const reqs: Record = { "A Complete Collection": Object.values(museum).flatMap((item) => @@ -68,6 +70,9 @@ export default function Museum() { const [bulkActionOpen, setBulkActionOpen] = useState(false); const [bulkType, setBulkType] = useState<"artifact" | "mineral">("artifact"); + const [artifactSearch, setArtifactSearch] = useState(""); + const [mineralSearch, setMineralSearch] = useState(""); + const getAchievementProgress = (name: string) => { let completed = false; let additionalDescription = ""; @@ -273,8 +278,26 @@ export default function Museum() { )}
+ {/* Search Bar Row */} +
+ + setArtifactSearch(v)} + placeholder="Search Artifacts" + /> + +
{Object.values(museum.artifacts) + .filter((f) => { + if (!artifactSearch) return true; + const name = + objects[f.itemID as keyof typeof objects]?.name || + ""; + return name + .toLowerCase() + .includes(artifactSearch.toLowerCase()); + }) .filter((f) => { if (_artifactFilter === "0") { return !museumArtifactCollected.has(f.itemID); // incompleted @@ -372,8 +395,25 @@ export default function Museum() { )}
+ {/* Search Bar Row */} +
+ + setMineralSearch(v)} + placeholder="Search Minerals" + /> + +
{Object.values(museum.minerals) + .filter((f) => { + if (!mineralSearch) return true; + const name = + objects[f.itemID as keyof typeof objects]?.name || ""; + return name + .toLowerCase() + .includes(mineralSearch.toLowerCase()); + }) .filter((f) => { if (_mineralFilter === "0") { return !museumMineralCollected.has(f.itemID); // incompleted From 0f1a1169a58348a090bec0712a842676d4e427bc Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sun, 15 Jun 2025 22:09:08 -0700 Subject: [PATCH 10/12] fix: linting error on fish-sheet --- src/components/sheets/fish-sheet.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/sheets/fish-sheet.tsx b/src/components/sheets/fish-sheet.tsx index 19d3f970..4b9235d8 100644 --- a/src/components/sheets/fish-sheet.tsx +++ b/src/components/sheets/fish-sheet.tsx @@ -121,11 +121,12 @@ export const FishSheet = ({ open, setIsOpen, fish }: Props) => { ) : ( <> - {fishCaught.has(fish?.itemID ?? 0) ? ( + {fishCaught.has(fish?.itemID?.toString() ?? "0") ? ( - -
- -
- ); + let foundLabel = "Set All Selected as Completed"; + let notFoundLabel = "Set All Selected as Incomplete"; + if (type === "museum") { + foundLabel = "Set All Selected as Found"; + notFoundLabel = "Set All Selected as Not Found"; + } else if (type === "shipping") { + foundLabel = "Set All Selected as Shipped"; + notFoundLabel = "Set All Selected as Unshipped"; } - if (type === "museum") { + if ( + type === "museum" || + type === "shipping" || + type === "cooking" || + type === "crafting" + ) { return ( @@ -113,18 +100,14 @@ export const BulkActionDialog = ({ onClick={() => handleBulkAction(2)} disabled={selectedItems.size === 0} > - {onBulkAction && window.location.pathname.includes("walnuts") - ? "Set All Selected as Found" - : "Set All Selected as Donated"} + {foundLabel}
From f0606c1ec999895d0070ffd517ab9af61660eb30 Mon Sep 17 00:00:00 2001 From: Jack LaFond Date: Sat, 21 Jun 2025 15:25:32 -0700 Subject: [PATCH 12/12] fix: add lucide (?) --- bun.lockb | Bin 256038 -> 256468 bytes package.json | 1 + 2 files changed, 1 insertion(+) diff --git a/bun.lockb b/bun.lockb index 8a25b3a83e266f7022c1c5b7c50a29b4d6bffb41..5503a05f7cb78acb80556d6a7afe291b9bb58983 100755 GIT binary patch delta 46450 zcmeEvcU)D++V$BRj9P?Td=Q0%>N`Ok!e*`mMERHlkN^z3+YB@2{Mni)WrS?U|Wp%HC(d)u;1(@^PNo z!GW<}e|gp3-16Ixr{(gSR&ldXNwG0~haswor^E3A^iLtl*H!W^;tE4Qn#18J069CS!;u?uSaL#qAH@9u z{YB_$ka-~oLKcFojY3#XH6`1FF9Llp%0d3rw0Q`W2eBiU!%-6QwB~RWgxu)HkjtRc@DCwrz{_ZOE=Q^( zZ7>3i=m*Ib)y^jqtbt^U`X(pGCijJr&}mo!NT#2QX0l}|F^LK20EY*7w)_@;FyD~D z$-|J>aSw^Tp>F|?Hl_`V88kS)pTqGT)1`t)icKDzFe2756#Z2K`T$5K>;lPx;$o5q zj~wK1Y(R5akhh8-65l_C6`xZTo`hrrjwl(Q95f(da34o^bQ{at1D^5==q%?==&2;) z(Z6g-pQM<+vB?g{m(aCnk1#wHDj4RJW^ipO9a5S7+HCVALU z7?>10JejuE!ML#J?nBbYB8y?<^B^!#W#}6>cwl_Ln56#6u_Iy!4NERmTpF;#tmRQ6 z^$t3R127~$DP|b#OdQSDI~-?ADp!n)9mHx5D*Xf`+j$N7Fg9scXCVC&xC1@sRY;oI zxs1#ot>nnK_`Y#S7oQwEDi-oGcvzX5_EuS0;E z#UzhRLiacv530y)zd_QhHgN=HQ>5oG*HZcJSCi&iYfeyn@-NWY58psC ze>tS1927f>OOj)H4Tl3Rn3g1EYFZZrXjeTY10cCJ5@yJv*LcSgN6==uNmO!lD_DH7t9_x%A|(X zcR1i)X+~RB$s&d{%8*MDpBfaTUqR0C5Hqh4DO3n3Nk)1 zewbsgxy`Fc>cIBWNUZ2V{S#saj6xaQqgHp2U9=dI#y9UMjhOUjxrslMYFPQX%P%ZB_aPkZe&XBwfZIk`?8FWWE|hC4UZvGT#x^ zV=Ew2*|OOPFvG?qS;2Vd>;X3$uyZA_8r!$!*% zR)OSDV!g#wOCI(Twx61gN4w8*mA>ELzQdB@2gOcELH`#+hLMP1!7EWP4RMc?6_$te zg>IkAazm$q*T%|p55~)ucSkuKy7wX36ESJBr;b3f+&xNS?2}`M@jMZf7&FA-Xi*Xo z?Bbe`Y++eQj{Rv=z^>hGYTiY(uT5X?3aL*Xj|)te##ERhjd}8z`{c3NK2F=mU;B7$ zAD5pzR@=v8b-d1%b>I&-GrUWr9-l7TxF3>zvK^A0zjcOeb7FjAY*4>Gj#bd(gZd7PA9g^OgEnlIjPC|X_YIpZ`w}PZn1q;qv6Z0HtBOFaJ*>>jr3dCcDuvRP`k#J13Wp0kJM$sw_a${rH?LDcRcbEO=b$!HMQDbrk) zw~F?bc_wd#YEc^{_p(FwR-%&8o8$x>nG6D-o28HbfmHO-8<6z% ze#3_(;K?8fyYk@SgZjqWr{^n(=e$2@PRQpVlw z`*p^EO55xCHuKh|o9X4<+CDR*yj#~j&0G~CoNYZFj_Rg&g>Y?>nO?zdd;z^SD)2P( zRds1TrV-%Q+L@67Zaqg%b6P-zb9YXMBf^Xh2-ixPMxYgmpTbPX&&y^8el|3Xif(P3 z8Ht~}%=C(G!yCSXohVH+^Hp>?n?rlaj1CNUeu_{}D^wl+(!>g-Ak>i|;}k-zkxREs zDPcxdb~|UnTYEEB|LFxY3g(1)I~)zn=tXrr5om9fa1tSuP&wRL7V9`tBeWW!K2|=j zJZ4m2gtG;f@D_~H-Y_$Q-P)I?QN?XE$Eed#4|7hS%V>l3uOqY^W}5()a|g6W=8V_s zX!*@tRU-@+Jcs6?rz*INDbQM4+L87O1KtcW*~NCVTYpzVs#T{Id?-tX9d*pLZFVFNeeYIYPg;Ki?Ds( zRl}X@5sF~QxW*9D!Ng%2=)N95Vwyy-lO8TkB~? z;wOfxuG_d$Qd*>$a{^pOP$`Ea)T$+)%VqS1Mr$$1HC)CjXzVrgA$s%*v?wbMQ$?$5 z8g93-tu$QQ;^y=S^D5(TG=qY!tl@I@g%)9EPN+kgW#+Vc*sH9=5dzH`cB6Kd1{X0B zpf$1L=rHd>YhaDFhIK`@4t8j@%=G$hZHbvt-)(%RVlZCt+VZmd4KrW3%jg767S^U> zm{#6AUM0f#S*3u5^<7#MGqR!Em|8*R@}eIaEdsI)vM~o5-2u}KPV~9bq&pM{#E>D* z(>zn#?HWL~vv^>eU2(1w`hc(AbR+iNmW}7gV(Nk$TI8}|M&{%;rirQzU(af#Io00gr z&rEOTHcAD{akW+iV<0qkn>Ed~ZDwSoTl>vSk8~Slt2i8pM9iEzuuW-LFRHk-G&3^F ztsOPfqukEwRoN9Yme=t@fIZ=0?>Rq!R>O>L7Os0$Gp99=(BSLM-P#J%XyL|c9*LhF z%=8v+V;0KlM?+ceE)p2RBm>I3z z#$j;u4Gc$Rm$P^+Oe1DA+9SjT#hPBudC)Mi1H-jXOrx#a*%>2I+nf;|Zp>xKDwE#& zA%ZPrF4Ta{t)1K14poM7jWyB{LicfTuI)1RKx575pynjXpvkreClGBa+iFuH$qlNJ8(GSR#wqG7~o}`1x*itzch0h#W8}iIWSXi z8(|*r5P`kZ=;(I#sE@bIW^~1H=P86bFl3ZPfo-kK`D(e0IncQE$oY2JjO^q#S~PSx z+E~_b!CeN8y@TLxv}xI zwo;s#8Ri8=R>?JM9W*XUmsVO)WR>4in8O_WP7+`^3N9bqKHA-jXL z{Khy1jlGNorGTDMpM|uQHG&) zUB)bERb>je;JeUd`Z*Z%drIT#7Ul&vWS3#4M!Afk(C7~i6V>Qz+L*aIMmR4r2?xpMJ7PJtn%rn!%yr8fzaR@2iAp*4sv!|3%QZIqwH=t zSPPfY4H`Sl@@MUJ(|F0P{c1+OghjrS!vPQDKwvZzp@o{6SiO*Dpp|BzJ0QNZ!!e9$ zux@1kv<8p!Q`(W~MXQX>*D%T48sMWB>0%xq7~$O4h3Dzaf#J@uu3U;4nvPHthP02& zj0Cqf*EAB{#?fvL2iEPh+*VgO;ehN=>4OcSvAuE%O@zkrlT+axXdxUR=Pw9#v}$P5 z!>X%8xVG3d2D`Oy&B(!Sqd+f*168w39AxI28Hkx>8bjR9>wPSXux3{2E9*t?VUQD{ z(WRhm2=ju{z*6#Ic3y@?BVXq5I99G$ z80|h`o=`eltM+Mx0#Q|vOu-f5<_h30*aQ)gY zz13jz_~;1dJdky)7Mw;1{y4FY*AROZVRZ@6uyJE$-G~s)z_x*d-mlPbD#f_f8!D%i zT=>(V;qeCjydNQ)HAd9&NQ|(;j$ml4(#hK zM`U?%1($O+G@O9&;QJdwE!i=8{gGzWga~8ONZUO*njb@>-EyNUGRon=0f*XPm(dy; zMfnbXLN*Uaosl!!xb614V~#=ZV5 z3CVqE2ASqoF~>B2Iop> zSQQ$F8@Cyg#^ zTO($lkpWH4MQ%@}Cd=JJdfltgq<^uiuA3Rt-A3&x_zqTX4S#dX^$$7whu5`QKsf~dqXAHG7&}45@D>u#dLu$#;9@YLaG&xF)EAeXf zcEv-(=>k1Dr@PD81+77rEq9<*wKVoe;B?uySgw1yvFmxGkrd7$e8ce z+s!p|Er`$-nvo0K&hvA*IP!d!*OX-yw5o4rnz=-Ta}UUH(_4f)e@7_Fsx)Gr-HC8@ z=ey9lTT{SqepYv4r8o(#HhHJl0(+8lWJq>nc$g;?yuLz$Ul79i8M7!%;64HpV3wk_ z*lQQNji11|Ww+FF=|Kz4X^SKDmlm4G7e{D+n8p&fQE!p-8|$pE&t7C6uN~q13?#Om z%5^*!qm%g@;~atz-r9wQJMSW7y;U>1E|LC=wWx|q-@SyzIs=#T5P}YNPC*FgW`r^j zYHfvlmsy76U05taZDo79x}3Y9p&2OtA$hB5ZI)yBtXu*i>!qOc20|za4X?X`eVVzf zjwb>#@(w~WGIFKW;VABPgkU%JPVZHgVc2X2BBcDfQJ5zc?kPs4*X-%Vy=ewC6pQ3n z5Ry}ieD&996}ROSgwQ!iaR8z2vMosA`-aNt9BhY-qX@}q#s$^)&8)T~{Xl3KOq8<= zAz4$KMqys7ZI?h6z0GQCKVAY7i)PO{o-$A|Tz9Q8qbf!iL)V}OZ5xa|(C962hSn}4 z=URJ0p6MRu2?gf?R5t}7HTU6k7bK6a^C-H2+0|77a`TVIIP5ORMS)2V@9rbJ0C)C zX}v2LX=bc<>-jdDxi&-?9XHE0#d+yioXtP@n*=9y<#=@~p zHU`U3cbE2onX%dJ%>9#2ZF4)P?#8xZMsEvuevVMI6)L;O4h_u;y^|Hn zz1QJrZl&vpP^1-Fkrld@6{@z+jvbv9I+7K7VZR;QAuF^Tp{7=O-(-aZ`?L<1)AP%m|#ZEklMu>c9x4rzm|4 zBnwIdasZP7rkkqtnUJgyyQyW^LP*9h1`ObJfbpwU{RqeeSc$9;B@Mx9!|Mc&g8&OS z4Dj}rxBI6yDEZ{1@g1=SzcaZ#0lD`fx z-S+@L*`*isUoBQjru$9ll+=F*Sk52xCA!{yfS>G={m0bIped612%_3xh)s_Btk_Cn$9h16L zdP){tOX-vh*2WLcg9u3Gt51@W{m*a%MNl$9W5_~~T^0YoDQRGLRc??Spkzc(6+y{h zFQro&=B2|J0`u145@y*WeA>j~r{XCYOi(gW@szp2kAkcKxkB;TCF@X70oS-=scQ!@CT(z8oaM-@*=gO5Ry%1}HdeJL|l5t&bsq)saS?<5W56(oKr z89bx(?20}CURLr;lK45~XA` zkeUE&dRaEjX?Ck0F`w6BYj% zBn`U=NkhL<{1qj?fn%owI*I3&BAznsVn4HVy4@l7H5p=1TkAh~n2gJg@lLNZ@3NS50NlI8V-Hl??BSP45god zgn_A!(+Ctr#P^W+=lBUfXvj?^e}~Kk{Q)HYIdtS=yc3enmK&0M9!T~?Ud0!NWIA6Z z{UK>^Sx6ppf-D~6UsWZj4#@&*LFR#M2FZe3DA^j46|{k5x=xVn>YggTkBaXL$(9d+ zsu6!SomY;-V!50+&9V9=LH0+v+ zzXi!I{}Ylua2JxFr%B8IpBBLa@2QMWlZ^jM#Z$7v2ax0+D#>QkH}bOiO46VL(3!qa z4u|>bNyA!$d{vt4lATdS@&AWXt^xm>6|iyDU^w4ZG*bUj1G zDXaef^^;sn_#TQMN(TRV zAN9}sDCH7wD3|!>ebhhiqrOzW@Xz}wYqk96ebhhiqyBjx_0RjLf8Iy^^FHdI_fh}6 zkFpL0|Gba-|NdUeeqESV@%B4k@2}TlW>U8!U);`E{zJW??-hKl<^0~o$0i<(F1g6x zqsx0!hTimbf4-sgKCdr5rW|U$w^Lr{w)?*Awhv8S_~yHNKApGwM$x9$VfR5Eeg}F$ ztSh5+5}nIx-om@A)?9m6bSVqMs~m^}Bn}Cq9EjZ{63c-&BKDAoD-WVrc@RfMe0dN) z6+mQ?_(1qp0C9}OxC$ULLOc8 zAnH~GaYm$91QAvV#0?T3i&~XHTqCip5{OU5brK6JYcGnHm9;4CbFrv0h-N`ZalbNB zoD-2jAa0Y`6a?aexI?V;| z6~q;>heTX85XGv2_(sH61K|?_B9p{-!Z!rOF%siKKwK9YBt}&S5mX(-47^B&OB`QMV?DnwY5O>s0HG_SXT=~=h`5=YlCu5Sb)$3g5aQj*%Ew7la`)NQ{aA z5flL;w@8Tqfj_l?{vhEk@OKo@A0%eDLF5tVNKCB5MlK}+#pdv)T$5S z8i{4~K@<|#Ni1vtBDw*HB4SYk5X~BbxKF}YL^cF*o5ZGuApFD~5^EcQ=+Ov7F|n=@ zh|Y~ccsB-7LUd^i!mA0010+fbqX~%JBodo|C?oceh-(U>SW^(?M0`^aKFvU6lBghj zn}Ik+Vq7y2fg*#%s7Me&ksvCGlt>T(Q6Mgn2oix&AkLDQ5e1@(I7ebCyQ*$;5YCgm-HYZqcPR2(LCE4v?rXj5Z*4lSpg>qM_JBBCainVr@Y* z7V&LC__PC&NusInZ3p5QiE-^fM2ZX&quPTAY7e5hNNEoupaY0YB%(!N2M}jT%;*53 zl{iOYYDW-tJA!B<(mR3(>jdHkiFTq^ClJ?2Eb9cKgSbv&VP_E0ok4UGi#p>+vo0X+ zljtHMyMVY&VpA6o-NYRdYrBHz(G^4wv92qK&fP$GcLUK&bm<1dt2>ATBw~co9mH-D ziQPf;6?;g;^#D<<2Z&e^-vfkCPY{_T1_*#0?V3qE;Ue*GMeu17f(iPGVtS z5Yc@>j1-Idf@szc#C;MeBC;Qd+axyi12IP2A+a_VM2}byte4Jcwf?#>In37a1f* zy#ylYB@nNQl$SsR3g78WLae%}sVI+arO(HP~#Oq=YiMV7C#gak1DdLks_zVM)Nn(xg z9R}hUiE+a~tP>d|MhyoMG#tbRkun@azz7hRNNf^;BS4%bF=GUXE#e%BsUtzu9SLHa zNFNCzY!rwaB({rMqd;6Ev1}BG9pXBPg()DSQ$Xwzi&8)|8x7(F?IHjM_c zPuwA~b_|FfV?Z1b>&AfSJQjraSP<`uE@MG>jRSFj#35mf1F@S#;y4gT#2ylH<3SV~ z58|kZ9}mJO6+|Y94}@I3vOa%AnMKlaZ{wv01-A5#0?U^iCQy3TqCh;CWu?& zI*Em|Kt#_1@uyfc3q-ToAnud6BO+&mxJ_cyY!LUv9TIEjfaoy?#C@@D4v5ZkL3qyv z@lbS`3&P6;ae#y-3=;(Zj-E&~L3oHgB;w|QC^ip-Q^d~$;WHmZCW)NFcRq+?B*x7L zVTcS8qZWV&S^y%qNLc_PK!CVJ!dnCi5NAou5Fql1b0nrN1W|V(hm(K~1`)j&L=mxQF^FbMK-?$cD6h!A`AiS4>C?UEm1L3tC!~qhegs~jNZW4*hL6i}DNW`rGQEUZ>aw2{O2%nW8 zGD%brzAHf-BQb6zh(M7+V$>=SL90Mi5-F=d1iS{~5{V!Y_!@|_Bxbw@qKY^NLaQpO zy$(@Lq*H{5uPCaET5mwq5OXMMit7}iqW+r@F0qIrOx&cXB_dZt)E28K!o?kmI-<=Q zh`M4OMTF4SLbyd2ih5!jMSWqcgJ>XPC>n}A5F&0pj4QSt#x)l4>p}Qz0Fg@v1%TXg3g+)v9T z&g|9P`0FU13wCO0+GH_dr&d8Lvg^=k?V&D)?9e>L#y#48J*c!exmWARgvL*0tSd8V zw|8ak)voB;HL>l0wgMBY%{vg$yKcUt_0r9fRSR0F`;W5zjBnC$(c_SYzjNiry_#L9 zw3EhYar>}#6*KPA5$%HWd~@6uV%*32uBxAEXLIo9LMvS{`_FLJ#~l~k*Gc0S`1T)! zz9&kZM$MMhc|5fT;>;&n319nfLH13E59XivWe)4_Lhkb3u7z7Qyyr<9yzRf+*KZT9 z%dkTRkj^!DNUZgDr<-osHS?mTYkIS{#oSBUG;PMNVqa*NHOTvywZX&1*0;23xz=`M z1-RkZO^oQE*DChlq5P1$ufF@?g+PApi!&Yc0(JRx%u7nVgy2ISpL{3dG7ATI_n93`znBuIoi{*=fMg-V z65Wq(d zi`^C6RnM>SZ)G|1qp$crFCXxo=+I3sk@_jR>NDUNkO3S34g!aOw}41wivqCnI--HL zKs$hQpLZ+Iz&|*ZId$^@yo>7%ya4c??gI?%L*PEZDagyf8ss04xLBO(xP`zQ0PjsN zMJpBq^MEP95?~oHAK#sCw6MZioz0Aqpi0Dq5VF0cSd1EvELfJwkOU=}a~ zco~?ZwQ@LKL0~zM0?Y>Z2VG5IDv%B=1f~H)fgwN=z_npG!1Z7-z%?KlNDv3Q>t#|0 zAQ%m_0NMcUfR;cjpgqtQXbtc!XcWNvFp)qrpehgoG(>;Z18O524pawf0Q`X zAwXTg1vCbl0uewHzztLf!hm`}4WNDwIDIVyY683kdmZ>5_yPD4_zCzK_yxEL{096E zEQe7;fX+Y{pc~K~;PvbPAQ-3$@Pa&-G&g#q9^i?v6UYJZZu;$1{I~xMh;2YpW zgiipxXTlp6UjzKT#oWLPz#8Bra0+-ISPSqsiY@~00f&IYz-K7yAS8cn=`6q-JRbp{ z0Ura!fzopeVqNoEvvZfE#WFqz&Yxs)RsQAQW%`FQLMCAP(pT^aL8AB{zW|f$xEzfg8XV zz*oR|;6vbZ;4p9yI0T#q&H#S^cYwRVJ>V~(FoygWUZdtE<~smyJ>LcfATkc<1@QKK zPJq8Bz~3FX2JTxR6Gril9Dfh%MZg=_0IUPn1N?=qBLFW;tp>IMn}IFBCg3fAr=y2l z0=U}$4#{6&xeMF_{sQg;4}dbLxE$~?DlQ6H1n>cTfu9l2-;`Mmw1M6p=m2yCIsu)5 zE zz-^H`UVoHv2}!wK76yC(?tVo9?tUWy?s)M?H;}%YfWT0oA`+AW_?tSzfZ@OhfH(L* zLMMF!Tm<;Xk2R4~nk?O<>qk=oNuGKsbzw0Pu&59JK+S&9;H(c`Q~*9+zZkG3IG*>8bD3pCv^3fz!Bh2 zfJY`Ck$5EHub=QYODHD;{B4pW$nzd>4!8-Vet{o*sh|>1pcFD!1WE&eKrx^aPzK=F zv1NhcKxH5pCWQvxb`w7FA!0&sQz0C*pG58zVtu7=~|I|y*5at+!DaLwY0#02I72T|C& zz=2)fvATzozq{jj2jDU_3@8V%v7GVj1%meTnebz&8MQmhS)>$S}9JA5p*K2Y`DXXUp%vZwlPU z?gQM*xUaFxIfKaC`y9L56X0UbMLY+P6JQx*fm{gZ2DnJl==_j$@F;}2t)+5b<2b43 z|Az+=o)6>!&)LB4S`ToAr5=iKb>JYt0=aT>pX5qvbHxy587$WiVW#0K%a$|DNmLqP zEU3X&_ce!70wk^CnT0dU&cvZfhMou{iERmbfy9ZRTtGai0RVfwKV);DJa7}Y_dMFEjOW1tbx5NH6@1Hu3-I9VoELAVkS2w?9?wd|ptVK5{} zl2#wk7`qLOqun7uO~6i1j)heRn3lSohrA15zqkQZX8nnW2*|oX9Uz>CsM-iHk`@un zK*>xD+ZNL}_5=%kT8r%Xr{y6>qnV#!4j>06n5z?8#1^n?vq#!tjx-b7g>YyXwvA=0 z*?^XcXW^`@HDLEv8-!VhT_)4BzEl!~Z5az=X8J`}6-S*s$Df6>YnjL9?Luj28-PRC z3t-RENam$vnRY(<8N#VnAxg;#)337^YF9)jrBgDF;p}PHLvcVNFc6@Vz6985>>gwT zXlMf9;xcI`Ajw3umR_HoXX5by3m*rtf+QdxFcv5TSgcY)~$p9;|3+C|He!xPe09?=LShD~++)Q8w&;sCs)e@)$Oa%gf>A-?3_IeVnUKWK+I0kov90uY(6j#SO|EcQ*gkuej9KJ z^!31Nz&cP!1PS}E^rt)1RMnD?ngC;STRg5*`g!}KbpP9gdvKq?cG(FFlt;6vyqM9dhyh*;WRe?dDg>JQf|1f2$L zH~%Aq&nTRQ{1UhfdPn(k zx_l|%TU_MO%iwxMsnL3M+@t6)TJMB!$2X1EOH{jp^0Lw)Tpa0(0sa6z$DUnT9whFM z)+-@*;27PdoW*kaQDgM7`SPOX$4V5N#^|lu@bz0MfR7M-)yD6@_}v(vhx{S=9UEWA z@wFW1d?Cn!06T&ozOv)1JHEo>>$+)wS;tL#p zan(>98mo6Qc=TuT?xNHov4;qT$PWG`=05^Rn)XZ@D*6G!;i*)(7A#;-FV_U%!4RA~%YNx}A~W{Q33v zx>h~7dTwl=l7eYIcnM-*~sZvU=dW zK3h6k#)MQ4uIgwas!hZ&TEf?lFJM^0wYAgoeD+z6A-Z-HOkOZD0WK?*%H!dyZ53^% zqy7)YnS#)+2yK$?t9#}U@8t3D(MpOrm#`$f_<~0cd|%(@J0y%2#eRqAE8KaJevG(z zP4^W$C+S7CdEyN6YJ0^Y#vBzF@_O_tb2f*yia(`6c1(LQ>xy33GZZH*v1&3JkS4la zK{>NUeF)(-1>dnO5of08zQ&TAmJg==A?i-i8|$84qUv|L4`P3ws+ST&9;H+iZYp?B zMNzdyg{kn(CrYvUpG`l{QPD-?l#IOt0Z4-qIiAAbJ_M-r)Jgtf9rrRR4xkzXKFX?{kQZH}*ZifTuY6Mpeu7+1EB4{=e z*#FJ?w4|2~^oY9tJ`!La(H+i-7$m^esxc7$_WuLlWkZ?oy(f*>k9483Adh@v>ulYp zru{$3x4Ls=$9~T~iyu{G|6lWOrmrkhvE^$KRt#1SJSmC$v*GP)M722(b4BbNtRc0< zb_&@7-0SdTL!MfOWv91jH5bXdh*KZxg`8Xj28hg$VEIsSW-dzIAkKV@IaJ)#Yxvp! zd3>&AU!9+r^GbDC5)xdE-SGvkR-u%5vCl+pU*Z6i3(G{^-wtj&p0{Qk(z#^1^5SPC zsA>Nv@)fHz`|EbMg1=e`K`b)=c9i6|CFDSGjMtI>u;?=A+R#I7M`R;{rUO?Kl?wYUp{$X z!?pKcT&-*4Ya#(^DI_kT1YE@Xb3UqAAu26EdGTlu7s3v`e|&g%cFZ4^QD`|Hm_#om zKynVYpZ#B^FDbO^`=XQf4@A1Ea*e($HZ4GT*Th!~VCEfBMIhHfu?@oSL`myjLt5MP z&o&qD9F+^Xl#`tmyAzJX3@K~f*Dl@8_bw1Aeug-sYZ`_cjpMUrFi5tl3R}iQD7oqm+a^&#xJS&~L_7DkhU`;D6N-aj&ZY1!AS}oVj zA1XE}=&5U!z~H=|HW`u~*0Rds*L;s3DTrD*0_c{*VlcDbLP>P!Vx_+-bI>Tg2MIWR zksuRU8Pio;dZEdmE3ikxU~7fAB@Q5~U%NnA_K*F#cDPjMU?U`8laaNjxQPVV<#R7V zx&;+wL1#LzKOOn|if`bO^jSP4PZn+_cxugyM7s3iIc<|twBF|E!Yl-WFICxfdKR@&0kUE9n}Y3B@~B2yF#gyMA|DW{uj` zYl*b8#_K=j`>O*c7?%|m+ypUPAGiMT-LAD!86nCE@RFyM@O};bHy7&!6J%Q-up!jI zP&n&IBkp*Y-chLD%3pNtR=8~E644D=#n-Rs`OCb9Z$vQB(vpWI4H}+^$J!GQntza9 z5`~L;c@y)LVI(QTufdtsr={)v-SO4eMc}+dKjrb8Up}}wmJPV6U z$Qp0||MndhT}{j5zkIE(RjVEh6GI#?xUEO}wEpMZZ*>R?!_>qauff%=k&5X!?5C72 z4+|a9wQ^uez|tO&*viskrxysHdt!SZveSM%Od4<94HsO?i>Cc3 z9=-u~q`0|8FIdvrif*9@dT(K8mtrl8y^?|d^)I-FD%_r zm55QNnb3No_-f>BfD~M03%+{(>kc0teN&}?541*19>n+_n`2y>v~`0OV|hofdLjXN z{RSZg9XNLU^^r+kzT&RT(*q{2wYB@!9Iq0DBy9if_^G%7NT3c$GZ9k&S-;zyGN;qm zL;ti2r~5coi5siY#J9z)B6woJTZ%Q%z7pXO{(m7Co;lN!_jlN^S|7@iU0)Ed2%92Q@_YD4kDT4ZZhUbeN% zyFWRXesLqf%2qAdg;#LaKBpINB)YD}1iC3^tkqA+?1hVC0-tFjmrLh&^Fs$M-28&7 z5GGv|bJpQuZ-ltBPG6!|tS1(%M*-3wa#;SbN?ciwbzonlT$(nN>ipW>Ne|0DD&m-^ zwgFFG9iv2x4Z4r+Y%cn5&_n#jHJ6ib;kHu0r;?ds`vx@fpt!t2FXjJ? z5kx09ZXx_PqQovOMCeA8c%h{%G3IcoqfHn3-a?5rP$F7!RV>?x0gys3+FI;JdcACG z@gwsGwH7%x=^>%6*3uJ3?YZT0){dmFU|2XgiXw(Szda*$Zi4|E%UO-H_9^5YvQ(VOAn$I~OX^f51+lrnLqR=*LFzavA)9pw<->z~! z(l1UPyD+`_AZ~%7)*P5A9&UrTs8U48TYAxgFXNp7yMINww91tx#rDao8QnzhxAZ~s z`KfR%Y#DdE$*w+m^}vvYGjGWgx}3({rCV&ixbMeO$(4&*Ew_$3suJC|s|eeUz7Fpx z5@5RCxTlz%fyWN)NbP!xm84$kDfS|XHcH&sj%{&rPub_!bDaF4NP)2*p-j$DW)P*` zMsF@c0`Ao9FN_YTh8gBqhGR5q3xnrrsXu+9*9rG=Wpwka@{})n9ym7 zy+p~q=+6-%-*LoF5jPL%MZ&5(&h(Nyc%!3#)pDO0I!&2?VZV+TF7m&mooHRL{GG)r z2CJcGZ!vQRTBFMJFWOr=)$z4c@|<-xz4WMzioL}}r1WV=(bLyY+46WvGj-lZ4S z^TdcjQ2hO4WOa#)_m`Rz^Uh%u!z}_!TcsGWbr;MFMYazh8^nmKyD-*mW5mH@dZ7|1 zF%Fq|3@G@?^oH-OK5;|YfOT_d%z}5Zu%;pb{qI~65u87*|J6rEri;|wuuh1e_t2Qv zkdA{M+G2F2;1xdyBOP`F4*C|6$#h4>DmLb*um<`w;fL||{{gvtkZVZ2u^SGihQFvv zhc9{b5v})Ndn?vQ9&tYYe&pFJMO$roRDTu3@SIn$e$58M51+cAV&DW#`iSMoTeFAC z>&!oF_sS(rY-hltHW)D+qF-Jp@>1MKccUMbI$q@aobSpW^OhEPC12$oi~+_*jR6L6 z)m|i2E}$OD5e4^fdyr`4TV%MO2%vNnP#YH6bKiFR$Z1;`&zRlSt_wk<>+m0Lob^4!($%ih@{dM^F&CNO9%4js+D^6@a zfT4Vbufaup;$*2S7v%~qv1j_vs?;oxw+ym|z*mlhubRf$BgTqx@4#T$gcjLmjq>DY zx7r%>BAzv#SR?+mjAs_EB78NqAjm2eYp-8wnXt#g@2SIV9d)d+EbNKCle6m?1wCzf z^tEeYBqzOenGjV(whqfGRC%B6v%eq4;_`UWw#j9;&EPo%3RSqk=SFIpI`#>*d8T)8#9xc$1HzI4@CjFG;{?Fb9kt17w%Z;;8i4Z&) z_Q$5f1;1Q$sg>`!FBY&GU~NvagSja@dq#bZ>hdh4xga(a}OW;d!O>d9O>}5e27=S&v>52d2otY@*$27)(hn7PdRP*FHe!rjCIPdt(LdJ>{V9p zVg#%{Y@3PaiDz%j8^wxD9BTK96PbFrf0fbFop#R|d%3~Nk+V^fdOi&sElQunVdJTe zAo{%V%x+fGTaBA$jf=JOs-tNM^wjRL(t$)7cjfbg>iSv@2;pf%b&+;jtT}}~xF#-~ z!ZLJK+=BL$lj%Jalo!)M9!YarPj$J+iJqr1f928CuRdInC!0z|hM3*krTnDo!s2Yb z{PGj;pVss0zT?E%(|Sq&^5f*2iPsK&ol^eenKhBxdUagQ;T|Unp3y5B9g%`>PON37 zARZ*q?Nda|nXJ9b_FUWVp6C#{o>-f{y^z{xLA(j%S<7Cg{b~%A4?f<7Z+HD=`j_?z z76+hbe7Rt6pSB5)Pad0<>q!y3_*bT>lkell8N7nWDYh`SRa`cB`a#yV+wZ}6dCIFF ztj8q`_|xC&W2u~9t%JlX`*Zsp%SRUV=pr(3^wzv&hH`mA-9L8Wr9D#R7}Uwm-r zr|2cyc(p)V=KA`*j{@^!c#buC_tdUg5iK8?qE7GfC|DfRv22<=@{IZO*PuhaR`WrJ z56$olYcufGYo>|rpJ7F6ktRlbh9{_0vF{lHq*gB9z z<8Lwa!dcY+2-5Kg{-DRKPCtCqDEPl zzyFTj^HK5-#0n&+`Kd~9^H$~clRa16DX2x%#hQxG6~yq&e&E9$O}rL0>Gmk=ui^%> z`tQV`bL6^h_39iOKQTeRj^&=APZXsu=pp`vC(0+fywSbH&9=+K9~Dt)q8M@kXViK~ z!3PrOfT)3&b65I`)uW^F>8xTR^El?VF28LkW30oPW5`7D4)Wr#@%aV3>Hb=jy9gm) zr#tBm^2>9*z+`doYorSohc2RHtnXY4h`g8e9C#`!aS!j_b6wJl`$gxF9{u^c5?2n? z*l-tbJ*87QJ`{(p>jiRJ9_HvPdR#&&W5k)99)JZG?aQ*4mD-o3KN~e1YfmCulL`YdwEfDHW#3 z_XIDjKYhBk=Td&&(vH|!a8+GvO=h(}5MQ&I1U)h$;;CUXwV#F7E z;s2muX_q+trJg@qLDrbsV{7%mlablSD0|!flSZk0*4O{I%>_4le#4(?)Kf`isrcz@ zy|CZk^tdQ-1+QS`T@Fp%?6B^6_~LIV;NpjUD_T`nNA z7B!9g)o*na@JxJbE*1!R@jL~X110Gd8*rugeB=92tQU2i>0RQ(3i z*Zvy?gBPC4Gk$6dVHFm_*AV~uh0=goa$g@dON>Wet-KK5=+#62HDRvVGGP(Of|yZ1 zgvZ2WgtRnJI7-GBK8{MW7krClK5Vuq`Yo1u%cz3>)*HAdEy}CChGGmc=Qgh3%>Gs{ z>mM>#ZUZe&4te?2QZM0K4*TU}gSp}~%Eez)!Ix}>ocKz>F-v%Shwfb}s(gnLP`7vF z?G1TfXPQ{?9lA+f=usDd9=q3LUk+L+!g0$7PiVXxgnK@R{&=oi9@gcMUUmQGh6@%R zd}EmwJ72D2zLPpPEavRVZ?@I$I7ZC4ZVjcnq=Z$?zH}t7ulULfF}^itqYN&``~Q6N zPRjW%o4ANs-RfAYV*c7aXWeJz1}=FNb5O)MJqm~+-|H>@hZky83H5%!SU&B(&huQ< zNfp(8gx6LTRebR+$O)19BR02-LhexKa?bd!(-eFW!6h+-uTGyGV-2qLY%JG*89XGk z8+NA^5WzR}9De5)$_4+Rr}qAr!~36=JyH!5Nv-h@kV?ee(0e~ew`9vAH!4}JHTbeV z5%!bbB~!sL2IG9G~`Y8i^ZorEv8y`>cL+v#zLhGc9XypW-)a`H;5TR~Qe8 zIe)_6)q?k!vCnXP_&pr|ITy9Z7E;@GRj0fS_hct#^$=_L+iCFFn5$MjJ`%D9`>N#> zaX&wnjJ2Xj0gwNnWUG+pS(%2`a_mDs9Y|2cB#42*unaxZ;K^)&Zwb3FdzK z$(1ovMBTq|v|R~fN?=#m6g@2cmzp2o^$}0VEM4Pa`eoB+pcP^ ze)N~#;`xtHnCvW6jD4tA&VIDZ>*>+x?{w>||5#tj{*VWBxp*jhCyEaq=)L~eO`7L1 z>OUw`j==om9#ylqimk9uw{k$A)6VDWo@e!l*spsuc`n=Md4xRfdyf@~))coEoaZV_ zJipD)*$I{pSjP#@hA~bLU%zJ`i{umvm-FsvCm$K}w3CmFd93Mt?_tk}M|0pm@%u-) zN?I3=pL7e{Qou>eSH$J?cx+-k#by34A0{xfpJAW1v|$-AF!i6h@TwL5vF+rs6X^4- zr`dP9=SlxmOaA>8{l^dXkD2^m-PD)+#^3H>UwM1@8V%RWuUnsLzS=+E5_9rl>&n{C zo^$t=MLxO1s%IB9M*md@;meogydKrKWMtOfC$GI<-zS^%4ZtL-Zi%ZGV50Q_b%l>Br|HQfUX=gmU zPh@kR{irrY;r#ff!yP8ujY10|kD(J3~Fp;sB&?7GD&>CbUK5^YMt*BQ}fPJ{~@L zv&~|(k4K9F^ES(uw`1oV>YDRl$z?kJCgeFEj}G>eVN((53j@}Oad=Mj+xM1yLAiWR zhfRGG&vkzEg7UQ3#s|r7#0d!hyGU0U=6oM}zV*Q0iiA8$mv{Suq8>h>f!pOcdY(@h z_5Rfrco%BFRA{7PKC7Pg)r^BpCq9blwOzDCUcWI&!Q<6|Rfj{?wO{}HqZEt9grYEF zuUG=%e+lWzAl>oOsiS`j8u`kjbdI;hX_Tawd0Tv6)T4#pskh}Tj`(6xq1(^iYk=Ov zeIUN$xh&fFp~S^vAcXGTAtw8IgoLixA)k{Mr;E-yL2Z-VKuM;2c zSboo^&>+O9x1+{R@sl4Oa!!jm#XSn<2n!CW>iAle@<-{vi~l!kASJ`L9b&4P&zWrt zCvB->0d^z}!RjQoPcM)GhMG1|g)q?cMJwyF-f^g2La4BV%5<=-$%q%UszAsDZlAsn zs8>1#r~))Pbb>u#y4#D~LkJZW(?5b-(KDS}78u0~pz3Znr@0wUyLAwuZp(CgpaRnq zPz6b1e>nN&MZO^v+=0qWwtl0W)jO*LA@gB+FHox)%MNJrkv7@H}_$O~bC9E6JK9n%HmfU#M)W4e|cvx~CWen>6} z2hu`7GwjR$A1_2Gz#3g( z3+_z+3)E`%0jl-Hw+8u|C85A&6>jV`cO$sN;u6o8SU4~-OFahI3#)|DMc=ylyO zy#c7gE*h#LCt=b5YZG@mB2*LtWyFEud2ykD>=BKZ{s@@}C1zlO4jfrio_R@KW*Vib2d`+zK@{3UR=s0vs3Go)XV| Gr~?3VrX}?N delta 46615 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 CCcNVS diff --git a/package.json b/package.json index c416baed..36e95e94 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "eslint": "8.44.0", "eslint-config-next": "13.4.12", "fast-xml-parser": "^4.2.6", + "lucide-react": "^0.522.0", "mysql2": "^3.10.1", "next": "^14.2.3", "next-themes": "^0.2.1",