diff --git a/apps/dispatch/app/(dispatch)/_components/map/BaseMaps.tsx b/apps/dispatch/app/(dispatch)/_components/map/BaseMaps.tsx
index 6f04e0dc..938e95d0 100644
--- a/apps/dispatch/app/(dispatch)/_components/map/BaseMaps.tsx
+++ b/apps/dispatch/app/(dispatch)/_components/map/BaseMaps.tsx
@@ -7,6 +7,7 @@ export const BaseMaps = () => {
>
);
diff --git a/apps/hub/app/(app)/admin/keyword/[id]/page.tsx b/apps/hub/app/(app)/admin/keyword/[id]/page.tsx
new file mode 100644
index 00000000..399adf90
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/[id]/page.tsx
@@ -0,0 +1,13 @@
+import { prisma } from '@repo/db';
+import { StationForm } from '../_components/Form';
+
+export default async ({ params }: { params: Promise<{ id: string }> }) => {
+ const { id } = await params;
+ const station = await prisma.station.findUnique({
+ where: {
+ id: parseInt(id),
+ },
+ });
+ if (!station) return
Station not found
;
+ return ;
+};
diff --git a/apps/hub/app/(app)/admin/keyword/_components/Form.tsx b/apps/hub/app/(app)/admin/keyword/_components/Form.tsx
new file mode 100644
index 00000000..c604dfa9
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/_components/Form.tsx
@@ -0,0 +1,245 @@
+"use client";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { StationOptionalDefaultsSchema } from "@repo/db/zod";
+import { set, useForm } from "react-hook-form";
+import { z } from "zod";
+import { BosUse, Country, Station } from "@repo/db";
+import { FileText, LocateIcon, PlaneIcon } from "lucide-react";
+import { Input } from "../../../../_components/ui/Input";
+import { useState } from "react";
+import { deleteStation, upsertStation } from "../action";
+import { Button } from "../../../../_components/ui/Button";
+import { redirect } from "next/navigation";
+
+export const StationForm = ({ station }: { station?: Station }) => {
+ const form = useForm>({
+ resolver: zodResolver(StationOptionalDefaultsSchema),
+ defaultValues: station,
+ });
+ const [loading, setLoading] = useState(false);
+ const [deleteLoading, setDeleteLoading] = useState(false);
+ return (
+ <>
+
+ >
+ );
+};
diff --git a/apps/hub/app/(app)/admin/keyword/action.ts b/apps/hub/app/(app)/admin/keyword/action.ts
new file mode 100644
index 00000000..819995ad
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/action.ts
@@ -0,0 +1,20 @@
+"use server";
+
+import { prisma, Prisma, Station } from "@repo/db";
+
+export const upsertKeyword = async (
+ station: Prisma.StationCreateInput,
+ id?: Station["id"],
+) => {
+ const newStation = id
+ ? await prisma.station.update({
+ where: { id: id },
+ data: station,
+ })
+ : await prisma.station.create({ data: station });
+ return newStation;
+};
+
+export const deleteStation = async (id: Station["id"]) => {
+ await prisma.station.delete({ where: { id: id } });
+};
diff --git a/apps/hub/app/(app)/admin/keyword/layout.tsx b/apps/hub/app/(app)/admin/keyword/layout.tsx
new file mode 100644
index 00000000..4b82ebd3
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/layout.tsx
@@ -0,0 +1,20 @@
+import { prisma } from "@repo/db";
+import { Error } from "_components/Error";
+import { getServerSession } from "api/auth/[...nextauth]/auth";
+
+export default async ({ children }: { children: React.ReactNode }) => {
+ const session = await getServerSession();
+
+ if (!session) return ;
+
+ const user = await prisma.user.findUnique({
+ where: {
+ id: session.user.id,
+ },
+ });
+
+ if (!user?.permissions.includes("ADMIN_STATION"))
+ return ;
+
+ return <>{children}>;
+};
diff --git a/apps/hub/app/(app)/admin/keyword/new/page.tsx b/apps/hub/app/(app)/admin/keyword/new/page.tsx
new file mode 100644
index 00000000..a7d5f4de
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/new/page.tsx
@@ -0,0 +1,5 @@
+import { StationForm } from '../_components/Form';
+
+export default () => {
+ return ;
+};
diff --git a/apps/hub/app/(app)/admin/keyword/page.tsx b/apps/hub/app/(app)/admin/keyword/page.tsx
new file mode 100644
index 00000000..39a98b33
--- /dev/null
+++ b/apps/hub/app/(app)/admin/keyword/page.tsx
@@ -0,0 +1,47 @@
+import { DatabaseBackupIcon } from 'lucide-react';
+import { PaginatedTable } from '../../../_components/PaginatedTable';
+import Link from 'next/link';
+
+export default () => {
+ return (
+ <>
+
+ Stationen
+
+ }
+ rightOfSearch={
+
+
+
+
+
+ }
+ />
+ >
+ );
+};
diff --git a/grafana/grafana.db b/grafana/grafana.db
index 131eabd6..8474221c 100644
Binary files a/grafana/grafana.db and b/grafana/grafana.db differ
diff --git a/packages/database/prisma/schema/keyword.prisma b/packages/database/prisma/schema/keyword.prisma
new file mode 100644
index 00000000..9e022cbc
--- /dev/null
+++ b/packages/database/prisma/schema/keyword.prisma
@@ -0,0 +1,10 @@
+enum KEYWORD_CATEGORY {
+ AB_ATMUNG: "AB_ATMUNG",
+}
+
+model Keyword {
+ id Int @id @default(autoincrement())
+ name String
+ description String
+ // relations:
+}