Moved Dispatch NAvbar component, to remove code dupl.; Fixed timezone bug in hub

This commit is contained in:
PxlLoewe
2026-01-31 22:11:46 +01:00
parent 580dc32ad0
commit d1c49a3208
18 changed files with 155 additions and 225 deletions

View File

@@ -0,0 +1,32 @@
import { ExitIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import { prisma } from "@repo/db";
import { ChangelogWrapper } from "_components/navbar/ChangelogWrapper";
import ModeSwitchDropdown from "_components/navbar/ModeSwitchDropdown";
export default async function Navbar({ children }: { children: React.ReactNode }) {
const latestChangelog = await prisma.changelog.findFirst({
orderBy: {
createdAt: "desc",
},
});
return (
<div className="navbar bg-base-100 flex justify-between gap-5 shadow-sm">
<div className="flex items-center gap-2">
<div>
<p className="text-xl font-semibold normal-case">VAR Operations Center</p>
<ChangelogWrapper latestChangelog={latestChangelog} />
</div>
</div>
<div className="flex items-center gap-2">
{children}
<ModeSwitchDropdown className="dropdown-center" btnClassName="btn-ghost" />
<Link href={"/logout"}>
<button className="btn btn-ghost">
<ExitIcon className="h-4 w-4" />
</button>
</Link>
</div>
</div>
);
}

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import { useSession } from "next-auth/react";
import { useDispatchConnectionStore } from "../../../../../_store/dispatch/connectionStore";
import { useDispatchConnectionStore } from "../../../../_store/dispatch/connectionStore";
import { useEffect, useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { Prisma } from "@repo/db";

View File

@@ -1,63 +0,0 @@
import { Connection } from "./_components/Connection";
import { Audio } from "../../../../_components/Audio/Audio";
import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import { Settings } from "./_components/Settings";
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 (
<div className="navbar bg-base-100 flex justify-between gap-5 shadow-sm">
<div className="flex items-center gap-2">
<div>
<p className="text-xl font-semibold normal-case">VAR Leitstelle</p>
<ChangelogWrapper latestChangelog={latestChangelog} />
</div>
{session?.user.permissions.includes("ADMIN_KICK") && <AdminPanel />}
</div>
<WarningAlert />
<div className="flex items-center gap-5">
<div className="flex items-center gap-2">
<Audio />
</div>
<div className="flex items-center">
<Connection />
</div>
<div className="flex items-center">
<Settings />
<Link href={"/tracker"} target="_blank" rel="noopener noreferrer">
<button className="btn btn-ghost">
<Radar size={19} /> Tracker
</button>
</Link>
<Link
href={process.env.NEXT_PUBLIC_HUB_URL || "#!"}
target="_blank"
rel="noopener noreferrer"
>
<button className="btn btn-ghost">
<ExternalLinkIcon className="h-4 w-4" /> HUB
</button>
</Link>
<Link href={"/logout"}>
<button className="btn btn-ghost">
<ExitIcon className="h-4 w-4" />
</button>
</Link>
</div>
</div>
</div>
);
}

View File

@@ -1,26 +0,0 @@
"use client";
import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
interface ThemeSwapProps {
isDark: boolean;
toggleTheme: () => void;
}
export const ThemeSwap: React.FC<ThemeSwapProps> = ({
isDark,
toggleTheme,
}) => {
return (
<label className="swap swap-rotate">
<input
type="checkbox"
className="theme-controller"
checked={isDark}
onChange={toggleTheme}
/>
<MoonIcon className="swap-off h-5 w-5 fill-current" />
<SunIcon className="swap-on h-5 w-5 fill-current" />
</label>
);
};

View File

@@ -1,7 +1,10 @@
import type { Metadata } from "next";
import Navbar from "./_components/navbar/Navbar";
import { getServerSession } from "api/auth/[...nextauth]/auth";
import { Error } from "_components/Error";
import Navbar from "(app)/_components/Navbar";
import { Audio } from "_components/Audio/Audio";
import { Connection } from "./_components/navbar/Connection";
import { Settings } from "./_components/navbar/Settings";
export const metadata: Metadata = {
title: "VAR: Disponent",
@@ -26,7 +29,11 @@ export default async function RootLayout({
return (
<>
<Navbar />
<Navbar>
<Audio />
<Connection />
<Settings />
</Navbar>
{children}
</>
);

View File

@@ -1,58 +0,0 @@
import { Connection } from "./_components/Connection";
import { Audio } from "_components/Audio/Audio";
import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
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 async function Navbar() {
const latestChangelog = await prisma.changelog.findFirst({
orderBy: {
createdAt: "desc",
},
});
return (
<div className="navbar bg-base-100 flex justify-between gap-5 shadow-sm">
<div className="flex items-center gap-2">
<div>
<p className="text-xl font-semibold normal-case">VAR Operations Center</p>
<ChangelogWrapper latestChangelog={latestChangelog} />
</div>
</div>
<WarningAlert />
<div className="flex items-center gap-5">
<div className="flex items-center gap-2">
<Audio />
</div>
<div className="flex items-center">
<Connection />
</div>
<div className="flex items-center">
<Settings />
<Link href={"/tracker"} target="_blank" rel="noopener noreferrer">
<button className="btn btn-ghost">
<Radar size={19} /> Tracker
</button>
</Link>
<Link
href={process.env.NEXT_PUBLIC_HUB_URL || "#!"}
target="_blank"
rel="noopener noreferrer"
>
<button className="btn btn-ghost">
<ExternalLinkIcon className="h-4 w-4" /> HUB
</button>
</Link>
<Link href={"/logout"}>
<button className="btn btn-ghost">
<ExitIcon className="h-4 w-4" />
</button>
</Link>
</div>
</div>
</div>
);
}

View File

@@ -1,26 +0,0 @@
"use client";
import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
interface ThemeSwapProps {
isDark: boolean;
toggleTheme: () => void;
}
export const ThemeSwap: React.FC<ThemeSwapProps> = ({
isDark,
toggleTheme,
}) => {
return (
<label className="swap swap-rotate">
<input
type="checkbox"
className="theme-controller"
checked={isDark}
onChange={toggleTheme}
/>
<MoonIcon className="swap-off h-5 w-5 fill-current" />
<SunIcon className="swap-on h-5 w-5 fill-current" />
</label>
);
};

View File

@@ -1,7 +1,10 @@
import type { Metadata } from "next";
import Navbar from "./_components/navbar/Navbar";
import { getServerSession } from "api/auth/[...nextauth]/auth";
import { Error } from "_components/Error";
import Navbar from "(app)/_components/Navbar";
import { Audio } from "_components/Audio/Audio";
import { Connection } from "./_components/navbar/Connection";
import { Settings } from "./_components/navbar/Settings";
export const metadata: Metadata = {
title: "VAR: Pilot",
@@ -26,7 +29,11 @@ export default async function RootLayout({
return (
<>
<Navbar />
<Navbar>
<Audio />
<Connection />
<Settings />
</Navbar>
{children}
</>
);

View File

@@ -1,18 +1,24 @@
"use client";
import { cn } from "@repo/shared-components";
import { ArrowLeftRight, Plane, Radar, Workflow } from "lucide-react";
import { ArrowLeftRight, ExternalLinkIcon, Plane, Radar, Workflow } from "lucide-react";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { usePathname } from "next/navigation";
export default function ModeSwitchDropdown({ className }: { className?: string }) {
export default function ModeSwitchDropdown({
className,
btnClassName,
}: {
className?: string;
btnClassName?: string;
}) {
const path = usePathname();
const session = useSession();
return (
<div className={cn("dropdown z-999999", className)}>
<div tabIndex={0} role="button" className="btn m-1">
<div tabIndex={0} role="button" className={cn("btn", btnClassName)}>
<ArrowLeftRight size={22} /> {path.includes("pilot") && "Pilot"}
{path.includes("dispatch") && "Leitstelle"}
</div>
@@ -39,6 +45,15 @@ export default function ModeSwitchDropdown({ className }: { className?: string }
<Radar size={22} /> Tracker
</Link>
</li>
<li>
<Link
href={process.env.NEXT_PUBLIC_HUB_URL || "#!"}
target="_blank"
rel="noopener noreferrer"
>
<ExternalLinkIcon size={22} /> HUB
</Link>
</li>
</ul>
</div>
);

View File

@@ -36,6 +36,7 @@ export const removeRolesFromMember = async (memberId: string, roleIds: string[])
console.error("Error removing roles from member:", error);
});
};
export const setStandardName = async ({
memberId,
userId,

View File

@@ -9,6 +9,7 @@ import { Button } from "@repo/shared-components";
import { formatTimeRange } from "../../helper/timerange";
import toast from "react-hot-toast";
import Link from "next/link";
import { error } from "console";
interface BookingTimelineModalProps {
isOpen: boolean;
@@ -318,12 +319,7 @@ export const BookingTimelineModal = ({
{canDeleteBooking(booking.User.publicId) && (
<Button
onClick={() => deleteBooking(booking.id)}
className={`btn btn-xs ${
currentUser?.permissions.includes("ADMIN_EVENT") &&
booking.User.publicId !== currentUser.publicId
? "btn-error"
: "btn-neutral"
}`}
className={`btn btn-xs btn-error`}
title="Buchung löschen"
>
<Trash2 size={12} />

View File

@@ -5,13 +5,14 @@ import { getServerSession } from "../../auth/[...nextauth]/auth";
// DELETE /api/booking/[id] - Delete a booking
export const DELETE = async (req: NextRequest, { params }: { params: { id: string } }) => {
try {
console.log(params);
const session = await getServerSession();
if (!session?.user) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
}
const bookingId = params.id;
const bookingId = (await params).id;
console.log("Attempting to delete booking with ID:", bookingId);
// Find the booking
const booking = await prisma.booking.findUnique({
@@ -40,14 +41,14 @@ export const DELETE = async (req: NextRequest, { params }: { params: { id: strin
};
// PUT /api/booking/[id] - Update a booking
export const PUT = async (req: NextRequest, { params }: { params: { id: string } }) => {
export const PATCH = async (req: NextRequest, { params }: { params: { id: string } }) => {
try {
const session = await getServerSession();
if (!session?.user) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
}
const bookingId = params.id;
const bookingId = (await params).id;
const body = await req.json();
const { type, stationId, startTime, endTime } = body;

View File

@@ -3,9 +3,9 @@ import { Booking } from "@repo/db";
export const formatTimeRange = (booking: Booking, options?: { includeDate?: boolean }) => {
const start = new Date(booking.startTime);
const end = new Date(booking.endTime);
const timeRange = `${start.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })} - ${end.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })}`;
const timeRange = `${start.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit", timeZone: "Europe/Berlin" })} - ${end.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit", timeZone: "Europe/Berlin" })}`;
if (options?.includeDate) {
return `${start.toLocaleDateString("de-DE")} ${timeRange}`;
return `${start.toLocaleDateString("de-DE", { timeZone: "Europe/Berlin" })} ${timeRange}`;
}
return timeRange;
};