From a5c4a1dc7cc8291d18e234bf04a9e21ca93b3582 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Thu, 24 Jul 2025 15:44:34 -0700 Subject: [PATCH 1/2] Improved Changelog, Changelog in Dispatch --- .../dispatch/_components/navbar/Navbar.tsx | 10 +- .../(app)/pilot/_components/navbar/Navbar.tsx | 10 +- apps/dispatch/app/_components/SmartPopup.tsx | 1 - .../_components/navbar/ChangelogWrapper.tsx | 34 +++++ apps/dispatch/package.json | 2 +- apps/hub/app/(app)/_components/Changelog.tsx | 124 ------------------ .../(app)/_components/ChangelogActions.tsx | 33 ----- .../(app)/_components/ChangelogWrapper.tsx | 26 ++++ apps/hub/app/(app)/_components/Footer.tsx | 19 ++- .../admin/changelog/_components/Form.tsx | 11 +- apps/hub/app/(app)/admin/changelog/action.ts | 1 - apps/hub/app/(app)/layout.tsx | 8 +- apps/hub/package.json | 2 +- .../components/Changelog.tsx | 102 ++++++++++++++ .../shared-components/components/index.ts | 1 + packages/shared-components/package.json | 2 + pnpm-lock.yaml | 6 + 17 files changed, 215 insertions(+), 177 deletions(-) create mode 100644 apps/dispatch/app/_components/navbar/ChangelogWrapper.tsx delete mode 100644 apps/hub/app/(app)/_components/Changelog.tsx delete mode 100644 apps/hub/app/(app)/_components/ChangelogActions.tsx create mode 100644 apps/hub/app/(app)/_components/ChangelogWrapper.tsx create mode 100644 packages/shared-components/components/Changelog.tsx diff --git a/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx b/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx index 8bf13e2e..de980a2b 100644 --- a/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx @@ -7,14 +7,22 @@ import AdminPanel from "_components/navbar/AdminPanel"; import { getServerSession } from "api/auth/[...nextauth]/auth"; import { WarningAlert } from "_components/navbar/PageAlert"; import { Radar } from "lucide-react"; +import { ChangelogWrapper } from "_components/navbar/ChangelogWrapper"; +import { prisma } from "@repo/db"; export default async function Navbar() { const session = await getServerSession(); + const latestChangelog = await prisma.changelog.findFirst({ + orderBy: { + createdAt: "desc", + }, + }); return (
-

VAR Leitstelle V2

+

VAR Leitstelle

+ {session?.user.permissions.includes("ADMIN_KICK") && }
diff --git a/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx b/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx index 612646b9..a5a3a24f 100644 --- a/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx @@ -5,12 +5,20 @@ import Link from "next/link"; import { Settings } from "./_components/Settings"; import { WarningAlert } from "_components/navbar/PageAlert"; import { Radar } from "lucide-react"; +import { prisma } from "@repo/db"; +import { ChangelogWrapper } from "_components/navbar/ChangelogWrapper"; -export default function Navbar() { +export default async function Navbar() { + const latestChangelog = await prisma.changelog.findFirst({ + orderBy: { + createdAt: "desc", + }, + }); return (

VAR Operations Center

+
diff --git a/apps/dispatch/app/_components/SmartPopup.tsx b/apps/dispatch/app/_components/SmartPopup.tsx index 1aff1840..44c57fca 100644 --- a/apps/dispatch/app/_components/SmartPopup.tsx +++ b/apps/dispatch/app/_components/SmartPopup.tsx @@ -113,7 +113,6 @@ export const SmartPopup = ( ); const handleConflict = useCallback(() => { - console.log("handleConflict in smartMarker", id, options); const newAnchor = calculateAnchor(id, "popup", options); setAnchor(newAnchor); }, [id, options]); diff --git a/apps/dispatch/app/_components/navbar/ChangelogWrapper.tsx b/apps/dispatch/app/_components/navbar/ChangelogWrapper.tsx new file mode 100644 index 00000000..7fabc27f --- /dev/null +++ b/apps/dispatch/app/_components/navbar/ChangelogWrapper.tsx @@ -0,0 +1,34 @@ +"use client"; +import { Changelog } from "@repo/db"; +import { ChangelogModalBtn } from "@repo/shared-components"; +import { useMutation } from "@tanstack/react-query"; +import { editUserAPI } from "_querys/user"; +import { useSession } from "next-auth/react"; +import toast from "react-hot-toast"; + +export const ChangelogWrapper = ({ latestChangelog }: { latestChangelog: Changelog | null }) => { + const { data: session } = useSession(); + const editUserMutation = useMutation({ + mutationFn: editUserAPI, + }); + + const autoOpen = !session?.user.changelogAck && !!latestChangelog; + + if (!latestChangelog) return null; + if (!session) return null; + + return ( + { + await editUserMutation.mutateAsync({ id: session?.user.id, user: { changelogAck: true } }); + if (!session?.user.changelogAck) { + toast.success("Changelog als gelesen markiert"); + } + }} + /> + ); +}; diff --git a/apps/dispatch/package.json b/apps/dispatch/package.json index cf2750f1..e195c34c 100644 --- a/apps/dispatch/package.json +++ b/apps/dispatch/package.json @@ -5,7 +5,7 @@ "private": true, "packageManager": "pnpm@10.13.1", "scripts": { - "dev": "next dev --turbopack -p 3001", + "dev": "next dev -p 3001", "build": "next build", "start": "next start", "lint": "next lint --max-warnings 0", diff --git a/apps/hub/app/(app)/_components/Changelog.tsx b/apps/hub/app/(app)/_components/Changelog.tsx deleted file mode 100644 index a70b0f15..00000000 --- a/apps/hub/app/(app)/_components/Changelog.tsx +++ /dev/null @@ -1,124 +0,0 @@ -"use client"; -import { useState } from "react"; -import Image from "next/image"; -import { Button } from "@repo/shared-components"; -import MDEditor from "@uiw/react-md-editor"; -import { RefreshCw } from "lucide-react"; -import { updateChangelogAck } from "./ChangelogActions"; -import { Changelog } from "@repo/db"; - -export const ChangelogModal = ({ - latestChangelog, - isOpen, - onClose, -}: { - latestChangelog: Changelog | null; - isOpen: boolean; - onClose: () => void; -}) => { - if (!isOpen || !latestChangelog) return null; - - const handleClose = async () => { - onClose(); - await updateChangelogAck(); - }; - - return ( -
- - -
-
- -
-

- {latestChangelog.title} ist nun Verfügbar! -

- -
- {latestChangelog.previewImage && ( - Preview - )} -
- -
- -
- -
- -
-
- -
-
- ); -}; - -export const ChangelogBtn = ({ latestChangelog }: { latestChangelog: Changelog | null }) => { - const [isOpen, setIsOpen] = useState(false); - - if (!latestChangelog) return null; - - return ( - <> - setIsOpen(true)} - > - {latestChangelog.title} - - setIsOpen(false)} - /> - - ); -}; - -export const OpenChangelogOnPageload = ({ - latestChangelog, -}: { - latestChangelog: Changelog | null; -}) => { - const [isOpen, setIsOpen] = useState(true); - - if (!latestChangelog) return null; - - return ( - <> - setIsOpen(false)} - /> - - ); -}; diff --git a/apps/hub/app/(app)/_components/ChangelogActions.tsx b/apps/hub/app/(app)/_components/ChangelogActions.tsx deleted file mode 100644 index 7a9ca313..00000000 --- a/apps/hub/app/(app)/_components/ChangelogActions.tsx +++ /dev/null @@ -1,33 +0,0 @@ -"use server"; -import { prisma } from "@repo/db"; -import { getServerSession } from "../../api/auth/[...nextauth]/auth"; - -export async function getLatestChangelog() { - try { - const latestChangelog = await prisma.changelog.findMany({ - orderBy: { - createdAt: "desc", - }, - take: 1, - }); - - if (latestChangelog.length > 0 && latestChangelog[0]) { - return latestChangelog[0]; - } - - return null; - } catch (error) { - console.error("Failed to fetch latest changelog:", error); - throw new Error("Failed to fetch latest changelog"); - } -} - -export async function updateChangelogAck() { - const session = await getServerSession(); - if (session?.user) { - await prisma.user.update({ - where: { id: session.user.id }, - data: { changelogAck: true }, - }); - } -} diff --git a/apps/hub/app/(app)/_components/ChangelogWrapper.tsx b/apps/hub/app/(app)/_components/ChangelogWrapper.tsx new file mode 100644 index 00000000..8b788772 --- /dev/null +++ b/apps/hub/app/(app)/_components/ChangelogWrapper.tsx @@ -0,0 +1,26 @@ +"use client"; +import { updateUser } from "(app)/settings/actions"; +import { Changelog } from "@repo/db"; +import { ChangelogModalBtn } from "@repo/shared-components"; +import { useSession } from "next-auth/react"; +import toast from "react-hot-toast"; + +export const ChangelogWrapper = ({ latestChangelog }: { latestChangelog: Changelog | null }) => { + const { data: session } = useSession(); + const autoOpen = !session?.user.changelogAck && !!latestChangelog; + + if (!latestChangelog) return null; + + return ( + { + await updateUser({ changelogAck: true }); + if (!session?.user.changelogAck) { + toast.success("Changelog als gelesen markiert"); + } + }} + /> + ); +}; diff --git a/apps/hub/app/(app)/_components/Footer.tsx b/apps/hub/app/(app)/_components/Footer.tsx index 2a914dc3..1c2a03db 100644 --- a/apps/hub/app/(app)/_components/Footer.tsx +++ b/apps/hub/app/(app)/_components/Footer.tsx @@ -2,11 +2,22 @@ import Image from "next/image"; import { DiscordLogoIcon, InstagramLogoIcon, ReaderIcon } from "@radix-ui/react-icons"; import YoutubeSvg from "./youtube_wider.svg"; import FacebookSvg from "./facebook.svg"; -import { ChangelogBtn } from "./Changelog"; -import { getLatestChangelog } from "./ChangelogActions"; +import { ChangelogModalBtn } from "@repo/shared-components"; +import { getServerSession } from "api/auth/[...nextauth]/auth"; +import { updateUser } from "(app)/settings/actions"; +import toast from "react-hot-toast"; +import { ChangelogWrapper } from "(app)/_components/ChangelogWrapper"; +import { prisma } from "@repo/db"; export const Footer = async () => { - const latestChangelog = await getLatestChangelog(); + const session = await getServerSession(); + const latestChangelog = await prisma.changelog.findFirst({ + orderBy: { + createdAt: "desc", + }, + }); + + const autoOpen = !session?.user.changelogAck && !!latestChangelog; return (
{/* Center: Copyright */} diff --git a/apps/hub/app/(app)/admin/changelog/_components/Form.tsx b/apps/hub/app/(app)/admin/changelog/_components/Form.tsx index 5b03958c..de9abaf2 100644 --- a/apps/hub/app/(app)/admin/changelog/_components/Form.tsx +++ b/apps/hub/app/(app)/admin/changelog/_components/Form.tsx @@ -9,7 +9,6 @@ import { useEffect, useState } from "react"; import { deleteChangelog, upsertChangelog } from "../action"; import { Button } from "../../../../_components/ui/Button"; import { redirect } from "next/navigation"; -import Image from "next/image"; import dynamic from "next/dynamic"; import toast from "react-hot-toast"; @@ -73,7 +72,13 @@ export const ChangelogForm = ({ changelog }: { changelog?: Changelog }) => {

Allgemeines

- + { {(() => { if (showImage && isValidImageUrl(previewImage) && !imageError) { return ( - Preview )} {!session.user.pathSelected && } - {session.user.pathSelected && latestChangelog && ( - - )} + {children}
diff --git a/apps/hub/package.json b/apps/hub/package.json index d39abbd7..68034aa6 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -4,7 +4,7 @@ "private": true, "packageManager": "pnpm@10.13.1", "scripts": { - "dev": "next dev --turbopack -p 3000", + "dev": "next dev -p 3000", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/packages/shared-components/components/Changelog.tsx b/packages/shared-components/components/Changelog.tsx new file mode 100644 index 00000000..72f4cd1e --- /dev/null +++ b/packages/shared-components/components/Changelog.tsx @@ -0,0 +1,102 @@ +"use client"; +import { useState } from "react"; +import { Button, cn } from "@repo/shared-components"; +import MDEditor from "@uiw/react-md-editor"; +import { RefreshCw } from "lucide-react"; +import { Changelog } from "@repo/db"; + +export const ChangelogModal = ({ + latestChangelog, + isOpen, + onClose, +}: { + latestChangelog: Changelog; + isOpen: boolean; + onClose: () => void; +}) => { + return ( + +
+
+ +
+

+ {latestChangelog.title} ist nun Verfügbar! +

+ +
+ {latestChangelog.previewImage && ( + Preview + )} +
+ +
+ +
+ +
+ +
+
+ +
+ ); +}; + +export const ChangelogModalBtn = ({ + latestChangelog, + autoOpen, + onClose, + className = "", + hideIcon = false, +}: { + latestChangelog: Changelog | null | undefined; + autoOpen: boolean; + onClose?: () => void; + className?: string; + hideIcon?: boolean; +}) => { + const [isOpen, setIsOpen] = useState(autoOpen); + + if (!latestChangelog) return null; + + return ( + <> + setIsOpen(true)} + > + {!hideIcon && } {latestChangelog.title} + + { + setIsOpen(false); + if (onClose) { + onClose(); + } + }} + /> + + ); +}; diff --git a/packages/shared-components/components/index.ts b/packages/shared-components/components/index.ts index 77206c39..5793249e 100644 --- a/packages/shared-components/components/index.ts +++ b/packages/shared-components/components/index.ts @@ -2,3 +2,4 @@ export * from "./Badge"; export * from "./PenaltyDropdown"; export * from "./Maintenance"; export * from "./Button"; +export * from "./Changelog"; diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 31ba17e0..6d35c3ad 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -10,8 +10,10 @@ "@repo/db": "workspace:*", "@repo/typescript-config": "workspace:*", "@types/node": "^22.15.29", + "@uiw/react-md-editor": "^4.0.8", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "lucide-react": "^0.525.0", "tailwind-merge": "^3.3.1" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db38b812..e37dcec1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -618,12 +618,18 @@ importers: '@types/node': specifier: ^22.15.29 version: 22.15.29 + '@uiw/react-md-editor': + specifier: ^4.0.8 + version: 4.0.8(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) clsx: specifier: ^2.1.1 version: 2.1.1 date-fns: specifier: ^4.1.0 version: 4.1.0 + lucide-react: + specifier: ^0.525.0 + version: 0.525.0(react@19.1.0) tailwind-merge: specifier: ^3.3.1 version: 3.3.1 From b6759e0b6c3be3af9ac5e43a0d7f86d349dc9a1c Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Thu, 24 Jul 2025 15:51:13 -0700 Subject: [PATCH 2/2] fixed zIndex-jungle --- apps/dispatch/app/(app)/pilot/page.tsx | 6 +++--- apps/dispatch/app/_components/map/Map.tsx | 2 +- .../app/tracker/_components/ConnectedDispatcher.tsx | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/dispatch/app/(app)/pilot/page.tsx b/apps/dispatch/app/(app)/pilot/page.tsx index af023c97..f68a358b 100644 --- a/apps/dispatch/app/(app)/pilot/page.tsx +++ b/apps/dispatch/app/(app)/pilot/page.tsx @@ -33,20 +33,20 @@ const PilotPage = () => {
{/* */}
-
+
-
+
-
+
{!simulatorConnected && status === "connected" && ( )} diff --git a/apps/dispatch/app/_components/map/Map.tsx b/apps/dispatch/app/_components/map/Map.tsx index 4e794e25..b2ba520f 100644 --- a/apps/dispatch/app/_components/map/Map.tsx +++ b/apps/dispatch/app/_components/map/Map.tsx @@ -37,7 +37,7 @@ const Map = () => { return ( { return (
-
+
{/*
Kein Disponent Online
*/} -
+
{connections} {connections == 1 ? "Verbundenes Mitglied" : "Verbundene Mitglieder"} -
+
0 ? "badge-success" : "badge-error" @@ -65,7 +65,7 @@ export const ConnectedDispatcher = () => { className="tooltip tooltip-right" data-tip={`vorraussichtliche Abmeldung in ${formatDistance(new Date(), new Date(d.esimatedLogoutTime), { locale: de })}`} > -

+

{new Date(d.esimatedLogoutTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", @@ -76,7 +76,7 @@ export const ConnectedDispatcher = () => {

{asPublicUser(d.publicUser).fullName}
-
{d.zone}
+
{d.zone}
{(() => {