diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json deleted file mode 100644 index f04877e..0000000 --- a/drizzle/meta/_journal.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"7","dialect":"postgresql","entries":[]} \ No newline at end of file diff --git a/package.json b/package.json index d7c3bcd..86c4a35 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-slider": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", "@t3-oss/env-nextjs": "^0.12.0", "@tanstack/react-query": "^5.69.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cab5fd4..484a496 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@radix-ui/react-popover': specifier: ^1.1.6 version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slider': + specifier: ^1.2.3 + version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@19.0.12)(react@19.0.0) @@ -918,6 +921,9 @@ packages: '@petamoriken/float16@3.9.2': resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==} + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/primitive@1.1.1': resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} @@ -1174,6 +1180,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.2.3': + resolution: {integrity: sha512-nNrLAWLjGESnhqBqcCNW4w2nn7LxudyMzeB6VgdyAnFLC6kfQgnAjSL2v6UkQTnDctJBlxrmxfplWS4iYjdUTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.1.2': resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} peerDependencies: @@ -1219,6 +1238,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.0': resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} peerDependencies: @@ -3839,6 +3867,8 @@ snapshots: '@petamoriken/float16@3.9.2': {} + '@radix-ui/number@1.1.0': {} + '@radix-ui/primitive@1.1.1': {} '@radix-ui/react-alert-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': @@ -4102,6 +4132,25 @@ snapshots: '@types/react': 19.0.12 '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-slider@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.12)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.12)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-slot@1.1.2(@types/react@19.0.12)(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.12)(react@19.0.0) @@ -4135,6 +4184,12 @@ snapshots: optionalDependencies: '@types/react': 19.0.12 + '@radix-ui/react-use-previous@1.1.0(@types/react@19.0.12)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.12 + '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.12)(react@19.0.0)': dependencies: '@radix-ui/rect': 1.1.0 diff --git a/public/game-placeholder.jpg b/public/game-placeholder.jpg new file mode 100644 index 0000000..15c96a6 Binary files /dev/null and b/public/game-placeholder.jpg differ diff --git a/src/app/(routes)/lobby/[id]/page.tsx b/src/app/(routes)/lobby/[id]/page.tsx index 4b27a8b..da684d9 100644 --- a/src/app/(routes)/lobby/[id]/page.tsx +++ b/src/app/(routes)/lobby/[id]/page.tsx @@ -15,7 +15,7 @@ async function Page({ const lobby = await api.lobby.get({ id }); if (!lobby) return notFound(); - return ; + return ; } export default Page; diff --git a/src/app/(routes)/lobby/page.tsx b/src/app/(routes)/lobby/page.tsx index 8468f71..03d4506 100644 --- a/src/app/(routes)/lobby/page.tsx +++ b/src/app/(routes)/lobby/page.tsx @@ -23,7 +23,7 @@ async function Page() { ); - return ; + return ; } export default Page; diff --git a/src/app/_components/lobby/game-selector.tsx b/src/app/_components/lobby/game-selector.tsx new file mode 100644 index 0000000..b5e7380 --- /dev/null +++ b/src/app/_components/lobby/game-selector.tsx @@ -0,0 +1,102 @@ +import { cn } from "@/lib/utils"; +import Image from "next/image"; +import React from "react"; +type Game = { + id: number; + name: string; + description: string; + image: string; + minPlayers: number; + maxPlayers: number; +}; +const mokGames: Array = [ + { + id: 1, + name: "Mok Game 1", + description: "A simple game of strategy and luck", + image: "/game-placeholder.jpg", + + maxPlayers: 2, + minPlayers: 2, + }, + { + id: 2, + name: "Mok Game 2", + description: "A simple game of strategy and luck", + image: "/game-placeholder.jpg", + + maxPlayers: 2, + minPlayers: 2, + }, + { + id: 3, + name: "Mok Game 3", + description: "A simple game of strategy and luck", + image: "/game-placeholder.jpg", + + maxPlayers: 2, + minPlayers: 2, + }, +]; + +function GameSelector({ + selectGame, + selectedGame, +}: { + selectGame?: (gameId: Game["id"]) => void; + selectedGame?: Game["id"]; +}) { + const [selected, setSelected] = React.useState(selectedGame ?? 1); + return ( +
+ {mokGames.map((game) => ( + setSelected(game.id)} + /> + ))} +
+ ); +} +const GameCard = ({ + game, + selected, + onClick, +}: { + game: Game; + selected?: boolean; + onClick?: () => void; +}) => { + return ( +
+
+ game-image +
+
+
+

{game.name}

+
{game.description}
+
+
+
+ ); +}; + +export default GameSelector; diff --git a/src/app/_components/lobby/lobby-form.tsx b/src/app/_components/lobby/lobby-form.tsx index dceefb8..9a3aeb3 100644 --- a/src/app/_components/lobby/lobby-form.tsx +++ b/src/app/_components/lobby/lobby-form.tsx @@ -20,6 +20,8 @@ import { api } from "@/trpc/react"; import { useRouter } from "next/navigation"; import { Button } from "@/components/ui/button"; import { appRoutes } from "@/config/app.routes"; +import { Slider } from "@/components/ui/slider"; +import { getMinMax } from "@/lib/validations/utils"; function LobbyForm({ server_lobby, @@ -36,7 +38,7 @@ function LobbyForm({ resolver: zodResolver(lobbyPatchSchema), defaultValues: { name: server_lobby?.name ?? "", - maxPlayers: server_lobby?.maxPlayers ?? 0, + maxPlayers: server_lobby?.maxPlayers ?? 2, }, }); async function onSubmit(lobby: z.infer) { @@ -53,6 +55,7 @@ function LobbyForm({ } } else toast.error("Something went wrong."); setLoading(false); + form.reset(); } return ( @@ -75,6 +78,32 @@ function LobbyForm({ )} /> + { + const { min, max } = getMinMax(lobbyPatchSchema.shape.maxPlayers); + return ( + + + Max Players + {`${field.value}`} + + + field.onChange(value[0])} + /> + + + + ); + }} + />
+ + + + ); +} + +export default KickPlayerDialog; diff --git a/src/app/_components/lobby/lobby_player-card.tsx b/src/app/_components/lobby/lobby-player/lobby-player-card.tsx similarity index 57% rename from src/app/_components/lobby/lobby_player-card.tsx rename to src/app/_components/lobby/lobby-player/lobby-player-card.tsx index f44c9f0..9b66933 100644 --- a/src/app/_components/lobby/lobby_player-card.tsx +++ b/src/app/_components/lobby/lobby-player/lobby-player-card.tsx @@ -1,23 +1,30 @@ import React from "react"; -import Avatar from "../../../components/avatar"; +import Avatar from "@/components/avatar"; import { cn } from "@/lib/utils"; -import { MoreHorizontal } from "lucide-react"; -import { Button } from "../../../components/ui/button"; import type { Player } from "@/server/db/schema"; +import LobbyPlayerOptions from "./lobby-player-options"; function LobbyPlayerCard({ + lobbyId, player, children, className, + highlight, + showOptions = false, }: { - player: Pick; + lobbyId: string; + player: Player; children?: React.ReactNode; className?: string; + highlight?: boolean; + showOptions?: boolean; }) { return (
@@ -26,13 +33,7 @@ function LobbyPlayerCard({

{player.displayName}

{children} - + {showOptions && }
); } diff --git a/src/app/_components/lobby/lobby-player/lobby-player-options.tsx b/src/app/_components/lobby/lobby-player/lobby-player-options.tsx new file mode 100644 index 0000000..d20692e --- /dev/null +++ b/src/app/_components/lobby/lobby-player/lobby-player-options.tsx @@ -0,0 +1,86 @@ +"use client"; +import React from "react"; +import { Button } from "@/components/ui/button"; +import { + Ban, + Eye, + Handshake, + HeartHandshake, + MoreHorizontal, + UserCog, + UserX, +} from "lucide-react"; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import type { Player } from "@/server/db/schema"; +import Link from "next/link"; +import { appRoutes } from "@/config/app.routes"; +import { cn } from "@/lib/utils"; +import KickPlayerDialog from "./kick-player-dialog"; +import { api } from "@/trpc/react"; +function LobbyPlayerOptions({ + player, + lobbyId, +}: { + player: Player; + lobbyId: string; +}) { + const [open, setOpen] = React.useState(false); + const dropdownItemClassName = " flex items-center gap-1 "; + const changeRole = api.lobby.changeRole.useMutation(); + return ( + + + + + + + {player.displayName} + + + + + + View Profile + + + + + + My Friend + + + + + + + Change Role + + e.preventDefault()} + className={cn( + dropdownItemClassName, + "group focus:border-destructive focus:text-destructive border border-transparent focus:font-bold", + )} + > + + + + + ); +} + +export default LobbyPlayerOptions; diff --git a/src/app/_components/lobby/lobby-settings-dialog.tsx b/src/app/_components/lobby/lobby-settings-dialog.tsx index db3ef61..d7f0245 100644 --- a/src/app/_components/lobby/lobby-settings-dialog.tsx +++ b/src/app/_components/lobby/lobby-settings-dialog.tsx @@ -14,8 +14,9 @@ import DeleteLobbyDialog from "./delete-lobby-dialog"; import { Settings } from "lucide-react"; function LobbySettingsDialog({ lobby }: { lobby: Lobby }) { + const [open, setOpen] = React.useState(false); return ( - +