added Station Add, Delete Update
This commit is contained in:
13
apps/hub/app/(app)/admin/station/[id]/page.tsx
Normal file
13
apps/hub/app/(app)/admin/station/[id]/page.tsx
Normal file
@@ -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 <div>Station not found</div>;
|
||||||
|
return <StationForm station={station} />;
|
||||||
|
};
|
||||||
246
apps/hub/app/(app)/admin/station/_components/Form.tsx
Normal file
246
apps/hub/app/(app)/admin/station/_components/Form.tsx
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
'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<z.infer<typeof StationOptionalDefaultsSchema>>({
|
||||||
|
resolver: zodResolver(StationOptionalDefaultsSchema),
|
||||||
|
defaultValues: station,
|
||||||
|
});
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
|
console.log(form.formState.errors);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(async (values) => {
|
||||||
|
setLoading(true);
|
||||||
|
const createdStation = await upsertStation(values, station?.id);
|
||||||
|
setLoading(false);
|
||||||
|
if (!station) redirect(`/admin/station`);
|
||||||
|
})}
|
||||||
|
className="grid grid-cols-6 gap-3"
|
||||||
|
>
|
||||||
|
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<FileText className="w-5 h-5" /> Allgemeines
|
||||||
|
</h2>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="BOS Rufname"
|
||||||
|
name="bosCallsign"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="BOS Rufname (kurz)"
|
||||||
|
name="bosCallsignShort"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Betreiber"
|
||||||
|
name="operator"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="ATC Rufname"
|
||||||
|
name="atcCallsign"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="FIR (Flight Information Region)"
|
||||||
|
name="fir"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Leitstelle Rufname"
|
||||||
|
name="bosRadioArea"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="form-control w-full">
|
||||||
|
<span className="label-text text-lg flex items-center gap-2">
|
||||||
|
BOS Nutzung
|
||||||
|
</span>
|
||||||
|
<select
|
||||||
|
className="input-sm select select-bordered select-sm"
|
||||||
|
{...form.register('bosUse')}
|
||||||
|
>
|
||||||
|
{Object.keys(BosUse).map((use) => (
|
||||||
|
<option key={use} value={use}>
|
||||||
|
{use}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<LocateIcon className="w-5 h-5" /> Standort + Ausrüstung
|
||||||
|
</h2>
|
||||||
|
<label className="form-control w-full">
|
||||||
|
<span className="label-text text-lg flex items-center gap-2">
|
||||||
|
Land
|
||||||
|
</span>
|
||||||
|
<select
|
||||||
|
className="input-sm select select-bordered select-sm"
|
||||||
|
{...form.register('country', {})}
|
||||||
|
>
|
||||||
|
{Object.keys(Country).map((use) => (
|
||||||
|
<option key={use} value={use}>
|
||||||
|
{use}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Bundesland"
|
||||||
|
name="locationState"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Bundesland (kurz)"
|
||||||
|
name="locationStateShort"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<span className="label-text text-lg flex items-center gap-2">
|
||||||
|
Ausgerüstet mit:
|
||||||
|
</span>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label cursor-pointer">
|
||||||
|
<span>Winde</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="toggle"
|
||||||
|
{...form.register('hasWinch')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="label cursor-pointer">
|
||||||
|
<span>Nachtsicht Gerät</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="toggle"
|
||||||
|
{...form.register('hasNvg')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="label cursor-pointer">
|
||||||
|
<span>24-Stunden Einsatzfähig</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="toggle"
|
||||||
|
{...form.register('is24h')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="label cursor-pointer">
|
||||||
|
<span>Bergetau</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="toggle"
|
||||||
|
{...form.register('hasRope')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Breitengrad"
|
||||||
|
name="latitude"
|
||||||
|
className="input-sm"
|
||||||
|
formOptions={{ valueAsNumber: true }}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Längengrad"
|
||||||
|
name="longitude"
|
||||||
|
className="input-sm"
|
||||||
|
formOptions={{ valueAsNumber: true }}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
<label className="label cursor-pointer">
|
||||||
|
<span className="text-lg">Reichweiten ausblenden</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="toggle"
|
||||||
|
{...form.register('hideRangeRings')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">
|
||||||
|
<PlaneIcon className="w-5 h-5" /> Hubschrauber
|
||||||
|
</h2>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Hubschrauber Typ"
|
||||||
|
name="aircraft"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
formOptions={{ valueAsNumber: true }}
|
||||||
|
type="number"
|
||||||
|
label="Hubschrauber Geschwindigkeit"
|
||||||
|
className="input-sm"
|
||||||
|
name="aircraftSpeed"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
form={form}
|
||||||
|
label="Hubschrauber Registrierung"
|
||||||
|
name="aircraftRegistration"
|
||||||
|
className="input-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||||
|
<div className="card-body ">
|
||||||
|
<div className="flex w-full gap-4">
|
||||||
|
<Button
|
||||||
|
isLoading={loading}
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-primary flex-1"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</Button>
|
||||||
|
{station && (
|
||||||
|
<Button
|
||||||
|
isLoading={deleteLoading}
|
||||||
|
onClick={async () => {
|
||||||
|
setDeleteLoading(true);
|
||||||
|
await deleteStation(station.id);
|
||||||
|
redirect('/admin/station');
|
||||||
|
}}
|
||||||
|
className="btn btn-error"
|
||||||
|
>
|
||||||
|
Löschen
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
20
apps/hub/app/(app)/admin/station/action.ts
Normal file
20
apps/hub/app/(app)/admin/station/action.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { prisma, Prisma, Station } from '@repo/db';
|
||||||
|
|
||||||
|
export const upsertStation = 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 } });
|
||||||
|
};
|
||||||
5
apps/hub/app/(app)/admin/station/new/page.tsx
Normal file
5
apps/hub/app/(app)/admin/station/new/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { StationForm } from '../_components/Form';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return <StationForm />;
|
||||||
|
};
|
||||||
42
apps/hub/app/(app)/admin/station/page.tsx
Normal file
42
apps/hub/app/(app)/admin/station/page.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { DatabaseBackupIcon } from 'lucide-react';
|
||||||
|
import { PaginatedTable } from '../../../_components/PaginatedTable';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-2xl font-semibold text-left flex items-center gap-2 justify-between">
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<DatabaseBackupIcon className="w-5 h-5" /> Stationen
|
||||||
|
</span>
|
||||||
|
<Link href={'/admin/station/new'}>
|
||||||
|
<button className="btn btn-sm btn-outline btn-primary">
|
||||||
|
Erstellen
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
<PaginatedTable
|
||||||
|
showEditButton
|
||||||
|
prismaModel="station"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
header: 'BOS Name',
|
||||||
|
accessorKey: 'bosCallsign',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Bos Use',
|
||||||
|
accessorKey: 'bosUse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Country',
|
||||||
|
accessorKey: 'country',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'operator',
|
||||||
|
accessorKey: 'operator',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
5
apps/hub/app/(app)/admin/user/loading.tsx
Normal file
5
apps/hub/app/(app)/admin/user/loading.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Loading } from '../../../_components/ui/Loading';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
<Loading />;
|
||||||
|
};
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
import Link from 'next/link';
|
import { User2 } from 'lucide-react';
|
||||||
import { PaginatedTable } from '../../../_components/PaginatedTable';
|
import { PaginatedTable } from '../../../_components/PaginatedTable';
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-2xl font-semibold text-left flex items-center gap-2">
|
||||||
|
<User2 className="w-5 h-5" /> Benutzer
|
||||||
|
</p>
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
showEditButton
|
showEditButton
|
||||||
prismaModel="user"
|
prismaModel="user"
|
||||||
@@ -25,5 +29,6 @@ export default async () => {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { PaginatedTable } from '../_components/PaginatedTable';
|
import { PaginatedTable } from '../_components/PaginatedTable';
|
||||||
import { Header } from '../_components/ui/Header';
|
import { Header } from '../_components/ui/Header';
|
||||||
import { PrismaClient } from '@repo/db';
|
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ export const SocialForm = ({
|
|||||||
type="number"
|
type="number"
|
||||||
className="input input-bordered w-full"
|
className="input input-bordered w-full"
|
||||||
placeholder="1445241"
|
placeholder="1445241"
|
||||||
|
defaultValue={user.vatsimCid as number | undefined}
|
||||||
{...form.register('vatsimCid', {
|
{...form.register('vatsimCid', {
|
||||||
valueAsNumber: true,
|
valueAsNumber: true,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function SortableTable<TData>({
|
|||||||
<Link
|
<Link
|
||||||
href={`/admin/${prismaModel as string}/${(row.original as any).id}`}
|
href={`/admin/${prismaModel as string}/${(row.original as any).id}`}
|
||||||
>
|
>
|
||||||
<button className="btn">Edit</button>
|
<button className="btn btn-sm">Edit</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|||||||
52
apps/hub/app/_components/ui/Input.tsx
Normal file
52
apps/hub/app/_components/ui/Input.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
FieldValues,
|
||||||
|
Path,
|
||||||
|
RegisterOptions,
|
||||||
|
UseFormReturn,
|
||||||
|
} from 'react-hook-form';
|
||||||
|
import { cn } from '../../../helper/cn';
|
||||||
|
|
||||||
|
interface InputProps<T extends FieldValues>
|
||||||
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'form'> {
|
||||||
|
name: Path<T>;
|
||||||
|
form: UseFormReturn<T>;
|
||||||
|
formOptions?: RegisterOptions<T>;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Input = <T extends FieldValues>({
|
||||||
|
name,
|
||||||
|
label = name,
|
||||||
|
placeholder = label,
|
||||||
|
form,
|
||||||
|
formOptions,
|
||||||
|
className,
|
||||||
|
...inputProps
|
||||||
|
}: InputProps<T>) => {
|
||||||
|
return (
|
||||||
|
<label className="form-control w-full">
|
||||||
|
<div className="label">
|
||||||
|
<span className="label-text text-lg flex items-center gap-2">
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
{...form.register(name, formOptions)}
|
||||||
|
type="text"
|
||||||
|
className={cn(
|
||||||
|
'input input-bordered w-full placeholder:text-neutral-600',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
{...inputProps}
|
||||||
|
/>
|
||||||
|
{form.formState.errors[name] && (
|
||||||
|
<p className="text-error">
|
||||||
|
{form.formState.errors[name].message as string}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
8
apps/hub/app/_components/ui/Loading.tsx
Normal file
8
apps/hub/app/_components/ui/Loading.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export const Loading = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="spinner"></div>
|
||||||
|
<p>LAAAADEN</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -29,7 +29,10 @@ export const VerticalNav = () => {
|
|||||||
</summary>
|
</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/admin/user">User</Link>
|
<Link href="/admin/user">Benutzer</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link href="/admin/station">Stationen</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
109
package-lock.json
generated
109
package-lock.json
generated
@@ -107,6 +107,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
"@next-auth/prisma-adapter": "^1.0.7",
|
"@next-auth/prisma-adapter": "^1.0.7",
|
||||||
|
"@repo/db": "*",
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
"@tanstack/react-table": "^8.20.6",
|
"@tanstack/react-table": "^8.20.6",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
@@ -1175,6 +1176,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-RrEBkd+HLZx+ydfmYT0jUj7wjLiS95wfTOSQ+8FQbvb6vHh5AeKfEPt/XUQ5+Buljj8hltEfOslEW57/wQIVeA=="
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-sXdqEVLyGAJ5/iUoG/Ea5AdHMN71m6PzMBWRQnLmhhOejzqAaEr8rUd623ql6OJpED4s/U4vIn4dg1qkF7vGag==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.3.1",
|
||||||
|
"@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0",
|
||||||
|
"@prisma/fetch-engine": "6.3.1",
|
||||||
|
"@prisma/get-platform": "6.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0.tgz",
|
||||||
|
"integrity": "sha512-R/ZcMuaWZT2UBmgX3Ko6PAV3f8//ZzsjRIG1eKqp3f2rqEqVtCv+mtzuH2rBPUC9ujJ5kCb9wwpxeyCkLcHVyA==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-HOf/0umOgt+/S2xtZze+FHKoxpVg4YpVxROr6g2YG09VsI3Ipyb+rGvD6QGbCqkq5NTWAAZoOGNL+oy7t+IhaQ==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.3.1",
|
||||||
|
"@prisma/engines-version": "6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0",
|
||||||
|
"@prisma/get-platform": "6.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/generator-helper": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-hX2fxjMksyAWAS0OcDi7GVmRUqsZ35ZY3Zla1EfO+uDYW6BY+om8kuKHyKkIvvRcUlTmL+xccl+nJwNToqP/aA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-AYLq6Hk9xG73JdLWJ3Ip9Wg/vlP7xPvftGBalsPzKDOHr/ImhwJ09eS8xC2vNT12DlzGxhfk8BkL0ve2OriNhQ==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-icons": {
|
"node_modules/@radix-ui/react-icons": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz",
|
||||||
@@ -2666,6 +2719,11 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/code-block-writer": {
|
||||||
|
"version": "12.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
|
||||||
|
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w=="
|
||||||
|
},
|
||||||
"node_modules/color": {
|
"node_modules/color": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
@@ -4274,7 +4332,6 @@
|
|||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -7314,6 +7371,33 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||||
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
||||||
},
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-JKCZWvBC3enxk51tY4TWzS4b5iRt4sSU1uHn2I183giZTvonXaQonzVtjLzpOHE7qu9MxY510kAtFGJwryKe3Q==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "6.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@@ -8869,7 +8953,7 @@
|
|||||||
"version": "5.5.4",
|
"version": "5.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -9282,6 +9366,24 @@
|
|||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zod-prisma-types": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod-prisma-types/-/zod-prisma-types-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-S4spVBMJAmecLv+aLyRhXK26qW9nWwcsOf1H1fRcEmiI8DPbftZ99u0fhqHlymuTpmMcUpuKxcNNOIqNY0ScSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/generator-helper": "^6.3.0",
|
||||||
|
"code-block-writer": "^12.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"zod": "^3.24.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"zod-prisma-types": "dist/bin.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@prisma/client": "^4.x.x || ^5.x.x || ^6.x.x",
|
||||||
|
"prisma": "^4.x.x || ^5.x.x || ^6.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zustand": {
|
"node_modules/zustand": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz",
|
||||||
@@ -9315,7 +9417,8 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.2.1"
|
"@prisma/client": "^6.2.1",
|
||||||
|
"zod-prisma-types": "^3.2.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/eslint-config": {
|
"packages/eslint-config": {
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
export { prisma } from './prisma/client'; // exports instance of prisma
|
export { prisma } from './prisma/client'; // exports instance of prisma
|
||||||
export * from './generated/client'; // exports generated types from prisma
|
export * from './generated/client'; // exports generated types from prisma
|
||||||
|
|
||||||
|
import * as zodTypes from './generated/zod';
|
||||||
|
|
||||||
|
export const zod = zodTypes;
|
||||||
|
|||||||
@@ -5,16 +5,18 @@
|
|||||||
"main": "generated/client/index.js",
|
"main": "generated/client/index.js",
|
||||||
"types": "generated/client/index.d.ts",
|
"types": "generated/client/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"db:generate": "npx prisma generate",
|
"db:generate": "npx prisma generate && npx prisma generate zod",
|
||||||
"db:migrate": "npx prisma migrate dev",
|
"db:migrate": "npx prisma migrate dev",
|
||||||
"db:deploy": "npx prisma migrate deploy"
|
"db:deploy": "npx prisma migrate deploy"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.ts"
|
".": "./index.ts",
|
||||||
|
"./zod": "./zod.ts"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.2.1"
|
"@prisma/client": "^6.2.1",
|
||||||
|
"zod-prisma-types": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "BosUse" AS ENUM ('PRIMARY', 'SECONDARY', 'DUAL_USE');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Country" AS ENUM ('DE', 'AT', 'CH');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Station" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"bosUse" "BosUse" NOT NULL,
|
||||||
|
"bosCallsign" TEXT NOT NULL,
|
||||||
|
"bosCallsignShort" TEXT NOT NULL,
|
||||||
|
"bosRadioArea" TEXT NOT NULL,
|
||||||
|
"country" "Country" NOT NULL,
|
||||||
|
"operator" TEXT NOT NULL,
|
||||||
|
"aircraft" TEXT NOT NULL,
|
||||||
|
"aircraftRegistration" TEXT NOT NULL,
|
||||||
|
"aircraftSpeed" INTEGER NOT NULL,
|
||||||
|
"hasWinch" BOOLEAN NOT NULL,
|
||||||
|
"is24h" BOOLEAN NOT NULL,
|
||||||
|
"hasNvg" BOOLEAN NOT NULL,
|
||||||
|
"locationState" TEXT NOT NULL,
|
||||||
|
"locationStateShort" TEXT NOT NULL,
|
||||||
|
"hasRope" BOOLEAN NOT NULL,
|
||||||
|
"fir" TEXT NOT NULL,
|
||||||
|
"latitude" DOUBLE PRECISION NOT NULL,
|
||||||
|
"longitude" DOUBLE PRECISION NOT NULL,
|
||||||
|
"atcCallsign" TEXT NOT NULL,
|
||||||
|
"hideRangeRings" BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Station_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
@@ -10,6 +10,12 @@ generator client {
|
|||||||
output = "../../generated/client"
|
output = "../../generated/client"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generator zod {
|
||||||
|
provider = "zod-prisma-types"
|
||||||
|
output = "../../generated/zod"
|
||||||
|
createOptionalDefaultValuesTypes = true
|
||||||
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
|
|||||||
35
packages/database/prisma/schema/station.prisma
Normal file
35
packages/database/prisma/schema/station.prisma
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
enum BosUse {
|
||||||
|
PRIMARY
|
||||||
|
SECONDARY
|
||||||
|
DUAL_USE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Country {
|
||||||
|
DE
|
||||||
|
AT
|
||||||
|
CH
|
||||||
|
}
|
||||||
|
|
||||||
|
model Station {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
bosUse BosUse
|
||||||
|
bosCallsign String
|
||||||
|
bosCallsignShort String
|
||||||
|
bosRadioArea String
|
||||||
|
country Country
|
||||||
|
operator String
|
||||||
|
aircraft String
|
||||||
|
aircraftRegistration String
|
||||||
|
aircraftSpeed Int
|
||||||
|
hasWinch Boolean
|
||||||
|
is24h Boolean
|
||||||
|
hasNvg Boolean
|
||||||
|
locationState String
|
||||||
|
locationStateShort String
|
||||||
|
hasRope Boolean
|
||||||
|
fir String
|
||||||
|
latitude Float
|
||||||
|
longitude Float
|
||||||
|
atcCallsign String
|
||||||
|
hideRangeRings Boolean
|
||||||
|
}
|
||||||
1
packages/database/zod.ts
Normal file
1
packages/database/zod.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './generated/zod';
|
||||||
Reference in New Issue
Block a user