shared library hinzugefügt

This commit is contained in:
PxlLoewe
2025-06-26 20:40:23 -07:00
parent a93e95eb95
commit 122cdda486
59 changed files with 163 additions and 246 deletions

View File

@@ -1,6 +1,6 @@
import { Award } from "lucide-react";
import { getServerSession } from "../../api/auth/[...nextauth]/auth";
import { Badge } from "../../_components/Badge/Badge";
import { Badge } from "@repo/shared-components";
import { JSX } from "react";
export const Badges: () => Promise<JSX.Element> = async () => {
@@ -16,8 +16,14 @@ export const Badges: () => Promise<JSX.Element> = async () => {
</span>
</h2>
<div className="flex flex-wrap gap-2">
{session.user.badges.length === 0 && (
<span className="text-sm text-gray-500">
Noch ziemlich leer hier. Du kannst dir Abzeichen erarbeiten indem du an Events
teilnimmst.
</span>
)}
{session.user.badges.map((badge, i) => {
return <Badge name={badge} key={`${badge} - ${i}`} />;
return <Badge badge={badge} key={`${badge} - ${i}`} />;
})}
</div>
</div>

View File

@@ -83,6 +83,8 @@ export const PilotStats = async () => {
const hours = Math.floor(totalPilotTime / (1000 * 60 * 60));
const minutes = Math.floor((totalPilotTime % (1000 * 60 * 60)) / (1000 * 60));
const totalFlownMissionsPercent = ((ownRankMissionsFlown * 100) / totalUserCount).toFixed(0);
return (
<div className="stats shadow">
<div className="stat">
@@ -104,7 +106,8 @@ export const PilotStats = async () => {
<div className="stat-title">Einsätze geflogen</div>
<div className="stat-value text-primary">{totalFlownMissions}</div>
<div className="stat-desc">
Du bist damit unter den top {((ownRankMissionsFlown * 100) / totalUserCount).toFixed(0)}%!
Du bist damit unter den top{" "}
{!isNaN(Number(totalFlownMissionsPercent)) ? totalFlownMissionsPercent : 0}%!
</div>
</div>
@@ -233,7 +236,6 @@ export const DispoStats = async () => {
</div>
<div className="stat-title">Einsätze disponiert</div>
<div className="stat-value text-primary">{totalDispatchedMissions}</div>
<div className="stat-desc">Du bist damit unter den top 9%!</div>
</div>
<div className="stat">

View File

@@ -1,105 +0,0 @@
import { ReactNode, useState } from "react";
import { cn } from "../../../../../../helper/cn";
export const PenaltyDropdown = ({
onClick,
btnClassName,
showDatePicker,
btnTip,
Icon,
}: {
onClick: (data: { reason: string; until: Date | null }) => void;
showDatePicker?: boolean;
btnClassName?: string;
btnTip?: string;
Icon: ReactNode;
}) => {
const [reason, setReason] = useState("");
const [until, setUntil] = useState<string>("default");
return (
<details className="dropdown dropdown-left dropdown-center">
<summary className={cn("btn btn-xs btn-square btn-soft", btnClassName)}>{Icon}</summary>
<div className="dropdown-content flex gap-3 items-center bg-base-100 rounded-box z-1 p-2 mr-3 shadow-sm">
<input
value={reason}
onChange={(e) => setReason(e.target.value)}
type="text"
className="input min-w-[250px]"
placeholder="Begründung"
/>
{showDatePicker && (
<select
className="select min-w-[150px] select-bordered"
value={until}
onChange={(e) => setUntil(e.target.value)}
>
<option value="default" disabled>
Unbegrenzt
</option>
<option value="1h">1 Stunde</option>
<option value="6h">6 Stunden</option>
<option value="12h">12 Stunden</option>
<option value="24h">24 Stunden</option>
<option value="72h">72 Stunden</option>
<option value="1w">1 Woche</option>
<option value="2w">2 Wochen</option>
<option value="1m">1 Monat</option>
<option value="3m">3 Monate</option>
<option value="6m">6 Monate</option>
<option value="1y">1 Jahr</option>
</select>
)}
<button
className={cn("btn btn-square btn-soft tooltip tooltip-bottom", btnClassName)}
data-tip={btnTip}
onClick={() => {
let untilDate: Date | null = null;
if (until !== "default") {
const now = new Date();
switch (until) {
case "1h":
untilDate = new Date(now.getTime() + 1 * 60 * 60 * 1000);
break;
case "6h":
untilDate = new Date(now.getTime() + 6 * 60 * 60 * 1000);
break;
case "12h":
untilDate = new Date(now.getTime() + 12 * 60 * 60 * 1000);
break;
case "24h":
untilDate = new Date(now.getTime() + 24 * 60 * 60 * 1000);
break;
case "72h":
untilDate = new Date(now.getTime() + 72 * 60 * 60 * 1000);
break;
case "1w":
untilDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
break;
case "2w":
untilDate = new Date(now.getTime() + 14 * 24 * 60 * 60 * 1000);
break;
case "1m":
untilDate = new Date(now.setMonth(now.getMonth() + 1));
break;
case "3m":
untilDate = new Date(now.setMonth(now.getMonth() + 3));
break;
case "6m":
untilDate = new Date(now.setMonth(now.getMonth() + 6));
break;
case "1y":
untilDate = new Date(now.setFullYear(now.getFullYear() + 1));
break;
default:
untilDate = null;
}
}
onClick({ reason, until: untilDate });
}}
>
{Icon}
</button>
</div>
</details>
);
};

View File

@@ -1,5 +1,6 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { PenaltyDropdown } from "@repo/shared-components";
import {
BADGES,
ConnectedAircraft,
@@ -56,7 +57,6 @@ import { Error } from "_components/Error";
import { useSession } from "next-auth/react";
import { setStandardName } from "../../../../../../helper/discord";
import { penaltyColumns } from "(app)/admin/penalty/columns";
import { PenaltyDropdown } from "(app)/admin/user/[id]/_components/AddPenaltyDropdown";
import { addPenalty, editPenalty, editPenaltys } from "(app)/admin/penalty/actions";
import { reportColumns } from "(app)/admin/report/columns";

View File

@@ -3,7 +3,7 @@ import { DrawingPinFilledIcon } from "@radix-ui/react-icons";
import { Event, Participant, EventAppointment, User } from "@repo/db";
import ModalBtn from "./modalBtn";
import MDEditor from "@uiw/react-md-editor";
import { Badge } from "../../../_components/Badge/Badge";
import { Badge } from "@repo/shared-components";
export const EventCard = ({
user,
@@ -46,7 +46,7 @@ export const EventCard = ({
</div>
<div className="flex col-span-2 justify-end">
{event.finishedBadges.map((b) => {
return <Badge name={b} key={b} />;
return <Badge badge={b} key={b} />;
})}
</div>
</div>

View File

@@ -5,6 +5,7 @@ import { redirect } from "next/navigation";
import { getServerSession } from "../api/auth/[...nextauth]/auth";
import { EmailVerification } from "_components/EmailVerification";
import { FirstPath } from "./_components/FirstPath";
import { Penalty } from "_components/Penalty";
export const metadata: Metadata = {
title: "VAR: Hub",
@@ -41,6 +42,7 @@ export default async function RootLayout({
{/* Scrollbarer Content-Bereich */}
<div className="flex-grow bg-base-100 px-6 rounded-lg shadow-md ml-4 overflow-auto h-full max-w-full w-full">
<Penalty />
{!session?.user.emailVerified && (
<div className="mb-4">
<EmailVerification />

View File

@@ -2,7 +2,7 @@ import Events from "./_components/FeaturedEvents";
import { Stats } from "./_components/Stats";
import { Badges } from "./_components/Badges";
import { RecentFlights } from "(app)/_components/RecentFlights";
import { Penalty } from "(app)/_components/Penalty";
import { Penalty } from "_components/Penalty";
export default async function Home({
searchParams,
@@ -13,7 +13,6 @@ export default async function Home({
const view = stats || "pilot";
return (
<div>
<Penalty />
<Stats stats={view} />
<div className="grid grid-cols-6 gap-4">
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">

View File

@@ -1,30 +0,0 @@
import { BADGES } from "@repo/db";
import P1 from "./p-1.png";
import P2 from "./p-2.png";
import P3 from "./p-3.png";
import D1 from "./d-1.png";
import D2 from "./d-2.png";
import D3 from "./d-3.png";
import DAY1 from "./day-1-member.png";
import { cn } from "../../../helper/cn";
const BadgeImage = {
[BADGES.P1]: P1,
[BADGES.P2]: P2,
[BADGES.P3]: P3,
[BADGES.D1]: D1,
[BADGES.D2]: D2,
[BADGES.D3]: D3,
[BADGES.DAY1]: DAY1,
[BADGES.V1Veteran]: DAY1,
};
export const Badge = ({ name, className }: { name: BADGES; className?: string }) => {
const image = BadgeImage[name];
return (
<span className={cn("flex h-fit p-1", className)}>
<img src={image.src} alt={name} width={100} />
</span>
);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,6 +1,6 @@
import { getPublicUser, prisma } from "@repo/db";
import { TriangleAlert } from "lucide-react";
import { PenaltyCountdown } from "./PenaltyCountdown";
import { PenaltyCountdown } from "../(app)/_components/PenaltyCountdown";
import { getServerSession } from "api/auth/[...nextauth]/auth";
export const Penalty = async () => {

View File

@@ -16,6 +16,7 @@
"@radix-ui/react-icons": "^1.3.2",
"@repo/db": "workspace:*",
"@repo/eslint-config": "workspace:*",
"@repo/shared-components": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@tailwindcss/postcss": "^4.1.8",
"@tanstack/react-query": "^5.79.2",

View File

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB