Added Data to SituationsBoard

This commit is contained in:
PxlLoewe
2025-06-02 22:44:49 -07:00
parent ff18b2d72d
commit 4acdb48344
15 changed files with 344 additions and 7103 deletions

View File

@@ -63,6 +63,7 @@ export const sendAlert = async (
stationId: aircraft.stationId, stationId: aircraft.stationId,
}, },
}); });
if (!existingMissionOnStationUser) if (!existingMissionOnStationUser)
await prisma.missionOnStationUsers.create({ await prisma.missionOnStationUsers.create({
data: { data: {
@@ -81,7 +82,7 @@ export const sendAlert = async (
stationId, stationId,
})), })),
}) })
.catch(() => { .catch((err) => {
// Ignore if the entry already exists // Ignore if the entry already exists
}); });

View File

@@ -3,10 +3,45 @@ import { useLeftMenuStore } from "_store/leftMenuStore";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { cn } from "_helpers/cn"; import { cn } from "_helpers/cn";
import { ListCollapse, Plane } from "lucide-react"; import { ListCollapse, Plane } from "lucide-react";
import { useQuery } from "@tanstack/react-query";
import { getMissionsAPI } from "_querys/missions";
import { MissionsOnStations, Station } from "@repo/db";
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
import { FMS_STATUS_COLORS, FMS_STATUS_TEXT_COLORS } from "_components/map/AircraftMarker";
import { useMapStore } from "_store/mapStore";
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
export const SituationBoard = () => { export const SituationBoard = () => {
const { setSituationTabOpen, situationTabOpen } = useLeftMenuStore(); const { setSituationTabOpen, situationTabOpen } = useLeftMenuStore();
const session = useSession(); const dispatcherConnected = useDispatchConnectionStore((state) => state.status === "connected");
const { data: missions } = useQuery({
queryKey: ["missions", "missions-on-stations"],
queryFn: () =>
getMissionsAPI(
{
state: {
not: "finished",
},
},
{
MissionsOnStations: {
include: {
Station: true,
},
},
},
{
createdAt: "desc",
},
),
});
const { data: connectedAircrafts } = useQuery({
queryKey: ["aircrafts"],
queryFn: () => getConnectedAircraftsAPI(),
});
const { setOpenAircraftMarker, setOpenMissionMarker, setMap } = useMapStore((state) => state);
console.log("station", connectedAircrafts);
return ( return (
<div className={cn("dropdown dropdown-top", situationTabOpen && "dropdown-open")}> <div className={cn("dropdown dropdown-top", situationTabOpen && "dropdown-open")}>
@@ -23,19 +58,117 @@ export const SituationBoard = () => {
{situationTabOpen && ( {situationTabOpen && (
<div <div
tabIndex={0} tabIndex={0}
className="dropdown-content card bg-base-200 w-150 shadow-md z-[1100] ml-2 border-1 border-info" className="dropdown-content card bg-base-200 shadow-md z-[1100] ml-2 border-1 border-info"
> >
<div className="card-body flex flex-row gap-4"> <div className="card-body flex flex-row gap-4">
<div className="flex-1"> <div className="flex-1">
<h2 className="inline-flex items-center gap-2 text-lg font-bold mb-2"> <h2 className="inline-flex items-center gap-2 text-lg font-bold mb-2">
<ListCollapse /> Einsatzliste <ListCollapse /> Einsatzliste
</h2> </h2>
<div className="overflow-x-auto">
<table className="table table-xs">
{/* head */}
<thead>
<tr>
<th>ID</th>
<th>Stichwort</th>
<th>Stadt</th>
<th>Stationen</th>
</tr>
</thead>
<tbody>
{/* row 1 */}
{missions?.map((mission) => (
<tr
onDoubleClick={() => {
setOpenMissionMarker({
open: [
{
id: mission.id,
tab: "home",
},
],
close: [],
});
setMap({
center: {
lat: mission.addressLat,
lng: mission.addressLng,
},
zoom: 14,
});
}}
key={mission.id}
className={cn(mission.state === "draft" && "bg-base-300")}
>
<td>{mission.publicId}</td>
<td>{mission.missionKeywordAbbreviation}</td>
<td>{mission.addressCity}</td>
<td>
{(mission as any).MissionsOnStations?.map(
(mos: { Station: Station }) => mos.Station?.bosCallsignShort,
).join(", ")}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div> </div>
<div className="w-px bg-gray-400 mx-2" /> <div className="w-px bg-gray-400 mx-2" />
<div className="flex-1"> <div className="flex-1">
<h2 className="inline-flex items-center gap-2 text-lg font-bold mb-2"> <h2 className="inline-flex items-center gap-2 text-lg font-bold mb-2">
<Plane /> Stationen <Plane /> Stationen
</h2> </h2>
<div className="overflow-x-auto">
<table className="table table-xs">
<thead>
<tr>
<th>BOS Name</th>
<th>Status</th>
<th>LST</th>
</tr>
</thead>
<tbody>
{connectedAircrafts?.map((station) => (
<tr
key={station.id}
onDoubleClick={() => {
setOpenAircraftMarker({
open: [
{
id: station.id,
tab: "home",
},
],
close: [],
});
if (station.posLat === null || station.posLng === null) return;
setMap({
center: {
lat: station.posLat,
lng: station.posLng,
},
zoom: 14,
});
}}
>
<td>{station.Station.bosCallsignShort}</td>
<td
className="text-center font-lg font-semibold"
style={{
color: FMS_STATUS_TEXT_COLORS[station.fmsStatus],
backgroundColor: FMS_STATUS_COLORS[station.fmsStatus],
}}
>
{station.fmsStatus}
</td>
<td className="whitespace-nowrap">{station.Station.bosRadioArea}</td>
</tr>
))}
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -169,6 +169,8 @@ const MissionPopupContent = ({
onClick={() => { onClick={() => {
setMissionFormValues({ setMissionFormValues({
...mission, ...mission,
addressMissionLocation: mission.addressMissionLocation ?? undefined,
addressAdditionalInfo: mission.addressAdditionalInfo ?? undefined,
state: "draft", state: "draft",
hpgLocationLat: mission.hpgLocationLat ?? undefined, hpgLocationLat: mission.hpgLocationLat ?? undefined,
hpgLocationLng: mission.hpgLocationLng ?? undefined, hpgLocationLng: mission.hpgLocationLng ?? undefined,

View File

@@ -2,10 +2,16 @@ import { Mission, MissionSdsLog, Prisma } from "@repo/db";
import axios from "axios"; import axios from "axios";
import { serverApi } from "_helpers/axios"; import { serverApi } from "_helpers/axios";
export const getMissionsAPI = async (filter?: Prisma.MissionWhereInput) => { export const getMissionsAPI = async (
filter?: Prisma.MissionWhereInput,
include?: Prisma.MissionInclude,
orderBy?: Prisma.MissionOrderByWithRelationInput,
) => {
const res = await axios.get<Mission[]>("/api/missions", { const res = await axios.get<Mission[]>("/api/missions", {
params: { params: {
filter: JSON.stringify(filter), filter: JSON.stringify(filter),
include: JSON.stringify(include),
orderBy: JSON.stringify(orderBy),
}, },
}); });
if (res.status !== 200) { if (res.status !== 200) {

View File

@@ -5,8 +5,12 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
const { searchParams } = new URL(req.url); const { searchParams } = new URL(req.url);
const id = searchParams.get("id"); const id = searchParams.get("id");
const filter = searchParams.get("filter"); const filter = searchParams.get("filter");
const include = searchParams.get("include");
const orderBy = searchParams.get("orderBy");
const filterParsed = JSON.parse(filter || "{}"); const filterParsed = JSON.parse(filter || "{}");
const includeParsed = JSON.parse(include || "{}");
const orderByParsed = JSON.parse(orderBy || "{}");
try { try {
const data = await prisma.mission.findMany({ const data = await prisma.mission.findMany({
@@ -14,6 +18,12 @@ export async function GET(req: NextRequest): Promise<NextResponse> {
id: id ? Number(id) : undefined, id: id ? Number(id) : undefined,
...filterParsed, ...filterParsed,
}, },
include: {
...includeParsed,
},
orderBy: {
...orderByParsed,
},
}); });
return NextResponse.json(data, { status: 200 }); return NextResponse.json(data, { status: 200 });

View File

@@ -19,6 +19,8 @@
"@next-auth/prisma-adapter": "^1.0.7", "@next-auth/prisma-adapter": "^1.0.7",
"@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-icons": "^1.3.2",
"@repo/db": "*", "@repo/db": "*",
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"@tailwindcss/postcss": "^4.1.8", "@tailwindcss/postcss": "^4.1.8",
"@tanstack/react-query": "^5.79.0", "@tanstack/react-query": "^5.79.0",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
@@ -50,8 +52,6 @@
"zustand-sync-tabs": "^0.2.2" "zustand-sync-tabs": "^0.2.2"
}, },
"devDependencies": { "devDependencies": {
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"@types/leaflet": "^1.9.18", "@types/leaflet": "^1.9.18",
"@types/node": "^22.15.29", "@types/node": "^22.15.29",
"@types/react": "^19.1.6", "@types/react": "^19.1.6",

View File

@@ -9,6 +9,11 @@
}, },
"packageManager": "pnpm@10.11.0", "packageManager": "pnpm@10.11.0",
"devDependencies": { "devDependencies": {
"concurrently": "^9.1.2",
"typescript": "latest"
},
"dependencies": {
"@react-email/components": "^0.0.41",
"@repo/db": "*", "@repo/db": "*",
"@repo/typescript-config": "*", "@repo/typescript-config": "*",
"@types/cors": "^2.8.18", "@types/cors": "^2.8.18",
@@ -16,11 +21,6 @@
"@types/node": "^22.15.29", "@types/node": "^22.15.29",
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/react": "^19.1.6", "@types/react": "^19.1.6",
"concurrently": "^9.1.2",
"typescript": "latest"
},
"dependencies": {
"@react-email/components": "^0.0.41",
"axios": "^1.9.0", "axios": "^1.9.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"cron": "^4.3.1", "cron": "^4.3.1",

View File

@@ -1,10 +1,11 @@
import { Award } from "lucide-react"; import { Award } from "lucide-react";
import { getServerSession } from "../../api/auth/[...nextauth]/auth"; import { getServerSession } from "../../api/auth/[...nextauth]/auth";
import { Badge } from "../../_components/Badge/Badge"; import { Badge } from "../../_components/Badge/Badge";
import { JSX } from "react";
export const Badges = async () => { export const Badges: () => Promise<JSX.Element> = async () => {
const session = await getServerSession(); const session = await getServerSession();
if (!session) return null; if (!session) return <div />;
return ( return (
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3"> <div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">

View File

@@ -14,6 +14,8 @@
"@next-auth/prisma-adapter": "^1.0.7", "@next-auth/prisma-adapter": "^1.0.7",
"@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-icons": "^1.3.2",
"@repo/db": "*", "@repo/db": "*",
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"@tanstack/react-query": "^5.79.0", "@tanstack/react-query": "^5.79.0",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@uiw/react-md-editor": "^4.0.7", "@uiw/react-md-editor": "^4.0.7",
@@ -42,19 +44,17 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3", "@eslint/eslintrc": "^3",
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"@tailwindcss/postcss": "^4.1.8", "@tailwindcss/postcss": "^4.1.8",
"@types/bcryptjs": "^3.0.0", "@types/bcryptjs": "^3.0.0",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
"@types/node": "^22.15.29", "@types/node": "^22.15.29",
"@types/react": "^19", "@types/react": "^19.1.6",
"@types/react-dom": "^19", "@types/react-dom": "^19.1.5",
"daisyui": "^5.0.43", "daisyui": "^5.0.43",
"eslint": "^9", "eslint": "^9.15.0",
"eslint-config-next": "^15.3.3", "eslint-config-next": "^15.3.3",
"postcss": "^8.5.4", "postcss": "^8.5.4",
"tailwindcss": "^4.1.8", "tailwindcss": "^4.1.8",
"typescript": "^5" "typescript": "^5.8.3"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@repo/db", "name": "@repo/db",
"version": "0.0.0", "version": "1.0.0",
"description": "VAR Databse package", "description": "VAR Databse package",
"main": "generated/client/index.js", "main": "generated/client/index.js",
"types": "generated/client/index.d.ts", "types": "generated/client/index.d.ts",

View File

@@ -1,8 +1,7 @@
{ {
"name": "@repo/eslint-config", "name": "@repo/eslint-config",
"version": "0.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"private": true,
"exports": { "exports": {
"./base": "./base.js", "./base": "./base.js",
"./next-js": "./next.js", "./next-js": "./next.js",
@@ -18,7 +17,7 @@
"eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-turbo": "^2.3.0", "eslint-plugin-turbo": "^2.3.0",
"globals": "^15.12.0", "globals": "^15.12.0",
"typescript": "^5.3.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.15.0" "typescript-eslint": "^8.15.0"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@repo/typescript-config", "name": "@repo/typescript-config",
"version": "0.0.0", "version": "1.0.0",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"publishConfig": { "publishConfig": {

7240
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,9 @@ packages:
- apps/* - apps/*
- packages/* - packages/*
onlyBuiltDependencies: onlyBuiltDependencies:
- '@prisma/client' - "@prisma/client"
- '@prisma/engines' - "@prisma/engines"
- '@tailwindcss/oxide' - "@tailwindcss/oxide"
- esbuild - esbuild
- prisma - prisma
- sharp - sharp

View File

@@ -42,6 +42,7 @@
"NEXT_PUBLIC_DISCORD_URL" "NEXT_PUBLIC_DISCORD_URL"
], ],
"ui": "tui", "ui": "tui",
"tasks": { "tasks": {
"generate": { "generate": {
"cache": false, "cache": false,