Fixed type errors in nextJS apps, added Docker files
Co-authored-by: Nicolas <nocnico@users.noreply.github.com>
This commit is contained in:
6
apps/dispatch-server/.dockerignore
Normal file
6
apps/dispatch-server/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
nodemon.json
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
29
apps/dispatch-server/Dockerfile
Normal file
29
apps/dispatch-server/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /usr/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Change ownership to the non-root user
|
||||||
|
RUN chown -R node:node /usr/app
|
||||||
|
|
||||||
|
# Copy the rest of the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 3002
|
||||||
|
|
||||||
|
# Run container as non-root (unprivileged) user
|
||||||
|
# The "node" user is provided in the Node.js Alpine base image
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
CMD ["node", "index.js"]
|
||||||
7
apps/dispatch/.dockerignore
Normal file
7
apps/dispatch/.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
.eslint.config.msj
|
||||||
|
.README.md
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
29
apps/dispatch/Dockerfile
Normal file
29
apps/dispatch/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /usr/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Change ownership to the non-root user
|
||||||
|
RUN chown -R node:node /usr/app
|
||||||
|
|
||||||
|
# Copy the rest of the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 3001
|
||||||
|
|
||||||
|
# Run container as non-root (unprivileged) user
|
||||||
|
# The "node" user is provided in the Node.js Alpine base image
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { usePilotConnectionStore } from "../../../_store/pilot/connectionStore";
|
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getStationsAPI } from "querys/stations";
|
import { getStationsAPI } from "querys/stations";
|
||||||
|
|||||||
6
apps/hub-server/.dockerignore
Normal file
6
apps/hub-server/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
nodemon.json
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
31
apps/hub-server/Dockerfile
Normal file
31
apps/hub-server/Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /usr/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Change ownership to the non-root user
|
||||||
|
RUN chown -R node:node /usr/app
|
||||||
|
|
||||||
|
# Copy the rest of the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 3003
|
||||||
|
|
||||||
|
# Run container as non-root (unprivileged) user
|
||||||
|
# The "node" user is provided in the Node.js Alpine base image
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
CMD ["node", "index.js"]
|
||||||
8
apps/hub-server/helper/events.ts
Normal file
8
apps/hub-server/helper/events.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Event, Participant } from "@repo/db";
|
||||||
|
|
||||||
|
export const eventCompleted = (event: Event, participant?: Participant) => {
|
||||||
|
if (!participant) return false;
|
||||||
|
if (event.finisherMoodleCourseId && !participant.finisherMoodleCurseCompleted) return false;
|
||||||
|
if (event.hasPresenceEvents && !participant.attended) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { getMoodleCourseCompletionStatus, getMoodleUserById } from "./moodle";
|
import { getMoodleCourseCompletionStatus, getMoodleUserById } from "./moodle";
|
||||||
import { CronJob } from "cron";
|
import { CronJob } from "cron";
|
||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { eventCompleted } from "@repo/ui/helper";
|
|
||||||
import { sendCourseCompletedEmail } from "modules/mail";
|
import { sendCourseCompletedEmail } from "modules/mail";
|
||||||
import { handleParticipantFinished } from "modules/event";
|
import { handleParticipantFinished } from "modules/event";
|
||||||
|
import { eventCompleted } from "helper/events";
|
||||||
|
|
||||||
const syncMoodleIds = async () => {
|
const syncMoodleIds = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
8
apps/hub/.dockerignore
Normal file
8
apps/hub/.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
node_modules
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
.gitignore
|
||||||
|
.eslint.config.msj
|
||||||
|
.README.md
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
29
apps/hub/Dockerfile
Normal file
29
apps/hub/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /usr/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Change ownership to the non-root user
|
||||||
|
RUN chown -R node:node /usr/app
|
||||||
|
|
||||||
|
# Copy the rest of the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Run container as non-root (unprivileged) user
|
||||||
|
# The "node" user is provided in the Node.js Alpine base image
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { Form } from "../_components/Form";
|
import { Form } from "../_components/Form";
|
||||||
|
|
||||||
export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const event = await prisma.event.findUnique({
|
const event = await prisma.event.findUnique({
|
||||||
where: {
|
where: {
|
||||||
@@ -10,4 +10,4 @@ export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
});
|
});
|
||||||
if (!event) return <div>Event not found</div>;
|
if (!event) return <div>Event not found</div>;
|
||||||
return <Form event={event} />;
|
return <Form event={event} />;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,34 +1,21 @@
|
|||||||
import { DateInput } from "../../../../_components/ui/DateInput";
|
import { Event, Participant } from "@repo/db";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { EventAppointmentOptionalDefaults } from "@repo/db/zod";
|
||||||
import { Event, Participant, Prisma } from "@repo/db";
|
|
||||||
import {
|
|
||||||
EventAppointmentOptionalDefaults,
|
|
||||||
EventAppointmentOptionalDefaultsSchema,
|
|
||||||
ParticipantOptionalDefaultsSchema,
|
|
||||||
} from "@repo/db/zod";
|
|
||||||
import { useSession } from "next-auth/react";
|
|
||||||
import { Controller, useForm, UseFormReturn } from "react-hook-form";
|
|
||||||
import { deleteAppoinement, upsertAppointment } from "../action";
|
|
||||||
import { Button } from "../../../../_components/ui/Button";
|
|
||||||
import {
|
|
||||||
PaginatedTable,
|
|
||||||
PaginatedTableRef,
|
|
||||||
} from "../../../../_components/PaginatedTable";
|
|
||||||
import { Ref, RefObject, useRef } from "react";
|
|
||||||
import { CellContext } from "@tanstack/react-table";
|
import { CellContext } from "@tanstack/react-table";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
import { RefObject, useRef } from "react";
|
||||||
|
import { UseFormReturn } from "react-hook-form";
|
||||||
|
import { PaginatedTable, PaginatedTableRef } from "../../../../_components/PaginatedTable";
|
||||||
|
import { Button } from "../../../../_components/ui/Button";
|
||||||
|
import { DateInput } from "../../../../_components/ui/DateInput";
|
||||||
import { upsertParticipant } from "../../../events/actions";
|
import { upsertParticipant } from "../../../events/actions";
|
||||||
import { Switch } from "../../../../_components/ui/Switch";
|
import { deleteAppoinement, upsertAppointment } from "../action";
|
||||||
|
|
||||||
interface AppointmentModalProps {
|
interface AppointmentModalProps {
|
||||||
event?: Event;
|
event?: Event;
|
||||||
ref: RefObject<HTMLDialogElement | null>;
|
ref: RefObject<HTMLDialogElement | null>;
|
||||||
participantModal: RefObject<HTMLDialogElement | null>;
|
participantModal: RefObject<HTMLDialogElement | null>;
|
||||||
appointmentsTableRef: React.RefObject<PaginatedTableRef>;
|
appointmentsTableRef: React.RefObject<PaginatedTableRef | null>;
|
||||||
appointmentForm: UseFormReturn<
|
appointmentForm: UseFormReturn<EventAppointmentOptionalDefaults, any, undefined>;
|
||||||
EventAppointmentOptionalDefaults,
|
|
||||||
any,
|
|
||||||
undefined
|
|
||||||
>;
|
|
||||||
participantForm: UseFormReturn<Participant, any, undefined>;
|
participantForm: UseFormReturn<Participant, any, undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,9 +43,7 @@ export const AppointmentModal = ({
|
|||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<h3 className="font-bold text-lg">
|
<h3 className="font-bold text-lg">Termin {appointmentForm.watch("id")}</h3>
|
||||||
Termin {appointmentForm.watch("id")}
|
|
||||||
</h3>
|
|
||||||
<form
|
<form
|
||||||
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||||
if (!event) return;
|
if (!event) return;
|
||||||
@@ -101,13 +86,7 @@ export const AppointmentModal = ({
|
|||||||
accessorKey: "enscriptionDate",
|
accessorKey: "enscriptionDate",
|
||||||
header: "Einschreibedatum",
|
header: "Einschreibedatum",
|
||||||
cell: ({ row }: CellContext<Participant, any>) => {
|
cell: ({ row }: CellContext<Participant, any>) => {
|
||||||
return (
|
return <span>{new Date(row.original.enscriptionDate).toLocaleString()}</span>;
|
||||||
<span>
|
|
||||||
{new Date(
|
|
||||||
row.original.enscriptionDate,
|
|
||||||
).toLocaleString()}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,11 +95,7 @@ export const AppointmentModal = ({
|
|||||||
if (row.original.attended) {
|
if (row.original.attended) {
|
||||||
return <span className="text-green-500">Ja</span>;
|
return <span className="text-green-500">Ja</span>;
|
||||||
} else if (row.original.appointmentCancelled) {
|
} else if (row.original.appointmentCancelled) {
|
||||||
return (
|
return <span className="text-red-500">Nein (Termin abgesagt)</span>;
|
||||||
<span className="text-red-500">
|
|
||||||
Nein (Termin abgesagt)
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return <span>?</span>;
|
return <span>?</span>;
|
||||||
}
|
}
|
||||||
@@ -159,33 +134,32 @@ export const AppointmentModal = ({
|
|||||||
Anwesend
|
Anwesend
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{!row.original.appointmentCancelled &&
|
{!row.original.appointmentCancelled && event?.hasPresenceEvents && (
|
||||||
event?.hasPresenceEvents && (
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
onSubmit={() => {}}
|
||||||
onSubmit={() => {}}
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await upsertParticipant({
|
||||||
await upsertParticipant({
|
eventId: event!.id,
|
||||||
eventId: event!.id,
|
userId: row.original.userId,
|
||||||
userId: row.original.userId,
|
attended: false,
|
||||||
attended: false,
|
appointmentCancelled: true,
|
||||||
appointmentCancelled: true,
|
statusLog: [
|
||||||
statusLog: [
|
...(row.original.statusLog as any),
|
||||||
...(row.original.statusLog as any),
|
{
|
||||||
{
|
event: "Gefehlt an Event",
|
||||||
event: "Gefehlt an Event",
|
timestamp: new Date().toISOString(),
|
||||||
timestamp: new Date().toISOString(),
|
user: `${session?.user?.firstname} ${session?.user?.lastname} - ${session?.user?.publicId}`,
|
||||||
user: `${session?.user?.firstname} ${session?.user?.lastname} - ${session?.user?.publicId}`,
|
},
|
||||||
},
|
],
|
||||||
],
|
});
|
||||||
});
|
participantTableRef.current?.refresh();
|
||||||
participantTableRef.current?.refresh();
|
}}
|
||||||
}}
|
className="btn btn-outline btn-error btn-sm"
|
||||||
className="btn btn-outline btn-error btn-sm"
|
>
|
||||||
>
|
abwesend
|
||||||
abwesend
|
</button>
|
||||||
</button>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,51 +1,42 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { BADGES, Event, EVENT_TYPE, Participant, PERMISSION, User } from "@repo/db";
|
||||||
import {
|
import {
|
||||||
EventAppointmentOptionalDefaults,
|
EventAppointmentOptionalDefaults,
|
||||||
EventAppointmentOptionalDefaultsSchema,
|
EventAppointmentOptionalDefaultsSchema,
|
||||||
EventOptionalDefaults,
|
EventOptionalDefaults,
|
||||||
EventOptionalDefaultsSchema,
|
EventOptionalDefaultsSchema,
|
||||||
ParticipantOptionalDefaultsSchema,
|
ParticipantSchema,
|
||||||
} from "@repo/db/zod";
|
} from "@repo/db/zod";
|
||||||
import { Controller, set, useForm } from "react-hook-form";
|
import { Bot, Calendar, FileText, UserIcon } from "lucide-react";
|
||||||
import {
|
|
||||||
BADGES,
|
|
||||||
Event,
|
|
||||||
EVENT_TYPE,
|
|
||||||
Participant,
|
|
||||||
PERMISSION,
|
|
||||||
prisma,
|
|
||||||
Prisma,
|
|
||||||
} from "@repo/db";
|
|
||||||
import { Bot, Calendar, FileText, User, UserIcon } from "lucide-react";
|
|
||||||
import { Input } from "../../../../_components/ui/Input";
|
|
||||||
import { useRef, useState } from "react";
|
|
||||||
import {
|
|
||||||
deleteAppoinement,
|
|
||||||
deleteEvent,
|
|
||||||
upsertAppointment,
|
|
||||||
upsertEvent,
|
|
||||||
} from "../action";
|
|
||||||
import { Button } from "../../../../_components/ui/Button";
|
|
||||||
import { redirect, useRouter } from "next/navigation";
|
|
||||||
import { Switch } from "../../../../_components/ui/Switch";
|
|
||||||
import DatePicker, { registerLocale } from "react-datepicker";
|
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
|
||||||
import {
|
|
||||||
PaginatedTable,
|
|
||||||
PaginatedTableRef,
|
|
||||||
} from "../../../../_components/PaginatedTable";
|
|
||||||
import { Select } from "../../../../_components/ui/Select";
|
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { PaginatedTable, PaginatedTableRef } from "../../../../_components/PaginatedTable";
|
||||||
|
import { Button } from "../../../../_components/ui/Button";
|
||||||
|
import { Input } from "../../../../_components/ui/Input";
|
||||||
import { MarkdownEditor } from "../../../../_components/ui/MDEditor";
|
import { MarkdownEditor } from "../../../../_components/ui/MDEditor";
|
||||||
|
import { Select } from "../../../../_components/ui/Select";
|
||||||
|
import { Switch } from "../../../../_components/ui/Switch";
|
||||||
|
import { deleteEvent, upsertEvent } from "../action";
|
||||||
import { AppointmentModal } from "./AppointmentModal";
|
import { AppointmentModal } from "./AppointmentModal";
|
||||||
import { ParticipantModal } from "./ParticipantModal";
|
import { ParticipantModal } from "./ParticipantModal";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
export const Form = ({ event }: { event?: Event }) => {
|
export const Form = ({ event }: { event?: Event }) => {
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const form = useForm({
|
const form = useForm<EventOptionalDefaults>({
|
||||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||||
defaultValues: event,
|
defaultValues: event
|
||||||
|
? {
|
||||||
|
...event,
|
||||||
|
discordRoleId: event.discordRoleId ?? undefined,
|
||||||
|
maxParticipants: event.maxParticipants ?? undefined,
|
||||||
|
finisherMoodleCourseId: event.finisherMoodleCourseId ?? undefined,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
});
|
});
|
||||||
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
||||||
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||||
@@ -55,7 +46,7 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const participantForm = useForm<Participant>({
|
const participantForm = useForm<Participant>({
|
||||||
resolver: zodResolver(ParticipantOptionalDefaultsSchema),
|
resolver: zodResolver(ParticipantSchema),
|
||||||
});
|
});
|
||||||
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -73,10 +64,7 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
appointmentsTableRef={appointmentsTableRef}
|
appointmentsTableRef={appointmentsTableRef}
|
||||||
event={event}
|
event={event}
|
||||||
/>
|
/>
|
||||||
<ParticipantModal
|
<ParticipantModal participantForm={participantForm} ref={participantModal} />
|
||||||
participantForm={participantForm}
|
|
||||||
ref={participantModal}
|
|
||||||
/>
|
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(async (values) => {
|
onSubmit={form.handleSubmit(async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -155,11 +143,7 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
value: key,
|
value: key,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
<Switch
|
<Switch form={form} name="hasPresenceEvents" label="Hat Live Event" />
|
||||||
form={form}
|
|
||||||
name="hasPresenceEvents"
|
|
||||||
label="Hat Live Event"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{form.watch("hasPresenceEvents") ? (
|
{form.watch("hasPresenceEvents") ? (
|
||||||
@@ -196,58 +180,61 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
Presenter: true,
|
Presenter: true,
|
||||||
Participants: true,
|
Participants: true,
|
||||||
}}
|
}}
|
||||||
columns={[
|
columns={
|
||||||
{
|
[
|
||||||
header: "Datum",
|
{
|
||||||
accessorKey: "appointmentDate",
|
header: "Datum",
|
||||||
accessorFn: (date) =>
|
accessorKey: "appointmentDate",
|
||||||
new Date(date.appointmentDate).toLocaleString(),
|
accessorFn: (date) => new Date(date.appointmentDate).toLocaleString(),
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Presenter",
|
|
||||||
accessorKey: "presenter",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="ml-2">
|
|
||||||
{(row.original as any).Presenter.firstname}{" "}
|
|
||||||
{(row.original as any).Presenter.lastname}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Teilnehmer",
|
|
||||||
accessorKey: "Participants",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<UserIcon className="w-5 h-5" />
|
|
||||||
<span className="ml-2">
|
|
||||||
{row.original.Participants.length}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Aktionen",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
|
||||||
onSubmit={() => false}
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
appointmentForm.reset(row.original);
|
|
||||||
appointmentModal.current?.showModal();
|
|
||||||
}}
|
|
||||||
className="btn btn-sm btn-outline"
|
|
||||||
>
|
|
||||||
Bearbeiten
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
]}
|
header: "Presenter",
|
||||||
|
accessorKey: "presenter",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="ml-2">
|
||||||
|
{row.original.Presenter.firstname} {row.original.Presenter.lastname}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Teilnehmer",
|
||||||
|
accessorKey: "Participants",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<UserIcon className="w-5 h-5" />
|
||||||
|
<span className="ml-2">{row.original.Participants.length}</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Aktionen",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onSubmit={() => false}
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
appointmentForm.reset(row.original);
|
||||||
|
appointmentModal.current?.showModal();
|
||||||
|
}}
|
||||||
|
className="btn btn-sm btn-outline"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as ColumnDef<
|
||||||
|
EventAppointmentOptionalDefaults & {
|
||||||
|
Presenter: User;
|
||||||
|
Participants: Participant[];
|
||||||
|
}
|
||||||
|
>[]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -311,11 +298,7 @@ export const Form = ({ event }: { event?: Event }) => {
|
|||||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||||
<div className="card-body ">
|
<div className="card-body ">
|
||||||
<div className="flex w-full gap-4">
|
<div className="flex w-full gap-4">
|
||||||
<Button
|
<Button isLoading={loading} type="submit" className="btn btn-primary flex-1">
|
||||||
isLoading={loading}
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-primary flex-1"
|
|
||||||
>
|
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
{event && (
|
{event && (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { KeywordForm } from "../_components/Form";
|
import { KeywordForm } from "../_components/Form";
|
||||||
|
|
||||||
export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const keyword = await prisma.keyword.findUnique({
|
const keyword = await prisma.keyword.findUnique({
|
||||||
where: {
|
where: {
|
||||||
@@ -10,4 +10,4 @@ export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
});
|
});
|
||||||
if (!keyword) return <div>Station not found</div>;
|
if (!keyword) return <div>Station not found</div>;
|
||||||
return <KeywordForm keyword={keyword} />;
|
return <KeywordForm keyword={keyword} />;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
import { Error } from "_components/Error";
|
import { Error } from "_components/Error";
|
||||||
import {
|
import { ReportAdmin, ReportSenderInfo } from "(app)/admin/report/_components/form";
|
||||||
ReportAdmin,
|
|
||||||
ReportSenderInfo,
|
|
||||||
} from "(app)/admin/report/_components/form";
|
|
||||||
|
|
||||||
export default async function ReportDetailsPage({
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: { id: string };
|
|
||||||
}) {
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
|
|
||||||
const report = await prisma.report.findUnique({
|
const report = await prisma.report.findUnique({
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { prisma } from '@repo/db';
|
import { prisma } from "@repo/db";
|
||||||
import { StationForm } from '../_components/Form';
|
import { StationForm } from "../_components/Form";
|
||||||
|
|
||||||
export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const station = await prisma.station.findUnique({
|
const station = await prisma.station.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: parseInt(id),
|
id: parseInt(id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!station) return <div>Station not found</div>;
|
if (!station) return <div>Station not found</div>;
|
||||||
return <StationForm station={station} />;
|
return <StationForm station={station} />;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { PersonIcon } from "@radix-ui/react-icons";
|
import { PersonIcon } from "@radix-ui/react-icons";
|
||||||
import { prisma, User } from "@repo/db";
|
import { prisma, User } from "@repo/db";
|
||||||
import {
|
import { AdminForm, ConnectionHistory, ProfileForm, UserReports } from "./_components/forms";
|
||||||
AdminForm,
|
|
||||||
ConnectionHistory,
|
|
||||||
ProfileForm,
|
|
||||||
UserReports,
|
|
||||||
} from "./_components/forms";
|
|
||||||
import { Error } from "../../../../_components/Error";
|
import { Error } from "../../../../_components/Error";
|
||||||
|
|
||||||
const Page = async ({ params }: { params: { id: string } }) => {
|
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
|
|
||||||
const user: User | null = await prisma.user.findUnique({
|
const user: User | null = await prisma.user.findUnique({
|
||||||
@@ -107,12 +102,7 @@ const Page = async ({ params }: { params: { id: string } }) => {
|
|||||||
<ProfileForm user={user} />
|
<ProfileForm user={user} />
|
||||||
</div>
|
</div>
|
||||||
<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">
|
||||||
<AdminForm
|
<AdminForm user={user} dispoTime={dispoTime} pilotTime={pilotTime} reports={reports} />
|
||||||
user={user}
|
|
||||||
dispoTime={dispoTime}
|
|
||||||
pilotTime={pilotTime}
|
|
||||||
reports={reports}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-6">
|
||||||
<UserReports user={user} />
|
<UserReports user={user} />
|
||||||
@@ -122,6 +112,4 @@ const Page = async ({ params }: { params: { id: string } }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Page;
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Aktive Events / Mandatory Events
|
|||||||
export default async function Home({
|
export default async function Home({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
searchParams: { stats?: "pilot" | "dispo" };
|
searchParams: Promise<{ stats?: "pilot" | "dispo" }>;
|
||||||
}) {
|
}) {
|
||||||
const { stats } = await searchParams;
|
const { stats } = await searchParams;
|
||||||
const view = stats || "pilot";
|
const view = stats || "pilot";
|
||||||
@@ -33,10 +33,7 @@ export default async function Home({
|
|||||||
<span className="card-title">
|
<span className="card-title">
|
||||||
<NotebookText className="w-4 h-4" /> Logbook
|
<NotebookText className="w-4 h-4" /> Logbook
|
||||||
</span>
|
</span>
|
||||||
<Link
|
<Link className="badge badge-sm badge-info badge-outline" href="/logbook">
|
||||||
className="badge badge-sm badge-info badge-outline"
|
|
||||||
href="/logbook"
|
|
||||||
>
|
|
||||||
Zum vollständigen Logbook <ArrowRight className="w-4 h-4" />
|
Zum vollständigen Logbook <ArrowRight className="w-4 h-4" />
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getServerSession } from "../../api/auth/[...nextauth]/auth";
|
|||||||
import { ProfileForm, SocialForm, PasswordForm, PilotForm } from "./_components/forms";
|
import { ProfileForm, SocialForm, PasswordForm, PilotForm } from "./_components/forms";
|
||||||
import { GearIcon } from "@radix-ui/react-icons";
|
import { GearIcon } from "@radix-ui/react-icons";
|
||||||
|
|
||||||
export const page = async () => {
|
export default async function Page() {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
if (!session) return null;
|
if (!session) return null;
|
||||||
const user = await prisma.user.findFirst({
|
const user = await prisma.user.findFirst({
|
||||||
@@ -37,6 +37,4 @@ export const page = async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default page;
|
|
||||||
|
|||||||
@@ -1,48 +1,20 @@
|
|||||||
|
import { services } from "../../../helper/authServices";
|
||||||
import { Authorize } from "./_components/Authorize";
|
import { Authorize } from "./_components/Authorize";
|
||||||
|
|
||||||
export const services = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
service: "dispatch",
|
|
||||||
name: "Leitstellendisposition",
|
|
||||||
approvedUrls: ["http://localhost:3001"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
secret: "jp2k430fnv",
|
|
||||||
service: "desktop",
|
|
||||||
name: "Desktop client",
|
|
||||||
approvedUrls: ["var://oAuth"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
secret: "d0f3e4e4",
|
|
||||||
service: "moodle",
|
|
||||||
name: "Moodle",
|
|
||||||
approvedUrls: [
|
|
||||||
"http://localhost:8081",
|
|
||||||
"https://moodle.virtualairrescue.com",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
export type Service = (typeof services)[number];
|
export type Service = (typeof services)[number];
|
||||||
|
|
||||||
const Page = async ({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ client_id: string; service: string }>;
|
||||||
}) => {
|
}) {
|
||||||
const { service: serviceId, client_id: clientId } = await searchParams;
|
const { service: serviceId, client_id: clientId } = await searchParams;
|
||||||
|
|
||||||
const service = services.find(
|
const service = services.find((service) => service.id === serviceId || service.id === clientId);
|
||||||
(service) => service.id === serviceId || service.id === clientId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
return <div>Service not found</div>;
|
return <div>Service not found</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Authorize service={service} />;
|
return <Authorize service={service} />;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Page;
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
import { sign } from "jsonwebtoken";
|
import { sign } from "jsonwebtoken";
|
||||||
import { services } from "(auth)/oauth/page";
|
|
||||||
import { prisma } from "@repo/db";
|
import { prisma } from "@repo/db";
|
||||||
|
import { services } from "../../../../helper/authServices";
|
||||||
|
|
||||||
export const POST = async (req: NextRequest) => {
|
export const POST = async (req: NextRequest) => {
|
||||||
try {
|
try {
|
||||||
if (
|
if (!req.headers.get("content-type")?.includes("application/x-www-form-urlencoded")) {
|
||||||
!req.headers
|
|
||||||
.get("content-type")
|
|
||||||
?.includes("application/x-www-form-urlencoded")
|
|
||||||
) {
|
|
||||||
return new Response("Unsupported Content-Type", { status: 415 });
|
return new Response("Unsupported Content-Type", { status: 415 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
apps/hub/helper/authServices.ts
Normal file
22
apps/hub/helper/authServices.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export const services = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
service: "dispatch",
|
||||||
|
name: "Leitstellendisposition",
|
||||||
|
approvedUrls: ["http://localhost:3001"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
secret: "jp2k430fnv",
|
||||||
|
service: "desktop",
|
||||||
|
name: "Desktop client",
|
||||||
|
approvedUrls: ["var://oAuth"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
secret: "d0f3e4e4",
|
||||||
|
service: "moodle",
|
||||||
|
name: "Moodle",
|
||||||
|
approvedUrls: ["http://localhost:8081", "https://moodle.virtualairrescue.com"],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"globalDependencies": ["**/.env.*local"],
|
"globalDependencies": ["**/.env.*local"],
|
||||||
"globalEnv": ["EMAIL_SERVER", "EMAIL_FROM", "SECRET"],
|
"globalEnv": ["EMAIL_SERVER", "EMAIL_FROM", "SECRET", "DATABASE_URL", "NEXTAUTH_SECRET"],
|
||||||
"ui": "tui",
|
"ui": "tui",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"generate": {
|
"generate": {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build", "generate", "migrate"],
|
"dependsOn": ["^build", "generate", "migrate"],
|
||||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||||
"outputs": [".next/**", "!.next/cache/**"]
|
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"dependsOn": ["^lint"]
|
"dependsOn": ["^lint"]
|
||||||
|
|||||||
Reference in New Issue
Block a user