formated project with prettier
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": true,
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
||||||
40
.vscode/settings.json
vendored
@@ -1,21 +1,23 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"files.autoSave": "off",
|
"files.autoSave": "off",
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.codeActionsOnSave": ["source.formatDocument", "source.fixAll.eslint"],
|
"editor.codeActionsOnSave": ["source.formatDocument", "source.fixAll.eslint"],
|
||||||
"eslint.workingDirectories": ["./client", "./server"],
|
"eslint.lintTask.enable": true,
|
||||||
"eslint.lintTask.enable": true,
|
"restoreTerminals.terminals": [
|
||||||
"restoreTerminals.terminals": [
|
{
|
||||||
{
|
"splitTerminals": [
|
||||||
"splitTerminals": [
|
{
|
||||||
{
|
"name": "Monorepo",
|
||||||
"name": "Monorepo",
|
"commands": ["npm run dev"]
|
||||||
"commands": ["npm run dev"]
|
}
|
||||||
}
|
]
|
||||||
]
|
}
|
||||||
}
|
],
|
||||||
],
|
"[prisma]": {
|
||||||
"[prisma]": {
|
"editor.defaultFormatter": "Prisma.prisma"
|
||||||
"editor.defaultFormatter": "Prisma.prisma"
|
},
|
||||||
}
|
"[xml]": {
|
||||||
|
"editor.defaultFormatter": "redhat.vscode-xml"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { NextPage } from 'next';
|
import { NextPage } from "next";
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
const AuthLayout: NextPage<
|
const AuthLayout: NextPage<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>
|
}>
|
||||||
> = ({ children }) => (
|
> = ({ children }) => (
|
||||||
<div
|
<div
|
||||||
className="hero min-h-screen"
|
className="hero min-h-screen"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'url(https://img.daisyui.com/images/stock/photo-1507358522600-9f71e620c44e.webp)',
|
"url(https://img.daisyui.com/images/stock/photo-1507358522600-9f71e620c44e.webp)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="hero-overlay bg-opacity-60"></div>
|
<div className="hero-overlay bg-opacity-60"></div>
|
||||||
<div className="hero-content text-neutral-content text-center ">
|
<div className="hero-content text-neutral-content text-center ">
|
||||||
<div className="max-w-lg">
|
<div className="max-w-lg">
|
||||||
<div className="card bg-base-100 w-full min-w-[500px] shadow-2xl max-md:min-w-[400px]">
|
<div className="card bg-base-100 w-full min-w-[500px] shadow-2xl max-md:min-w-[400px]">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default AuthLayout;
|
export default AuthLayout;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Login } from './_components/Login';
|
import { Login } from "./_components/Login";
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Login />
|
<Login />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { signOut } from 'next-auth/react';
|
import { signOut } from "next-auth/react";
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
signOut({
|
signOut({
|
||||||
callbackUrl: '/login',
|
callbackUrl: "/login",
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h1 className="text-5xl">logging out...</h1>
|
<h1 className="text-5xl">logging out...</h1>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { Authorize } from './_components/Authorize';
|
import { Authorize } from "./_components/Authorize";
|
||||||
|
|
||||||
export const services = [
|
export const services = [
|
||||||
{
|
{
|
||||||
id: '123456',
|
id: "123456",
|
||||||
service: 'dispatch',
|
service: "dispatch",
|
||||||
name: 'Leitstellendisposition',
|
name: "Leitstellendisposition",
|
||||||
approvedUrls: ['http://localhost:3001'],
|
approvedUrls: ["http://localhost:3001"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
export type Service = (typeof services)[number];
|
export type Service = (typeof services)[number];
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
}) => {
|
}) => {
|
||||||
const { service: serviceId } = await searchParams;
|
const { service: serviceId } = await searchParams;
|
||||||
const service = services.find((service) => service.id === serviceId);
|
const service = services.find((service) => service.id === serviceId);
|
||||||
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
return <div>Service not found</div>;
|
return <div>Service not found</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Authorize service={service} />;
|
return <Authorize service={service} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,35 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
export const ChangeRufgruppe = () => {
|
export const ChangeRufgruppe = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<details className="dropdown">
|
<details className="dropdown">
|
||||||
<summary className="dropdown flex items-center gap-1">
|
<summary className="dropdown flex items-center gap-1">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<div className="badge badge-soft badge-success">1</div>
|
<div className="badge badge-soft badge-success">1</div>
|
||||||
</summary>
|
</summary>
|
||||||
<ul className="menu dropdown-content bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
<ul className="menu dropdown-content bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
||||||
<li><a>Rufgruppe 1</a></li>
|
<li>
|
||||||
<li><a>Rufgruppe 2</a></li>
|
<a>Rufgruppe 1</a>
|
||||||
</ul>
|
</li>
|
||||||
</details>
|
<li>
|
||||||
</>
|
<a>Rufgruppe 2</a>
|
||||||
);
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import L from 'leaflet';
|
import L from "leaflet";
|
||||||
import 'leaflet/dist/leaflet.css';
|
import "leaflet/dist/leaflet.css";
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const mapRef = useRef<HTMLDivElement>(null);
|
const mapRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mapRef.current) return;
|
if (!mapRef.current) return;
|
||||||
|
|
||||||
// Initialisiere die Leaflet-Karte
|
// Initialisiere die Leaflet-Karte
|
||||||
const map = L.map(mapRef.current).setView([51.1657, 10.4515], 6); // Deutschland
|
const map = L.map(mapRef.current).setView([51.1657, 10.4515], 6); // Deutschland
|
||||||
|
|
||||||
// OpenStreetMap Tile Layer hinzufügen
|
// OpenStreetMap Tile Layer hinzufügen
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
attribution:
|
attribution:
|
||||||
'© <a href="https://www.openstreetmap.org/copyright">OSM</a> contributors',
|
'© <a href="https://www.openstreetmap.org/copyright">OSM</a> contributors',
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
map.remove(); // Karte beim Unmounten bereinigen
|
map.remove(); // Karte beim Unmounten bereinigen
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div ref={mapRef} className="w-full h-full rounded-lg shadow-lg" />;
|
return <div ref={mapRef} className="w-full h-full rounded-lg shadow-lg" />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { ToggleTalkButton } from '../_components/ToggleTalkButton';
|
import { ToggleTalkButton } from "../_components/ToggleTalkButton";
|
||||||
import { ChangeRufgruppe } from '../_components/ChangeRufgruppe';
|
import { ChangeRufgruppe } from "../_components/ChangeRufgruppe";
|
||||||
import { Notifications } from '../_components/Notifications';
|
import { Notifications } from "../_components/Notifications";
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
<div className="navbar bg-base-100 shadow-sm">
|
<div className="navbar bg-base-100 shadow-sm">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<a className="btn btn-ghost text-xl">VAR Leitstelle V2</a>
|
<a className="btn btn-ghost text-xl">VAR Leitstelle V2</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<ul className="menu menu-horizontal bg-base-200 rounded-box">
|
<ul className="menu menu-horizontal bg-base-200 rounded-box">
|
||||||
<li>
|
<li>
|
||||||
<ToggleTalkButton />
|
<ToggleTalkButton />
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ChangeRufgruppe />
|
<ChangeRufgruppe />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Notifications />
|
<Notifications />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<label className="swap swap-rotate">
|
<label className="swap swap-rotate">
|
||||||
{/* this hidden checkbox controls the state */}
|
{/* this hidden checkbox controls the state */}
|
||||||
<input type="checkbox" className="theme-controller" value="dark" />
|
<input type="checkbox" className="theme-controller" value="dark" />
|
||||||
|
|
||||||
{/* sun icon */}
|
{/* sun icon */}
|
||||||
<svg
|
<svg
|
||||||
className="swap-off h-10 w-10 fill-current"
|
className="swap-off h-10 w-10 fill-current"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{/* moon icon */}
|
{/* moon icon */}
|
||||||
<svg
|
<svg
|
||||||
className="swap-on h-10 w-10 fill-current"
|
className="swap-on h-10 w-10 fill-current"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<div className="dropdown dropdown-end">
|
<div className="dropdown dropdown-end">
|
||||||
<div tabIndex={0} role="button" className="btn btn-ghost">
|
<div tabIndex={0} role="button" className="btn btn-ghost">
|
||||||
...
|
...
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
className="menu menu-sm dropdown-content bg-base-100 box z-1 mt-3 w-52 p-2 shadow"
|
className="menu menu-sm dropdown-content bg-base-100 box z-1 mt-3 w-52 p-2 shadow"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<a className="justify-between">Profil</a>
|
<a className="justify-between">Profil</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a>Einstellungen</a>
|
<a>Einstellungen</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href={'/logout'}>
|
<Link href={"/logout"}>
|
||||||
<p>Logout</p>
|
<p>Logout</p>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,42 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
export const Notifications = () => {
|
export const Notifications = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<details className="dropdown">
|
<details className="dropdown">
|
||||||
<summary className="btn btn-ghost dropdown flex items-center gap-1">
|
<summary className="btn btn-ghost dropdown flex items-center gap-1">
|
||||||
<div className="badge badge-sm badge-secondary">+99</div>
|
<div className="badge badge-sm badge-secondary">+99</div>
|
||||||
</summary>
|
</summary>
|
||||||
<ul className="menu dropdown-content bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
<ul className="menu dropdown-content bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
||||||
<li><a>Testbenachrichtigung 1</a></li>
|
<li>
|
||||||
<li className="divider"></li>
|
<a>Testbenachrichtigung 1</a>
|
||||||
<li><a>Testbenachrichtigung 2</a></li>
|
</li>
|
||||||
<li className="divider"></li>
|
<li className="divider"></li>
|
||||||
<li><a>Testbenachrichtigung 3</a></li>
|
<li>
|
||||||
<li className="divider"></li>
|
<a>Testbenachrichtigung 2</a>
|
||||||
<li><a>Testbenachrichtigung 4</a></li>
|
</li>
|
||||||
<li className="divider"></li>
|
<li className="divider"></li>
|
||||||
<li><a>Testbenachrichtigung 5</a></li>
|
<li>
|
||||||
<li className="divider"></li>
|
<a>Testbenachrichtigung 3</a>
|
||||||
<li><a>Testbenachrichtigung 6</a></li>
|
</li>
|
||||||
<li className="divider"></li>
|
<li className="divider"></li>
|
||||||
<li><a>Testbenachrichtigung 7</a></li>
|
<li>
|
||||||
</ul>
|
<a>Testbenachrichtigung 4</a>
|
||||||
</details>
|
</li>
|
||||||
</>
|
<li className="divider"></li>
|
||||||
);
|
<li>
|
||||||
};
|
<a>Testbenachrichtigung 5</a>
|
||||||
|
</li>
|
||||||
|
<li className="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a>Testbenachrichtigung 6</a>
|
||||||
|
</li>
|
||||||
|
<li className="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a>Testbenachrichtigung 7</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { useTalkStore } from '../../_store/useTalkStore';
|
import { useTalkStore } from "../../_store/useTalkStore";
|
||||||
|
|
||||||
export const ToggleTalkButton = () => {
|
export const ToggleTalkButton = () => {
|
||||||
const { isTalking, toggleTalking } = useTalkStore();
|
const { isTalking, toggleTalking } = useTalkStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={toggleTalking}
|
onClick={toggleTalking}
|
||||||
className={`${
|
className={`${
|
||||||
isTalking
|
isTalking
|
||||||
? 'bg-red-500 hover:bg-red-600'
|
? "bg-red-500 hover:bg-red-600"
|
||||||
: 'bg-transparent hover:bg-neutral-300'
|
: "bg-transparent hover:bg-neutral-300"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
d="M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z"
|
d="M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from "next";
|
||||||
import Navbar from './_components/Navbar';
|
import Navbar from "./_components/Navbar";
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from "next-auth/react";
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from "next/navigation";
|
||||||
import { getServerSession } from '../api/auth/[...nextauth]/auth';
|
import { getServerSession } from "../api/auth/[...nextauth]/auth";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'VAR Leitstelle v2',
|
title: "VAR Leitstelle v2",
|
||||||
description: 'Die neue VAR Leitstelle.',
|
description: "Die neue VAR Leitstelle.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect('/login');
|
redirect("/login");
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from "next/dynamic";
|
||||||
const Map = dynamic(() => import('./_components/Map'), { ssr: false });
|
const Map = dynamic(() => import("./_components/Map"), { ssr: false });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<Map />
|
<Map />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { SessionProvider } from 'next-auth/react';
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { Session } from 'next-auth';
|
import { Session } from "next-auth";
|
||||||
|
|
||||||
export const NextAuthSessionProvider = ({
|
export const NextAuthSessionProvider = ({
|
||||||
children,
|
children,
|
||||||
session,
|
session,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
session: Session | null;
|
session: Session | null;
|
||||||
}) => <SessionProvider session={session}>{children}</SessionProvider>;
|
}) => <SessionProvider session={session}>{children}</SessionProvider>;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
type TalkState = {
|
type TalkState = {
|
||||||
isTalking: boolean;
|
isTalking: boolean;
|
||||||
toggleTalking: () => void;
|
toggleTalking: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTalkStore = create<TalkState>((set) => ({
|
export const useTalkStore = create<TalkState>((set) => ({
|
||||||
isTalking: false,
|
isTalking: false,
|
||||||
toggleTalking: () => set((state) => ({ isTalking: !state.isTalking })),
|
toggleTalking: () => set((state) => ({ isTalking: !state.isTalking })),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from "next";
|
||||||
import localFont from 'next/font/local';
|
import localFont from "next/font/local";
|
||||||
import './globals.css';
|
import "./globals.css";
|
||||||
import { NextAuthSessionProvider } from './_components/AuthSessionProvider';
|
import { NextAuthSessionProvider } from "./_components/AuthSessionProvider";
|
||||||
import { getServerSession } from './api/auth/[...nextauth]/auth';
|
import { getServerSession } from "./api/auth/[...nextauth]/auth";
|
||||||
|
|
||||||
const geistSans = localFont({
|
const geistSans = localFont({
|
||||||
src: './fonts/GeistVF.woff',
|
src: "./fonts/GeistVF.woff",
|
||||||
variable: '--font-geist-sans',
|
variable: "--font-geist-sans",
|
||||||
});
|
});
|
||||||
const geistMono = localFont({
|
const geistMono = localFont({
|
||||||
src: './fonts/GeistMonoVF.woff',
|
src: "./fonts/GeistMonoVF.woff",
|
||||||
variable: '--font-geist-mono',
|
variable: "--font-geist-mono",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'VAR Leitstelle v2',
|
title: "VAR Leitstelle v2",
|
||||||
description: 'Die neue VAR Leitstelle.',
|
description: "Die neue VAR Leitstelle.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
return (
|
return (
|
||||||
<html lang="de" data-theme="dark">
|
<html lang="de" data-theme="dark">
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} h-screen flex flex-col`}
|
className={`${geistSans.variable} ${geistMono.variable} h-screen flex flex-col`}
|
||||||
>
|
>
|
||||||
<NextAuthSessionProvider session={session}>
|
<NextAuthSessionProvider session={session}>
|
||||||
{children}
|
{children}
|
||||||
</NextAuthSessionProvider>
|
</NextAuthSessionProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,188 +1,194 @@
|
|||||||
.page {
|
.page {
|
||||||
--gray-rgb: 0, 0, 0;
|
--gray-rgb: 0, 0, 0;
|
||||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||||
|
|
||||||
--button-primary-hover: #383838;
|
--button-primary-hover: #383838;
|
||||||
--button-secondary-hover: #f2f2f2;
|
--button-secondary-hover: #f2f2f2;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 20px 1fr 20px;
|
grid-template-rows: 20px 1fr 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
min-height: 100svh;
|
min-height: 100svh;
|
||||||
padding: 80px;
|
padding: 80px;
|
||||||
gap: 64px;
|
gap: 64px;
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.page {
|
.page {
|
||||||
--gray-rgb: 255, 255, 255;
|
--gray-rgb: 255, 255, 255;
|
||||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||||
|
|
||||||
--button-primary-hover: #ccc;
|
--button-primary-hover: #ccc;
|
||||||
--button-secondary-hover: #1a1a1a;
|
--button-secondary-hover: #1a1a1a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
grid-row-start: 2;
|
grid-row-start: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main ol {
|
.main ol {
|
||||||
font-family: var(--font-geist-mono);
|
font-family: var(--font-geist-mono);
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main li:not(:last-of-type) {
|
.main li:not(:last-of-type) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main code {
|
.main code {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
background: var(--gray-alpha-100);
|
background: var(--gray-alpha-100);
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas {
|
.ctas {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas a {
|
.ctas a {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: 128px;
|
border-radius: 128px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border: none;
|
border: none;
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
transition:
|
||||||
cursor: pointer;
|
background 0.2s,
|
||||||
display: flex;
|
color 0.2s,
|
||||||
align-items: center;
|
border-color 0.2s;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
display: flex;
|
||||||
line-height: 20px;
|
align-items: center;
|
||||||
font-weight: 500;
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.primary {
|
a.primary {
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary {
|
a.secondary {
|
||||||
border-color: var(--gray-alpha-200);
|
border-color: var(--gray-alpha-200);
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.secondary {
|
button.secondary {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: 128px;
|
border-radius: 128px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border: none;
|
border: none;
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
transition:
|
||||||
cursor: pointer;
|
background 0.2s,
|
||||||
display: flex;
|
color 0.2s,
|
||||||
align-items: center;
|
border-color 0.2s;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
display: flex;
|
||||||
line-height: 20px;
|
align-items: center;
|
||||||
font-weight: 500;
|
justify-content: center;
|
||||||
background: transparent;
|
font-size: 16px;
|
||||||
border-color: var(--gray-alpha-200);
|
line-height: 20px;
|
||||||
min-width: 180px;
|
font-weight: 500;
|
||||||
|
background: transparent;
|
||||||
|
border-color: var(--gray-alpha-200);
|
||||||
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
grid-row-start: 3;
|
grid-row-start: 3;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer a {
|
.footer a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer img {
|
.footer img {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable hover only on non-touch devices */
|
/* Enable hover only on non-touch devices */
|
||||||
@media (hover: hover) and (pointer: fine) {
|
@media (hover: hover) and (pointer: fine) {
|
||||||
a.primary:hover {
|
a.primary:hover {
|
||||||
background: var(--button-primary-hover);
|
background: var(--button-primary-hover);
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary:hover {
|
a.secondary:hover {
|
||||||
background: var(--button-secondary-hover);
|
background: var(--button-secondary-hover);
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer a:hover {
|
.footer a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-underline-offset: 4px;
|
text-underline-offset: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.page {
|
.page {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main ol {
|
.main ol {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas {
|
.ctas {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas a {
|
.ctas a {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary {
|
a.secondary {
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.logo {
|
.logo {
|
||||||
filter: invert();
|
filter: invert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "dispatch",
|
"name": "dispatch",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack -p 3001",
|
"dev": "next dev --turbopack -p 3001",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint --max-warnings 0",
|
"lint": "next lint --max-warnings 0",
|
||||||
"check-types": "tsc --noEmit"
|
"check-types": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-icons": "^1.3.2",
|
"@radix-ui/react-icons": "^1.3.2",
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
"@tailwindcss/postcss": "^4.0.2",
|
"@tailwindcss/postcss": "^4.0.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"next": "^15.1.0",
|
"next": "^15.1.0",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"tailwindcss": "^4.0.2",
|
"tailwindcss": "^4.0.2",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@repo/eslint-config": "*",
|
"@repo/eslint-config": "*",
|
||||||
"@repo/typescript-config": "*",
|
"@repo/typescript-config": "*",
|
||||||
"@types/leaflet": "^1.9.16",
|
"@types/leaflet": "^1.9.16",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "18.3.1",
|
"@types/react": "18.3.1",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"daisyui": "^5.0.0-beta.6",
|
"daisyui": "^5.0.0-beta.6",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/** @type {import('postcss-load-config').Config} */
|
/** @type {import('postcss-load-config').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
"@tailwindcss/postcss": {},
|
"@tailwindcss/postcss": {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80">
|
||||||
|
<path fill="#000"
|
||||||
|
d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z" />
|
||||||
|
<path fill="#000"
|
||||||
|
d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,19 +1,41 @@
|
|||||||
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M130.998 30.6565V22.3773H91.0977V30.6565H106.16V58.1875H115.935V30.6565H130.998Z" fill="black"/>
|
<path d="M130.998 30.6565V22.3773H91.0977V30.6565H106.16V58.1875H115.935V30.6565H130.998Z"
|
||||||
<path d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2275V22.3773H162.768V41.2799C162.768 47.0155 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0155 144.315 41.2799V22.3773H134.539V42.2275C134.539 52.5018 141.272 58.7362 153.542 58.7362Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M187.508 46.3173H197.234L204.914 58.1875H216.136L207.458 45.2699C212.346 43.5243 215.338 39.634 215.338 34.3473C215.338 26.6665 209.603 22.3773 200.874 22.3773H177.732V58.1875H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.053 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z" fill="black"/>
|
<path
|
||||||
<path d="M219.887 58.1875H245.472C253.452 58.1875 258.041 54.397 258.041 48.0629C258.041 43.8235 255.348 40.9308 252.156 39.634C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1875ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0205 248.615 46.9657C248.615 48.9108 247.168 50.2075 244.525 50.2075H229.263V43.7238Z" fill="black"/>
|
d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2275V22.3773H162.768V41.2799C162.768 47.0155 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0155 144.315 41.2799V22.3773H134.539V42.2275C134.539 52.5018 141.272 58.7362 153.542 58.7362Z"
|
||||||
<path d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.786 281.942 58.786C294.461 58.786 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2575C288.525 30.2575 293.463 34.1478 293.463 40.2824C293.463 46.417 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.417 270.421 40.2824C270.421 34.1478 275.359 30.2575 281.942 30.2575Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M317.526 46.3173H327.251L334.932 58.1875H346.154L337.476 45.2699C342.364 43.5243 345.356 39.634 345.356 34.3473C345.356 26.6665 339.62 22.3773 330.892 22.3773H307.75V58.1875H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.053 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z" fill="black"/>
|
<path
|
||||||
<path d="M349.904 22.3773V58.1875H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6565H384.717V22.3773H349.904Z" fill="black"/>
|
d="M187.508 46.3173H197.234L204.914 58.1875H216.136L207.458 45.2699C212.346 43.5243 215.338 39.634 215.338 34.3473C215.338 26.6665 209.603 22.3773 200.874 22.3773H177.732V58.1875H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.053 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z"
|
||||||
<path d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1875H399.204V46.7662ZM399.204 38.6365V30.5568H411.673C415.164 30.5568 417.059 32.053 417.059 34.5967C417.059 37.0904 415.164 38.6365 411.673 38.6365H399.204Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.786 450.948 58.786C463.467 58.786 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2575C457.532 30.2575 462.469 34.1478 462.469 40.2824C462.469 46.417 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.417 439.427 40.2824C439.427 34.1478 444.365 30.2575 450.948 30.2575Z" fill="black"/>
|
<path
|
||||||
<path d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z" fill="black"/>
|
d="M219.887 58.1875H245.472C253.452 58.1875 258.041 54.397 258.041 48.0629C258.041 43.8235 255.348 40.9308 252.156 39.634C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1875ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0205 248.615 46.9657C248.615 48.9108 247.168 50.2075 244.525 50.2075H229.263V43.7238Z"
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z" fill="url(#paint0_linear_2028_278)"/>
|
fill="black" />
|
||||||
<defs>
|
<path
|
||||||
<linearGradient id="paint0_linear_2028_278" x1="41.443" y1="11.5372" x2="10.5567" y2="42.4236" gradientUnits="userSpaceOnUse">
|
d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.786 281.942 58.786C294.461 58.786 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2575C288.525 30.2575 293.463 34.1478 293.463 40.2824C293.463 46.417 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.417 270.421 40.2824C270.421 34.1478 275.359 30.2575 281.942 30.2575Z"
|
||||||
<stop stop-color="#0096FF"/>
|
fill="black" />
|
||||||
<stop offset="1" stop-color="#FF1E56"/>
|
<path
|
||||||
</linearGradient>
|
d="M317.526 46.3173H327.251L334.932 58.1875H346.154L337.476 45.2699C342.364 43.5243 345.356 39.634 345.356 34.3473C345.356 26.6665 339.62 22.3773 330.892 22.3773H307.75V58.1875H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.053 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z"
|
||||||
</defs>
|
fill="black" />
|
||||||
</svg>
|
<path
|
||||||
|
d="M349.904 22.3773V58.1875H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6565H384.717V22.3773H349.904Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1875H399.204V46.7662ZM399.204 38.6365V30.5568H411.673C415.164 30.5568 417.059 32.053 417.059 34.5967C417.059 37.0904 415.164 38.6365 411.673 38.6365H399.204Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.786 450.948 58.786C463.467 58.786 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2575C457.532 30.2575 462.469 34.1478 462.469 40.2824C462.469 46.417 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.417 439.427 40.2824C439.427 34.1478 444.365 30.2575 450.948 30.2575Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z"
|
||||||
|
fill="black" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z"
|
||||||
|
fill="url(#paint0_linear_2028_278)" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_2028_278" x1="41.443" y1="11.5372" x2="10.5567"
|
||||||
|
y2="42.4236" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0096FF" />
|
||||||
|
<stop offset="1" stop-color="#FF1E56" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -1,19 +1,41 @@
|
|||||||
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M130.998 30.6566V22.3773H91.0977V30.6566H106.16V58.1876H115.935V30.6566H130.998Z" fill="white"/>
|
<path d="M130.998 30.6566V22.3773H91.0977V30.6566H106.16V58.1876H115.935V30.6566H130.998Z"
|
||||||
<path d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2276V22.3773H162.768V41.2799C162.768 47.0156 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0156 144.315 41.2799V22.3773H134.539V42.2276C134.539 52.5018 141.272 58.7362 153.542 58.7362Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M187.508 46.3173H197.234L204.914 58.1876H216.136L207.458 45.2699C212.346 43.5243 215.338 39.6341 215.338 34.3473C215.338 26.6666 209.603 22.3773 200.874 22.3773H177.732V58.1876H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.0531 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z" fill="white"/>
|
<path
|
||||||
<path d="M219.887 58.1876H245.472C253.452 58.1876 258.041 54.3971 258.041 48.0629C258.041 43.8236 255.348 40.9308 252.156 39.6341C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1876ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0206 248.615 46.9657C248.615 48.9108 247.168 50.2076 244.525 50.2076H229.263V43.7238Z" fill="white"/>
|
d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2276V22.3773H162.768V41.2799C162.768 47.0156 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0156 144.315 41.2799V22.3773H134.539V42.2276C134.539 52.5018 141.272 58.7362 153.542 58.7362Z"
|
||||||
<path d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.7861 281.942 58.7861C294.461 58.7861 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2576C288.525 30.2576 293.463 34.1478 293.463 40.2824C293.463 46.4171 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.4171 270.421 40.2824C270.421 34.1478 275.359 30.2576 281.942 30.2576Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M317.526 46.3173H327.251L334.932 58.1876H346.154L337.476 45.2699C342.364 43.5243 345.356 39.6341 345.356 34.3473C345.356 26.6666 339.62 22.3773 330.892 22.3773H307.75V58.1876H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.0531 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z" fill="white"/>
|
<path
|
||||||
<path d="M349.904 22.3773V58.1876H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6566H384.717V22.3773H349.904Z" fill="white"/>
|
d="M187.508 46.3173H197.234L204.914 58.1876H216.136L207.458 45.2699C212.346 43.5243 215.338 39.6341 215.338 34.3473C215.338 26.6666 209.603 22.3773 200.874 22.3773H177.732V58.1876H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.0531 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z"
|
||||||
<path d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1876H399.204V46.7662ZM399.204 38.6366V30.5568H411.673C415.164 30.5568 417.059 32.0531 417.059 34.5967C417.059 37.0904 415.164 38.6366 411.673 38.6366H399.204Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.7861 450.948 58.7861C463.467 58.7861 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2576C457.532 30.2576 462.469 34.1478 462.469 40.2824C462.469 46.4171 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.4171 439.427 40.2824C439.427 34.1478 444.365 30.2576 450.948 30.2576Z" fill="white"/>
|
<path
|
||||||
<path d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z" fill="white"/>
|
d="M219.887 58.1876H245.472C253.452 58.1876 258.041 54.3971 258.041 48.0629C258.041 43.8236 255.348 40.9308 252.156 39.6341C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1876ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0206 248.615 46.9657C248.615 48.9108 247.168 50.2076 244.525 50.2076H229.263V43.7238Z"
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z" fill="url(#paint0_linear_2028_477)"/>
|
fill="white" />
|
||||||
<defs>
|
<path
|
||||||
<linearGradient id="paint0_linear_2028_477" x1="41.443" y1="11.5372" x2="10.5567" y2="42.4236" gradientUnits="userSpaceOnUse">
|
d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.7861 281.942 58.7861C294.461 58.7861 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2576C288.525 30.2576 293.463 34.1478 293.463 40.2824C293.463 46.4171 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.4171 270.421 40.2824C270.421 34.1478 275.359 30.2576 281.942 30.2576Z"
|
||||||
<stop stop-color="#0096FF"/>
|
fill="white" />
|
||||||
<stop offset="1" stop-color="#FF1E56"/>
|
<path
|
||||||
</linearGradient>
|
d="M317.526 46.3173H327.251L334.932 58.1876H346.154L337.476 45.2699C342.364 43.5243 345.356 39.6341 345.356 34.3473C345.356 26.6666 339.62 22.3773 330.892 22.3773H307.75V58.1876H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.0531 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z"
|
||||||
</defs>
|
fill="white" />
|
||||||
</svg>
|
<path
|
||||||
|
d="M349.904 22.3773V58.1876H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6566H384.717V22.3773H349.904Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1876H399.204V46.7662ZM399.204 38.6366V30.5568H411.673C415.164 30.5568 417.059 32.0531 417.059 34.5967C417.059 37.0904 415.164 38.6366 411.673 38.6366H399.204Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.7861 450.948 58.7861C463.467 58.7861 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2576C457.532 30.2576 462.469 34.1478 462.469 40.2824C462.469 46.4171 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.4171 439.427 40.2824C439.427 34.1478 444.365 30.2576 450.948 30.2576Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z"
|
||||||
|
fill="white" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z"
|
||||||
|
fill="url(#paint0_linear_2028_477)" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_2028_477" x1="41.443" y1="11.5372" x2="10.5567"
|
||||||
|
y2="42.4236" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0096FF" />
|
||||||
|
<stop offset="1" stop-color="#FF1E56" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -1,10 +1,10 @@
|
|||||||
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_977_547)">
|
<g clip-path="url(#clip0_977_547)">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white" />
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_977_547">
|
<clipPath id="clip0_977_547">
|
||||||
<rect width="16" height="16" fill="white" transform="translate(2.5 2)"/>
|
<rect width="16" height="16" fill="white" transform="translate(2.5 2)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 420 B |
@@ -1,3 +1,5 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z" fill="#666666"/>
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
</svg>
|
d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z"
|
||||||
|
fill="#666666" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 770 B |
@@ -1,18 +1,18 @@
|
|||||||
import type { Config } from 'tailwindcss';
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
background: 'var(--background)',
|
background: "var(--background)",
|
||||||
foreground: 'var(--foreground)',
|
foreground: "var(--foreground)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"extends": "@repo/typescript-config/nextjs.json",
|
"extends": "@repo/typescript-config/nextjs.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"next.config.js",
|
"next.config.js",
|
||||||
".next/types/**/*.ts",
|
".next/types/**/*.ts",
|
||||||
"../hub/app/_components/PaginatedTable.tsx",
|
"../hub/app/_components/PaginatedTable.tsx",
|
||||||
"../hub/app/_components/Table.tsx"
|
"../hub/app/_components/Table.tsx"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--background: #0a0a0a;
|
--background: #0a0a0a;
|
||||||
--foreground: #ededed;
|
--foreground: #ededed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imgDark {
|
.imgDark {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imgLight {
|
.imgLight {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.imgDark {
|
.imgDark {
|
||||||
display: unset;
|
display: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,29 +3,29 @@ import localFont from "next/font/local";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const geistSans = localFont({
|
const geistSans = localFont({
|
||||||
src: "./fonts/GeistVF.woff",
|
src: "./fonts/GeistVF.woff",
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
});
|
});
|
||||||
const geistMono = localFont({
|
const geistMono = localFont({
|
||||||
src: "./fonts/GeistMonoVF.woff",
|
src: "./fonts/GeistMonoVF.woff",
|
||||||
variable: "--font-geist-mono",
|
variable: "--font-geist-mono",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Create Next App",
|
||||||
description: "Generated by create next app",
|
description: "Generated by create next app",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||||
{children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,188 +1,194 @@
|
|||||||
.page {
|
.page {
|
||||||
--gray-rgb: 0, 0, 0;
|
--gray-rgb: 0, 0, 0;
|
||||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||||
|
|
||||||
--button-primary-hover: #383838;
|
--button-primary-hover: #383838;
|
||||||
--button-secondary-hover: #f2f2f2;
|
--button-secondary-hover: #f2f2f2;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 20px 1fr 20px;
|
grid-template-rows: 20px 1fr 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
min-height: 100svh;
|
min-height: 100svh;
|
||||||
padding: 80px;
|
padding: 80px;
|
||||||
gap: 64px;
|
gap: 64px;
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.page {
|
.page {
|
||||||
--gray-rgb: 255, 255, 255;
|
--gray-rgb: 255, 255, 255;
|
||||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||||
|
|
||||||
--button-primary-hover: #ccc;
|
--button-primary-hover: #ccc;
|
||||||
--button-secondary-hover: #1a1a1a;
|
--button-secondary-hover: #1a1a1a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
grid-row-start: 2;
|
grid-row-start: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main ol {
|
.main ol {
|
||||||
font-family: var(--font-geist-mono);
|
font-family: var(--font-geist-mono);
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main li:not(:last-of-type) {
|
.main li:not(:last-of-type) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main code {
|
.main code {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
background: var(--gray-alpha-100);
|
background: var(--gray-alpha-100);
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas {
|
.ctas {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas a {
|
.ctas a {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: 128px;
|
border-radius: 128px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border: none;
|
border: none;
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
transition:
|
||||||
cursor: pointer;
|
background 0.2s,
|
||||||
display: flex;
|
color 0.2s,
|
||||||
align-items: center;
|
border-color 0.2s;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
display: flex;
|
||||||
line-height: 20px;
|
align-items: center;
|
||||||
font-weight: 500;
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.primary {
|
a.primary {
|
||||||
background: var(--foreground);
|
background: var(--foreground);
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary {
|
a.secondary {
|
||||||
border-color: var(--gray-alpha-200);
|
border-color: var(--gray-alpha-200);
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.secondary {
|
button.secondary {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: 128px;
|
border-radius: 128px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
border: none;
|
border: none;
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
transition:
|
||||||
cursor: pointer;
|
background 0.2s,
|
||||||
display: flex;
|
color 0.2s,
|
||||||
align-items: center;
|
border-color 0.2s;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
display: flex;
|
||||||
line-height: 20px;
|
align-items: center;
|
||||||
font-weight: 500;
|
justify-content: center;
|
||||||
background: transparent;
|
font-size: 16px;
|
||||||
border-color: var(--gray-alpha-200);
|
line-height: 20px;
|
||||||
min-width: 180px;
|
font-weight: 500;
|
||||||
|
background: transparent;
|
||||||
|
border-color: var(--gray-alpha-200);
|
||||||
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
font-family: var(--font-geist-sans);
|
font-family: var(--font-geist-sans);
|
||||||
grid-row-start: 3;
|
grid-row-start: 3;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer a {
|
.footer a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer img {
|
.footer img {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable hover only on non-touch devices */
|
/* Enable hover only on non-touch devices */
|
||||||
@media (hover: hover) and (pointer: fine) {
|
@media (hover: hover) and (pointer: fine) {
|
||||||
a.primary:hover {
|
a.primary:hover {
|
||||||
background: var(--button-primary-hover);
|
background: var(--button-primary-hover);
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary:hover {
|
a.secondary:hover {
|
||||||
background: var(--button-secondary-hover);
|
background: var(--button-secondary-hover);
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer a:hover {
|
.footer a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-underline-offset: 4px;
|
text-underline-offset: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.page {
|
.page {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main ol {
|
.main ol {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas {
|
.ctas {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas a {
|
.ctas a {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.secondary {
|
a.secondary {
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.logo {
|
.logo {
|
||||||
filter: invert();
|
filter: invert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,100 +3,100 @@ import { Button } from "@repo/ui/button";
|
|||||||
import styles from "./page.module.css";
|
import styles from "./page.module.css";
|
||||||
|
|
||||||
type Props = Omit<ImageProps, "src"> & {
|
type Props = Omit<ImageProps, "src"> & {
|
||||||
srcLight: string;
|
srcLight: string;
|
||||||
srcDark: string;
|
srcDark: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ThemeImage = (props: Props) => {
|
const ThemeImage = (props: Props) => {
|
||||||
const { srcLight, srcDark, ...rest } = props;
|
const { srcLight, srcDark, ...rest } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Image {...rest} src={srcLight} className="imgLight" />
|
<Image {...rest} src={srcLight} className="imgLight" />
|
||||||
<Image {...rest} src={srcDark} className="imgDark" />
|
<Image {...rest} src={srcDark} className="imgDark" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<ThemeImage
|
<ThemeImage
|
||||||
className={styles.logo}
|
className={styles.logo}
|
||||||
srcLight="turborepo-dark.svg"
|
srcLight="turborepo-dark.svg"
|
||||||
srcDark="turborepo-light.svg"
|
srcDark="turborepo-light.svg"
|
||||||
alt="Turborepo logo"
|
alt="Turborepo logo"
|
||||||
width={180}
|
width={180}
|
||||||
height={38}
|
height={38}
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
Get started by editing <code>apps/docs/app/page.tsx</code>
|
Get started by editing <code>apps/docs/app/page.tsx</code>
|
||||||
</li>
|
</li>
|
||||||
<li>Save and see your changes instantly.</li>
|
<li>Save and see your changes instantly.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div className={styles.ctas}>
|
<div className={styles.ctas}>
|
||||||
<a
|
<a
|
||||||
className={styles.primary}
|
className={styles.primary}
|
||||||
href="https://vercel.com/new/clone?demo-description=Learn+to+implement+a+monorepo+with+a+two+Next.js+sites+that+has+installed+three+local+packages.&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F4K8ZISWAzJ8X1504ca0zmC%2F0b21a1c6246add355e55816278ef54bc%2FBasic.png&demo-title=Monorepo+with+Turborepo&demo-url=https%3A%2F%2Fexamples-basic-web.vercel.sh%2F&from=templates&project-name=Monorepo+with+Turborepo&repository-name=monorepo-turborepo&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fturborepo%2Ftree%2Fmain%2Fexamples%2Fbasic&root-directory=apps%2Fdocs&skippable-integrations=1&teamSlug=vercel&utm_source=create-turbo"
|
href="https://vercel.com/new/clone?demo-description=Learn+to+implement+a+monorepo+with+a+two+Next.js+sites+that+has+installed+three+local+packages.&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F4K8ZISWAzJ8X1504ca0zmC%2F0b21a1c6246add355e55816278ef54bc%2FBasic.png&demo-title=Monorepo+with+Turborepo&demo-url=https%3A%2F%2Fexamples-basic-web.vercel.sh%2F&from=templates&project-name=Monorepo+with+Turborepo&repository-name=monorepo-turborepo&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fturborepo%2Ftree%2Fmain%2Fexamples%2Fbasic&root-directory=apps%2Fdocs&skippable-integrations=1&teamSlug=vercel&utm_source=create-turbo"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
className={styles.logo}
|
className={styles.logo}
|
||||||
src="/vercel.svg"
|
src="/vercel.svg"
|
||||||
alt="Vercel logomark"
|
alt="Vercel logomark"
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
/>
|
/>
|
||||||
Deploy now
|
Deploy now
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://turbo.build/repo/docs?utm_source"
|
href="https://turbo.build/repo/docs?utm_source"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={styles.secondary}
|
className={styles.secondary}
|
||||||
>
|
>
|
||||||
Read our docs
|
Read our docs
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Button appName="docs" className={styles.secondary}>
|
<Button appName="docs" className={styles.secondary}>
|
||||||
Open alert
|
Open alert
|
||||||
</Button>
|
</Button>
|
||||||
</main>
|
</main>
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<a
|
<a
|
||||||
href="https://vercel.com/templates?search=turborepo&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
href="https://vercel.com/templates?search=turborepo&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
aria-hidden
|
aria-hidden
|
||||||
src="/window.svg"
|
src="/window.svg"
|
||||||
alt="Window icon"
|
alt="Window icon"
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
Examples
|
Examples
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://turbo.build?utm_source=create-turbo"
|
href="https://turbo.build?utm_source=create-turbo"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
aria-hidden
|
aria-hidden
|
||||||
src="/globe.svg"
|
src="/globe.svg"
|
||||||
alt="Globe icon"
|
alt="Globe icon"
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
Go to turbo.build →
|
Go to turbo.build →
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
{
|
{
|
||||||
"name": "docs",
|
"name": "docs",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack -p 4000",
|
"dev": "next dev --turbopack -p 4000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint --max-warnings 0",
|
"lint": "next lint --max-warnings 0",
|
||||||
"check-types": "tsc --noEmit"
|
"check-types": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
"next": "^15.1.0",
|
"next": "^15.1.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@repo/eslint-config": "*",
|
"@repo/eslint-config": "*",
|
||||||
"@repo/typescript-config": "*",
|
"@repo/typescript-config": "*",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "18.3.1",
|
"@types/react": "18.3.1",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 13.5V6.5V5.41421C14.5 5.149 14.3946 4.89464 14.2071 4.70711L9.79289 0.292893C9.60536 0.105357 9.351 0 9.08579 0H8H3H1.5V1.5V13.5C1.5 14.8807 2.61929 16 4 16H12C13.3807 16 14.5 14.8807 14.5 13.5ZM13 13.5V6.5H9.5H8V5V1.5H3V13.5C3 14.0523 3.44772 14.5 4 14.5H12C12.5523 14.5 13 14.0523 13 13.5ZM9.5 5V2.12132L12.3787 5H9.5ZM5.13 5.00062H4.505V6.25062H5.13H6H6.625V5.00062H6H5.13ZM4.505 8H5.13H11H11.625V9.25H11H5.13H4.505V8ZM5.13 11H4.505V12.25H5.13H11H11.625V11H11H5.13Z" fill="#666666"/>
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
</svg>
|
d="M14.5 13.5V6.5V5.41421C14.5 5.149 14.3946 4.89464 14.2071 4.70711L9.79289 0.292893C9.60536 0.105357 9.351 0 9.08579 0H8H3H1.5V1.5V13.5C1.5 14.8807 2.61929 16 4 16H12C13.3807 16 14.5 14.8807 14.5 13.5ZM13 13.5V6.5H9.5H8V5V1.5H3V13.5C3 14.0523 3.44772 14.5 4 14.5H12C12.5523 14.5 13 14.0523 13 13.5ZM9.5 5V2.12132L12.3787 5H9.5ZM5.13 5.00062H4.505V6.25062H5.13H6H6.625V5.00062H6H5.13ZM4.505 8H5.13H11H11.625V9.25H11H5.13H4.505V8ZM5.13 11H4.505V12.25H5.13H11H11.625V11H11H5.13Z"
|
||||||
|
fill="#666666" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 645 B After Width: | Height: | Size: 665 B |
@@ -1,10 +1,12 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_868_525)">
|
<g clip-path="url(#clip0_868_525)">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.268 14.0934C11.9051 13.4838 13.2303 12.2333 13.9384 10.6469C13.1192 10.7941 12.2138 10.9111 11.2469 10.9925C11.0336 12.2005 10.695 13.2621 10.268 14.0934ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8.48347 14.4823C8.32384 14.494 8.16262 14.5 8 14.5C7.83738 14.5 7.67616 14.494 7.51654 14.4823C7.5132 14.4791 7.50984 14.4759 7.50647 14.4726C7.2415 14.2165 6.94578 13.7854 6.67032 13.1558C6.41594 12.5744 6.19979 11.8714 6.04101 11.0778C6.67605 11.1088 7.33104 11.125 8 11.125C8.66896 11.125 9.32395 11.1088 9.95899 11.0778C9.80021 11.8714 9.58406 12.5744 9.32968 13.1558C9.05422 13.7854 8.7585 14.2165 8.49353 14.4726C8.49016 14.4759 8.4868 14.4791 8.48347 14.4823ZM11.4187 9.72246C12.5137 9.62096 13.5116 9.47245 14.3724 9.28806C14.4561 8.87172 14.5 8.44099 14.5 8C14.5 7.55901 14.4561 7.12828 14.3724 6.71194C13.5116 6.52755 12.5137 6.37904 11.4187 6.27753C11.4719 6.83232 11.5 7.40867 11.5 8C11.5 8.59133 11.4719 9.16768 11.4187 9.72246ZM10.1525 6.18401C10.2157 6.75982 10.25 7.36805 10.25 8C10.25 8.63195 10.2157 9.24018 10.1525 9.81598C9.46123 9.85455 8.7409 9.875 8 9.875C7.25909 9.875 6.53877 9.85455 5.84749 9.81598C5.7843 9.24018 5.75 8.63195 5.75 8C5.75 7.36805 5.7843 6.75982 5.84749 6.18401C6.53877 6.14545 7.25909 6.125 8 6.125C8.74091 6.125 9.46123 6.14545 10.1525 6.18401ZM11.2469 5.00748C12.2138 5.08891 13.1191 5.20593 13.9384 5.35306C13.2303 3.7667 11.9051 2.51622 10.268 1.90662C10.695 2.73788 11.0336 3.79953 11.2469 5.00748ZM8.48347 1.51771C8.4868 1.52089 8.49016 1.52411 8.49353 1.52737C8.7585 1.78353 9.05422 2.21456 9.32968 2.84417C9.58406 3.42562 9.80021 4.12856 9.95899 4.92219C9.32395 4.89118 8.66896 4.875 8 4.875C7.33104 4.875 6.67605 4.89118 6.04101 4.92219C6.19978 4.12856 6.41594 3.42562 6.67032 2.84417C6.94578 2.21456 7.2415 1.78353 7.50647 1.52737C7.50984 1.52411 7.51319 1.52089 7.51653 1.51771C7.67615 1.50597 7.83738 1.5 8 1.5C8.16262 1.5 8.32384 1.50597 8.48347 1.51771ZM5.73202 1.90663C4.0949 2.51622 2.76975 3.7667 2.06159 5.35306C2.88085 5.20593 3.78617 5.08891 4.75309 5.00748C4.96639 3.79953 5.30497 2.73788 5.73202 1.90663ZM4.58133 6.27753C3.48633 6.37904 2.48837 6.52755 1.62761 6.71194C1.54392 7.12828 1.5 7.55901 1.5 8C1.5 8.44099 1.54392 8.87172 1.62761 9.28806C2.48837 9.47245 3.48633 9.62096 4.58133 9.72246C4.52807 9.16768 4.5 8.59133 4.5 8C4.5 7.40867 4.52807 6.83232 4.58133 6.27753ZM4.75309 10.9925C3.78617 10.9111 2.88085 10.7941 2.06159 10.6469C2.76975 12.2333 4.0949 13.4838 5.73202 14.0934C5.30497 13.2621 4.96639 12.2005 4.75309 10.9925Z" fill="#666666"/>
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
</g>
|
d="M10.268 14.0934C11.9051 13.4838 13.2303 12.2333 13.9384 10.6469C13.1192 10.7941 12.2138 10.9111 11.2469 10.9925C11.0336 12.2005 10.695 13.2621 10.268 14.0934ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8.48347 14.4823C8.32384 14.494 8.16262 14.5 8 14.5C7.83738 14.5 7.67616 14.494 7.51654 14.4823C7.5132 14.4791 7.50984 14.4759 7.50647 14.4726C7.2415 14.2165 6.94578 13.7854 6.67032 13.1558C6.41594 12.5744 6.19979 11.8714 6.04101 11.0778C6.67605 11.1088 7.33104 11.125 8 11.125C8.66896 11.125 9.32395 11.1088 9.95899 11.0778C9.80021 11.8714 9.58406 12.5744 9.32968 13.1558C9.05422 13.7854 8.7585 14.2165 8.49353 14.4726C8.49016 14.4759 8.4868 14.4791 8.48347 14.4823ZM11.4187 9.72246C12.5137 9.62096 13.5116 9.47245 14.3724 9.28806C14.4561 8.87172 14.5 8.44099 14.5 8C14.5 7.55901 14.4561 7.12828 14.3724 6.71194C13.5116 6.52755 12.5137 6.37904 11.4187 6.27753C11.4719 6.83232 11.5 7.40867 11.5 8C11.5 8.59133 11.4719 9.16768 11.4187 9.72246ZM10.1525 6.18401C10.2157 6.75982 10.25 7.36805 10.25 8C10.25 8.63195 10.2157 9.24018 10.1525 9.81598C9.46123 9.85455 8.7409 9.875 8 9.875C7.25909 9.875 6.53877 9.85455 5.84749 9.81598C5.7843 9.24018 5.75 8.63195 5.75 8C5.75 7.36805 5.7843 6.75982 5.84749 6.18401C6.53877 6.14545 7.25909 6.125 8 6.125C8.74091 6.125 9.46123 6.14545 10.1525 6.18401ZM11.2469 5.00748C12.2138 5.08891 13.1191 5.20593 13.9384 5.35306C13.2303 3.7667 11.9051 2.51622 10.268 1.90662C10.695 2.73788 11.0336 3.79953 11.2469 5.00748ZM8.48347 1.51771C8.4868 1.52089 8.49016 1.52411 8.49353 1.52737C8.7585 1.78353 9.05422 2.21456 9.32968 2.84417C9.58406 3.42562 9.80021 4.12856 9.95899 4.92219C9.32395 4.89118 8.66896 4.875 8 4.875C7.33104 4.875 6.67605 4.89118 6.04101 4.92219C6.19978 4.12856 6.41594 3.42562 6.67032 2.84417C6.94578 2.21456 7.2415 1.78353 7.50647 1.52737C7.50984 1.52411 7.51319 1.52089 7.51653 1.51771C7.67615 1.50597 7.83738 1.5 8 1.5C8.16262 1.5 8.32384 1.50597 8.48347 1.51771ZM5.73202 1.90663C4.0949 2.51622 2.76975 3.7667 2.06159 5.35306C2.88085 5.20593 3.78617 5.08891 4.75309 5.00748C4.96639 3.79953 5.30497 2.73788 5.73202 1.90663ZM4.58133 6.27753C3.48633 6.37904 2.48837 6.52755 1.62761 6.71194C1.54392 7.12828 1.5 7.55901 1.5 8C1.5 8.44099 1.54392 8.87172 1.62761 9.28806C2.48837 9.47245 3.48633 9.62096 4.58133 9.72246C4.52807 9.16768 4.5 8.59133 4.5 8C4.5 7.40867 4.52807 6.83232 4.58133 6.27753ZM4.75309 10.9925C3.78617 10.9111 2.88085 10.7941 2.06159 10.6469C2.76975 12.2333 4.0949 13.4838 5.73202 14.0934C5.30497 13.2621 4.96639 12.2005 4.75309 10.9925Z"
|
||||||
<defs>
|
fill="#666666" />
|
||||||
<clipPath id="clip0_868_525">
|
</g>
|
||||||
<rect width="16" height="16" fill="white"/>
|
<defs>
|
||||||
</clipPath>
|
<clipPath id="clip0_868_525">
|
||||||
</defs>
|
<rect width="16" height="16" fill="white" />
|
||||||
</svg>
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -1 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80">
|
||||||
|
<path fill="#000"
|
||||||
|
d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z" />
|
||||||
|
<path fill="#000"
|
||||||
|
d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,19 +1,41 @@
|
|||||||
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M130.998 30.6565V22.3773H91.0977V30.6565H106.16V58.1875H115.935V30.6565H130.998Z" fill="black"/>
|
<path d="M130.998 30.6565V22.3773H91.0977V30.6565H106.16V58.1875H115.935V30.6565H130.998Z"
|
||||||
<path d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2275V22.3773H162.768V41.2799C162.768 47.0155 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0155 144.315 41.2799V22.3773H134.539V42.2275C134.539 52.5018 141.272 58.7362 153.542 58.7362Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M187.508 46.3173H197.234L204.914 58.1875H216.136L207.458 45.2699C212.346 43.5243 215.338 39.634 215.338 34.3473C215.338 26.6665 209.603 22.3773 200.874 22.3773H177.732V58.1875H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.053 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z" fill="black"/>
|
<path
|
||||||
<path d="M219.887 58.1875H245.472C253.452 58.1875 258.041 54.397 258.041 48.0629C258.041 43.8235 255.348 40.9308 252.156 39.634C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1875ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0205 248.615 46.9657C248.615 48.9108 247.168 50.2075 244.525 50.2075H229.263V43.7238Z" fill="black"/>
|
d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2275V22.3773H162.768V41.2799C162.768 47.0155 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0155 144.315 41.2799V22.3773H134.539V42.2275C134.539 52.5018 141.272 58.7362 153.542 58.7362Z"
|
||||||
<path d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.786 281.942 58.786C294.461 58.786 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2575C288.525 30.2575 293.463 34.1478 293.463 40.2824C293.463 46.417 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.417 270.421 40.2824C270.421 34.1478 275.359 30.2575 281.942 30.2575Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M317.526 46.3173H327.251L334.932 58.1875H346.154L337.476 45.2699C342.364 43.5243 345.356 39.634 345.356 34.3473C345.356 26.6665 339.62 22.3773 330.892 22.3773H307.75V58.1875H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.053 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z" fill="black"/>
|
<path
|
||||||
<path d="M349.904 22.3773V58.1875H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6565H384.717V22.3773H349.904Z" fill="black"/>
|
d="M187.508 46.3173H197.234L204.914 58.1875H216.136L207.458 45.2699C212.346 43.5243 215.338 39.634 215.338 34.3473C215.338 26.6665 209.603 22.3773 200.874 22.3773H177.732V58.1875H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.053 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z"
|
||||||
<path d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1875H399.204V46.7662ZM399.204 38.6365V30.5568H411.673C415.164 30.5568 417.059 32.053 417.059 34.5967C417.059 37.0904 415.164 38.6365 411.673 38.6365H399.204Z" fill="black"/>
|
fill="black" />
|
||||||
<path d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.786 450.948 58.786C463.467 58.786 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2575C457.532 30.2575 462.469 34.1478 462.469 40.2824C462.469 46.417 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.417 439.427 40.2824C439.427 34.1478 444.365 30.2575 450.948 30.2575Z" fill="black"/>
|
<path
|
||||||
<path d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z" fill="black"/>
|
d="M219.887 58.1875H245.472C253.452 58.1875 258.041 54.397 258.041 48.0629C258.041 43.8235 255.348 40.9308 252.156 39.634C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1875ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0205 248.615 46.9657C248.615 48.9108 247.168 50.2075 244.525 50.2075H229.263V43.7238Z"
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z" fill="url(#paint0_linear_2028_278)"/>
|
fill="black" />
|
||||||
<defs>
|
<path
|
||||||
<linearGradient id="paint0_linear_2028_278" x1="41.443" y1="11.5372" x2="10.5567" y2="42.4236" gradientUnits="userSpaceOnUse">
|
d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.786 281.942 58.786C294.461 58.786 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2575C288.525 30.2575 293.463 34.1478 293.463 40.2824C293.463 46.417 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.417 270.421 40.2824C270.421 34.1478 275.359 30.2575 281.942 30.2575Z"
|
||||||
<stop stop-color="#0096FF"/>
|
fill="black" />
|
||||||
<stop offset="1" stop-color="#FF1E56"/>
|
<path
|
||||||
</linearGradient>
|
d="M317.526 46.3173H327.251L334.932 58.1875H346.154L337.476 45.2699C342.364 43.5243 345.356 39.634 345.356 34.3473C345.356 26.6665 339.62 22.3773 330.892 22.3773H307.75V58.1875H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.053 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z"
|
||||||
</defs>
|
fill="black" />
|
||||||
</svg>
|
<path
|
||||||
|
d="M349.904 22.3773V58.1875H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6565H384.717V22.3773H349.904Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1875H399.204V46.7662ZM399.204 38.6365V30.5568H411.673C415.164 30.5568 417.059 32.053 417.059 34.5967C417.059 37.0904 415.164 38.6365 411.673 38.6365H399.204Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.786 450.948 58.786C463.467 58.786 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2575C457.532 30.2575 462.469 34.1478 462.469 40.2824C462.469 46.417 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.417 439.427 40.2824C439.427 34.1478 444.365 30.2575 450.948 30.2575Z"
|
||||||
|
fill="black" />
|
||||||
|
<path
|
||||||
|
d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z"
|
||||||
|
fill="black" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z"
|
||||||
|
fill="url(#paint0_linear_2028_278)" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_2028_278" x1="41.443" y1="11.5372" x2="10.5567"
|
||||||
|
y2="42.4236" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0096FF" />
|
||||||
|
<stop offset="1" stop-color="#FF1E56" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -1,19 +1,41 @@
|
|||||||
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="473" height="76" viewBox="0 0 473 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M130.998 30.6566V22.3773H91.0977V30.6566H106.16V58.1876H115.935V30.6566H130.998Z" fill="white"/>
|
<path d="M130.998 30.6566V22.3773H91.0977V30.6566H106.16V58.1876H115.935V30.6566H130.998Z"
|
||||||
<path d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2276V22.3773H162.768V41.2799C162.768 47.0156 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0156 144.315 41.2799V22.3773H134.539V42.2276C134.539 52.5018 141.272 58.7362 153.542 58.7362Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M187.508 46.3173H197.234L204.914 58.1876H216.136L207.458 45.2699C212.346 43.5243 215.338 39.6341 215.338 34.3473C215.338 26.6666 209.603 22.3773 200.874 22.3773H177.732V58.1876H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.0531 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z" fill="white"/>
|
<path
|
||||||
<path d="M219.887 58.1876H245.472C253.452 58.1876 258.041 54.3971 258.041 48.0629C258.041 43.8236 255.348 40.9308 252.156 39.6341C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1876ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0206 248.615 46.9657C248.615 48.9108 247.168 50.2076 244.525 50.2076H229.263V43.7238Z" fill="white"/>
|
d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2276V22.3773H162.768V41.2799C162.768 47.0156 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0156 144.315 41.2799V22.3773H134.539V42.2276C134.539 52.5018 141.272 58.7362 153.542 58.7362Z"
|
||||||
<path d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.7861 281.942 58.7861C294.461 58.7861 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2576C288.525 30.2576 293.463 34.1478 293.463 40.2824C293.463 46.4171 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.4171 270.421 40.2824C270.421 34.1478 275.359 30.2576 281.942 30.2576Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M317.526 46.3173H327.251L334.932 58.1876H346.154L337.476 45.2699C342.364 43.5243 345.356 39.6341 345.356 34.3473C345.356 26.6666 339.62 22.3773 330.892 22.3773H307.75V58.1876H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.0531 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z" fill="white"/>
|
<path
|
||||||
<path d="M349.904 22.3773V58.1876H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6566H384.717V22.3773H349.904Z" fill="white"/>
|
d="M187.508 46.3173H197.234L204.914 58.1876H216.136L207.458 45.2699C212.346 43.5243 215.338 39.6341 215.338 34.3473C215.338 26.6666 209.603 22.3773 200.874 22.3773H177.732V58.1876H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.0531 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z"
|
||||||
<path d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1876H399.204V46.7662ZM399.204 38.6366V30.5568H411.673C415.164 30.5568 417.059 32.0531 417.059 34.5967C417.059 37.0904 415.164 38.6366 411.673 38.6366H399.204Z" fill="white"/>
|
fill="white" />
|
||||||
<path d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.7861 450.948 58.7861C463.467 58.7861 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2576C457.532 30.2576 462.469 34.1478 462.469 40.2824C462.469 46.4171 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.4171 439.427 40.2824C439.427 34.1478 444.365 30.2576 450.948 30.2576Z" fill="white"/>
|
<path
|
||||||
<path d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z" fill="white"/>
|
d="M219.887 58.1876H245.472C253.452 58.1876 258.041 54.3971 258.041 48.0629C258.041 43.8236 255.348 40.9308 252.156 39.6341C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1876ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0206 248.615 46.9657C248.615 48.9108 247.168 50.2076 244.525 50.2076H229.263V43.7238Z"
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z" fill="url(#paint0_linear_2028_477)"/>
|
fill="white" />
|
||||||
<defs>
|
<path
|
||||||
<linearGradient id="paint0_linear_2028_477" x1="41.443" y1="11.5372" x2="10.5567" y2="42.4236" gradientUnits="userSpaceOnUse">
|
d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.7861 281.942 58.7861C294.461 58.7861 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2576C288.525 30.2576 293.463 34.1478 293.463 40.2824C293.463 46.4171 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.4171 270.421 40.2824C270.421 34.1478 275.359 30.2576 281.942 30.2576Z"
|
||||||
<stop stop-color="#0096FF"/>
|
fill="white" />
|
||||||
<stop offset="1" stop-color="#FF1E56"/>
|
<path
|
||||||
</linearGradient>
|
d="M317.526 46.3173H327.251L334.932 58.1876H346.154L337.476 45.2699C342.364 43.5243 345.356 39.6341 345.356 34.3473C345.356 26.6666 339.62 22.3773 330.892 22.3773H307.75V58.1876H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.0531 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z"
|
||||||
</defs>
|
fill="white" />
|
||||||
</svg>
|
<path
|
||||||
|
d="M349.904 22.3773V58.1876H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6566H384.717V22.3773H349.904Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1876H399.204V46.7662ZM399.204 38.6366V30.5568H411.673C415.164 30.5568 417.059 32.0531 417.059 34.5967C417.059 37.0904 415.164 38.6366 411.673 38.6366H399.204Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.7861 450.948 58.7861C463.467 58.7861 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2576C457.532 30.2576 462.469 34.1478 462.469 40.2824C462.469 46.4171 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.4171 439.427 40.2824C439.427 34.1478 444.365 30.2576 450.948 30.2576Z"
|
||||||
|
fill="white" />
|
||||||
|
<path
|
||||||
|
d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z"
|
||||||
|
fill="white" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z"
|
||||||
|
fill="url(#paint0_linear_2028_477)" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_2028_477" x1="41.443" y1="11.5372" x2="10.5567"
|
||||||
|
y2="42.4236" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0096FF" />
|
||||||
|
<stop offset="1" stop-color="#FF1E56" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -1,10 +1,10 @@
|
|||||||
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_977_547)">
|
<g clip-path="url(#clip0_977_547)">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white" />
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_977_547">
|
<clipPath id="clip0_977_547">
|
||||||
<rect width="16" height="16" fill="white" transform="translate(2.5 2)"/>
|
<rect width="16" height="16" fill="white" transform="translate(2.5 2)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 420 B |
@@ -1,3 +1,5 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z" fill="#666666"/>
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
</svg>
|
d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z"
|
||||||
|
fill="#666666" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 770 B |
@@ -1,20 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": "@repo/typescript-config/nextjs.json",
|
"extends": "@repo/typescript-config/nextjs.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"next.config.js",
|
"next.config.js",
|
||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": ["node_modules"]
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
apps/hub-server/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import "modules/chron";
|
||||||
|
console.log("VAR hub Server started");
|
||||||
100
apps/hub-server/modules/chron.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { getMoodleCourseCompletionStatus, getMoodleUserById } from "./moodle";
|
||||||
|
import { CronJob } from "cron";
|
||||||
|
import { prisma } from "@repo/db";
|
||||||
|
|
||||||
|
const syncMoodleIds = async () => {
|
||||||
|
try {
|
||||||
|
const user = await prisma.user.findMany({
|
||||||
|
where: {
|
||||||
|
moodleId: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
user.map(async (u) => {
|
||||||
|
const moodleUser = await getMoodleUserById(u.id.toString());
|
||||||
|
const moodleId = moodleUser?.id.toString();
|
||||||
|
if (u.moodleId === parseInt(moodleId)) return;
|
||||||
|
await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: u.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
moodleId: parseInt(moodleId),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateParticipantMoodleResults =
|
||||||
|
(usage: "starter" | "finisher") => async () => {
|
||||||
|
const participantsMoodlePending = await prisma.participant.findMany({
|
||||||
|
where: {
|
||||||
|
[usage === "starter"
|
||||||
|
? "starterMoodleCourseCompleted"
|
||||||
|
: "finisherMoodleCourseCompleted"]: false,
|
||||||
|
Event: {
|
||||||
|
[usage === "starter"
|
||||||
|
? "starterMoodleCourseId"
|
||||||
|
: "finisherMoodleCourseId"]: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
Event: true,
|
||||||
|
User: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
participantsMoodlePending.map(async (p) => {
|
||||||
|
if (!p.User) return;
|
||||||
|
if (!p.User.moodleId) return;
|
||||||
|
|
||||||
|
const quizzResult = await getMoodleCourseCompletionStatus(
|
||||||
|
p.User.moodleId.toString(),
|
||||||
|
p.Event[
|
||||||
|
usage === "starter"
|
||||||
|
? "starterMoodleCourseId"
|
||||||
|
: "finisherMoodleCourseId"
|
||||||
|
]!,
|
||||||
|
);
|
||||||
|
p.Event.finisherMoodleCourseId;
|
||||||
|
if (quizzResult?.completionstatus?.completed === true) {
|
||||||
|
await prisma.participant.update({
|
||||||
|
where: {
|
||||||
|
id: p.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
[usage === "starter"
|
||||||
|
? "starterMoodleCurseCompleted"
|
||||||
|
: "finisherMoodleCurseCompleted"]: true,
|
||||||
|
statusLog: {
|
||||||
|
push: {
|
||||||
|
event: "Starter course completed",
|
||||||
|
timestamp: new Date(),
|
||||||
|
user: "system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CronJob.from({ cronTime: "0 * * * *", onTick: syncMoodleIds, start: true });
|
||||||
|
CronJob.from({
|
||||||
|
cronTime: "*/5 * * * *",
|
||||||
|
onTick: async () => {
|
||||||
|
await updateParticipantMoodleResults("starter");
|
||||||
|
await updateParticipantMoodleResults("finisher");
|
||||||
|
},
|
||||||
|
start: true,
|
||||||
|
});
|
||||||
89
apps/hub-server/modules/moodle.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export const getMoodleUserById = async (id: string) => {
|
||||||
|
const { data: user } = await axios.get(
|
||||||
|
"https://moodle.virtualairrescue.com/webservice/rest/server.php",
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
wstoken: process.env.MOODLE_TOKEN,
|
||||||
|
wsfunction: "core_user_get_users_by_field",
|
||||||
|
moodlewsrestformat: "json",
|
||||||
|
field: "idnumber",
|
||||||
|
values: [id],
|
||||||
|
},
|
||||||
|
paramsSerializer: {
|
||||||
|
indexes: true, // use brackets with indexes
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const u = user[0];
|
||||||
|
return (
|
||||||
|
(u as {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
fullname: string;
|
||||||
|
email: string;
|
||||||
|
}) || null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMoodleQuizResult = async (userId: string, quizId: string) => {
|
||||||
|
const { data: quizzes } = await axios.get(
|
||||||
|
"https://moodle.virtualairrescue.com/webservice/rest/server.php",
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
wstoken: process.env.MOODLE_TOKEN,
|
||||||
|
wsfunction: "mod_quiz_get_user_attempts",
|
||||||
|
moodlewsrestformat: "json",
|
||||||
|
quizid: quizId,
|
||||||
|
userid: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return quizzes;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMoodleCourseCompletionStatus = async (
|
||||||
|
userId: string,
|
||||||
|
courseId: string,
|
||||||
|
) => {
|
||||||
|
const { data: completionStatus } = await axios.get(
|
||||||
|
"https://moodle.virtualairrescue.com/webservice/rest/server.php",
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
wstoken: process.env.MOODLE_TOKEN,
|
||||||
|
wsfunction: "core_completion_get_course_completion_status",
|
||||||
|
moodlewsrestformat: "json",
|
||||||
|
userid: userId,
|
||||||
|
courseid: courseId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return completionStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enrollUserInCourse = async (
|
||||||
|
courseid: number | string,
|
||||||
|
userid: number | string,
|
||||||
|
) => {
|
||||||
|
const { data: enrollmentResponse } = await axios.get(
|
||||||
|
"https://moodle.virtualairrescue.com/webservice/rest/server.php",
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
wstoken: process.env.MOODLE_TOKEN,
|
||||||
|
wsfunction: "enrol_manual_enrol_users",
|
||||||
|
moodlewsrestformat: "json",
|
||||||
|
enrolments: [
|
||||||
|
{
|
||||||
|
roleid: 5,
|
||||||
|
userid,
|
||||||
|
courseid,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return enrollmentResponse;
|
||||||
|
};
|
||||||
5
apps/hub-server/nodemon.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"watch": ["."],
|
||||||
|
"ext": "ts",
|
||||||
|
"exec": "tsx index.ts"
|
||||||
|
}
|
||||||
17
apps/hub-server/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon",
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/db": "*",
|
||||||
|
"@repo/typescript-config": "*",
|
||||||
|
"@types/node": "^22.13.5",
|
||||||
|
"concurrently": "^9.1.2",
|
||||||
|
"typescript": "latest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"cron": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
apps/hub-server/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/typescript-config/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"allowImportingTsExtensions": false,
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "./index.ts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
@@ -1,26 +1,13 @@
|
|||||||
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 ({ 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: {
|
||||||
id: parseInt(id),
|
id: parseInt(id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const users = await prisma.user.findMany({
|
if (!event) return <div>Event not found</div>;
|
||||||
select: {
|
return <Form event={event} />;
|
||||||
id: true,
|
|
||||||
firstname: true,
|
|
||||||
lastname: true,
|
|
||||||
publicId: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const appointments = await prisma.eventAppointment.findMany({
|
|
||||||
where: {
|
|
||||||
eventId: parseInt(id),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!event) return <div>Event not found</div>;
|
|
||||||
return <Form event={event} users={users} appointments={appointments} />;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,277 +1,264 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import {
|
import {
|
||||||
EventAppointmentOptionalDefaults,
|
EventAppointmentOptionalDefaults,
|
||||||
EventAppointmentOptionalDefaultsSchema,
|
EventAppointmentOptionalDefaultsSchema,
|
||||||
EventOptionalDefaults,
|
EventOptionalDefaults,
|
||||||
EventOptionalDefaultsSchema,
|
EventOptionalDefaultsSchema,
|
||||||
ParticipantOptionalDefaultsSchema,
|
ParticipantOptionalDefaultsSchema,
|
||||||
} from '@repo/db/zod';
|
} from "@repo/db/zod";
|
||||||
import { set, useForm } from 'react-hook-form';
|
import { set, useForm } from "react-hook-form";
|
||||||
import { BADGES, Event, EventAppointment, User } from '@repo/db';
|
import { BADGES, Event, EventAppointment, User } from "@repo/db";
|
||||||
import { Bot, Calendar, FileText, UserIcon } from 'lucide-react';
|
import { Bot, Calendar, FileText, UserIcon } from "lucide-react";
|
||||||
import { Input } from '../../../../_components/ui/Input';
|
import { Input } from "../../../../_components/ui/Input";
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from "react";
|
||||||
import { deleteEvent, upsertAppointment, upsertEvent } from '../action';
|
import { deleteEvent, upsertAppointment, upsertEvent } from "../action";
|
||||||
import { Button } from '../../../../_components/ui/Button';
|
import { Button } from "../../../../_components/ui/Button";
|
||||||
import { redirect, useRouter } from 'next/navigation';
|
import { redirect, useRouter } from "next/navigation";
|
||||||
import { Switch } from '../../../../_components/ui/Switch';
|
import { Switch } from "../../../../_components/ui/Switch";
|
||||||
import {
|
import {
|
||||||
PaginatedTable,
|
PaginatedTable,
|
||||||
PaginatedTableRef,
|
PaginatedTableRef,
|
||||||
} from '../../../../_components/PaginatedTable';
|
} from "../../../../_components/PaginatedTable";
|
||||||
import { Select } from '../../../../_components/ui/Select';
|
import { Select } from "../../../../_components/ui/Select";
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from "next-auth/react";
|
||||||
import { MarkdownEditor } from '../../../../_components/ui/MDEditor';
|
import { MarkdownEditor } from "../../../../_components/ui/MDEditor";
|
||||||
|
|
||||||
export const Form = ({
|
export const Form = ({ event }: { event?: Event }) => {
|
||||||
event,
|
const { data: session } = useSession();
|
||||||
users,
|
const form = useForm({
|
||||||
appointments = [],
|
resolver: zodResolver(EventOptionalDefaultsSchema),
|
||||||
}: {
|
defaultValues: event,
|
||||||
event?: Event;
|
});
|
||||||
users: {
|
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
||||||
id: string;
|
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
||||||
firstname: string;
|
defaultValues: {
|
||||||
lastname: string;
|
eventId: event?.id,
|
||||||
publicId: string;
|
presenterId: session?.user?.id,
|
||||||
}[];
|
},
|
||||||
appointments?: EventAppointment[];
|
});
|
||||||
}) => {
|
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
||||||
const { data: session } = useSession();
|
const [loading, setLoading] = useState(false);
|
||||||
const form = useForm({
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
resolver: zodResolver(EventOptionalDefaultsSchema),
|
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
||||||
defaultValues: event,
|
return (
|
||||||
});
|
<>
|
||||||
const appointmentForm = useForm<EventAppointmentOptionalDefaults>({
|
<dialog ref={addParticipantModal} className="modal">
|
||||||
resolver: zodResolver(EventAppointmentOptionalDefaultsSchema),
|
<div className="modal-box">
|
||||||
defaultValues: {
|
<form method="dialog">
|
||||||
eventId: event?.id,
|
{/* if there is a button in form, it will close the modal */}
|
||||||
presenterId: session?.user?.id,
|
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
|
||||||
},
|
✕
|
||||||
});
|
</button>
|
||||||
const appointmentsTableRef = useRef<PaginatedTableRef>(null);
|
</form>
|
||||||
const [loading, setLoading] = useState(false);
|
<h3 className="font-bold text-lg">Termin hinzufügen</h3>
|
||||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
<form
|
||||||
const addParticipantModal = useRef<HTMLDialogElement>(null);
|
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
||||||
return (
|
if (!event) return;
|
||||||
<>
|
const createdAppointment = await upsertAppointment({
|
||||||
<dialog ref={addParticipantModal} className="modal">
|
appointmentDate: values.appointmentDate,
|
||||||
<div className="modal-box">
|
eventId: event?.id,
|
||||||
<form method="dialog">
|
presenterId: values.presenterId,
|
||||||
{/* if there is a button in form, it will close the modal */}
|
});
|
||||||
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
|
console.log(createdAppointment);
|
||||||
✕
|
addParticipantModal.current?.close();
|
||||||
</button>
|
appointmentsTableRef.current?.refresh();
|
||||||
</form>
|
})}
|
||||||
<h3 className="font-bold text-lg">Termin hinzufügen</h3>
|
className="flex flex-col"
|
||||||
<form
|
>
|
||||||
onSubmit={appointmentForm.handleSubmit(async (values) => {
|
<Input
|
||||||
if (!event) return;
|
form={appointmentForm}
|
||||||
const createdAppointment = await upsertAppointment({
|
label="Datum"
|
||||||
appointmentDate: values.appointmentDate,
|
name="appointmentDate"
|
||||||
eventId: event?.id,
|
type="date"
|
||||||
presenterId: values.presenterId,
|
/>
|
||||||
});
|
<div className="modal-action">
|
||||||
console.log(createdAppointment);
|
<Button type="submit" className="btn btn-primary">
|
||||||
addParticipantModal.current?.close();
|
Hinzufügen
|
||||||
appointmentsTableRef.current?.refresh();
|
</Button>
|
||||||
})}
|
</div>
|
||||||
className="flex flex-col"
|
</form>
|
||||||
>
|
</div>
|
||||||
<Input
|
</dialog>
|
||||||
form={appointmentForm}
|
<form
|
||||||
label="Datum"
|
onSubmit={form.handleSubmit(async (values) => {
|
||||||
name="appointmentDate"
|
setLoading(true);
|
||||||
type="date"
|
const createdEvent = await upsertEvent(values, event?.id);
|
||||||
/>
|
setLoading(false);
|
||||||
<div className="modal-action">
|
if (!event) redirect(`/admin/event`);
|
||||||
<Button type="submit" className="btn btn-primary">
|
})}
|
||||||
Hinzufügen
|
className="grid grid-cols-6 gap-3"
|
||||||
</Button>
|
>
|
||||||
</div>
|
<div className="card bg-base-200 shadow-xl col-span-3 max-xl:col-span-6">
|
||||||
</form>
|
<div className="card-body">
|
||||||
</div>
|
<h2 className="card-title">
|
||||||
</dialog>
|
<FileText className="w-5 h-5" /> Allgemeines
|
||||||
<form
|
</h2>
|
||||||
onSubmit={form.handleSubmit(async (values) => {
|
<Input form={form} label="Name" name="name" className="input-sm" />
|
||||||
setLoading(true);
|
<MarkdownEditor form={form} name="description" />
|
||||||
const createdEvent = await upsertEvent(values, event?.id);
|
<Input
|
||||||
setLoading(false);
|
form={form}
|
||||||
if (!event) redirect(`/admin/event`);
|
label="Maximale Teilnehmer (Nur für live Events)"
|
||||||
})}
|
className="input-sm"
|
||||||
className="grid grid-cols-6 gap-3"
|
{...form.register("maxParticipants", {
|
||||||
>
|
valueAsNumber: true,
|
||||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
})}
|
||||||
<div className="card-body">
|
/>
|
||||||
<h2 className="card-title">
|
<Switch form={form} name="hidden" label="Versteckt" />
|
||||||
<FileText className="w-5 h-5" /> Allgemeines
|
</div>
|
||||||
</h2>
|
</div>
|
||||||
<Input form={form} label="Name" name="name" className="input-sm" />
|
<div className="card bg-base-200 shadow-xl col-span-3 max-xl:col-span-6">
|
||||||
<MarkdownEditor form={form} name="description" />
|
<div className="card-body">
|
||||||
<Input
|
<h2 className="card-title">
|
||||||
form={form}
|
<Bot className="w-5 h-5" /> Automation
|
||||||
label="Maximale Teilnehmer (Nur für live Events)"
|
</h2>
|
||||||
className="input-sm"
|
<Input
|
||||||
{...form.register('maxParticipants', {
|
form={form}
|
||||||
valueAsNumber: true,
|
name="starterMoodleCourseId"
|
||||||
})}
|
label="Moodle Anmelde Kurs ID"
|
||||||
/>
|
className="input-sm"
|
||||||
<Switch form={form} name="hidden" label="Versteckt" />
|
/>
|
||||||
</div>
|
<Input
|
||||||
</div>
|
name="finisherMoodleCourseId"
|
||||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
form={form}
|
||||||
<div className="card-body">
|
label="Moodle Abschluss Kurs ID"
|
||||||
<h2 className="card-title">
|
className="input-sm"
|
||||||
<Bot className="w-5 h-5" /> Automation
|
/>
|
||||||
</h2>
|
<Select
|
||||||
<Input
|
isMulti
|
||||||
form={form}
|
form={form}
|
||||||
name="starterMoodleCourseId"
|
name="finishedBadges"
|
||||||
label="Moodle Anmelde Kurs ID"
|
label="Badges bei Abschluss"
|
||||||
className="input-sm"
|
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||||
/>
|
label: value,
|
||||||
<Input
|
value: key,
|
||||||
name="finisherMoodleCourseId"
|
}))}
|
||||||
form={form}
|
/>
|
||||||
label="Moodle Abschluss Kurs ID"
|
<Select
|
||||||
className="input-sm"
|
isMulti
|
||||||
/>
|
form={form}
|
||||||
<Select
|
name="requiredBadges"
|
||||||
isMulti
|
label="Benötigte Badges"
|
||||||
form={form}
|
options={Object.entries(BADGES).map(([key, value]) => ({
|
||||||
name="finishedBadges"
|
label: value,
|
||||||
label="Badges bei Abschluss"
|
value: key,
|
||||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
}))}
|
||||||
label: value,
|
/>
|
||||||
value: key,
|
<Switch
|
||||||
}))}
|
form={form}
|
||||||
/>
|
name="hasPresenceEvents"
|
||||||
<Select
|
label="Hat Live Event"
|
||||||
isMulti
|
/>
|
||||||
form={form}
|
</div>
|
||||||
name="requiredBadges"
|
</div>
|
||||||
label="Benötigte Badges"
|
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||||
options={Object.entries(BADGES).map(([key, value]) => ({
|
<div className="card-body">
|
||||||
label: value,
|
<div className="flex justify-between">
|
||||||
value: key,
|
<h2 className="card-title">
|
||||||
}))}
|
<Calendar className="w-5 h-5" /> Termine
|
||||||
/>
|
</h2>
|
||||||
<Switch
|
{event && (
|
||||||
form={form}
|
<button
|
||||||
name="hasPresenceEvents"
|
className="btn btn-primary btn-outline"
|
||||||
label="Hat Live Event"
|
onClick={() => addParticipantModal.current?.showModal()}
|
||||||
/>
|
>
|
||||||
</div>
|
Hinzufügen
|
||||||
</div>
|
</button>
|
||||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
)}
|
||||||
<div className="card-body">
|
</div>
|
||||||
<div className="flex justify-between">
|
|
||||||
<h2 className="card-title">
|
|
||||||
<Calendar className="w-5 h-5" /> Termine
|
|
||||||
</h2>
|
|
||||||
{event && (
|
|
||||||
<button
|
|
||||||
className="btn btn-primary btn-outline"
|
|
||||||
onClick={() => addParticipantModal.current?.showModal()}
|
|
||||||
>
|
|
||||||
Hinzufügen
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
ref={appointmentsTableRef}
|
ref={appointmentsTableRef}
|
||||||
prismaModel={'eventAppointment'}
|
prismaModel={"eventAppointment"}
|
||||||
filter={{
|
filter={{
|
||||||
eventId: event?.id,
|
eventId: event?.id,
|
||||||
}}
|
}}
|
||||||
include={{
|
include={{
|
||||||
Presenter: true,
|
Presenter: true,
|
||||||
Participants: true,
|
Participants: true,
|
||||||
}}
|
}}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
header: 'Datum',
|
header: "Datum",
|
||||||
accessorKey: 'appointmentDate',
|
accessorKey: "appointmentDate",
|
||||||
accessorFn: (date) =>
|
accessorFn: (date) =>
|
||||||
new Date(date.appointmentDate).toLocaleDateString(),
|
new Date(date.appointmentDate).toLocaleDateString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Presenter',
|
header: "Presenter",
|
||||||
accessorKey: 'presenter',
|
accessorKey: "presenter",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
{(row.original as any).Presenter.firstname}{' '}
|
{(row.original as any).Presenter.firstname}{" "}
|
||||||
{(row.original as any).Presenter.lastname}
|
{(row.original as any).Presenter.lastname}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Teilnehmer',
|
header: "Teilnehmer",
|
||||||
accessorKey: 'Participants',
|
accessorKey: "Participants",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<UserIcon className="w-5 h-5" />
|
<UserIcon className="w-5 h-5" />
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
{row.original.Participants.length}
|
{row.original.Participants.length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Aktionen',
|
header: "Aktionen",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onSubmit={() => false}
|
onSubmit={() => false}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
console.log(row.original);
|
console.log(row.original);
|
||||||
// TODO: open modal to edit appointment
|
// TODO: open modal to edit appointment
|
||||||
}}
|
}}
|
||||||
className="btn btn-sm btn-outline"
|
className="btn btn-sm btn-outline"
|
||||||
>
|
>
|
||||||
Bearbeiten
|
Bearbeiten
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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}
|
isLoading={loading}
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary flex-1"
|
className="btn btn-primary flex-1"
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
{event && (
|
{event && (
|
||||||
<Button
|
<Button
|
||||||
isLoading={deleteLoading}
|
isLoading={deleteLoading}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setDeleteLoading(true);
|
setDeleteLoading(true);
|
||||||
await deleteEvent(event.id);
|
await deleteEvent(event.id);
|
||||||
redirect('/admin/event');
|
redirect("/admin/event");
|
||||||
}}
|
}}
|
||||||
className="btn btn-error"
|
className="btn btn-error"
|
||||||
>
|
>
|
||||||
Löschen
|
Löschen
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import { prisma } from '@repo/db';
|
import { prisma } from "@repo/db";
|
||||||
import { Form } from '../_components/Form';
|
import { Form } from "../_components/Form";
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
const users = await prisma.user.findMany({
|
return <Form />;
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
firstname: true,
|
|
||||||
lastname: true,
|
|
||||||
publicId: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return <Form users={users} />;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,107 +1,107 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { DrawingPinFilledIcon, EnterIcon } from '@radix-ui/react-icons';
|
import { DrawingPinFilledIcon, EnterIcon } from "@radix-ui/react-icons";
|
||||||
import { Event, User } from '@repo/db';
|
import { Event, User } from "@repo/db";
|
||||||
import ModalBtn from './modalBtn';
|
import ModalBtn from "./modalBtn";
|
||||||
import MDEditor from '@uiw/react-md-editor';
|
import MDEditor from "@uiw/react-md-editor";
|
||||||
|
|
||||||
export const KursItem = ({ user, event }: { user: User; event: Event }) => {
|
export const KursItem = ({ user, event }: { user: User; event: Event }) => {
|
||||||
return (
|
return (
|
||||||
<div className="col-span-full">
|
<div className="col-span-full">
|
||||||
<div className="card bg-base-200 shadow-xl mb-4">
|
<div className="card bg-base-200 shadow-xl mb-4">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h2 className="card-title">{event.name}</h2>
|
<h2 className="card-title">{event.name}</h2>
|
||||||
<div className="absolute top-0 right-0 m-4">
|
<div className="absolute top-0 right-0 m-4">
|
||||||
<span className="badge badge-info badge-outline">
|
<span className="badge badge-info badge-outline">
|
||||||
Zusatzqualifikation
|
Zusatzqualifikation
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-6 gap-4">
|
<div className="grid grid-cols-6 gap-4">
|
||||||
<div className="col-span-4">
|
<div className="col-span-4">
|
||||||
<div className="text-left text-balance">
|
<div className="text-left text-balance">
|
||||||
<MDEditor.Markdown
|
<MDEditor.Markdown
|
||||||
source={event.description}
|
source={event.description}
|
||||||
className="whitespace-pre-wrap"
|
className="whitespace-pre-wrap"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: "transparent",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2">{event.finishedBadges}</div>
|
<div className="col-span-2">{event.finishedBadges}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-actions flex justify-between items-center mt-5">
|
<div className="card-actions flex justify-between items-center mt-5">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600 text-left flex items-center gap-2">
|
<p className="text-gray-600 text-left flex items-center gap-2">
|
||||||
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen: </b>
|
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen: </b>
|
||||||
{(!event.starterMoodleCourseId ||
|
{(!event.starterMoodleCourseId ||
|
||||||
!event.requiredBadges.length) &&
|
!event.requiredBadges.length) &&
|
||||||
'Keine'}
|
"Keine"}
|
||||||
{event.starterMoodleCourseId && (
|
{event.starterMoodleCourseId && (
|
||||||
<a className="link link-info" href="">
|
<a className="link link-info" href="">
|
||||||
Moodle Kurs {event.starterMoodleCourseId}
|
Moodle Kurs {event.starterMoodleCourseId}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{!!event.requiredBadges.length && (
|
{!!event.requiredBadges.length && (
|
||||||
<div className="flex ml-6">
|
<div className="flex ml-6">
|
||||||
<b className="text-gray-600 text-left">Abzeichen:</b>
|
<b className="text-gray-600 text-left">Abzeichen:</b>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{event.requiredBadges.map((badge) => (
|
{event.requiredBadges.map((badge) => (
|
||||||
<div className="badge badge-secondary badge-outline">
|
<div className="badge badge-secondary badge-outline">
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ModalBtn
|
<ModalBtn
|
||||||
title={event.name}
|
title={event.name}
|
||||||
dates={['Dienstag, 25 Februar 2025', 'Mittwoch, 26 Februar 2025']}
|
dates={["Dienstag, 25 Februar 2025", "Mittwoch, 26 Februar 2025"]}
|
||||||
modalId={`${event.name}_modal.${event.id}`}
|
modalId={`${event.name}_modal.${event.id}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PilotKurs = ({ user }: { user: User }) => {
|
export const PilotKurs = ({ user }: { user: User }) => {
|
||||||
{
|
{
|
||||||
/* STATISCH, DA FÜR ALLE NEUEN MITGLIEDER MANDATORY, WIRD AUSGEBLENDET WENN ABSOLVIERT */
|
/* STATISCH, DA FÜR ALLE NEUEN MITGLIEDER MANDATORY, WIRD AUSGEBLENDET WENN ABSOLVIERT */
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="col-span-full">
|
<div className="col-span-full">
|
||||||
<div className="card card-bordered border-secondary bg-base-200 shadow-xl mb-4">
|
<div className="card card-bordered border-secondary bg-base-200 shadow-xl mb-4">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h2 className="card-title">Einsteigerkurs für Piloten</h2>
|
<h2 className="card-title">Einsteigerkurs für Piloten</h2>
|
||||||
<div className="absolute top-0 right-0 m-4">
|
<div className="absolute top-0 right-0 m-4">
|
||||||
<span className="badge badge-secondary badge-outline">
|
<span className="badge badge-secondary badge-outline">
|
||||||
Verpflichtend
|
Verpflichtend
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-6 gap-4">
|
<div className="grid grid-cols-6 gap-4">
|
||||||
<div className="col-span-4">
|
<div className="col-span-4">
|
||||||
<p className="text-left text-balance">
|
<p className="text-left text-balance">
|
||||||
In diesem Kurs lernen Piloten die Grundlagen der Luftrettung,
|
In diesem Kurs lernen Piloten die Grundlagen der Luftrettung,
|
||||||
Einsatzverfahren, den Umgang mit dem BOS-Funk und einige
|
Einsatzverfahren, den Umgang mit dem BOS-Funk und einige
|
||||||
medizinische Basics. Der Kurs bietet eine ideale Vorbereitung
|
medizinische Basics. Der Kurs bietet eine ideale Vorbereitung
|
||||||
für alle Standard Operations bei Virtual Air Rescue.
|
für alle Standard Operations bei Virtual Air Rescue.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2">Badge</div>
|
<div className="col-span-2">Badge</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-actions flex justify-between items-center mt-5">
|
<div className="card-actions flex justify-between items-center mt-5">
|
||||||
<p className="text-gray-600 text-left flex items-center gap-2">
|
<p className="text-gray-600 text-left flex items-center gap-2">
|
||||||
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen:</b> Keine
|
<DrawingPinFilledIcon /> <b>Teilnahmevoraussetzungen:</b> Keine
|
||||||
</p>
|
</p>
|
||||||
<button className="btn btn-outline btn-secondary btn-wide">
|
<button className="btn btn-outline btn-secondary btn-wide">
|
||||||
<EnterIcon /> Zum Moodle Kurs
|
<EnterIcon /> Zum Moodle Kurs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import {
|
import {
|
||||||
DiscordLogoIcon,
|
DiscordLogoIcon,
|
||||||
InstagramLogoIcon,
|
InstagramLogoIcon,
|
||||||
ReaderIcon,
|
ReaderIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import { HorizontalNav, VerticalNav } from "../_components/Nav";
|
import { HorizontalNav, VerticalNav } from "../_components/Nav";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
@@ -11,95 +11,95 @@ import { getServerSession } from "../api/auth/[...nextauth]/auth";
|
|||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Create Next App",
|
||||||
description: "Generated by create next app",
|
description: "Generated by create next app",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
|
|
||||||
if (!session) redirect(`/login`);
|
if (!session) redirect(`/login`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="hero min-h-screen"
|
className="hero min-h-screen"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: "url('/bg.png')",
|
backgroundImage: "url('/bg.png')",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="hero-overlay bg-opacity-30"></div>
|
<div className="hero-overlay bg-opacity-30"></div>
|
||||||
<div>
|
<div>
|
||||||
<Toaster position="top-center" reverseOrder={false} />
|
<Toaster position="top-center" reverseOrder={false} />
|
||||||
</div>
|
</div>
|
||||||
{/* Card */}
|
{/* Card */}
|
||||||
<div className="hero-content text-neutral-content text-center w-full max-w-full h-full m-10">
|
<div className="hero-content text-neutral-content text-center w-full max-w-full h-full m-10">
|
||||||
<div className="card bg-base-100 shadow-2xl w-full min-h-full h-full max-h-[calc(100vh-13rem)] p-4 flex flex-col mr-24 ml-24">
|
<div className="card bg-base-100 shadow-2xl w-full min-h-full h-full max-h-[calc(100vh-13rem)] p-4 flex flex-col mr-24 ml-24">
|
||||||
{/* Top Navbar */}
|
{/* Top Navbar */}
|
||||||
<HorizontalNav />
|
<HorizontalNav />
|
||||||
|
|
||||||
{/* Hauptlayout: Sidebar + Content (nimmt Resthöhe ein) */}
|
{/* Hauptlayout: Sidebar + Content (nimmt Resthöhe ein) */}
|
||||||
<div className="flex grow overflow-hidden">
|
<div className="flex grow overflow-hidden">
|
||||||
{/* Linke Sidebar */}
|
{/* Linke Sidebar */}
|
||||||
<VerticalNav />
|
<VerticalNav />
|
||||||
|
|
||||||
{/* Scrollbarer Content-Bereich */}
|
{/* Scrollbarer Content-Bereich */}
|
||||||
<div className="flex-grow bg-base-100 p-6 rounded-lg shadow-md ml-4 overflow-auto h-full max-w-full w-full">
|
<div className="flex-grow bg-base-100 p-6 rounded-lg shadow-md ml-4 overflow-auto h-full max-w-full w-full">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="footer flex justify-between items-center p-4 bg-base-200 mt-4 rounded-lg shadow-md">
|
<footer className="footer flex justify-between items-center p-4 bg-base-200 mt-4 rounded-lg shadow-md">
|
||||||
{/* Left: Impressum & Datenschutz */}
|
{/* Left: Impressum & Datenschutz */}
|
||||||
<div className="flex gap-4 text-sm">
|
<div className="flex gap-4 text-sm">
|
||||||
<a href="/impressum" className="hover:text-primary">
|
<a href="/impressum" className="hover:text-primary">
|
||||||
Impressum
|
Impressum
|
||||||
</a>
|
</a>
|
||||||
<a href="/datenschutz" className="hover:text-primary">
|
<a href="/datenschutz" className="hover:text-primary">
|
||||||
Datenschutzerklärung
|
Datenschutzerklärung
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Center: Copyright */}
|
{/* Center: Copyright */}
|
||||||
<p className="text-sm">
|
<p className="text-sm">
|
||||||
Copyright © {new Date().getFullYear()} - Virtual Air Rescue
|
Copyright © {new Date().getFullYear()} - Virtual Air Rescue
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Right: Social Icons */}
|
{/* Right: Social Icons */}
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div className="tooltip tooltip-top" data-tip="Discord">
|
<div className="tooltip tooltip-top" data-tip="Discord">
|
||||||
<a
|
<a
|
||||||
href="https://discord.com"
|
href="https://discord.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="hover:text-primary"
|
className="hover:text-primary"
|
||||||
>
|
>
|
||||||
<DiscordLogoIcon className="w-5 h-5" />
|
<DiscordLogoIcon className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="tooltip tooltip-top" data-tip="Instagram">
|
<div className="tooltip tooltip-top" data-tip="Instagram">
|
||||||
<a
|
<a
|
||||||
href="https://instagram.com"
|
href="https://instagram.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="hover:text-primary"
|
className="hover:text-primary"
|
||||||
>
|
>
|
||||||
<InstagramLogoIcon className="w-5 h-5" />
|
<InstagramLogoIcon className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="tooltip tooltip-top" data-tip="Knowledgebase">
|
<div className="tooltip tooltip-top" data-tip="Knowledgebase">
|
||||||
<a href="/docs" className="hover:text-primary">
|
<a href="/docs" className="hover:text-primary">
|
||||||
<ReaderIcon className="w-5 h-5" />
|
<ReaderIcon className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,79 +3,79 @@ import { PaginatedTable } from "../_components/PaginatedTable";
|
|||||||
import { Header } from "../_components/ui/Header";
|
import { Header } from "../_components/ui/Header";
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header />
|
<Header />
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
rowsPerPage={10}
|
rowsPerPage={10}
|
||||||
prismaModel={"user"}
|
prismaModel={"user"}
|
||||||
searchFields={[]}
|
searchFields={[]}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
header: "ID",
|
header: "ID",
|
||||||
accessorKey: "id",
|
accessorKey: "id",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Email",
|
header: "Email",
|
||||||
accessorKey: "email",
|
accessorKey: "email",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "First Name",
|
header: "First Name",
|
||||||
accessorKey: "firstname",
|
accessorKey: "firstname",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "Last Name",
|
header: "Last Name",
|
||||||
accessorKey: "lastname",
|
accessorKey: "lastname",
|
||||||
footer: "Total",
|
footer: "Total",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
Map
|
Map
|
||||||
<br />
|
<br />
|
||||||
Logbuch
|
Logbuch
|
||||||
<br />
|
<br />
|
||||||
Einlog-Zeit (7 Tage, total)
|
Einlog-Zeit (7 Tage, total)
|
||||||
<br />
|
<br />
|
||||||
Stats
|
Stats
|
||||||
<br />
|
<br />
|
||||||
Badges
|
Badges
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
gd
|
gd
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { NextPage } from 'next';
|
import { NextPage } from "next";
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
const AuthLayout: NextPage<
|
const AuthLayout: NextPage<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>
|
}>
|
||||||
> = ({ children }) => (
|
> = ({ children }) => (
|
||||||
<div
|
<div
|
||||||
className="hero min-h-screen"
|
className="hero min-h-screen"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'url(https://img.daisyui.com/images/stock/photo-1507358522600-9f71e620c44e.webp)',
|
"url(https://img.daisyui.com/images/stock/photo-1507358522600-9f71e620c44e.webp)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="hero-overlay bg-neutral/60"></div>
|
<div className="hero-overlay bg-neutral/60"></div>
|
||||||
<div className="hero-content text-center ">
|
<div className="hero-content text-center ">
|
||||||
<div className="max-w-lg">
|
<div className="max-w-lg">
|
||||||
<div className="card rounded-2xl bg-base-100 w-full min-w-[500px] shadow-2xl max-md:min-w-[400px]">
|
<div className="card rounded-2xl bg-base-100 w-full min-w-[500px] shadow-2xl max-md:min-w-[400px]">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default AuthLayout;
|
export default AuthLayout;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { SessionProvider } from 'next-auth/react';
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { Session } from 'next-auth';
|
import { Session } from "next-auth";
|
||||||
|
|
||||||
export const NextAuthSessionProvider = ({
|
export const NextAuthSessionProvider = ({
|
||||||
children,
|
children,
|
||||||
session,
|
session,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
session: Session | null;
|
session: Session | null;
|
||||||
}) => <SessionProvider session={session}>{children}</SessionProvider>;
|
}) => <SessionProvider session={session}>{children}</SessionProvider>;
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
PersonIcon,
|
PersonIcon,
|
||||||
GearIcon,
|
GearIcon,
|
||||||
ExitIcon,
|
ExitIcon,
|
||||||
LockClosedIcon,
|
LockClosedIcon,
|
||||||
RocketIcon,
|
RocketIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export const VerticalNav = () => {
|
export const VerticalNav = () => {
|
||||||
return (
|
return (
|
||||||
<ul className="menu w-64 bg-base-300 p-4 rounded-lg shadow-md">
|
<ul className="menu w-64 bg-base-300 p-4 rounded-lg shadow-md">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<HomeIcon /> Dashboard
|
<HomeIcon /> Dashboard
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/profile">
|
<Link href="/profile">
|
||||||
<PersonIcon /> Profile
|
<PersonIcon /> Profile
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/events">
|
<Link href="/events">
|
||||||
<RocketIcon />
|
<RocketIcon />
|
||||||
Events & Kurse
|
Events & Kurse
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<details open>
|
<details open>
|
||||||
<summary>
|
<summary>
|
||||||
<LockClosedIcon />
|
<LockClosedIcon />
|
||||||
Admin
|
Admin
|
||||||
</summary>
|
</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/admin/user">Benutzer</Link>
|
<Link href="/admin/user">Benutzer</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/admin/station">Stationen</Link>
|
<Link href="/admin/station">Stationen</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/admin/event">Events</Link>
|
<Link href="/admin/event">Events</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/settings">
|
<Link href="/settings">
|
||||||
<GearIcon />
|
<GearIcon />
|
||||||
Einstellungen
|
Einstellungen
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HorizontalNav = () => (
|
export const HorizontalNav = () => (
|
||||||
<div className="navbar bg-base-200 shadow-md rounded-lg mb-4">
|
<div className="navbar bg-base-200 shadow-md rounded-lg mb-4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<a className="btn btn-ghost normal-case text-xl">
|
<a className="btn btn-ghost normal-case text-xl">
|
||||||
Virtual Air Rescue - HUB
|
Virtual Air Rescue - HUB
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center ml-auto">
|
<div className="flex items-center ml-auto">
|
||||||
<ul className="flex space-x-2 px-1">
|
<ul className="flex space-x-2 px-1">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<button className="btn btn-sm btn-outline btn-primary">
|
<button className="btn btn-sm btn-outline btn-primary">
|
||||||
Zur Leitstelle
|
Zur Leitstelle
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/logout">
|
<Link href="/logout">
|
||||||
<button className="btn btn-sm btn-ghost">
|
<button className="btn btn-sm btn-ghost">
|
||||||
<ExitIcon /> Logout
|
<ExitIcon /> Logout
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,122 +1,122 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import {
|
import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
Ref,
|
Ref,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
} from 'react';
|
} from "react";
|
||||||
import SortableTable, { Pagination, SortableTableProps } from './Table';
|
import SortableTable, { Pagination, SortableTableProps } from "./Table";
|
||||||
import { PrismaClient } from '@repo/db';
|
import { PrismaClient } from "@repo/db";
|
||||||
import { getData } from './pagiantedTableActions';
|
import { getData } from "./pagiantedTableActions";
|
||||||
|
|
||||||
export interface PaginatedTableRef {
|
export interface PaginatedTableRef {
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PaginatedTableProps<TData>
|
interface PaginatedTableProps<TData>
|
||||||
extends Omit<SortableTableProps<TData>, 'data'> {
|
extends Omit<SortableTableProps<TData>, "data"> {
|
||||||
prismaModel: keyof PrismaClient;
|
prismaModel: keyof PrismaClient;
|
||||||
filter?: Record<string, any>;
|
filter?: Record<string, any>;
|
||||||
rowsPerPage?: number;
|
rowsPerPage?: number;
|
||||||
showEditButton?: boolean;
|
showEditButton?: boolean;
|
||||||
searchFields?: string[];
|
searchFields?: string[];
|
||||||
include?: Record<string, boolean>;
|
include?: Record<string, boolean>;
|
||||||
leftOfSearch?: React.ReactNode;
|
leftOfSearch?: React.ReactNode;
|
||||||
rightOfSearch?: React.ReactNode;
|
rightOfSearch?: React.ReactNode;
|
||||||
ref?: Ref<PaginatedTableRef>;
|
ref?: Ref<PaginatedTableRef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PaginatedTable<TData>({
|
export function PaginatedTable<TData>({
|
||||||
prismaModel,
|
prismaModel,
|
||||||
rowsPerPage = 10,
|
rowsPerPage = 10,
|
||||||
showEditButton = false,
|
showEditButton = false,
|
||||||
searchFields = [],
|
searchFields = [],
|
||||||
filter,
|
filter,
|
||||||
include,
|
include,
|
||||||
ref,
|
ref,
|
||||||
leftOfSearch,
|
leftOfSearch,
|
||||||
rightOfSearch,
|
rightOfSearch,
|
||||||
...restProps
|
...restProps
|
||||||
}: PaginatedTableProps<TData>) {
|
}: PaginatedTableProps<TData>) {
|
||||||
const [data, setData] = useState<TData[]>([]);
|
const [data, setData] = useState<TData[]>([]);
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
|
||||||
|
|
||||||
const RefreshTableData = async () => {
|
const RefreshTableData = async () => {
|
||||||
getData(
|
getData(
|
||||||
prismaModel,
|
prismaModel,
|
||||||
rowsPerPage,
|
rowsPerPage,
|
||||||
page * rowsPerPage,
|
page * rowsPerPage,
|
||||||
debouncedSearchTerm,
|
debouncedSearchTerm,
|
||||||
searchFields,
|
searchFields,
|
||||||
filter,
|
filter,
|
||||||
include
|
include,
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
setData(result.data);
|
setData(result.data);
|
||||||
setTotal(result.total);
|
setTotal(result.total);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
refresh: () => {
|
refresh: () => {
|
||||||
console.log('refresh');
|
console.log("refresh");
|
||||||
RefreshTableData();
|
RefreshTableData();
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const debounce = (func: Function, delay: number) => {
|
const debounce = (func: Function, delay: number) => {
|
||||||
let timer: NodeJS.Timeout;
|
let timer: NodeJS.Timeout;
|
||||||
return (...args: any[]) => {
|
return (...args: any[]) => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = setTimeout(() => func(...args), delay);
|
timer = setTimeout(() => func(...args), delay);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearchChange = useCallback(
|
const handleSearchChange = useCallback(
|
||||||
debounce((value: string) => {
|
debounce((value: string) => {
|
||||||
setDebouncedSearchTerm(value);
|
setDebouncedSearchTerm(value);
|
||||||
}, 500),
|
}, 500),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
RefreshTableData();
|
RefreshTableData();
|
||||||
}, [page, debouncedSearchTerm]);
|
}, [page, debouncedSearchTerm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 m-4">
|
<div className="space-y-4 m-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex-1">{leftOfSearch}</div>
|
<div className="flex-1">{leftOfSearch}</div>
|
||||||
{searchFields.length > 0 && (
|
{searchFields.length > 0 && (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Suchen..."
|
placeholder="Suchen..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSearchTerm(e.target.value);
|
setSearchTerm(e.target.value);
|
||||||
handleSearchChange(e.target.value);
|
handleSearchChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
className="input input-bordered w-full max-w-xs justify-end"
|
className="input input-bordered w-full max-w-xs justify-end"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="flex justify-center">{rightOfSearch}</div>
|
<div className="flex justify-center">{rightOfSearch}</div>
|
||||||
</div>
|
</div>
|
||||||
<SortableTable
|
<SortableTable
|
||||||
data={data}
|
data={data}
|
||||||
prismaModel={prismaModel}
|
prismaModel={prismaModel}
|
||||||
showEditButton={showEditButton}
|
showEditButton={showEditButton}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
<Pagination
|
<Pagination
|
||||||
totalPages={Math.ceil(total / rowsPerPage)}
|
totalPages={Math.ceil(total / rowsPerPage)}
|
||||||
page={page}
|
page={page}
|
||||||
setPage={setPage}
|
setPage={setPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,138 +1,138 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { useState } from 'react';
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
useReactTable,
|
useReactTable,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
SortingState,
|
SortingState,
|
||||||
flexRender,
|
flexRender,
|
||||||
} from '@tanstack/react-table';
|
} from "@tanstack/react-table";
|
||||||
import { ArrowLeft, ArrowRight, ChevronDown, ChevronUp } from 'lucide-react'; // Icons for sorting
|
import { ArrowLeft, ArrowRight, ChevronDown, ChevronUp } from "lucide-react"; // Icons for sorting
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
import { PrismaClient } from '@repo/db';
|
import { PrismaClient } from "@repo/db";
|
||||||
|
|
||||||
export interface SortableTableProps<TData> {
|
export interface SortableTableProps<TData> {
|
||||||
data: TData[];
|
data: TData[];
|
||||||
columns: ColumnDef<TData>[];
|
columns: ColumnDef<TData>[];
|
||||||
showEditButton?: boolean;
|
showEditButton?: boolean;
|
||||||
prismaModel?: keyof PrismaClient;
|
prismaModel?: keyof PrismaClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SortableTable<TData>({
|
export default function SortableTable<TData>({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
prismaModel,
|
prismaModel,
|
||||||
showEditButton,
|
showEditButton,
|
||||||
}: SortableTableProps<TData>) {
|
}: SortableTableProps<TData>) {
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns: showEditButton
|
columns: showEditButton
|
||||||
? [
|
? [
|
||||||
...columns,
|
...columns,
|
||||||
{
|
{
|
||||||
header: 'Actions',
|
header: "Actions",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<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 btn-sm">Edit</button>
|
<button className="btn btn-sm">Edit</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: columns,
|
: columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
state: { sorting },
|
state: { sorting },
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="table table-zebra w-full">
|
<table className="table table-zebra w-full">
|
||||||
<thead>
|
<thead>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<tr key={headerGroup.id}>
|
<tr key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header) => (
|
{headerGroup.headers.map((header) => (
|
||||||
<th
|
<th
|
||||||
key={header.id}
|
key={header.id}
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1 cursor-pointer">
|
<div className="flex items-center gap-1 cursor-pointer">
|
||||||
{flexRender(
|
{flexRender(
|
||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext()
|
header.getContext(),
|
||||||
)}
|
)}
|
||||||
{header.column.getIsSorted() === 'asc' && (
|
{header.column.getIsSorted() === "asc" && (
|
||||||
<ChevronUp size={16} />
|
<ChevronUp size={16} />
|
||||||
)}
|
)}
|
||||||
{header.column.getIsSorted() === 'desc' && (
|
{header.column.getIsSorted() === "desc" && (
|
||||||
<ChevronDown size={16} />
|
<ChevronDown size={16} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{table.getRowModel().rows.map((row) => (
|
{table.getRowModel().rows.map((row) => (
|
||||||
<tr key={row.id}>
|
<tr key={row.id}>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<td key={cell.id}>
|
<td key={cell.id}>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Pagination = ({
|
export const Pagination = ({
|
||||||
page,
|
page,
|
||||||
totalPages,
|
totalPages,
|
||||||
setPage,
|
setPage,
|
||||||
}: {
|
}: {
|
||||||
page: number;
|
page: number;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
setPage: (page: number) => void;
|
setPage: (page: number) => void;
|
||||||
}) => {
|
}) => {
|
||||||
if (totalPages === 0) return null;
|
if (totalPages === 0) return null;
|
||||||
return (
|
return (
|
||||||
<div className="join w-full justify-end">
|
<div className="join w-full justify-end">
|
||||||
<button
|
<button
|
||||||
className="join-item btn"
|
className="join-item btn"
|
||||||
disabled={page === 0}
|
disabled={page === 0}
|
||||||
onClick={() => setPage(page - 1)}
|
onClick={() => setPage(page - 1)}
|
||||||
>
|
>
|
||||||
<ArrowLeft size={16} />
|
<ArrowLeft size={16} />
|
||||||
</button>
|
</button>
|
||||||
<select
|
<select
|
||||||
className="select join-item w-16"
|
className="select join-item w-16"
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(e) => setPage(Number(e.target.value))}
|
onChange={(e) => setPage(Number(e.target.value))}
|
||||||
>
|
>
|
||||||
{Array.from({ length: totalPages }).map((_, i) => (
|
{Array.from({ length: totalPages }).map((_, i) => (
|
||||||
<option key={i} value={i}>
|
<option key={i} value={i}>
|
||||||
{i + 1}
|
{i + 1}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
className="join-item btn"
|
className="join-item btn"
|
||||||
disabled={page === totalPages - 1}
|
disabled={page === totalPages - 1}
|
||||||
onClick={() => page < totalPages && setPage(page + 1)}
|
onClick={() => page < totalPages && setPage(page + 1)}
|
||||||
>
|
>
|
||||||
<ArrowRight size={16} />
|
<ArrowRight size={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
'use server';
|
"use server";
|
||||||
import { PrismaClient } from '@repo/db';
|
import { PrismaClient } from "@repo/db";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
export async function getData(
|
export async function getData(
|
||||||
model: keyof PrismaClient,
|
model: keyof PrismaClient,
|
||||||
limit: number,
|
limit: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
searchFields: string[],
|
searchFields: string[],
|
||||||
filter?: Record<string, any>,
|
filter?: Record<string, any>,
|
||||||
include?: Record<string, boolean>
|
include?: Record<string, boolean>,
|
||||||
) {
|
) {
|
||||||
if (!model || !prisma[model]) {
|
if (!model || !prisma[model]) {
|
||||||
return { data: [], total: 0 };
|
return { data: [], total: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedId = searchTerm.match(/^VAR(\d+)$/)?.[1];
|
const formattedId = searchTerm.match(/^VAR(\d+)$/)?.[1];
|
||||||
|
|
||||||
const where = searchTerm
|
const where = searchTerm
|
||||||
? {
|
? {
|
||||||
OR: [
|
OR: [
|
||||||
formattedId ? { id: formattedId } : undefined,
|
formattedId ? { id: formattedId } : undefined,
|
||||||
...searchFields.map((field) => ({
|
...searchFields.map((field) => ({
|
||||||
[field]: { contains: searchTerm },
|
[field]: { contains: searchTerm },
|
||||||
})),
|
})),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
...filter,
|
...filter,
|
||||||
}
|
}
|
||||||
: { ...filter };
|
: { ...filter };
|
||||||
|
|
||||||
if (!prisma[model]) {
|
if (!prisma[model]) {
|
||||||
return { data: [], total: 0 };
|
return { data: [], total: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await (prisma[model] as any).findMany({
|
const data = await (prisma[model] as any).findMany({
|
||||||
where,
|
where,
|
||||||
take: limit,
|
take: limit,
|
||||||
skip: offset,
|
skip: offset,
|
||||||
include,
|
include,
|
||||||
});
|
});
|
||||||
|
|
||||||
const total = await (prisma[model] as any).count({ where });
|
const total = await (prisma[model] as any).count({ where });
|
||||||
|
|
||||||
return { data, total };
|
return { data, total };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import 'tailwindcss';
|
@import "tailwindcss";
|
||||||
@plugin "daisyui";
|
@plugin "daisyui";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -10,22 +10,22 @@
|
|||||||
color utility to any element that depends on these defaults.
|
color utility to any element that depends on these defaults.
|
||||||
*/
|
*/
|
||||||
@layer base {
|
@layer base {
|
||||||
*,
|
*,
|
||||||
::after,
|
::after,
|
||||||
::before,
|
::before,
|
||||||
::backdrop,
|
::backdrop,
|
||||||
::file-selector-button {
|
::file-selector-button {
|
||||||
border-color: var(--color-gray-200, currentColor);
|
border-color: var(--color-gray-200, currentColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
/* --p: 47.67% 0.2484 267.02; */
|
/* --p: 47.67% 0.2484 267.02; */
|
||||||
--nc: #a6adbb;
|
--nc: #a6adbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.modal-open {
|
body.modal-open {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import { NextAuthSessionProvider } from "./_components/AuthSessionProvider";
|
import { NextAuthSessionProvider } from "./_components/AuthSessionProvider";
|
||||||
import { getServerSession } from "./api/auth/[...nextauth]/auth";
|
import { getServerSession } from "./api/auth/[...nextauth]/auth";
|
||||||
import './globals.css';
|
import "./globals.css";
|
||||||
|
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: '--font-geist-sans',
|
variable: "--font-geist-sans",
|
||||||
subsets: ['latin'],
|
subsets: ["latin"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
const geistMono = Geist_Mono({
|
||||||
variable: '--font-geist-mono',
|
variable: "--font-geist-mono",
|
||||||
subsets: ['latin'],
|
subsets: ["latin"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) => {
|
}>) => {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<NextAuthSessionProvider session={session}>
|
<NextAuthSessionProvider session={session}>
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</NextAuthSessionProvider>
|
</NextAuthSessionProvider>
|
||||||
</html>
|
</html>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { nextJsConfig } from '@repo/eslint-config/next-js';
|
import { nextJsConfig } from "@repo/eslint-config/next-js";
|
||||||
|
|
||||||
/** @type {import("eslint").Linter.Config} */
|
/** @type {import("eslint").Linter.Config} */
|
||||||
export default nextJsConfig;
|
export default nextJsConfig;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import clsx, { ClassValue } from 'clsx';
|
import clsx, { ClassValue } from "clsx";
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export const cn = (...inputs: ClassValue[]) => {
|
export const cn = (...inputs: ClassValue[]) => {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const removeImports = require('next-remove-imports')();
|
const removeImports = require("next-remove-imports")();
|
||||||
const nextConfig = removeImports({});
|
/* const nextConfig = removeImports({}); */
|
||||||
|
const nextConfig = {};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
{
|
{
|
||||||
"name": "hub",
|
"name": "hub",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack -p 3000",
|
"dev": "next dev --turbopack -p 3000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"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/db": "*",
|
||||||
"@repo/ui": "*",
|
"@repo/ui": "*",
|
||||||
"@tanstack/react-table": "^8.20.6",
|
"@tanstack/react-table": "^8.20.6",
|
||||||
"@uiw/react-md-editor": "^4.0.5",
|
"@uiw/react-md-editor": "^4.0.5",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"next": "15.1.4",
|
"next": "15.1.4",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"next-remove-imports": "^1.0.12",
|
"next-remove-imports": "^1.0.12",
|
||||||
"npm": "^11.1.0",
|
"npm": "^11.1.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-day-picker": "^9.5.1",
|
"react-day-picker": "^9.5.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-hot-toast": "^2.5.1",
|
"react-hot-toast": "^2.5.1",
|
||||||
"react-select": "^5.10.0",
|
"react-select": "^5.10.0",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"zod": "^3.24.1"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@tailwindcss/postcss": "^4.0.8",
|
"@tailwindcss/postcss": "^4.0.8",
|
||||||
"@types/bcryptjs": "^2.4.6",
|
"@types/bcryptjs": "^2.4.6",
|
||||||
"@types/jsonwebtoken": "^9.0.8",
|
"@types/jsonwebtoken": "^9.0.8",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"daisyui": "^5.0.0-beta.8",
|
"daisyui": "^5.0.0-beta.8",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.1.4",
|
"eslint-config-next": "15.1.4",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^4.0.8",
|
"tailwindcss": "^4.0.8",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/** @type {import('postcss-load-config').Config} */
|
/** @type {import('postcss-load-config').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
"@tailwindcss/postcss": {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z"
|
||||||
|
clip-rule="evenodd" fill="#666" fill-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 414 B |
@@ -1 +1,12 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1"
|
||||||
|
fill="#666" />
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="#fff" d="M0 0h16v16H0z" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80">
|
||||||
|
<path fill="#000"
|
||||||
|
d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z" />
|
||||||
|
<path fill="#000"
|
||||||
|
d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1 +1,3 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000">
|
||||||
|
<path d="m577.3 0 577.4 1000H0z" fill="#fff" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 135 B |
@@ -1 +1,5 @@
|
|||||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5"
|
||||||
|
fill="#666" />
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 408 B |
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": "@repo/typescript-config/nextjs.json",
|
"extends": "@repo/typescript-config/nextjs.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"next.config.js",
|
"next.config.js",
|
||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
34
apps/hub/types/next-auth.d.ts
vendored
@@ -1,21 +1,21 @@
|
|||||||
import NextAuth from 'next-auth';
|
import NextAuth from "next-auth";
|
||||||
import { User as IUser } from '@repo/db';
|
import { User as IUser } from "@repo/db";
|
||||||
|
|
||||||
declare module 'next-auth' {
|
declare module "next-auth" {
|
||||||
/**
|
/**
|
||||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||||
*/
|
*/
|
||||||
interface Session {
|
interface Session {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
}
|
}
|
||||||
type User = IUser;
|
type User = IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'next-auth/jwt' {
|
declare module "next-auth/jwt" {
|
||||||
interface JWT {
|
interface JWT {
|
||||||
uid: string;
|
uid: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# docker-compose.dev.yml
|
# docker-compose.dev.yml
|
||||||
|
|
||||||
version: '3.8'
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:13
|
image: postgres:13
|
||||||
@@ -17,8 +17,8 @@ services:
|
|||||||
image: dpage/pgadmin4:latest
|
image: dpage/pgadmin4:latest
|
||||||
container_name: pgadmin
|
container_name: pgadmin
|
||||||
environment:
|
environment:
|
||||||
PGADMIN_DEFAULT_EMAIL: dev@var.de
|
PGADMIN_DEFAULT_EMAIL: dev@var.de
|
||||||
PGADMIN_DEFAULT_PASSWORD: dev
|
PGADMIN_DEFAULT_PASSWORD: dev
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
29886
package-lock.json
generated
44
package.json
@@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "var-monorepo",
|
"name": "var-monorepo",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo build",
|
"build": "turbo build",
|
||||||
"dev": "turbo dev",
|
"dev": "turbo dev",
|
||||||
"migrate": "turbo db:migrate",
|
"migrate": "turbo db:migrate",
|
||||||
"lint": "turbo lint",
|
"lint": "turbo lint",
|
||||||
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"turbo": "^2.4.0",
|
"turbo": "^2.4.0",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"packageManager": "npm@10.2.3",
|
"packageManager": "npm@10.2.3",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +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';
|
import * as zodTypes from "./generated/zod";
|
||||||
|
|
||||||
export const zod = zodTypes;
|
export const zod = zodTypes;
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/db",
|
"name": "@repo/db",
|
||||||
"version": "0.0.0",
|
"version": "0.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",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"db:generate": "npx prisma generate && npx prisma generate zod",
|
"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"
|
"./zod": "./zod.ts"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.0",
|
"@prisma/client": "^6.4.0",
|
||||||
"zod-prisma-types": "^3.2.4"
|
"zod-prisma-types": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PrismaClient } from '../generated/client';
|
import { PrismaClient } from "../generated/client";
|
||||||
|
|
||||||
const globalForPrisma = global as unknown as { prisma: PrismaClient };
|
const globalForPrisma = global as unknown as { prisma: PrismaClient };
|
||||||
|
|
||||||
export const prisma = globalForPrisma.prisma || new PrismaClient();
|
export const prisma = globalForPrisma.prisma || new PrismaClient();
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
|
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ model Account {
|
|||||||
@@map(name: "accounts")
|
@@map(name: "accounts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
userId Int @map(name: "user_id")
|
userId Int @map(name: "user_id")
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ model User {
|
|||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
vatsimCid Int? @map(name: "vatsim_cid")
|
vatsimCid Int? @map(name: "vatsim_cid")
|
||||||
|
moodleId Int? @map(name: "moodle_id")
|
||||||
emailVerified DateTime? @map(name: "email_verified")
|
emailVerified DateTime? @map(name: "email_verified")
|
||||||
image String?
|
image String?
|
||||||
badges BADGES[] @default([])
|
badges BADGES[] @default([])
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './generated/zod';
|
export * from "./generated/zod";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import js from '@eslint/js';
|
import js from "@eslint/js";
|
||||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
import turboPlugin from 'eslint-plugin-turbo';
|
import turboPlugin from "eslint-plugin-turbo";
|
||||||
import tseslint from 'typescript-eslint';
|
import tseslint from "typescript-eslint";
|
||||||
import onlyWarn from 'eslint-plugin-only-warn';
|
import onlyWarn from "eslint-plugin-only-warn";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A shared ESLint configuration for the repository.
|
* A shared ESLint configuration for the repository.
|
||||||
@@ -10,23 +10,23 @@ import onlyWarn from 'eslint-plugin-only-warn';
|
|||||||
* @type {import("eslint").Linter.Config}
|
* @type {import("eslint").Linter.Config}
|
||||||
* */
|
* */
|
||||||
export const config = [
|
export const config = [
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
eslintConfigPrettier,
|
eslintConfigPrettier,
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
turbo: turboPlugin,
|
turbo: turboPlugin,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'turbo/no-undeclared-env-vars': ['error', { allowList: true }],
|
"turbo/no-undeclared-env-vars": ["error", { allowList: true }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
onlyWarn,
|
onlyWarn,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ignores: ['dist/**'],
|
ignores: ["dist/**"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,37 +13,37 @@ import { config as baseConfig } from "./base.js";
|
|||||||
* @type {import("eslint").Linter.Config}
|
* @type {import("eslint").Linter.Config}
|
||||||
* */
|
* */
|
||||||
export const nextJsConfig = [
|
export const nextJsConfig = [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
eslintConfigPrettier,
|
eslintConfigPrettier,
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
...pluginReact.configs.flat.recommended,
|
...pluginReact.configs.flat.recommended,
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
...pluginReact.configs.flat.recommended.languageOptions,
|
...pluginReact.configs.flat.recommended.languageOptions,
|
||||||
globals: {
|
globals: {
|
||||||
...globals.serviceworker,
|
...globals.serviceworker,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
"@next/next": pluginNext,
|
"@next/next": pluginNext,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
...pluginNext.configs.recommended.rules,
|
...pluginNext.configs.recommended.rules,
|
||||||
...pluginNext.configs["core-web-vitals"].rules,
|
...pluginNext.configs["core-web-vitals"].rules,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
"react-hooks": pluginReactHooks,
|
"react-hooks": pluginReactHooks,
|
||||||
},
|
},
|
||||||
settings: { react: { version: "detect" } },
|
settings: { react: { version: "detect" } },
|
||||||
rules: {
|
rules: {
|
||||||
...pluginReactHooks.configs.recommended.rules,
|
...pluginReactHooks.configs.recommended.rules,
|
||||||
// React scope no longer necessary with new JSX transform.
|
// React scope no longer necessary with new JSX transform.
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/eslint-config",
|
"name": "@repo/eslint-config",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"exports": {
|
"exports": {
|
||||||
"./base": "./base.js",
|
"./base": "./base.js",
|
||||||
"./next-js": "./next.js",
|
"./next-js": "./next.js",
|
||||||
"./react-internal": "./react-internal.js"
|
"./react-internal": "./react-internal.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@next/eslint-plugin-next": "^15.1.0",
|
"@next/eslint-plugin-next": "^15.1.0",
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-only-warn": "^1.1.0",
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
"eslint-plugin-react": "^7.37.2",
|
"eslint-plugin-react": "^7.37.2",
|
||||||
"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.3.3",
|
||||||
"typescript-eslint": "^8.15.0"
|
"typescript-eslint": "^8.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
packages/eslint-config/react-internal.js
vendored
@@ -11,29 +11,29 @@ import { config as baseConfig } from "./base.js";
|
|||||||
*
|
*
|
||||||
* @type {import("eslint").Linter.Config} */
|
* @type {import("eslint").Linter.Config} */
|
||||||
export const config = [
|
export const config = [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
eslintConfigPrettier,
|
eslintConfigPrettier,
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
pluginReact.configs.flat.recommended,
|
pluginReact.configs.flat.recommended,
|
||||||
{
|
{
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
...pluginReact.configs.flat.recommended.languageOptions,
|
...pluginReact.configs.flat.recommended.languageOptions,
|
||||||
globals: {
|
globals: {
|
||||||
...globals.serviceworker,
|
...globals.serviceworker,
|
||||||
...globals.browser,
|
...globals.browser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
"react-hooks": pluginReactHooks,
|
"react-hooks": pluginReactHooks,
|
||||||
},
|
},
|
||||||
settings: { react: { version: "detect" } },
|
settings: { react: { version: "detect" } },
|
||||||
rules: {
|
rules: {
|
||||||
...pluginReactHooks.configs.recommended.rules,
|
...pluginReactHooks.configs.recommended.rules,
|
||||||
// React scope no longer necessary with new JSX transform.
|
// React scope no longer necessary with new JSX transform.
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"incremental": false,
|
"incremental": false,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"noUncheckedIndexedAccess": true,
|
"allowImportingTsExtensions": true,
|
||||||
"resolveJsonModule": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"skipLibCheck": true,
|
"resolveJsonModule": true,
|
||||||
"strict": true,
|
"skipLibCheck": true,
|
||||||
"target": "ES2022"
|
"strict": true,
|
||||||
}
|
"target": "ES2022"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"extends": "./base.json",
|
"extends": "./base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"plugins": [{ "name": "next" }],
|
"plugins": [{ "name": "next" }],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"noEmit": true
|
"noEmit": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/typescript-config",
|
"name": "@repo/typescript-config",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"extends": "./base.json",
|
"extends": "./base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/ui",
|
"name": "@repo/ui",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"exports": {
|
"exports": {
|
||||||
"./button": "./src/button.tsx",
|
"./button": "./src/button.tsx",
|
||||||
"./card": "./src/card.tsx",
|
"./card": "./src/card.tsx",
|
||||||
"./code": "./src/code.tsx"
|
"./code": "./src/code.tsx"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --max-warnings 0",
|
"lint": "eslint . --max-warnings 0",
|
||||||
"generate:component": "turbo gen react-component",
|
"generate:component": "turbo gen react-component",
|
||||||
"check-types": "tsc --noEmit"
|
"check-types": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@repo/eslint-config": "*",
|
"@repo/eslint-config": "*",
|
||||||
"@repo/typescript-config": "*",
|
"@repo/typescript-config": "*",
|
||||||
"@turbo/gen": "^1.12.4",
|
"@turbo/gen": "^1.12.4",
|
||||||
"@types/node": "^20.11.24",
|
"@types/node": "^20.11.24",
|
||||||
"@types/react": "18.3.0",
|
"@types/react": "18.3.0",
|
||||||
"@types/react-dom": "18.3.1",
|
"@types/react-dom": "18.3.1",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Button = ({ children, className, appName }: ButtonProps) => {
|
export const Button = ({ children, className, appName }: ButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={className}
|
className={className}
|
||||||
onClick={() => alert(`Hello from your ${appName} app!`)}
|
onClick={() => alert(`Hello from your ${appName} app!`)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { type JSX } from "react";
|
import { type JSX } from "react";
|
||||||
|
|
||||||
export function Card({
|
export function Card({
|
||||||
className,
|
className,
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
href,
|
href,
|
||||||
}: {
|
}: {
|
||||||
className?: string;
|
className?: string;
|
||||||
title: string;
|
title: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
href: string;
|
href: string;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={className}
|
className={className}
|
||||||
href={`${href}?utm_source=create-turbo&utm_medium=basic&utm_campaign=create-turbo"`}
|
href={`${href}?utm_source=create-turbo&utm_medium=basic&utm_campaign=create-turbo"`}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<h2>
|
<h2>
|
||||||
{title} <span>-></span>
|
{title} <span>-></span>
|
||||||
</h2>
|
</h2>
|
||||||
<p>{children}</p>
|
<p>{children}</p>
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { type JSX } from "react";
|
import { type JSX } from "react";
|
||||||
|
|
||||||
export function Code({
|
export function Code({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return <code className={className}>{children}</code>;
|
return <code className={className}>{children}</code>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@repo/typescript-config/react-library.json",
|
"extends": "@repo/typescript-config/react-library.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "dist"
|
"outDir": "dist"
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,28 +3,28 @@ import type { PlopTypes } from "@turbo/gen";
|
|||||||
// Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
|
// Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation
|
||||||
|
|
||||||
export default function generator(plop: PlopTypes.NodePlopAPI): void {
|
export default function generator(plop: PlopTypes.NodePlopAPI): void {
|
||||||
// A simple generator to add a new React component to the internal UI library
|
// A simple generator to add a new React component to the internal UI library
|
||||||
plop.setGenerator("react-component", {
|
plop.setGenerator("react-component", {
|
||||||
description: "Adds a new react component",
|
description: "Adds a new react component",
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
type: "input",
|
type: "input",
|
||||||
name: "name",
|
name: "name",
|
||||||
message: "What is the name of the component?",
|
message: "What is the name of the component?",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: "add",
|
type: "add",
|
||||||
path: "src/{{kebabCase name}}.tsx",
|
path: "src/{{kebabCase name}}.tsx",
|
||||||
templateFile: "templates/component.hbs",
|
templateFile: "templates/component.hbs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "append",
|
type: "append",
|
||||||
path: "package.json",
|
path: "package.json",
|
||||||
pattern: /"exports": {(?<insertion>)/g,
|
pattern: /"exports": {(?<insertion>)/g,
|
||||||
template: ' "./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
|
template: ' "./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
70
turbo.json
@@ -1,37 +1,37 @@
|
|||||||
{
|
{
|
||||||
"$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"],
|
||||||
"ui": "tui",
|
"ui": "tui",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"apps:install": {
|
"apps:install": {
|
||||||
"dependsOn": ["^apps:install"],
|
"dependsOn": ["^apps:install"],
|
||||||
"inputs": ["apps/**/package.json"],
|
"inputs": ["apps/**/package.json"],
|
||||||
"outputs": ["apps/**/node_modules"]
|
"outputs": ["apps/**/node_modules"]
|
||||||
},
|
},
|
||||||
"db:generate": {
|
"db:generate": {
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
"db:migrate": {
|
"db:migrate": {
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build", "db:generate", "db:migrate"],
|
"dependsOn": ["^build", "db:generate", "db:migrate"],
|
||||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||||
"outputs": [".next/**", "!.next/cache/**"]
|
"outputs": [".next/**", "!.next/cache/**"]
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"dependsOn": ["^lint"]
|
"dependsOn": ["^lint"]
|
||||||
},
|
},
|
||||||
"check-types": {
|
"check-types": {
|
||||||
"dependsOn": ["^check-types"]
|
"dependsOn": ["^check-types"]
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"dependsOn": [],
|
"dependsOn": [],
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||