}
>
- Region File Name
+ Team File Name
Open
diff --git a/apps/dashboard/src/hooks/useBuildTeamData.ts b/apps/dashboard/src/hooks/useBuildTeamData.ts
index 86c478f1..203351eb 100644
--- a/apps/dashboard/src/hooks/useBuildTeamData.ts
+++ b/apps/dashboard/src/hooks/useBuildTeamData.ts
@@ -1,14 +1,55 @@
'use client';
+import { getUserBuildTeams } from '@/actions/user';
import { useLocalStorage } from '@mantine/hooks';
+import { useSession } from 'next-auth/react';
+import { useCallback } from 'react';
import useSWR from 'swr';
-export default function useBuildTeamData(link: string) {
- const [activeBuildTeamId] = useLocalStorage
({
+type BuildTeam = {
+ id: string;
+ slug: string;
+ name: string;
+ icon: string;
+};
+
+export default function useSelectableBuildTeams() {
+ const { data: session } = useSession();
+ const userId = session?.user.id;
+
+ const [activeBuildTeam, setActiveBuildTeam] = useLocalStorage({
key: 'bte-active-build-team',
});
- const data = useSWR(`/buildteams/${activeBuildTeamId}${link}`);
+ const { data: buildteams = [] } = useSWR(
+ userId ? (['user-build-teams', userId] as const) : null,
+ ([, id]: readonly [string, string]) => getUserBuildTeams(id),
+ // ([, id]: readonly [string, string]) => [],
+ {
+ revalidateOnFocus: false,
+ dedupingInterval: 10 * 60 * 1000,
+ },
+ );
+
+ const selectBuildTeam = useCallback(
+ (id: string) => {
+ if (id !== activeBuildTeam.id) {
+ const team = buildteams.find((buildTeam) => buildTeam.id === id);
+ if (team) {
+ setActiveBuildTeam(team);
+ }
+ }
+ },
+ [buildteams, activeBuildTeam, setActiveBuildTeam],
+ );
+
+ return [buildteams, activeBuildTeam, selectBuildTeam] as const;
+}
+
+export function useActiveBuildTeam() {
+ const [activeBuildTeam] = useLocalStorage({
+ key: 'bte-active-build-team',
+ });
- return data;
+ return activeBuildTeam;
}
diff --git a/apps/dashboard/src/hooks/useFormAction.ts b/apps/dashboard/src/hooks/useFormAction.ts
index 8e169eed..f97fc209 100644
--- a/apps/dashboard/src/hooks/useFormAction.ts
+++ b/apps/dashboard/src/hooks/useFormAction.ts
@@ -5,13 +5,20 @@ import { useTransition } from 'react';
* @param action A React Server Action
* @returns A tuple of a boolean that represents the status of the action's execution and an action that will execute
*/
-export function useFormAction(action: (...data: any[]) => Promise): [(...data: any[]) => Promise, boolean] {
+export function useFormAction(
+ action: (...data: any[]) => Promise,
+): [(...data: any[]) => Promise, boolean] {
const [isPending, startTransition] = useTransition();
const actionWithStatus = async (...data: any[]) => {
- startTransition(async () => {
- await action(...data);
+ let result: T;
+ await new Promise((resolve) => {
+ startTransition(async () => {
+ result = await action(...data);
+ resolve();
+ });
});
+ return result!;
};
return [actionWithStatus, isPending];
@@ -24,7 +31,7 @@ export function useFormAction(action: (...data: any[]) => Promise): [(...da
*/
export function useFormActions(
actions: Array<(...data: any[]) => Promise>,
-): [Array<(...data: any[]) => Promise>, boolean] {
+): [Array<(...data: any[]) => Promise>, boolean] {
const [isPending, startTransition] = useTransition();
const actionsWithStatus = actions.map((action) => async (...data: any[]) => {
diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts
index 429326b2..fe4b21a6 100644
--- a/apps/dashboard/src/middleware.ts
+++ b/apps/dashboard/src/middleware.ts
@@ -1,3 +1,24 @@
-export { default } from 'next-auth/middleware';
+import { withAuth } from 'next-auth/middleware';
-export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|auth).*)'] };
+export default withAuth({
+ callbacks: {
+ authorized: ({ token }) => {
+ // Block access if token has error or doesn't exist
+ if (
+ token?.error === 'RefreshAccessTokenError' ||
+ token?.error === 'TokenInvalidated' ||
+ token?.error === 'ForceLogout'
+ ) {
+ return false;
+ }
+ return !!token;
+ },
+ },
+ pages: {
+ signIn: '/auth/signin',
+ },
+});
+
+export const config = {
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|auth).*)'],
+};
diff --git a/apps/dashboard/src/types/User.ts b/apps/dashboard/src/types/User.ts
index 915afd0c..f937088c 100644
--- a/apps/dashboard/src/types/User.ts
+++ b/apps/dashboard/src/types/User.ts
@@ -46,6 +46,7 @@ export type WebsiteKeycloakUser = {
avatar: string;
username: string;
minecraft: string;
+ email: string;
emailVerified: boolean;
createdTimestamp: number;
enabled: boolean;
diff --git a/apps/dashboard/src/util/application/reviewActivity.ts b/apps/dashboard/src/util/application/reviewActivity.ts
index 3d22f3af..970d96a3 100644
--- a/apps/dashboard/src/util/application/reviewActivity.ts
+++ b/apps/dashboard/src/util/application/reviewActivity.ts
@@ -2,8 +2,8 @@ import { ApplicationStatus } from '@repo/db';
import prisma from '../db';
/**
- * Calculate Review Activity Score for a BuildTeam
- * @param buildteamId BuildTeam ID
+ * Calculate Review Activity Score for a Build Team
+ * @param buildteamId Build Team ID
* @returns PAR: Pending Application Ratio, PS: Pending Score, ART: Average Review Time, RES: Review Efficiency Score, RAS: Review Activity Score
*/
export async function getReviewActivityScore(
diff --git a/apps/dashboard/src/util/auth.ts b/apps/dashboard/src/util/auth.ts
index b976ec19..fe1ff033 100644
--- a/apps/dashboard/src/util/auth.ts
+++ b/apps/dashboard/src/util/auth.ts
@@ -2,6 +2,39 @@ import { AuthOptions, Session, getServerSession } from 'next-auth';
import { JWT } from 'next-auth/jwt';
import KeycloakProvider from 'next-auth/providers/keycloak';
+import { isSessionInvalidated, markSessionAsChecked } from './invalidatedSessions';
+
+const TOKEN_EXPIRY_SKEW_MS = 15_000;
+const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 300;
+
+const decodeJwtPayload = (jwt: string): Record | null => {
+ try {
+ const [, payload] = jwt.split('.');
+ if (!payload) return null;
+
+ const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
+ const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
+ const decoded = Buffer.from(padded, 'base64').toString('utf-8');
+
+ return JSON.parse(decoded) as Record;
+ } catch {
+ return null;
+ }
+};
+
+const getAccessTokenExpiry = (
+ accessToken: string | undefined,
+ fallbackTtlSeconds = DEFAULT_ACCESS_TOKEN_TTL_SECONDS,
+) => {
+ const payload = accessToken ? decodeJwtPayload(accessToken) : null;
+ const exp = payload?.exp;
+
+ if (typeof exp === 'number' && exp > 0) {
+ return exp * 1000 - TOKEN_EXPIRY_SKEW_MS;
+ }
+
+ return Date.now() + fallbackTtlSeconds * 1000 - TOKEN_EXPIRY_SKEW_MS;
+};
/**
* Refreshes access token to continue the session after token expiration
@@ -10,21 +43,25 @@ import KeycloakProvider from 'next-auth/providers/keycloak';
*/
const refreshAccessToken = async (token: JWT) => {
try {
- if (Date.now() > token.refreshTokenExpired) throw Error;
+ if (!token.refreshToken) throw new Error('Missing refresh token');
+ if (token.refreshTokenExpired && Date.now() >= token.refreshTokenExpired) throw new Error('Refresh token expired');
+
const details = {
- client_id: process.env.KEYCLOAK_ID,
+ client_id: process.env.NEXT_PUBLIC_KEYCLOAK_ID,
client_secret: process.env.KEYCLOAK_SECRET,
grant_type: 'refresh_token',
refresh_token: token.refreshToken,
};
+
const formBody: string[] = [];
- Object.entries(details).forEach(([key, value]: [string, any]) => {
+ Object.entries(details).forEach(([key, value]: [string, string | undefined]) => {
const encodedKey = encodeURIComponent(key);
- const encodedValue = encodeURIComponent(value);
+ const encodedValue = encodeURIComponent(value ?? '');
formBody.push(encodedKey + '=' + encodedValue);
});
+
const formData = formBody.join('&');
- const url = `${process.env.KEYCLOAK_URL}/protocol/openid-connect/token`;
+ const url = `${process.env.NEXT_PUBLIC_KEYCLOAK_URL}/protocol/openid-connect/token`;
const response = await fetch(url, {
method: 'POST',
headers: {
@@ -32,16 +69,30 @@ const refreshAccessToken = async (token: JWT) => {
},
body: formData,
});
+
const refreshedTokens = await response.json();
if (!response.ok) throw refreshedTokens;
+
+ const refreshedAccessExpiresIn =
+ typeof refreshedTokens.expires_in === 'number' && refreshedTokens.expires_in > 0
+ ? refreshedTokens.expires_in
+ : DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
+ const refreshedRefreshExpiresIn = refreshedTokens.refresh_expires_in ?? 0;
+ const nextAccessToken = refreshedTokens.access_token ?? token.accessToken;
+
+ const nextRefreshExpiry = refreshedRefreshExpiresIn
+ ? Date.now() + (refreshedRefreshExpiresIn - 15) * 1000
+ : token.refreshTokenExpired;
+
return {
...token,
- accessToken: refreshedTokens.access_token,
- accessTokenExpired: Date.now() + (refreshedTokens.expires_in - 15) * 1000,
+ accessToken: nextAccessToken,
+ accessTokenExpired: getAccessTokenExpiry(nextAccessToken, refreshedAccessExpiresIn),
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken,
- refreshTokenExpired: Date.now() + (refreshedTokens.refresh_expires_in - 15) * 1000,
+ refreshTokenExpired: nextRefreshExpiry,
};
} catch (error) {
+ console.error('Failed to refresh access token', error);
return {
...token,
error: 'RefreshAccessTokenError',
@@ -51,11 +102,19 @@ const refreshAccessToken = async (token: JWT) => {
export const authOptions: AuthOptions = {
secret: process.env.NEXTAUTH_SECRET,
+ pages: {
+ signIn: '/auth/signin',
+ },
providers: [
KeycloakProvider({
clientId: process.env.NEXT_PUBLIC_KEYCLOAK_ID || '',
clientSecret: process.env.KEYCLOAK_SECRET || '',
issuer: process.env.NEXT_PUBLIC_KEYCLOAK_URL || '',
+ authorization: {
+ params: {
+ scope: 'openid email profile',
+ },
+ },
profile: (profile) => {
return {
...profile,
@@ -74,25 +133,57 @@ export const authOptions: AuthOptions = {
return '/unauthorized';
}
},
- jwt: async ({ token, account, user }: any) => {
+
+ jwt: async ({ token, account, user }) => {
// Initial sign in
if (account && user) {
// Add access_token, refresh_token and expirations to the token right after signin
+ const accessExpiresIn =
+ typeof account.expires_in === 'number' && account.expires_in > 0
+ ? account.expires_in
+ : DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
+ const refreshExpiresIn = account.refresh_expires_in ?? 0;
+
token.accessToken = account.access_token;
token.refreshToken = account.refresh_token;
- token.accessTokenExpired = Date.now() + (account.expires_in - 15) * 1000;
- token.refreshTokenExpired = Date.now() + (account.refresh_expires_in - 15) * 1000;
+ token.accessTokenExpired = getAccessTokenExpiry(account.access_token, accessExpiresIn);
+ token.refreshTokenExpired = refreshExpiresIn ? Date.now() + (refreshExpiresIn - 15) * 1000 : undefined;
+ token.sessionId = account.session_state;
token.user = user;
return token;
}
+
+ // If token already has an error (from previous check), return it immediately
+ if (token.error) {
+ return token;
+ }
+
+ // Check if this session was invalidated via back-channel logout
+ const sessionId = token.sessionId as string | undefined;
+ const userId = token.sub;
+ if (isSessionInvalidated(sessionId, userId)) {
+ markSessionAsChecked(sessionId, userId);
+ return { ...token, error: 'TokenInvalidated' };
+ }
+
// Return previous token if the access token has not expired yet
- if (Date.now() < token.accessTokenExpired) return token;
+ if (Date.now() < token.accessTokenExpired || token.accessTokenExpired == null) return token;
// Access token has expired, try to update it
return refreshAccessToken(token);
},
- session: async ({ session, token }: any) => {
+ session: async ({ session, token }: { session: Session; token: JWT }) => {
if (token) {
+ // If refresh token failed or token was invalidated, end the session
+ if (token.error === 'RefreshAccessTokenError' || token.error === 'TokenInvalidated') {
+ // Return a minimal session with only the error flag and no user data
+ return {
+ expires: session.expires,
+ error: 'ForceLogout',
+ } as Session;
+ }
+
+ // @ts-expect-error shut up typescript
session.user = token.user;
session.error = token.error;
session.accessToken = token.accessToken;
@@ -100,16 +191,22 @@ export const authOptions: AuthOptions = {
return session;
},
},
- pages: {
- signIn: '/auth/signin',
- },
};
/**
* Helper function to get the session on the server without having to import the authOptions object every single time
* @returns The session object or null
*/
-export const getSession = () => getServerSession(authOptions);
+export const getSession = async () => {
+ const session = await getServerSession(authOptions);
+
+ // Treat errored/incomplete sessions as unauthenticated everywhere on the server.
+ if (!session || session.error === 'ForceLogout' || !session.user) {
+ return null;
+ }
+
+ return session;
+};
/**
* Helper function to check if a user has a specific role
@@ -121,5 +218,5 @@ export function hasRole(
session: Session | null | undefined | { user: { realm_access?: { roles: string[] } } },
role: string,
) {
- return session?.user?.realm_access?.roles?.includes(role);
+ return session?.user?.realm_access?.roles?.includes(role) || false;
}
diff --git a/apps/dashboard/src/util/coordinates.ts b/apps/dashboard/src/util/coordinates.ts
new file mode 100644
index 00000000..d107814f
--- /dev/null
+++ b/apps/dashboard/src/util/coordinates.ts
@@ -0,0 +1,89 @@
+import * as turf from '@turf/turf';
+
+import { point, polygon } from '@turf/helpers';
+import { Point, Polygon } from 'geojson';
+
+// lat, lng
+export function toPoint(coords: string, splitter?: string): Point {
+ return point(coords.split(splitter || ', ').map(Number) as [number, number]).geometry;
+}
+
+// ["lat, lng","lat, lng","lat, lng","lat, lng"]
+export function toPolygon(coords: string[], splitter?: string, reverse?: boolean): Polygon {
+ return polygon([
+ coords.map((c) => {
+ const s = c.split(splitter || ', ');
+ return [parseFloat(s[reverse ? 1 : 0]), parseFloat(s[reverse ? 0 : 1])];
+ }),
+ ]).geometry;
+}
+
+export function toLngLat(coords: string, latFirst?: boolean, splitter?: string): { lat: number; lng: number } {
+ const s = coords.split(splitter || ', ');
+ return {
+ lat: parseFloat(s[latFirst ? 0 : 1]),
+ lng: parseFloat(s[latFirst ? 1 : 0]),
+ };
+}
+
+export function toOverpassPolygon(coords: string[]): string {
+ return coords.map((c) => `${c.split(', ')[1]} ${c.split(', ')[0]}`).join(' ');
+}
+
+export const CoordinateType = {
+ STRING_REVERSE: 'stringreverse',
+ STRING: 'string',
+ OBJECT: 'object',
+ STRING_ARRAY: 'stringarray',
+ STRING_ARRAY_REVERSE: 'stringarrayreverse',
+ ARRAY: 'array',
+ ARRAY_REVERSE: 'arrayreverse',
+ NUMBER_ARRAY: 'numberarray',
+ NUMBER_ARRAY_REVERSE: 'numberarrayreverse',
+};
+
+export function parseCoordinates(coords: any, type: string) {
+ switch (type) {
+ // "lat, lng; lat, lng; lat, lng"
+ case CoordinateType.STRING_REVERSE: {
+ return coords.split(';').map((c: string) => `${c.split(',')[1].trim()}, ${c.split(',')[0].trim()}`);
+ }
+ // "lng, lat; lng, lat; lng, lat"
+ case CoordinateType.STRING: {
+ return coords.split(';').map((c: string) => c.trim());
+ }
+ // [{lat: lat, lng: lng}] or [{lat: lat, lon: lng}]
+ case CoordinateType.OBJECT: {
+ return coords.map((c: { lng: any; lon: any; lat: string }) => `${(c.lng || c.lon).trim()}, ${c.lat.trim()}`);
+ }
+ // ["lng, lat","lng, lat","lng, lat"]
+ case CoordinateType.STRING_ARRAY: {
+ return coords;
+ }
+ // ["lat, lng","lat, lng","lat, lng"]
+ case CoordinateType.STRING_ARRAY_REVERSE: {
+ return coords.map((c: string) => `${c.split(',')[1].trim()}, ${c.split(',')[0].trim()}`);
+ }
+ // [["lng","lat"],["lng","lat"],["lng","lat"]]
+ case CoordinateType.ARRAY: {
+ return coords.map((c: string[]) => `${c[0].trim()}, ${c[1].trim()}`);
+ }
+ // [["lat","lng"],["lat","lng"],["lat","lng"]]
+ case CoordinateType.ARRAY_REVERSE: {
+ return coords.map((c: string[]) => `${c[1].trim()}, ${c[0].trim()}`);
+ }
+ // [[lng,lat],[lng,lat],[lng,lat]]
+ case CoordinateType.NUMBER_ARRAY: {
+ return coords.map((c: any[]) => `${c[0]}, ${c[1]}`);
+ }
+ // [[lat,lng],[lat,lng],[lat,lng]]
+ case CoordinateType.NUMBER_ARRAY_REVERSE: {
+ return coords.map((c: any[]) => `${c[1]}, ${c[0]}`);
+ }
+ default: {
+ return [];
+ }
+ }
+}
+
+export default turf;
diff --git a/apps/dashboard/src/util/cron/reviewActivityCheck.ts b/apps/dashboard/src/util/cron/reviewActivityCheck.ts
index b2077a23..f687453c 100644
--- a/apps/dashboard/src/util/cron/reviewActivityCheck.ts
+++ b/apps/dashboard/src/util/cron/reviewActivityCheck.ts
@@ -35,7 +35,7 @@ async function fetchPastData(writeLog: (line: string) => void) {
}
async function fetchBuildTeams(writeLog: (line: string) => void) {
- writeLog('Fetching build teams...');
+ writeLog('Fetching build Teams...');
return prisma.buildTeam.findMany({
select: { id: true, name: true },
where: { allowApplications: true },
@@ -55,7 +55,7 @@ async function calculateReviewActivityScores(
const reviewActivities = await Promise.all(
buildTeams.map(async (buildTeam, i) => {
- writeLog(`Calculating review activity score for build team ${buildTeam.id} (${i + 1}/${buildTeams.length})...`);
+ writeLog(`Calculating review activity score for build Team ${buildTeam.id} (${i + 1}/${buildTeams.length})...`);
const reviewActivity = await getReviewActivityScore(buildTeam.id);
const pastReviewActivity = pastData.current.find((team: any) => team.id === buildTeam.id) || {
id: buildTeam.id,
diff --git a/apps/dashboard/src/util/data.tsx b/apps/dashboard/src/util/data.tsx
index 156aed49..1c699bf0 100644
--- a/apps/dashboard/src/util/data.tsx
+++ b/apps/dashboard/src/util/data.tsx
@@ -101,7 +101,9 @@ export function revalidateWebsitePath(path: string) {
}
export function revalidateWebsitePaths(paths: string[]) {
return globalFetcher(
- `${process.env.NEXT_PUBLIC_FRONTEND_URL}/api/revalidate?secret=${process.env.FRONTEND_KEY}&paths=${JSON.stringify(paths)}`,
+ `${process.env.NEXT_PUBLIC_FRONTEND_URL}/api/revalidate?secret=${process.env.FRONTEND_KEY}&paths=${JSON.stringify(
+ paths,
+ )}`,
{
method: 'GET',
headers: {
diff --git a/apps/dashboard/src/util/geojsonHelpers.ts b/apps/dashboard/src/util/geojsonHelpers.ts
new file mode 100644
index 00000000..27a261cb
--- /dev/null
+++ b/apps/dashboard/src/util/geojsonHelpers.ts
@@ -0,0 +1,83 @@
+import { Claim } from '@repo/db';
+import axios from 'axios';
+import { toOverpassPolygon } from './coordinates';
+
+export async function updateClaimBuildingCount(claim: {
+ id?: string;
+ area: string[];
+}): Promise {
+ const polygon = toOverpassPolygon(claim.area);
+
+ const overpassQuery = `[out:json][timeout:25];
+ (
+ node["building"]["building"!~"grandstand"]["building"!~"roof"]["building"!~"garage"]["building"!~"hut"]["building"!~"shed"](poly: "${polygon}");
+ way["building"]["building"!~"grandstand"]["building"!~"roof"]["building"!~"garage"]["building"!~"hut"]["building"!~"shed"](poly: "${polygon}");
+ relation["building"]["building"!~"grandstand"]["building"!~"roof"]["building"!~"garage"]["building"!~"hut"]["building"!~"shed"](poly: "${polygon}");
+ );
+ out count;`;
+
+ try {
+ const { data } = await axios.post(
+ `https://overpass.private.coffee/api/interpreter?`,
+ `data=${overpassQuery.replace('\n', '')}`,
+ );
+
+ if (!data?.elements || data?.elements.length <= 0) {
+ console.info(
+ `Claim did not contain any elements, setting building count to 0 (https://overpass.private.coffee/api/interpreter; claim: ${claim.id})`,
+ );
+ return 0;
+ }
+
+ return parseInt(data?.elements[0]?.tags?.total) || 0;
+ } catch (e) {
+ if (e instanceof Error) {
+ console.error(e.message + ` (https://overpass.private.coffee/api/interpreter; claim: ${claim.id})`);
+ return { message: e.message };
+ } else {
+ console.error(String(e) + ` (https://overpass.private.coffee/api/interpreter; claim: ${claim.id})`);
+ return { message: String(e) };
+ }
+ }
+}
+
+export async function updateClaimOSMDetails(claim: {
+ id?: string;
+ center: string;
+ name?: string;
+}): Promise<{ osmName: string; city: string; name: string } | undefined> {
+ try {
+ const { data } = await axios.get(
+ `https://nominatim.openstreetmap.org/reverse?lat=${claim.center.split(', ')[1]}&lon=${
+ claim.center.split(', ')[0]
+ }&format=json&accept-language=en&zoom=18`,
+ { headers: { 'User-Agent': 'BTE/1.0' } },
+ );
+
+ if (data?.error) {
+ console.error(`OSM reverse geocoding error: ${data.error} (https://nominatim.openstreetmap.org/reverse)`);
+ }
+
+ const parsed = {
+ osmName: data.display_name,
+ name: claim.name
+ ? claim.name
+ : data.name != ''
+ ? data.name
+ : `${data.address?.road || ''} ${data.address?.house_number || ''}`.trim() || data.display_name.split(',')[0],
+ city:
+ data.address?.city ||
+ data.address?.town ||
+ data.address?.hamlet ||
+ data.address?.township ||
+ data.address?.village ||
+ data.address?.suburb ||
+ data.address?.neighbourhood ||
+ data.address?.county,
+ };
+
+ return parsed;
+ } catch (e) {
+ console.error(`OSM reverse geocoding error: ${e} (https://nominatim.openstreetmap.org/reverse)`);
+ }
+}
diff --git a/apps/dashboard/src/util/invalidatedSessions.ts b/apps/dashboard/src/util/invalidatedSessions.ts
new file mode 100644
index 00000000..6a3ea42c
--- /dev/null
+++ b/apps/dashboard/src/util/invalidatedSessions.ts
@@ -0,0 +1,35 @@
+/**
+ * Global store for invalidated sessions
+ * This is set by the backchannel-logout endpoint when Keycloak notifies
+ */
+
+declare global {
+ // eslint-disable-next-line no-var
+ var invalidatedSessions: Set | undefined;
+}
+
+export function isSessionInvalidated(sessionId: string | undefined, userId: string | undefined): boolean {
+ if (!globalThis.invalidatedSessions) {
+ return false;
+ }
+
+ if (sessionId && globalThis.invalidatedSessions.has(sessionId)) {
+ return true;
+ }
+
+ if (userId && globalThis.invalidatedSessions.has(userId)) {
+ return true;
+ }
+
+ return false;
+}
+
+export function markSessionAsChecked(sessionId: string | undefined, userId: string | undefined): void {
+ // Remove from invalidated sessions after checking to prevent memory buildup
+ if (sessionId) {
+ globalThis.invalidatedSessions?.delete(sessionId);
+ }
+ if (userId) {
+ globalThis.invalidatedSessions?.delete(userId);
+ }
+}
diff --git a/apps/dashboard/src/util/keycloak.ts b/apps/dashboard/src/util/keycloak.ts
new file mode 100644
index 00000000..e0e3e20c
--- /dev/null
+++ b/apps/dashboard/src/util/keycloak.ts
@@ -0,0 +1,30 @@
+import KcAdminClient from '@keycloak/keycloak-admin-client';
+
+const keycloakAdminClientSingleton = () => {
+ const client = new KcAdminClient({
+ baseUrl: process.env.NEXT_PUBLIC_KEYCLOAK_URL?.split('/realms/')[0],
+ realmName: process.env.NEXT_PUBLIC_KEYCLOAK_URL?.split('/realms/')[1],
+ });
+
+ client
+ .auth({
+ grantType: 'client_credentials',
+ clientId: process.env.NEXT_PUBLIC_KEYCLOAK_ID || '',
+ clientSecret: process.env.KEYCLOAK_SECRET,
+ })
+ .then(() => {})
+ .catch((err) => {
+ console.error('Keycloak client authentication failed:', err);
+ });
+ return client;
+};
+
+declare const globalThis: {
+ keycloakAdminGlobal: ReturnType;
+} & typeof global;
+
+const keycloakAdmin = globalThis.keycloakAdminGlobal ?? keycloakAdminClientSingleton();
+
+export default keycloakAdmin;
+
+if (process.env.NODE_ENV !== 'production') globalThis.keycloakAdminGlobal = keycloakAdmin;
diff --git a/apps/dashboard/src/util/links.ts b/apps/dashboard/src/util/links.ts
index b0689318..80093082 100644
--- a/apps/dashboard/src/util/links.ts
+++ b/apps/dashboard/src/util/links.ts
@@ -25,15 +25,42 @@ export const meNavLinks: NavLink[] = [
},
{
link: '/me/applications',
- label: 'My Applications',
+ label: 'Your Applications',
icon: 'Forms',
},
{
- link: '/me/claims',
- label: 'Claim Overview',
+ link: '/editor',
+ label: 'Claim Editor',
icon: 'Polygon',
},
+ // ---- BuildTeam Specific ----
+ {
+ link: '',
+ label: 'Your BuildTeams',
+ icon: null,
+ divider: true,
+ permission: 'team|get-team|get-team-questions|review-team|get-team-members',
+ },
+ {
+ link: '/team/[team_slug]',
+ label: 'Info Overview',
+ icon: 'FileInfo',
+ permission: 'get-team',
+ },
+ {
+ link: '/team/[team_slug]/applications',
+ label: 'Applications',
+ icon: 'Search',
+ permission: 'review-team',
+ },
+ {
+ link: '/team/[team_slug]/members',
+ label: 'Members',
+ icon: 'UsersGroup',
+ permission: 'get-team-members',
+ },
+
// ---- Account Links ----
{ link: '', label: 'Your Account', icon: null, divider: true },
{
@@ -48,7 +75,7 @@ export const meNavLinks: NavLink[] = [
},
{
link: '/me/settings',
- label: 'Global Settings',
+ label: 'Profile Settings',
icon: 'Settings',
},
@@ -60,18 +87,6 @@ export const meNavLinks: NavLink[] = [
permission: 'bte_staff',
divider: true,
},
- {
- link: '/am/users',
- label: 'Website Users',
- permission: 'get-users',
- icon: 'UsersGroup',
- },
- {
- link: '/am/teams',
- label: 'BuildTeams',
- permission: 'get-teams',
- icon: 'UsersGroup',
- },
{
link: '/am/faq',
label: 'FAQ',
@@ -80,19 +95,43 @@ export const meNavLinks: NavLink[] = [
},
{
link: '/am/claims',
- label: 'Map Claims',
+ label: 'Claims',
permission: 'get-claims',
icon: 'Polygon',
},
{
link: '/am/applications',
- label: 'Team Applications',
+ label: 'Applications',
permission: 'get-applications',
icon: 'Forms',
},
+ {
+ link: '/am/users',
+ label: 'Website Users',
+ permission: 'get-users',
+ icon: 'UsersGroup',
+ },
+ {
+ link: '/am/teams',
+ label: 'Build Teams',
+ permission: 'get-teams',
+ icon: 'UsersGroup',
+ },
+ {
+ link: '/am/contacts',
+ label: 'Contacts',
+ permission: 'get-config',
+ icon: 'Mail',
+ },
+ {
+ link: '/am/uploads',
+ label: 'Uploads',
+ permission: 'get-config',
+ icon: 'Upload',
+ },
{
link: '/am/sso',
- label: 'SSO Configuration and Security',
+ label: 'SSO Configuration',
permission: 'get-config',
icon: 'Settings',
},
diff --git a/apps/dashboard/src/util/theme/index.ts b/apps/dashboard/src/util/theme/index.ts
index 1b24dae1..e146c816 100644
--- a/apps/dashboard/src/util/theme/index.ts
+++ b/apps/dashboard/src/util/theme/index.ts
@@ -14,7 +14,7 @@ export const theme = createTheme({
xl: '88em',
},
primaryColor: 'buildtheearth',
- primaryShade: 6,
+ primaryShade: 7,
colors: {
buildtheearth: [
'#f0f1fa',
@@ -55,6 +55,6 @@ export const theme = createTheme({
// ],
},
- autoContrast: true,
- luminanceThreshold: 0.33,
+ // autoContrast: true,
+ // luminanceThreshold: 0.33,
});
diff --git a/apps/dashboard/src/util/transformers.ts b/apps/dashboard/src/util/transformers.ts
index 38912e5e..b7fb060a 100644
--- a/apps/dashboard/src/util/transformers.ts
+++ b/apps/dashboard/src/util/transformers.ts
@@ -48,13 +48,13 @@ export function applicationStatusToIcon(status: ApplicationStatus) {
export function applicationStatusToTooltip(status: ApplicationStatus) {
switch (status) {
case ApplicationStatus.SEND:
- return 'The team has received your application and is reviewing it.';
+ return 'The Team has received your application and is reviewing it.';
case ApplicationStatus.TRIAL:
- return 'You have been accepted to the team on a trial basis.';
+ return 'You have been accepted to the Team on a trial basis.';
case ApplicationStatus.DECLINED:
return 'Your application has been declined. Please check the reason for more information.';
case ApplicationStatus.ACCEPTED:
- return 'You have been accepted to the team.';
+ return 'You have been accepted to the Team.';
default:
return 'Unknown status.';
}
@@ -71,7 +71,7 @@ export function applicationStatusToAlert(status: ApplicationStatus): {
icon: applicationStatusToIcon(status),
title: 'Application pending review',
description:
- 'The BuildTeam has received your application and is reviewing it. As soon as a decision is made, you will be notified via a Direct Message on Discord. If you have any questions about the status of this application, please contact the BuildTeam directly.',
+ 'The Build Team has received your application and is reviewing it. As soon as a decision is made, you will be notified via a Direct Message on Discord. If you have any questions about the status of this application, please contact the Build Team directly.',
color: applicationStatusToColor(status),
};
case ApplicationStatus.TRIAL:
@@ -79,7 +79,7 @@ export function applicationStatusToAlert(status: ApplicationStatus): {
icon: applicationStatusToIcon(status),
title: 'Trial Application accepted',
description:
- 'Congratulations! Your application has been accepted and you have been added to the BuildTeam as a Trial Member. If you have any questions about the Trial role or the status of this application, please contact the BuildTeam directly.',
+ 'Congratulations! Your application has been accepted and you have been added to the Build Team as a Trial Member. If you have any questions about the Trial role or the status of this application, please contact the Build Team directly.',
color: applicationStatusToColor(status),
};
case ApplicationStatus.DECLINED:
@@ -87,7 +87,7 @@ export function applicationStatusToAlert(status: ApplicationStatus): {
icon: applicationStatusToIcon(status),
title: 'Application declined',
description:
- 'This application has been declined by the BuildTeam. Please check the reason for more information about possible mistakes and how to improve your application. If you have any questions about this feedback, please contact the BuildTeam directly. You can reapply to this BuildTeam at any time.',
+ 'This application has been declined by the Build Team. Please check the reason for more information about possible mistakes and how to improve your application. If you have any questions about this feedback, please contact the Build Team directly. You can reapply to this Build Team at any time.',
color: applicationStatusToColor(status),
};
case ApplicationStatus.ACCEPTED:
@@ -95,7 +95,7 @@ export function applicationStatusToAlert(status: ApplicationStatus): {
icon: applicationStatusToIcon(status),
title: 'Application accepted',
description:
- 'Congratulations! Your application has been accepted by the BuildTeam. You are now a member of the BuildTeam. If you have any questions about your new role or the status of this application, please contact the BuildTeam directly.',
+ 'Congratulations! Your application has been accepted by the Build Team. You are now a member of the Build Team. If you have any questions about your new role or the status of this application, please contact the Build Team directly.',
color: applicationStatusToColor(status),
};
default:
diff --git a/apps/dashboard/src/util/webhooks.ts b/apps/dashboard/src/util/webhooks.ts
new file mode 100644
index 00000000..4bcf7e9c
--- /dev/null
+++ b/apps/dashboard/src/util/webhooks.ts
@@ -0,0 +1,28 @@
+export async function sendBtWebhook(url: string | null, type: WebhookType, content: any) {
+ if (url) {
+ try {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ body: JSON.stringify({ type, data: content }),
+ });
+ console.info(`Sent ${type} to ${url}`);
+ } catch (e) {
+ console.error(`Failed to send ${type} to ${url}: ${e}`);
+ return false;
+ }
+ }
+}
+
+export const WebhookType = {
+ APPLICATION: 'APPLICATION',
+ APPLICATION_SEND: 'APPLICATION_SEND',
+ CLAIM_CREATE: 'CLAIM_CREATE',
+ CLAIM_UPDATE: 'CLAIM_UPDATE',
+ CLAIM_DELETE: 'CLAIM_DELETE',
+};
+
+export type WebhookType = (typeof WebhookType)[keyof typeof WebhookType];
diff --git a/apps/dashboard/typings/mapbox-gl-draw-snap-mode/mapbox-gl-draw-snap-mode.d.ts b/apps/dashboard/typings/mapbox-gl-draw-snap-mode/mapbox-gl-draw-snap-mode.d.ts
new file mode 100644
index 00000000..2698e056
--- /dev/null
+++ b/apps/dashboard/typings/mapbox-gl-draw-snap-mode/mapbox-gl-draw-snap-mode.d.ts
@@ -0,0 +1 @@
+declare module 'mapbox-gl-draw-snap-mode';
diff --git a/apps/dashboard/typings/next-auth/next-auth.d.ts b/apps/dashboard/typings/next-auth/next-auth.d.ts
index b8fc54b9..29f30faf 100644
--- a/apps/dashboard/typings/next-auth/next-auth.d.ts
+++ b/apps/dashboard/typings/next-auth/next-auth.d.ts
@@ -1,110 +1,113 @@
-import type { User } from "next-auth";
+import type { User } from 'next-auth';
-declare module "next-auth" {
- /**
- * Returned by `useSession`, `getSession` and received as a prop on the `Provider` React Context
- */
- interface Session {
- user: {
- exp: number;
- iat: number;
- auth_time: number;
- jti: string;
- iss: string;
- aud: string;
- sub: string;
- typ: string;
- azp: string;
- session_state: string;
- at_hash: string;
- sid: string;
- email_verified: boolean;
- name: string;
- preferred_username: string;
- locale: string;
- given_name: string;
- family_name: string;
- email: string;
- username: string;
- id: string;
- realm_access: {
- roles: string[]
- },
- resource_access: {
- [string|"account"]: {
- roles: string[]
- }
- },
- };
- accessToken: string;
- error: string;
- }
- /**
- * The shape of the user object returned in the OAuth providers' `profile` callback,
- * or the second parameter of the `session` callback, when using a database.
- */
- interface User {
- sub: string;
- email_verified: boolean;
- name: string;
- telephone: string;
- preferred_username: string;
- org_name: string;
- given_name: string;
- family_name: string;
- email: string;
- id: string;
- }
- /**
- * Usually contains information about the provider being used
- * and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
- */
- interface Account {
- provider: string;
- type: string;
- id: string;
- accessToken: string;
- accessTokenExpires?: any;
- refreshToken: string;
- idToken: string;
- access_token: string;
- expires_in: number;
- refresh_expires_in: number;
- refresh_token: string;
- token_type: string;
- id_token: string;
- "not-before-policy": number;
- session_state: string;
- scope: string;
- }
- /** The OAuth profile returned from your provider */
- interface Profile {
- sub: string;
- email_verified: boolean;
- name: string;
- telephone: string;
- preferred_username: string;
- org_name: string;
- given_name: string;
- family_name: string;
- email: string;
- }
+declare module 'next-auth' {
+ /**
+ * Returned by `useSession`, `getSession` and received as a prop on the `Provider` React Context
+ */
+ interface Session {
+ user: {
+ exp: number;
+ iat: number;
+ auth_time: number;
+ jti: string;
+ iss: string;
+ aud: string;
+ sub: string;
+ typ: string;
+ azp: string;
+ session_state: string;
+ at_hash: string;
+ sid: string;
+ email_verified: boolean;
+ name: string;
+ preferred_username: string;
+ locale: string;
+ given_name: string;
+ family_name: string;
+ email: string;
+ username: string;
+ id: string;
+ realm_access: {
+ roles: string[];
+ };
+ resource_access: {
+ [string | 'account']: {
+ roles: string[];
+ };
+ };
+ minecraft?: string;
+ minecraft_verified?: boolean;
+ };
+ accessToken: string;
+ error: string;
+ }
+ /**
+ * The shape of the user object returned in the OAuth providers' `profile` callback,
+ * or the second parameter of the `session` callback, when using a database.
+ */
+ interface User {
+ sub: string;
+ email_verified: boolean;
+ name: string;
+ telephone: string;
+ preferred_username: string;
+ org_name: string;
+ given_name: string;
+ family_name: string;
+ email: string;
+ id: string;
+ }
+ /**
+ * Usually contains information about the provider being used
+ * and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
+ */
+ interface Account {
+ provider: string;
+ type: string;
+ id: string;
+ accessToken: string;
+ accessTokenExpires?: number;
+ refreshToken: string;
+ idToken: string;
+ access_token: string;
+ expires_in: number;
+ refresh_expires_in: number;
+ refresh_token: string;
+ token_type: string;
+ id_token: string;
+ 'not-before-policy': number;
+ session_state: string;
+ scope: string;
+ }
+ /** The OAuth profile returned from your provider */
+ interface Profile {
+ sub: string;
+ email_verified: boolean;
+ name: string;
+ telephone: string;
+ preferred_username: string;
+ org_name: string;
+ given_name: string;
+ family_name: string;
+ email: string;
+ }
}
-declare module "next-auth/jwt" {
- /** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
- interface JWT {
- name: string;
- email: string;
- sub: string;
- name: string;
- email: string;
- sub: string;
- accessToken: string;
- refreshToken: string;
- accessTokenExpired: number;
- refreshTokenExpired: number;
- user: User;
- error: string;
- }
+declare module 'next-auth/jwt' {
+ /** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
+ interface JWT {
+ name: string;
+ email: string;
+ sub: string;
+ name: string;
+ email: string;
+ sub: string;
+ accessToken: string;
+ refreshToken: string;
+ accessTokenExpired: number;
+ refreshTokenExpired: number | undefined;
+ sessionId?: string;
+ user: User;
+ error: string;
+ }
}
diff --git a/apps/frontend/Dockerfile b/apps/frontend/Dockerfile
index dc79abef..a35381e8 100644
--- a/apps/frontend/Dockerfile
+++ b/apps/frontend/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:21-alpine AS base
+FROM node:22-alpine AS base
FROM base AS builder
RUN apk update
@@ -18,8 +18,8 @@ WORKDIR /app
# First install the dependencies (as they change less often)
COPY --from=builder /app/out/json/ .
+RUN corepack enable
RUN yarn install
-RUN yarn list postcss
# Build the project
COPY --from=builder /app/out/full/ .
diff --git a/apps/frontend/next.config.js b/apps/frontend/next.config.js
index 1ad23cbe..3052b17a 100644
--- a/apps/frontend/next.config.js
+++ b/apps/frontend/next.config.js
@@ -28,6 +28,12 @@ const nextConfig = {
port: '',
pathname: '/**',
},
+ {
+ protocol: 'https',
+ hostname: 'raw.githubusercontent.com',
+ port: '',
+ pathname: '/**',
+ },
],
},
i18n: {
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 2a0a830a..73b9fff1 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -17,18 +17,18 @@
"@emotion/react": "^11.10.4",
"@emotion/server": "^11.10.0",
"@icons-pack/react-simple-icons": "^12.6.0",
- "@mantine/carousel": "7.17.4",
- "@mantine/core": "7.17.4",
- "@mantine/dates": "7.17.4",
- "@mantine/dropzone": "7.17.4",
- "@mantine/form": "7.17.4",
- "@mantine/hooks": "7.17.4",
- "@mantine/modals": "7.17.4",
+ "@mantine/carousel": "^8.2.3",
+ "@mantine/core": "^8.2.3",
+ "@mantine/dates": "^8.2.3",
+ "@mantine/dropzone": "^8.2.3",
+ "@mantine/form": "^8.2.3",
+ "@mantine/hooks": "^8.2.3",
+ "@mantine/modals": "^8.2.3",
"@mantine/next": "^6.0.22",
- "@mantine/notifications": "7.17.4",
- "@mantine/nprogress": "7.17.4",
- "@mantine/spotlight": "7.17.4",
- "@mantine/tiptap": "7.17.4",
+ "@mantine/notifications": "^8.2.3",
+ "@mantine/nprogress": "^8.2.3",
+ "@mantine/spotlight": "^8.2.3",
+ "@mantine/tiptap": "^8.2.3",
"@mapbox/mapbox-gl-draw": "^1.4.3",
"@react-three/fiber": "^8.10.1",
"@tabler/icons-react": "^2.35.0",
diff --git a/apps/frontend/public/locales/en/getstarted.json b/apps/frontend/public/locales/en/getstarted.json
index 425e38a4..d562efd1 100644
--- a/apps/frontend/public/locales/en/getstarted.json
+++ b/apps/frontend/public/locales/en/getstarted.json
@@ -3,7 +3,7 @@
"title": "How would you like to participate?",
"visit": {
"title": "Explore BuildTheEarth",
- "subtitle": "Visit countries, cities and even villages, which we have already build.",
+ "subtitle": "Visit countries, cities and even villages, which we have already built.",
"button": "Join the server"
},
"build": {
diff --git a/apps/frontend/src/components/BackgroundImage.tsx b/apps/frontend/src/components/BackgroundImage.tsx
index ea7499db..686ba60f 100644
--- a/apps/frontend/src/components/BackgroundImage.tsx
+++ b/apps/frontend/src/components/BackgroundImage.tsx
@@ -22,12 +22,9 @@ export default function Background({
;
- // }
+
+ useEffect(() => {
+ console.log('Session changed:', session);
+ if (session?.data?.error === 'ForceLogout') {
+ signOut();
+ }
+ }, [session]);
+
+ if (session?.data?.error === 'ForceLogout') {
+ return null;
+ }
+
return (
- {session.status !== 'authenticated' ? (
+ {session.status !== 'authenticated' || !session.data ? (
<>
)}
diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts
index 5f03588f..0d6cf6e3 100644
--- a/apps/frontend/src/middleware.ts
+++ b/apps/frontend/src/middleware.ts
@@ -1,11 +1,14 @@
import { NextRequestWithAuth, withAuth } from 'next-auth/middleware';
+import { NextResponse } from 'next/server';
export function middleware(request: NextRequestWithAuth) {
if (request.nextUrl.pathname.includes('/manage') || request.nextUrl.pathname.includes('/me')) {
return withAuth(request);
}
+ // Allow other requests to continue normally
+ return NextResponse.next();
}
export const config = {
- matcher: ['/((?!api|_next/static|_next/image|auth|favicon.ico|robots.txt|images|$).*)'],
+ matcher: ['/((?!api|_next/static|_next/image|auth|favicon.ico|robots.txt|sitemap.xml|sitemap|images|$).*)'],
};
diff --git a/apps/frontend/src/pages/_app.tsx b/apps/frontend/src/pages/_app.tsx
index c5155a30..8d0a20dd 100644
--- a/apps/frontend/src/pages/_app.tsx
+++ b/apps/frontend/src/pages/_app.tsx
@@ -18,13 +18,15 @@ import { MantineProvider } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import { ModalsProvider } from '@mantine/modals';
import { Notifications } from '@mantine/notifications';
-import { SessionProvider } from 'next-auth/react';
+import { SessionProvider, signOut } from 'next-auth/react';
import { appWithTranslation } from 'next-i18next';
import { DefaultSeo } from 'next-seo';
import defaultSeo from 'next-seo.config';
import type { AppProps } from 'next/app';
import { Inter } from 'next/font/google';
import localFont from 'next/font/local';
+import { usePathname } from 'next/navigation';
+import { useRouter } from 'next/router';
import { useEffect } from 'react';
export const interFont = Inter({
@@ -43,6 +45,8 @@ function MyApp({ Component, pageProps }: AppProps) {
key: 'accessToken',
defaultValue: '',
});
+ const router = useRouter();
+ const session = pageProps.session;
useEffect(() => {
//@ts-ignore
@@ -66,7 +70,7 @@ function MyApp({ Component, pageProps }: AppProps) {
--font-minecraft: ${minecraftFont.style.fontFamily};
}
`}
-
+
diff --git a/apps/frontend/src/pages/api/auth/[...nextauth].ts b/apps/frontend/src/pages/api/auth/[...nextauth].ts
index 82ae48e3..18f5d888 100644
--- a/apps/frontend/src/pages/api/auth/[...nextauth].ts
+++ b/apps/frontend/src/pages/api/auth/[...nextauth].ts
@@ -1,25 +1,28 @@
-import NextAuth, { NextAuthOptions } from 'next-auth';
-
+import NextAuth, { AuthOptions, Session, getServerSession } from 'next-auth';
import { JWT } from 'next-auth/jwt';
import KeycloakProvider from 'next-auth/providers/keycloak';
const refreshAccessToken = async (token: JWT) => {
try {
- if (Date.now() > token.refreshTokenExpired) throw Error;
+ if (!token.refreshToken) throw new Error('Missing refresh token');
+ if (token.refreshTokenExpired && Date.now() >= token.refreshTokenExpired) throw new Error('Refresh token expired');
+
const details = {
- client_id: process.env.KEYCLOAK_ID,
+ client_id: process.env.NEXT_PUBLIC_KEYCLOAK_ID,
client_secret: process.env.KEYCLOAK_SECRET,
grant_type: 'refresh_token',
refresh_token: token.refreshToken,
};
+
const formBody: string[] = [];
- Object.entries(details).forEach(([key, value]: [string, any]) => {
+ Object.entries(details).forEach(([key, value]: [string, string | undefined]) => {
const encodedKey = encodeURIComponent(key);
- const encodedValue = encodeURIComponent(value);
+ const encodedValue = encodeURIComponent(value ?? '');
formBody.push(encodedKey + '=' + encodedValue);
});
+
const formData = formBody.join('&');
- const url = `${process.env.KEYCLOAK_URL}/protocol/openid-connect/token`;
+ const url = `${process.env.NEXT_PUBLIC_KEYCLOAK_URL}/protocol/openid-connect/token`;
const response = await fetch(url, {
method: 'POST',
headers: {
@@ -27,16 +30,26 @@ const refreshAccessToken = async (token: JWT) => {
},
body: formData,
});
+
const refreshedTokens = await response.json();
if (!response.ok) throw refreshedTokens;
+
+ const refreshedAccessExpiresIn = refreshedTokens.expires_in ?? 0;
+ const refreshedRefreshExpiresIn = refreshedTokens.refresh_expires_in ?? 0;
+
+ const nextRefreshExpiry = refreshedRefreshExpiresIn
+ ? Date.now() + (refreshedRefreshExpiresIn - 15) * 1000
+ : token.refreshTokenExpired;
+
return {
...token,
accessToken: refreshedTokens.access_token,
- accessTokenExpired: Date.now() + (refreshedTokens.expires_in - 15) * 1000,
+ accessTokenExpired: Date.now() + (refreshedAccessExpiresIn - 15) * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken,
- refreshTokenExpired: Date.now() + (refreshedTokens.refresh_expires_in - 15) * 1000,
+ refreshTokenExpired: nextRefreshExpiry,
};
} catch (error) {
+ console.error('Failed to refresh access token', error);
return {
...token,
error: 'RefreshAccessTokenError',
@@ -44,13 +57,16 @@ const refreshAccessToken = async (token: JWT) => {
}
};
-export const nextAuthConfig: NextAuthOptions = {
+export const authOptions: AuthOptions = {
secret: process.env.NEXTAUTH_SECRET,
+ pages: {
+ signIn: '/auth/signin',
+ },
providers: [
KeycloakProvider({
- clientId: process.env.KEYCLOAK_ID || '',
+ clientId: process.env.NEXT_PUBLIC_KEYCLOAK_ID || '',
clientSecret: process.env.KEYCLOAK_SECRET || '',
- issuer: process.env.KEYCLOAK_URL || '',
+ issuer: process.env.NEXT_PUBLIC_KEYCLOAK_URL || '',
profile: (profile) => {
return {
...profile,
@@ -69,25 +85,38 @@ export const nextAuthConfig: NextAuthOptions = {
return '/unauthorized';
}
},
- jwt: async ({ token, account, user }: any) => {
+
+ jwt: async ({ token, account, user }) => {
// Initial sign in
if (account && user) {
// Add access_token, refresh_token and expirations to the token right after signin
+ const accessExpiresIn = account.expires_in ?? 0;
+ const refreshExpiresIn = account.refresh_expires_in ?? 0;
+
token.accessToken = account.access_token;
token.refreshToken = account.refresh_token;
- token.accessTokenExpired = Date.now() + (account.expires_in - 15) * 1000;
- token.refreshTokenExpired = Date.now() + (account.refresh_expires_in - 15) * 1000;
+ token.accessTokenExpired = Date.now() + (accessExpiresIn - 15) * 1000;
+ token.refreshTokenExpired = refreshExpiresIn ? Date.now() + (refreshExpiresIn - 15) * 1000 : undefined;
token.user = user;
return token;
}
// Return previous token if the access token has not expired yet
- if (Date.now() < token.accessTokenExpired) return token;
+ if (Date.now() < token.accessTokenExpired || token.accessTokenExpired == null) return token;
+
+ console.log('Access token has expired, trying to refresh it');
// Access token has expired, try to update it
return refreshAccessToken(token);
},
- session: async ({ session, token }: any) => {
+ session: async ({ session, token }: { session: Session; token: JWT }) => {
if (token) {
+ // If refresh token failed, end the session by returning null
+ if (token.error === 'RefreshAccessTokenError') {
+ console.error('Refresh token expired or invalid - ending session');
+ return { ...session, error: 'ForceLogout' };
+ }
+
+ // @ts-expect-error shut up typescript
session.user = token.user;
session.error = token.error;
session.accessToken = token.accessToken;
@@ -95,9 +124,8 @@ export const nextAuthConfig: NextAuthOptions = {
return session;
},
},
- pages: {
- signIn: '/auth/signin',
- },
};
-export default NextAuth(nextAuthConfig);
+export default NextAuth(authOptions);
+
+export const getSession = () => getServerSession(authOptions);
diff --git a/apps/frontend/src/pages/auth/signin.tsx b/apps/frontend/src/pages/auth/signin.tsx
index b9939e43..0622e518 100644
--- a/apps/frontend/src/pages/auth/signin.tsx
+++ b/apps/frontend/src/pages/auth/signin.tsx
@@ -3,16 +3,18 @@ import { Button, Text, useMantineTheme } from '@mantine/core';
import Page from '@/components/Page';
import thumbnail from '@/public/images/placeholder.webp';
import { NextPage } from 'next';
-import { signIn } from 'next-auth/react';
+import { signIn, useSession } from 'next-auth/react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
const SignIn: NextPage = () => {
const router = useRouter();
const theme = useMantineTheme();
+ const { status } = useSession();
if (router.query.error) {
console.error('Sign In Error: ', router.query.error, ' Please report to BuildTheEarth');
}
+
return (
{
noAnimation
hash={
img?.hash ||
- "'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj0NE3+g8AAqUBjTCztj4AAAAASUVORK5CYII='"
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj0NE3+g8AAqUBjTCztj4AAAAASUVORK5CYII'
}
/>
@@ -67,20 +67,22 @@ const MePage: NextPage = ({ data }: any) => {
data
?.sort((a: any, b: any) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
.slice(activePage * 20 - 20, activePage * 20)
- .map((d: any) => ({
- name: d?.title + (d?.city ? ', ' + d.city : ''),
- src: `https://cdn.buildtheearth.net/uploads/${d?.image?.name}`,
- date: d?.createdAt,
- team: {
- name: d?.buildTeam.name,
- slug: d?.buildTeam.slug,
- logo: d?.buildTeam.icon,
- },
- hash:
- d?.image?.hash ||
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj0NE3+g8AAqUBjTCztj4AAAAASUVORK5CYII=',
- onClick: () => setFocus(d?.image?.name),
- })) || [{}]
+ .map((d: any) => {
+ return {
+ name: d?.title + (d?.city ? ', ' + d.city : ''),
+ src: `https://cdn.buildtheearth.net/uploads/${d?.image?.name}`,
+ date: d?.createdAt,
+ team: {
+ name: d?.buildTeam.name,
+ slug: d?.buildTeam.slug,
+ logo: d?.buildTeam.icon,
+ },
+ hash:
+ d?.image?.hash ||
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj0NE3+g8AAqUBjTCztj4AAAAASUVORK5CYII=',
+ onClick: () => setFocus(d?.image?.name),
+ };
+ }) || [{}]
}
showTooltipOnHover={true}
/>
diff --git a/apps/frontend/src/pages/teams/index.tsx b/apps/frontend/src/pages/teams/index.tsx
index 897b7d13..e5320956 100644
--- a/apps/frontend/src/pages/teams/index.tsx
+++ b/apps/frontend/src/pages/teams/index.tsx
@@ -19,6 +19,7 @@ import getCountryName from '@/utils/ISOCountries';
import { NextPage } from 'next';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
+import Link from 'next/link';
import { useRouter } from 'next/router';
import { useState } from 'react';
@@ -41,6 +42,9 @@ const Teams: NextPage = ({ data }: any) => {
{t('description')}
+ View teams on the map
+
+
{t('joining')}
setSearch(search)} />
diff --git a/apps/frontend/typings/next-auth/next-auth.d.ts b/apps/frontend/typings/next-auth/next-auth.d.ts
index 1c42dce0..ac869233 100644
--- a/apps/frontend/typings/next-auth/next-auth.d.ts
+++ b/apps/frontend/typings/next-auth/next-auth.d.ts
@@ -6,17 +6,6 @@ declare module 'next-auth' {
*/
interface Session {
user: {
- // username: string;
- // sub: string;
- // email_verified: boolean;
- // name: string;
- // preferred_username: string;
- // given_name: string;
- // family_name: string;
- // email: string;
- // id: string;
- // org_name?: string;
- // telephone?: string;
exp: number;
iat: number;
auth_time: number;
@@ -38,6 +27,16 @@ declare module 'next-auth' {
email: string;
username: string;
id: string;
+ realm_access: {
+ roles: string[];
+ };
+ resource_access: {
+ [string | 'account']: {
+ roles: string[];
+ };
+ };
+ minecraft?: string;
+ minecraft_verified?: boolean;
};
accessToken: string;
error: string;
@@ -67,7 +66,7 @@ declare module 'next-auth' {
type: string;
id: string;
accessToken: string;
- accessTokenExpires?: any;
+ accessTokenExpires?: number;
refreshToken: string;
idToken: string;
access_token: string;
@@ -106,7 +105,7 @@ declare module 'next-auth/jwt' {
accessToken: string;
refreshToken: string;
accessTokenExpired: number;
- refreshTokenExpired: number;
+ refreshTokenExpired: number | undefined;
user: User;
error: string;
}
diff --git a/package.json b/package.json
index 9831e10a..8808588e 100644
--- a/package.json
+++ b/package.json
@@ -26,14 +26,19 @@
"devDependencies": {
"@repo/prettier-config": "*",
"@repo/typescript-config": "*",
- "husky": "^9.1.5",
+ "husky": "^9.1.7",
"lint-staged": "^15.2.10",
- "turbo": "^2.5.0"
+ "turbo": "^2.5.4"
},
"workspaces": [
"apps/*",
"packages/*"
],
+ "resolutions": {
+ "prosemirror-model": "1.25.0",
+ "prosemirror-transform": "1.10.2",
+ "prosemirror-view": "1.39.1"
+ },
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
"prettier": "@repo/prettier-config"
}
diff --git a/packages/db/package.json b/packages/db/package.json
index c65c20ea..ac1c5a23 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -11,18 +11,18 @@
"types": "dist/index.d.ts",
"scripts": {
"db:generate": "prisma generate",
- "db:migrate": "prisma migrate deploy",
+ "db:migrate": "prisma migrate",
"db:studio": "prisma studio",
"db:pull": "prisma db pull",
"env:copy": "cp .env.example .env",
"build": "tsc"
},
"dependencies": {
- "@prisma/client": "5.21.1",
+ "@prisma/client": "6",
"@repo/typescript-config": "*"
},
"devDependencies": {
- "prisma": "5.21.1",
+ "prisma": "6",
"typescript": "^5.6.3"
},
"exports": {
diff --git a/packages/db/prisma/migrations/20250613201530_update_to_prisma_v6/migration.sql b/packages/db/prisma/migrations/20250613201530_update_to_prisma_v6/migration.sql
new file mode 100644
index 00000000..d7f0e7ed
--- /dev/null
+++ b/packages/db/prisma/migrations/20250613201530_update_to_prisma_v6/migration.sql
@@ -0,0 +1,11 @@
+-- AlterTable
+ALTER TABLE "_builders" ADD CONSTRAINT "_builders_AB_pkey" PRIMARY KEY ("A", "B");
+
+-- DropIndex
+DROP INDEX "_builders_AB_unique";
+
+-- AlterTable
+ALTER TABLE "_members" ADD CONSTRAINT "_members_AB_pkey" PRIMARY KEY ("A", "B");
+
+-- DropIndex
+DROP INDEX "_members_AB_unique";
diff --git a/packages/db/prisma/migrations/migration_lock.toml b/packages/db/prisma/migrations/migration_lock.toml
index fbffa92c..044d57cd 100644
--- a/packages/db/prisma/migrations/migration_lock.toml
+++ b/packages/db/prisma/migrations/migration_lock.toml
@@ -1,3 +1,3 @@
# Please do not edit this file manually
-# It should be added in your version-control system (i.e. Git)
-provider = "postgresql"
\ No newline at end of file
+# It should be added in your version-control system (e.g., Git)
+provider = "postgresql"
diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma
index 5580c86e..3627006d 100644
--- a/packages/db/prisma/schema.prisma
+++ b/packages/db/prisma/schema.prisma
@@ -1,5 +1,6 @@
generator client {
provider = "prisma-client-js"
+ // binaryTargets = ["native", "linux-musl","linux-musl-openssl-3.0.x"]
}
datasource db {
diff --git a/packages/prettier-config/index.json b/packages/prettier-config/index.json
index 1d7d4a6d..eba3f407 100644
--- a/packages/prettier-config/index.json
+++ b/packages/prettier-config/index.json
@@ -4,6 +4,5 @@
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
- "endOfLine": "lf",
- "plugins": ["prettier-plugin-organize-imports"]
+ "endOfLine": "lf"
}
diff --git a/test.json b/test.json
deleted file mode 100644
index a070f2e1..00000000
--- a/test.json
+++ /dev/null
@@ -1,174 +0,0 @@
-{
- "name": "web",
- "version": "1.1.0",
- "private": true,
- "author": {
- "name": "BuildTheEarth Development",
- "url": "https://buildtheearth.net/contact",
- "email": "development@buildtheearth.net"
- },
- "license": "MIT",
- "scripts": {
- "build": "turbo build",
- "build:api": "turbo build --filter=api...",
- "build:frontend": "turbo build --filter=frontend...",
- "clean": "turbo clean",
- "start": "turbo start",
- "dev": "turbo dev",
- "lint": "turbo lint",
- "prettier": "turbo prettier --ui=stream",
- "db:generate": "turbo db:generate",
- "db:studio": "yarn workspace @repo/db db:studio",
- "db:migrate": "yarn workspace api db:migrate",
- "git:pull": "git pull && yarn install && yarn run db:generate",
- "ws": "yarn workspace"
- },
- "devDependencies": {
- "@repo/prettier-config": "*",
- "@repo/typescript-config": "*",
- "husky": "^9.1.5",
- "lint-staged": "^15.2.10",
- "turbo": "^2.5.0",
- "@types/express": "^5.0.1",
- "@types/express-session": "^1.18.1",
- "@types/jsonwebtoken": "^9.0.9",
- "@types/multer": "^1.4.12",
- "@types/validator": "^13.12.3",
- "eslint-plugin-import": "^2.31.0",
- "ts-node": "^10.9.2",
- "eslint-plugin-storybook": "^0.8.0",
- "postcss": "^8.5.3",
- "postcss-preset-mantine": "^1.17.0",
- "postcss-simple-vars": "^7.0.1",
- "@types/node": "18.7.15",
- "@types/nprogress": "^0.2.0",
- "@types/react": "19.1.1",
- "@types/react-dom": "19.1.2",
- "@types/uuid": "^9.0.1",
- "@typescript-eslint/eslint-plugin": "^5.27.1",
- "@typescript-eslint/parser": "^5.27.1",
- "babel-eslint": "^10.1.0",
- "eslint": "^8.16.0",
- "eslint-config-next": "15.3.0",
- "eslint-plugin-compat": "^4.0.2",
- "eslint-plugin-escompat": "^3.1.0",
- "eslint-plugin-eslint-comments": "^3.2.0",
- "eslint-plugin-filenames": "^1.3.2",
- "eslint-plugin-i18n-text": "^1.0.1",
- "eslint-plugin-no-only-tests": "^3.0.0",
- "eslint-plugin-path": "^1.3.0",
- "eslint-plugin-prettier": "^4.0.0",
- "eslint-plugin-react": "^7.30.0",
- "prettier": "3.3.3",
- "prettier-plugin-organize-imports": "3.2.4",
- "prisma": "5.21.1",
- "typescript": "^5.6.3"
- },
- "dependencies": {
- "@prisma/client": "5.21.1",
- "@aws-sdk/client-s3": "^3.787.0",
- "@bte-germany/terraconvert": "^1.1.2",
- "@keycloak/keycloak-admin-client": "24.0.5",
- "@repo/db": "*",
- "@repo/prettier-config": "*",
- "@repo/typescript-config": "*",
- "@turf/helpers": "^7.2.0",
- "@turf/turf": "^7.2.0",
- "@types/showdown": "^2.0.6",
- "axios": "^1.8.4",
- "blurhash": "^2.0.5",
- "body-parser": "^2.2.0",
- "cors": "^2.8.5",
- "cron": "^4.1.4",
- "dotenv": "^16.5.0",
- "express": "^5.1.0",
- "express-session": "^1.18.1",
- "express-validator": "^7.2.1",
- "express-yup-middleware": "^2.0.0",
- "jsonwebtoken": "^9.0.2",
- "keycloak-connect": "^22.0.0",
- "minimatch": "^10.0.1",
- "multer": "^1.4.5-lts.2",
- "mysql": "^2.18.1",
- "plaiceholder": "^3.0.0",
- "sharp": "^0.34.1",
- "showdown": "^2.1.0",
- "uuid": "^11.1.0",
- "winston": "^3.17.0",
- "yup": "^1.6.1",
- "@mantine/charts": "^7.17.4",
- "@mantine/code-highlight": "^7.17.4",
- "@mantine/core": "^7.17.4",
- "@mantine/dates": "^7.17.4",
- "@mantine/form": "^7.17.4",
- "@mantine/hooks": "^7.17.4",
- "@mantine/modals": "^7.17.4",
- "@mantine/notifications": "^7.17.4",
- "@mantine/nprogress": "^7.17.4",
- "@mantine/spotlight": "^7.17.4",
- "@mantine/tiptap": "^7.17.4",
- "@mapbox/mapbox-gl-draw": "^1.4.3",
- "@tabler/icons-react": "^3.9.0",
- "@tiptap/core": "^2.11.7",
- "@tiptap/extension-highlight": "^2.11.7",
- "@tiptap/extension-link": "^2.11.7",
- "@tiptap/extension-placeholder": "^2.11.7",
- "@tiptap/extension-subscript": "^2.11.7",
- "@tiptap/extension-superscript": "^2.11.7",
- "@tiptap/extension-text-align": "^2.11.7",
- "@tiptap/extension-underline": "^2.11.7",
- "@tiptap/pm": "^2.11.7",
- "@tiptap/react": "^2.11.7",
- "@tiptap/starter-kit": "^2.11.7",
- "@types/mapbox__mapbox-gl-draw": "1.4.4",
- "clsx": "^2.1.1",
- "dayjs": "^1.11.11",
- "mantine-contextmenu": "^7.11.0",
- "mantine-datatable": "^7.12.4",
- "mapbox-gl": "2.13.0",
- "mapbox-gl-draw-snap-mode": "^0.2.0",
- "mapbox-gl-style-switcher": "^1.0.11",
- "moment": "^2.30.1",
- "moment-timezone": "^0.5.45",
- "next": "15.3.0",
- "next-auth": "^4.24.7",
- "next-transpile-modules": "^10.0.1",
- "react": "19.1.0",
- "react-dom": "19.1.0",
- "recharts": "^2.13.3",
- "swr": "^2.2.5",
- "@emotion/react": "^11.10.4",
- "@emotion/server": "^11.10.0",
- "@icons-pack/react-simple-icons": "^12.6.0",
- "@mantine/carousel": "7.17.4",
- "@mantine/dropzone": "7.17.4",
- "@mantine/next": "^6.0.22",
- "@react-three/fiber": "^8.10.1",
- "@types/sanitize-html": "^2.9.0",
- "cookies-next": "^4.1.0",
- "embla-carousel-react": "^7.0.2",
- "eslint-import-resolver-webpack": "^0.13.10",
- "flag-icons": "^6.6.6",
- "geolib": "^3.3.4",
- "highlight.js": "^11.10.0",
- "i18next": "^22.0.6",
- "keycloak-js": "^19.0.1",
- "motion": "^12.6.5",
- "next-i18next": "^13.0.3",
- "next-seo": "^6.1.0",
- "oauth-pkce": "^0.0.6",
- "postcss": "^8.5.3",
- "postcss-preset-mantine": "^1.17.0",
- "postcss-simple-vars": "^7.0.1",
- "react-i18next": "^12.2.0",
- "react-map-gl": "^7.0.16",
- "sanitize-html": "^2.12.1",
- "tabler-icons-react": "^1.48.0",
- "three": "^0.149.0",
- "tiptap-markdown": "^0.8.10",
- "vague-time": "^2.4.2"
- },
- "workspaces": ["apps/*", "packages/*"],
- "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
- "prettier": "@repo/prettier-config"
-}
diff --git a/yarn.lock b/yarn.lock
index c6ab0573..3f1ec285 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1480,50 +1480,62 @@ __metadata:
languageName: node
linkType: hard
-"@mantine/carousel@npm:7.17.4":
- version: 7.17.4
- resolution: "@mantine/carousel@npm:7.17.4"
+"@keycloak/keycloak-admin-client@npm:^26.2.0":
+ version: 26.2.0
+ resolution: "@keycloak/keycloak-admin-client@npm:26.2.0"
+ dependencies:
+ camelize-ts: "npm:^3.0.0"
+ url-join: "npm:^5.0.0"
+ url-template: "npm:^3.1.1"
+ checksum: 10c0/1a5e70883c9f387ca296542a3969b992fbe4db506b106ae74ac5658f77a1b16424eae5f3b76926e52323a56db9c57e85b7e48372e0e707ec4b5ca8f4d519130c
+ languageName: node
+ linkType: hard
+
+"@mantine/carousel@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/carousel@npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
- embla-carousel-react: ">=7.0.0"
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
+ embla-carousel: ">=8.0.0"
+ embla-carousel-react: ">=8.0.0"
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/bd518fae7bdfb8a465b09c0be0912905a0bd781d0bc89bee117b3c7b43c30b949ed82e8c93aceb1701f04fc2cb489f67511e463519523c2d1636f34313c8db8a
+ checksum: 10c0/3ede55a73677f257991c6d562722f7feb91f61cbe4b74573f50b4e434623d4761964cf862455c642827916826f790998de6c3554807de3a87a4372376ae55daf
languageName: node
linkType: hard
-"@mantine/charts@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/charts@npm:7.17.4"
+"@mantine/charts@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/charts@npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- recharts: ^2.13.3
- checksum: 10c0/05ca1f8236cd914c2ced2f20d0bc74ee6b4b79146f66d30466548658c8c0ed5789866481a0ad3c903e780281b90b178294032fedb07a2c7177a4f766857dca41
+ recharts: ">=2.13.3"
+ checksum: 10c0/88b5f46ac751515f1ce1a13c63ae3fabdf45455ac4dc296c616fd85eb24c7472875ac4d3c74e790fbe2aa4a0cfa5d03390ba2408e8e4b43f82db538e6ded473b
languageName: node
linkType: hard
-"@mantine/code-highlight@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/code-highlight@npm:7.17.4"
+"@mantine/code-highlight@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/code-highlight@npm:8.2.3"
dependencies:
clsx: "npm:^2.1.1"
highlight.js: "npm:^11.10.0"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/cc08ee18036cd1da105e58259db8ce823be40bd4a34dbf55dc0806a65782821c733ad49dd5e3133eeed0404dba9071e5627bdd1184f1a30a62332ba185033ba4
+ checksum: 10c0/7515255ea06c76036ae85f7dc1b12663301e76028f6e3f1d5a6e1ddefcfd86eddce776b74102010ab709d507b274c66d2dd704e2df2360524b35c0054ef680ed
languageName: node
linkType: hard
-"@mantine/core@npm:7.17.4, @mantine/core@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/core@npm:7.17.4"
+"@mantine/core@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/core@npm:8.2.3"
dependencies:
"@floating-ui/react": "npm:^0.26.28"
clsx: "npm:^2.1.1"
@@ -1532,72 +1544,72 @@ __metadata:
react-textarea-autosize: "npm:8.5.9"
type-fest: "npm:^4.27.0"
peerDependencies:
- "@mantine/hooks": 7.17.4
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/30d3ee636d2d6b77d6c2195867b0ede907de46d65470c59ba7d49427e02cb559da3f9337d8d71c782c82dc816c0946a5224019d4c2d7590ff1c71e2f2671ed9f
+ checksum: 10c0/cd5fc2a5f00c85f39151e8c08396838bf7aeb8fdffe9b8fcaf14099141a9fb8b62d91097ae9943f99be9e7410e4154ca9da6d7e9c55daa6f58099472a2144a96
languageName: node
linkType: hard
-"@mantine/dates@npm:7.17.4, @mantine/dates@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/dates@npm:7.17.4"
+"@mantine/dates@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/dates@npm:8.2.3"
dependencies:
clsx: "npm:^2.1.1"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
dayjs: ">=1.0.0"
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/a9d9964525be159520e272898ac090b59336fcf8c10542d571963f7a6b1b42019e1951deec78d3c08b8e6c149b2708f31222b719e651088579ef260cf108febe
+ checksum: 10c0/daca9928f045c1bf98c6389d8acf087491f53d9c54174ebc3b1242987a948bc5a80c0c9c94544c9f16ac99406dc35acc128765da1660ba0b38037c4ebc8411cb
languageName: node
linkType: hard
-"@mantine/dropzone@npm:7.17.4":
- version: 7.17.4
- resolution: "@mantine/dropzone@npm:7.17.4"
+"@mantine/dropzone@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/dropzone@npm:8.2.3"
dependencies:
- react-dropzone-esm: "npm:15.2.0"
+ react-dropzone: "npm:14.3.8"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/7c576c057fb438aa4fb7521abde3711df66917ec971ac983924fec2459f2f618c9da244a81cf178975e559243d2ce5e17f391791ff8ce186b97d9a39768d1f8a
+ checksum: 10c0/03c1f29fb6c21e0c8484d8659653c7f17ffd37e304e836880c987d0f124db353baa0286b4fc24676ca9dc8994297b974d67a24b8100db642ec57810640366cdf
languageName: node
linkType: hard
-"@mantine/form@npm:7.17.4, @mantine/form@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/form@npm:7.17.4"
+"@mantine/form@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/form@npm:8.2.3"
dependencies:
fast-deep-equal: "npm:^3.1.3"
klona: "npm:^2.0.6"
peerDependencies:
react: ^18.x || ^19.x
- checksum: 10c0/7cd922e2626f2499515929242fa41f213cca60402b98467479c650bd4e9ee6a0cec822ffaf3e2b43091d5e6f8473333e4e03555f6a86e40f4f25e6693d28276d
+ checksum: 10c0/9baea9363732600c5f7971d522929ac19ef3404e1323dca2b74cc91c532ee9772a07c8ce47cbe7a2c72b417e8cdab33e42a097e09295aadd8b08342952810afd
languageName: node
linkType: hard
-"@mantine/hooks@npm:7.17.4, @mantine/hooks@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/hooks@npm:7.17.4"
+"@mantine/hooks@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/hooks@npm:8.2.3"
peerDependencies:
react: ^18.x || ^19.x
- checksum: 10c0/e0ef1b5a53fa6a1d0be73590221422a45de715f8debc518af3f858272b108e4bcee13399a8129ab475db7af322f563f30b3ba6f839081538dda7fb11b572d8b4
+ checksum: 10c0/ef32f7fc7693835b33b84f0642ab54b6a87a450a6d451e11f7398a9c1b1dded234081a29120386414066ede45924c3bf2a4b11e97c34eec3b72d239d25f85dc9
languageName: node
linkType: hard
-"@mantine/modals@npm:7.17.4, @mantine/modals@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/modals@npm:7.17.4"
+"@mantine/modals@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/modals@npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/fbdd680c0668eea9ddce3723dfdf6384abdc94bad8c84c5b802d9b17e3260be7596ffcd9432805f9b57fb01c97821787bbbf68be8f85448d2e6adf1267b923f0
+ checksum: 10c0/c8bc340cf0f70003f2222f5be926c065304287d04d67f95d6eb0269cfea00bec0a65185dd849792a5b6005e208b66eda540bb185125610df5b06c17f0c288dc6
languageName: node
linkType: hard
@@ -1615,46 +1627,46 @@ __metadata:
languageName: node
linkType: hard
-"@mantine/notifications@npm:7.17.4, @mantine/notifications@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/notifications@npm:7.17.4"
+"@mantine/notifications@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/notifications@npm:8.2.3"
dependencies:
- "@mantine/store": "npm:7.17.4"
+ "@mantine/store": "npm:8.2.3"
react-transition-group: "npm:4.4.5"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/5eb380c234a7e2ce691ab198d9e796aa51b4653ecf01a281ae2ee5619c984dd167a1eb22891bb7b7e7fb688b6267699061040c3f908af68335659e8bd9dbb7c9
+ checksum: 10c0/d777d38b390211868f81786ca580d68369f6413bc790b8e286a0ca8cee2498a694f04e66e840d6a0b9cdee0a29f7f74afde17ab7013c772374e1c86dc5637174
languageName: node
linkType: hard
-"@mantine/nprogress@npm:7.17.4, @mantine/nprogress@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/nprogress@npm:7.17.4"
+"@mantine/nprogress@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/nprogress@npm:8.2.3"
dependencies:
- "@mantine/store": "npm:7.17.4"
+ "@mantine/store": "npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/6b23a88a3df9ccf9294eb8984078025e8a30955dd442e82384249fe6d8e54d9d91050a89d9b973c729fa434cd76ef381bfd0f663cba9a1574deb2bb54f283bb2
+ checksum: 10c0/22d37ac22f75d24e2c85056d63f080cb3f7e29daf3324fadd27f67d33f889e3f42cca6f149cdaa3345a1188716c9839e216f359c343292c38eb16534d9f241d2
languageName: node
linkType: hard
-"@mantine/spotlight@npm:7.17.4, @mantine/spotlight@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/spotlight@npm:7.17.4"
+"@mantine/spotlight@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/spotlight@npm:8.2.3"
dependencies:
- "@mantine/store": "npm:7.17.4"
+ "@mantine/store": "npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/17465b96895e19b64c659f117b5ac3cc504ee6b44dd6003ccbd459eabe11ab7c49a80b9df38130801011dd2b7d6d2b5b15ac35d9bd6033e84bef79806ebe7456
+ checksum: 10c0/3b83d3b93d778e9ffce0622f117e254da08aed101bf1701d4626a1569a949364d90fee77181324a2f07c1ef8520e89be191357c4ef320a6572429921fa0d03d3
languageName: node
linkType: hard
@@ -1673,12 +1685,12 @@ __metadata:
languageName: node
linkType: hard
-"@mantine/store@npm:7.17.4":
- version: 7.17.4
- resolution: "@mantine/store@npm:7.17.4"
+"@mantine/store@npm:8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/store@npm:8.2.3"
peerDependencies:
react: ^18.x || ^19.x
- checksum: 10c0/41b8a4075109924a64704234e4d47503b09b2cd40c0e8164f44c58ce6cee1a20963eb85458e0fd5c45e6382275bec81ec7bc82a6de6ffa001d899956e21c5701
+ checksum: 10c0/ae8d7006ab8f0fbdaa59cee5026c9179358f88505937a10bc2359334d27257ab6c42d4acb2ea0772687dd352114b56b0706664d8f7ea804669384d0abf2190aa
languageName: node
linkType: hard
@@ -1696,17 +1708,17 @@ __metadata:
languageName: node
linkType: hard
-"@mantine/tiptap@npm:7.17.4, @mantine/tiptap@npm:^7.17.4":
- version: 7.17.4
- resolution: "@mantine/tiptap@npm:7.17.4"
+"@mantine/tiptap@npm:^8.2.3":
+ version: 8.2.3
+ resolution: "@mantine/tiptap@npm:8.2.3"
peerDependencies:
- "@mantine/core": 7.17.4
- "@mantine/hooks": 7.17.4
+ "@mantine/core": 8.2.3
+ "@mantine/hooks": 8.2.3
"@tiptap/extension-link": ">=2.1.12"
"@tiptap/react": ">=2.1.12"
react: ^18.x || ^19.x
react-dom: ^18.x || ^19.x
- checksum: 10c0/c93b4d655882e024ce554dcd5befe086959c0232340c9479bdd413152fec407ac45f0743bcd80d9862c25e28a718c205ae5312638ce48d50a3400bfd89f87928
+ checksum: 10c0/a1ce7b51a479b7c037befc07dcfb66536ace75700c0aa4d79c18685e12ff86e92fcbe02d706928c826c3c55af45b8e63a851707937257932956759d2a523b66a
languageName: node
linkType: hard
@@ -1800,6 +1812,19 @@ __metadata:
languageName: node
linkType: hard
+"@mapbox/mapbox-gl-draw@npm:^1.5.0":
+ version: 1.5.0
+ resolution: "@mapbox/mapbox-gl-draw@npm:1.5.0"
+ dependencies:
+ "@mapbox/geojson-area": "npm:^0.2.2"
+ "@mapbox/geojson-normalize": "npm:^0.0.1"
+ "@mapbox/point-geometry": "npm:^1.1.0"
+ fast-deep-equal: "npm:^3.1.3"
+ nanoid: "npm:^5.0.9"
+ checksum: 10c0/435e55979ac7b198342568145cdd7c935a75e73f316bcb60ec89663731037c076abd030f129ee1f294a83fbd172c13d8db81666b88fe427c0846ba1574eff472
+ languageName: node
+ linkType: hard
+
"@mapbox/mapbox-gl-supported@npm:^1.5.0":
version: 1.5.0
resolution: "@mapbox/mapbox-gl-supported@npm:1.5.0"
@@ -1816,6 +1841,13 @@ __metadata:
languageName: node
linkType: hard
+"@mapbox/mapbox-gl-supported@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "@mapbox/mapbox-gl-supported@npm:3.0.0"
+ checksum: 10c0/4e9641072eab382ef6930be55199a5cf621bb96552dc941d865eb0007f746219352e1d2238b578eaaf4c0121018248ec73d108cc40b8ce177e2ad33c89dfea61
+ languageName: node
+ linkType: hard
+
"@mapbox/point-geometry@npm:0.1.0, @mapbox/point-geometry@npm:^0.1.0, @mapbox/point-geometry@npm:~0.1.0":
version: 0.1.0
resolution: "@mapbox/point-geometry@npm:0.1.0"
@@ -1823,6 +1855,13 @@ __metadata:
languageName: node
linkType: hard
+"@mapbox/point-geometry@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@mapbox/point-geometry@npm:1.1.0"
+ checksum: 10c0/fe43d00a92592a28835090722df771be50182ff5fc40705cbd571534e2397beef884a97f701869b4a99a61289700cf709f588883f4b085c034bbe722cf17155d
+ languageName: node
+ linkType: hard
+
"@mapbox/tiny-sdf@npm:^1.1.1":
version: 1.2.5
resolution: "@mapbox/tiny-sdf@npm:1.2.5"
@@ -1899,6 +1938,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/env@npm:15.3.6":
+ version: 15.3.6
+ resolution: "@next/env@npm:15.3.6"
+ checksum: 10c0/e448ec3893f1c0e8a35ea30c4834c2107f976112289d15b845675f36faa27d604f398b41c670343a948667e25c5a134b3bb73abd3f3c53f7ccf9c22981e10f27
+ languageName: node
+ linkType: hard
+
"@next/eslint-plugin-next@npm:15.3.0":
version: 15.3.0
resolution: "@next/eslint-plugin-next@npm:15.3.0"
@@ -1915,6 +1961,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-darwin-arm64@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-darwin-arm64@npm:15.3.5"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@next/swc-darwin-x64@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-darwin-x64@npm:15.3.0"
@@ -1922,6 +1975,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-darwin-x64@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-darwin-x64@npm:15.3.5"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@next/swc-linux-arm64-gnu@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-linux-arm64-gnu@npm:15.3.0"
@@ -1929,6 +1989,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-linux-arm64-gnu@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-linux-arm64-gnu@npm:15.3.5"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@next/swc-linux-arm64-musl@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-linux-arm64-musl@npm:15.3.0"
@@ -1936,6 +2003,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-linux-arm64-musl@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-linux-arm64-musl@npm:15.3.5"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@next/swc-linux-x64-gnu@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-linux-x64-gnu@npm:15.3.0"
@@ -1943,6 +2017,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-linux-x64-gnu@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-linux-x64-gnu@npm:15.3.5"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@next/swc-linux-x64-musl@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-linux-x64-musl@npm:15.3.0"
@@ -1950,6 +2031,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-linux-x64-musl@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-linux-x64-musl@npm:15.3.5"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@next/swc-win32-arm64-msvc@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-win32-arm64-msvc@npm:15.3.0"
@@ -1957,6 +2045,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-win32-arm64-msvc@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-win32-arm64-msvc@npm:15.3.5"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@next/swc-win32-x64-msvc@npm:15.3.0":
version: 15.3.0
resolution: "@next/swc-win32-x64-msvc@npm:15.3.0"
@@ -1964,6 +2059,13 @@ __metadata:
languageName: node
linkType: hard
+"@next/swc-win32-x64-msvc@npm:15.3.5":
+ version: 15.3.5
+ resolution: "@next/swc-win32-x64-msvc@npm:15.3.5"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -2041,61 +2143,73 @@ __metadata:
languageName: node
linkType: hard
-"@prisma/client@npm:5.21.1":
- version: 5.21.1
- resolution: "@prisma/client@npm:5.21.1"
+"@prisma/client@npm:6":
+ version: 6.9.0
+ resolution: "@prisma/client@npm:6.9.0"
peerDependencies:
prisma: "*"
+ typescript: ">=5.1.0"
peerDependenciesMeta:
prisma:
optional: true
- checksum: 10c0/066bf678d6bda048893eb9080818339a99bba995a0ba19935cccda30a10eb46c6674bc445bbbbb37881e52d3010c72ce4a28565156b80bfea5438cafd9868941
+ typescript:
+ optional: true
+ checksum: 10c0/4877d9d0f93ca404c066ce0536f4c2180a8a16a2b0ade8f15df9dd5286c74db55c5a99f4632f13b32045b966cb5f01e010806edda865aa22bbb0aef65f8b6e9f
+ languageName: node
+ linkType: hard
+
+"@prisma/config@npm:6.9.0":
+ version: 6.9.0
+ resolution: "@prisma/config@npm:6.9.0"
+ dependencies:
+ jiti: "npm:2.4.2"
+ checksum: 10c0/749c801e31d5284d78ce0df9a41b07a2a1d756d966b857472c2dcbfa1239da3cf8e60f3522d42d201fa08594b8ecbb88816f9381b521230ddac992a9bc566445
languageName: node
linkType: hard
-"@prisma/debug@npm:5.21.1":
- version: 5.21.1
- resolution: "@prisma/debug@npm:5.21.1"
- checksum: 10c0/5d69a13ae83efe3748efabb16ceae1dc0e9c2cc0c132e7fe46016a85a6d403eaa994094e8b30fb660b42e23abf5034b8ac1733bca30d6d66cb3fd763281e64e1
+"@prisma/debug@npm:6.9.0":
+ version: 6.9.0
+ resolution: "@prisma/debug@npm:6.9.0"
+ checksum: 10c0/abb6822299285d6847d7f86723ad4c222a828cb0cec0086bdf616c9d92e3c2c48b9f00d1b49655c489bbe6ad1a33cf4818c468ea8448b58a3233d67a967bb0ee
languageName: node
linkType: hard
-"@prisma/engines-version@npm:5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36":
- version: 5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36
- resolution: "@prisma/engines-version@npm:5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36"
- checksum: 10c0/3b4ca9f8911c36da847740951f73b2ab66f2f3619530dea7a9667764e8853675d2b7fbbd24e2568ece5ad9c5a78c5498e44be94af2979372d7b8743c83ee27a1
+"@prisma/engines-version@npm:6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e":
+ version: 6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e
+ resolution: "@prisma/engines-version@npm:6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e"
+ checksum: 10c0/a81ba1a619d3945ef7840d28f4b6eb0ed345aac86011d03ec8b9388129273e70887a6ece8052195a8d909bfce8f469cdab2a3fe079fe89d432d5510f35ebfa0b
languageName: node
linkType: hard
-"@prisma/engines@npm:5.21.1":
- version: 5.21.1
- resolution: "@prisma/engines@npm:5.21.1"
+"@prisma/engines@npm:6.9.0":
+ version: 6.9.0
+ resolution: "@prisma/engines@npm:6.9.0"
dependencies:
- "@prisma/debug": "npm:5.21.1"
- "@prisma/engines-version": "npm:5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36"
- "@prisma/fetch-engine": "npm:5.21.1"
- "@prisma/get-platform": "npm:5.21.1"
- checksum: 10c0/5a6a76457da5cb075e39a21e566b7e03295cacd357182b0f96f849e9950676c2a9d49cdaac7b974b1b9af6a350d96791a8fd30cf7814a669fa77e14ba17a7531
+ "@prisma/debug": "npm:6.9.0"
+ "@prisma/engines-version": "npm:6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e"
+ "@prisma/fetch-engine": "npm:6.9.0"
+ "@prisma/get-platform": "npm:6.9.0"
+ checksum: 10c0/62fb62141f277376a3194637a1e6123c4eecdd54df89138d0694813dc5d3007b6cec0bcd15dceb76d383f9379f844dffd0a62de87a0c5c761c61d46c91a7c6ba
languageName: node
linkType: hard
-"@prisma/fetch-engine@npm:5.21.1":
- version: 5.21.1
- resolution: "@prisma/fetch-engine@npm:5.21.1"
+"@prisma/fetch-engine@npm:6.9.0":
+ version: 6.9.0
+ resolution: "@prisma/fetch-engine@npm:6.9.0"
dependencies:
- "@prisma/debug": "npm:5.21.1"
- "@prisma/engines-version": "npm:5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36"
- "@prisma/get-platform": "npm:5.21.1"
- checksum: 10c0/068663fa15cb204d033b166588157915be11571a1584c914372968248faab0f7c236ec1086aaf194929ece3ae2a3d2d0b28e33dac114a1545e0dee1011dc669c
+ "@prisma/debug": "npm:6.9.0"
+ "@prisma/engines-version": "npm:6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e"
+ "@prisma/get-platform": "npm:6.9.0"
+ checksum: 10c0/cdab9a5e1e0b8f52cb32dfacb8fff4b50cccb64235a8fdc06ae1930089f6acf28cd6b4af4bb306aae25773db76db2d2d378244d23f7b491acf6f3f8d135e0ef6
languageName: node
linkType: hard
-"@prisma/get-platform@npm:5.21.1":
- version: 5.21.1
- resolution: "@prisma/get-platform@npm:5.21.1"
+"@prisma/get-platform@npm:6.9.0":
+ version: 6.9.0
+ resolution: "@prisma/get-platform@npm:6.9.0"
dependencies:
- "@prisma/debug": "npm:5.21.1"
- checksum: 10c0/3e6f2378e8a6086db0cc02e95e5b872b602263d0b2e55c3e18f6041bbbf9edea5edbfb9efce331527e75e13411eee68d2b34c24b9aeac9c579e75aac2a39432c
+ "@prisma/debug": "npm:6.9.0"
+ checksum: 10c0/e70bd4e9733d959b7dd5614d4f6f7af6d9f135e1719ddcf91a6c8baa4235882674b46f1c965e63ecfb7251327e7e670b0396d11c0a9c28648fc430de843da806
languageName: node
linkType: hard
@@ -2152,9 +2266,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@repo/db@workspace:packages/db"
dependencies:
- "@prisma/client": "npm:5.21.1"
+ "@prisma/client": "npm:6"
"@repo/typescript-config": "npm:*"
- prisma: "npm:5.21.1"
+ prisma: "npm:6"
typescript: "npm:^5.6.3"
languageName: unknown
linkType: soft
@@ -6114,7 +6228,7 @@ __metadata:
languageName: node
linkType: hard
-"@turf/turf@npm:^7.2.0":
+"@turf/turf@npm:^7.1.0, @turf/turf@npm:^7.2.0":
version: 7.2.0
resolution: "@turf/turf@npm:7.2.0"
dependencies:
@@ -6487,6 +6601,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/geojson-vt@npm:^3.2.5":
+ version: 3.2.5
+ resolution: "@types/geojson-vt@npm:3.2.5"
+ dependencies:
+ "@types/geojson": "npm:*"
+ checksum: 10c0/bfd9157c7d0441dc4b420e0c6df65b4e4b29f3d33cc77667b3dc5acd68ba6326bfbfd867642645357382e3374ceebfb9c5d15f2b2c0ee3e3c492e0b5a2bb71be
+ languageName: node
+ linkType: hard
+
"@types/geojson@npm:*, @types/geojson@npm:^7946.0.10":
version: 7946.0.14
resolution: "@types/geojson@npm:7946.0.14"
@@ -6501,7 +6624,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/geojson@npm:^7946.0.14":
+"@types/geojson@npm:^7946.0.14, @types/geojson@npm:^7946.0.16":
version: 7946.0.16
resolution: "@types/geojson@npm:7946.0.16"
checksum: 10c0/1ff24a288bd5860b766b073ead337d31d73bdc715e5b50a2cee5cb0af57a1ed02cc04ef295f5fa68dc40fe3e4f104dd31282b2b818a5ba3231bc1001ba084e3c
@@ -6579,6 +6702,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/mapbox-gl@npm:^3.4.1":
+ version: 3.4.1
+ resolution: "@types/mapbox-gl@npm:3.4.1"
+ dependencies:
+ "@types/geojson": "npm:*"
+ checksum: 10c0/475f48063a39cc791a0efa099f4a62b84df56a52e1d111e32d11930fcee8e281f20b157f4069ff0ff4d5e13889ce9b452ca4a57e5c27c6d70a32fb3f5a184e72
+ languageName: node
+ linkType: hard
+
"@types/mapbox__mapbox-gl-draw@npm:1.4.4":
version: 1.4.4
resolution: "@types/mapbox__mapbox-gl-draw@npm:1.4.4"
@@ -6589,6 +6721,34 @@ __metadata:
languageName: node
linkType: hard
+"@types/mapbox__mapbox-gl-draw@npm:^1.4.8":
+ version: 1.4.8
+ resolution: "@types/mapbox__mapbox-gl-draw@npm:1.4.8"
+ dependencies:
+ "@types/geojson": "npm:*"
+ mapbox-gl: "npm:*"
+ checksum: 10c0/320d46d88860259d193403080c0580a2949eb13ebc701938d8754b1317a3bb5f0c54de0bff9fb198f0a0ef9a44ec231dece229dd0e2d33d34022da76942d53cc
+ languageName: node
+ linkType: hard
+
+"@types/mapbox__point-geometry@npm:*, @types/mapbox__point-geometry@npm:^0.1.4":
+ version: 0.1.4
+ resolution: "@types/mapbox__point-geometry@npm:0.1.4"
+ checksum: 10c0/670191664ea0a6ccb4563500fe815a9aba029ba2f0528d42f9eb560ccb44f6542ba8674e2a3f6d41bd10ad8855b4df4782b5340c980ca182ef9fe6752f2737b8
+ languageName: node
+ linkType: hard
+
+"@types/mapbox__vector-tile@npm:^1.3.4":
+ version: 1.3.4
+ resolution: "@types/mapbox__vector-tile@npm:1.3.4"
+ dependencies:
+ "@types/geojson": "npm:*"
+ "@types/mapbox__point-geometry": "npm:*"
+ "@types/pbf": "npm:*"
+ checksum: 10c0/082907ed9cf96b82327dabf3b4c3a14746a825e4a81f0abf46b50e2557f25cbda652725d8af002e5edcc344a83c85e1a4b71a2d39ef4d829c243344a85ac13a6
+ languageName: node
+ linkType: hard
+
"@types/markdown-it@npm:^13.0.7":
version: 13.0.9
resolution: "@types/markdown-it@npm:13.0.9"
@@ -6694,6 +6854,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/pbf@npm:*, @types/pbf@npm:^3.0.5":
+ version: 3.0.5
+ resolution: "@types/pbf@npm:3.0.5"
+ checksum: 10c0/c32348c6c81e6c31fe4a1f59983e3a9904727b809fb1e5ddec4fad49abaf93070ec26ee0c04c6516536c181c945b3c7d9e226549eaac3b2e12cb7b57f549a49c
+ languageName: node
+ linkType: hard
+
"@types/prop-types@npm:*":
version: 15.7.13
resolution: "@types/prop-types@npm:15.7.13"
@@ -6805,6 +6972,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/supercluster@npm:^7.1.3":
+ version: 7.1.3
+ resolution: "@types/supercluster@npm:7.1.3"
+ dependencies:
+ "@types/geojson": "npm:*"
+ checksum: 10c0/0d55dad98df0990fc38a7bb64dc23dda46014187c0d7638e6f2b6717ba8931b13e5b1d394789833a2ac822014c977ef64623dffd81a0bbf39e52c53c8183741f
+ languageName: node
+ linkType: hard
+
"@types/triple-beam@npm:^1.3.2":
version: 1.3.5
resolution: "@types/triple-beam@npm:1.3.5"
@@ -7642,6 +7818,13 @@ __metadata:
languageName: node
linkType: hard
+"attr-accept@npm:^2.2.4":
+ version: 2.2.5
+ resolution: "attr-accept@npm:2.2.5"
+ checksum: 10c0/9b4cb82213925cab2d568f71b3f1c7a7778f9192829aac39a281e5418cd00c04a88f873eb89f187e0bf786fa34f8d52936f178e62cbefb9254d57ecd88ada99b
+ languageName: node
+ linkType: hard
+
"available-typed-arrays@npm:^1.0.7":
version: 1.0.7
resolution: "available-typed-arrays@npm:1.0.7"
@@ -7680,6 +7863,17 @@ __metadata:
languageName: node
linkType: hard
+"axios@npm:^1.9.0":
+ version: 1.9.0
+ resolution: "axios@npm:1.9.0"
+ dependencies:
+ follow-redirects: "npm:^1.15.6"
+ form-data: "npm:^4.0.0"
+ proxy-from-env: "npm:^1.1.0"
+ checksum: 10c0/9371a56886c2e43e4ff5647b5c2c3c046ed0a3d13482ef1d0135b994a628c41fbad459796f101c655e62f0c161d03883454474d2e435b2e021b1924d9f24994c
+ languageName: node
+ linkType: hard
+
"axobject-query@npm:^4.1.0":
version: 4.1.0
resolution: "axobject-query@npm:4.1.0"
@@ -8122,6 +8316,13 @@ __metadata:
languageName: node
linkType: hard
+"cheap-ruler@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "cheap-ruler@npm:4.0.0"
+ checksum: 10c0/0dab0383f7f60ead35d8a10265a532fba33e529509094625a694987906352badd11ad64cadc30a4c3f2e95cb7ff9ede58d1b9f0a24a803e5723c1cd714a5c314
+ languageName: node
+ linkType: hard
+
"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
@@ -8672,18 +8873,19 @@ __metadata:
resolution: "dashboard@workspace:apps/dashboard"
dependencies:
"@bte-germany/terraconvert": "npm:^1.1.2"
- "@mantine/charts": "npm:^7.17.4"
- "@mantine/code-highlight": "npm:^7.17.4"
- "@mantine/core": "npm:^7.17.4"
- "@mantine/dates": "npm:^7.17.4"
- "@mantine/form": "npm:^7.17.4"
- "@mantine/hooks": "npm:^7.17.4"
- "@mantine/modals": "npm:^7.17.4"
- "@mantine/notifications": "npm:^7.17.4"
- "@mantine/nprogress": "npm:^7.17.4"
- "@mantine/spotlight": "npm:^7.17.4"
- "@mantine/tiptap": "npm:^7.17.4"
- "@mapbox/mapbox-gl-draw": "npm:^1.4.3"
+ "@keycloak/keycloak-admin-client": "npm:^26.2.0"
+ "@mantine/charts": "npm:^8.2.3"
+ "@mantine/code-highlight": "npm:^8.2.3"
+ "@mantine/core": "npm:^8.2.3"
+ "@mantine/dates": "npm:^8.2.3"
+ "@mantine/form": "npm:^8.2.3"
+ "@mantine/hooks": "npm:^8.2.3"
+ "@mantine/modals": "npm:^8.2.3"
+ "@mantine/notifications": "npm:^8.2.3"
+ "@mantine/nprogress": "npm:^8.2.3"
+ "@mantine/spotlight": "npm:^8.2.3"
+ "@mantine/tiptap": "npm:^8.2.3"
+ "@mapbox/mapbox-gl-draw": "npm:^1.5.0"
"@repo/db": "npm:*"
"@repo/prettier-config": "npm:*"
"@repo/typescript-config": "npm:*"
@@ -8699,10 +8901,14 @@ __metadata:
"@tiptap/pm": "npm:^2.11.7"
"@tiptap/react": "npm:^2.11.7"
"@tiptap/starter-kit": "npm:^2.11.7"
- "@types/mapbox__mapbox-gl-draw": "npm:1.4.4"
+ "@turf/helpers": "npm:^7.2.0"
+ "@turf/turf": "npm:^7.2.0"
+ "@types/mapbox-gl": "npm:^3.4.1"
+ "@types/mapbox__mapbox-gl-draw": "npm:^1.4.8"
"@types/node": "npm:^20"
"@types/react": "npm:19.1.1"
"@types/react-dom": "npm:19.1.2"
+ axios: "npm:^1.9.0"
clsx: "npm:^2.1.1"
dayjs: "npm:^1.11.11"
eslint: "npm:^8"
@@ -8711,11 +8917,11 @@ __metadata:
mantine-contextmenu: "npm:^7.11.0"
mantine-datatable: "npm:^7.12.4"
mapbox-gl: "npm:2.13.0"
- mapbox-gl-draw-snap-mode: "npm:^0.2.0"
+ mapbox-gl-draw-snap-mode: "npm:^0.4.0"
mapbox-gl-style-switcher: "npm:^1.0.11"
moment: "npm:^2.30.1"
moment-timezone: "npm:^0.5.45"
- next: "npm:15.3.0"
+ next: "npm:15.3.6"
next-auth: "npm:^4.24.7"
next-transpile-modules: "npm:^10.0.1"
postcss: "npm:^8.5.3"
@@ -8725,7 +8931,9 @@ __metadata:
react-dom: "npm:19.1.0"
recharts: "npm:^2.13.3"
swr: "npm:^2.2.5"
+ tiptap-markdown: "npm:^0.9.0"
typescript: "npm:^5.6.3"
+ zustand: "npm:^5.0.4"
languageName: unknown
linkType: soft
@@ -9143,6 +9351,13 @@ __metadata:
languageName: node
linkType: hard
+"earcut@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "earcut@npm:3.0.1"
+ checksum: 10c0/cc1d3ed87a08db1a601b3b836bfb2fe22c8f923dfcfcd7419c64a5d4c270ac6d966d8d1e673cc699f8d69dc32bce84fd6d87136c49431fa35057bd7141627769
+ languageName: node
+ linkType: hard
+
"eastasianwidth@npm:^0.2.0":
version: 0.2.0
resolution: "eastasianwidth@npm:0.2.0"
@@ -10585,6 +10800,15 @@ __metadata:
languageName: node
linkType: hard
+"file-selector@npm:^2.1.0":
+ version: 2.1.2
+ resolution: "file-selector@npm:2.1.2"
+ dependencies:
+ tslib: "npm:^2.7.0"
+ checksum: 10c0/fe827e0e95410aacfcc3eabc38c29cc36055257f03c1c06b631a2b5af9730c142ad2c52f5d64724d02231709617bda984701f52bd1f4b7aca50fb6585a27c1d2
+ languageName: node
+ linkType: hard
+
"fill-range@npm:^7.1.1":
version: 7.1.1
resolution: "fill-range@npm:7.1.1"
@@ -10782,18 +11006,18 @@ __metadata:
"@emotion/react": "npm:^11.10.4"
"@emotion/server": "npm:^11.10.0"
"@icons-pack/react-simple-icons": "npm:^12.6.0"
- "@mantine/carousel": "npm:7.17.4"
- "@mantine/core": "npm:7.17.4"
- "@mantine/dates": "npm:7.17.4"
- "@mantine/dropzone": "npm:7.17.4"
- "@mantine/form": "npm:7.17.4"
- "@mantine/hooks": "npm:7.17.4"
- "@mantine/modals": "npm:7.17.4"
+ "@mantine/carousel": "npm:^8.2.3"
+ "@mantine/core": "npm:^8.2.3"
+ "@mantine/dates": "npm:^8.2.3"
+ "@mantine/dropzone": "npm:^8.2.3"
+ "@mantine/form": "npm:^8.2.3"
+ "@mantine/hooks": "npm:^8.2.3"
+ "@mantine/modals": "npm:^8.2.3"
"@mantine/next": "npm:^6.0.22"
- "@mantine/notifications": "npm:7.17.4"
- "@mantine/nprogress": "npm:7.17.4"
- "@mantine/spotlight": "npm:7.17.4"
- "@mantine/tiptap": "npm:7.17.4"
+ "@mantine/notifications": "npm:^8.2.3"
+ "@mantine/nprogress": "npm:^8.2.3"
+ "@mantine/spotlight": "npm:^8.2.3"
+ "@mantine/tiptap": "npm:^8.2.3"
"@mapbox/mapbox-gl-draw": "npm:^1.4.3"
"@react-three/fiber": "npm:^8.10.1"
"@repo/prettier-config": "npm:*"
@@ -10909,25 +11133,6 @@ __metadata:
languageName: node
linkType: hard
-"fsevents@npm:2.3.3":
- version: 2.3.3
- resolution: "fsevents@npm:2.3.3"
- dependencies:
- node-gyp: "npm:latest"
- checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
- conditions: os=darwin
- languageName: node
- linkType: hard
-
-"fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin":
- version: 2.3.3
- resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
- dependencies:
- node-gyp: "npm:latest"
- conditions: os=darwin
- languageName: node
- linkType: hard
-
"function-bind@npm:^1.1.2":
version: 1.1.2
resolution: "function-bind@npm:1.1.2"
@@ -11022,6 +11227,13 @@ __metadata:
languageName: node
linkType: hard
+"geojson-vt@npm:^4.0.2":
+ version: 4.0.2
+ resolution: "geojson-vt@npm:4.0.2"
+ checksum: 10c0/f2ca14d868e46f6262f5d3862f8e38a2418ecf9bd7cd6938a67bf87f1e2f8fbf65345d95996711b07cdf8f05ef512be0e2c20f0a8179c6393c2fd41badcb0532
+ languageName: node
+ linkType: hard
+
"geolib@npm:^3.3.4":
version: 3.3.4
resolution: "geolib@npm:3.3.4"
@@ -11554,12 +11766,12 @@ __metadata:
languageName: node
linkType: hard
-"husky@npm:^9.1.5":
- version: 9.1.5
- resolution: "husky@npm:9.1.5"
+"husky@npm:^9.1.7":
+ version: 9.1.7
+ resolution: "husky@npm:9.1.7"
bin:
husky: bin.js
- checksum: 10c0/f42efb95a026303eb880898760f802d88409780dd72f17781d2dfc302177d4f80b641cf1f1694f53f6d97c536c7397684133d8c8fe4a4426f7460186a7d1c6b8
+ checksum: 10c0/35bb110a71086c48906aa7cd3ed4913fb913823715359d65e32e0b964cb1e255593b0ae8014a5005c66a68e6fa66c38dcfa8056dbbdfb8b0187c0ffe7ee3a58f
languageName: node
linkType: hard
@@ -12291,6 +12503,15 @@ __metadata:
languageName: node
linkType: hard
+"jiti@npm:2.4.2":
+ version: 2.4.2
+ resolution: "jiti@npm:2.4.2"
+ bin:
+ jiti: lib/jiti-cli.mjs
+ checksum: 10c0/4ceac133a08c8faff7eac84aabb917e85e8257f5ad659e843004ce76e981c457c390a220881748ac67ba1b940b9b729b30fb85cbaf6e7989f04b6002c94da331
+ languageName: node
+ linkType: hard
+
"jose@npm:^4.15.5":
version: 4.15.9
resolution: "jose@npm:4.15.9"
@@ -12474,6 +12695,13 @@ __metadata:
languageName: node
linkType: hard
+"kdbush@npm:^4.0.2":
+ version: 4.0.2
+ resolution: "kdbush@npm:4.0.2"
+ checksum: 10c0/d50183b299c57e2573114e902ab47aed7494be3ca41b66d456779ecc3b2f153f491de341f9609965414784f728894f9a9001152eb9f3a40cd3755521c06a45a3
+ languageName: node
+ linkType: hard
+
"keycloak-connect@npm:^22.0.0":
version: 22.0.5
resolution: "keycloak-connect@npm:22.0.5"
@@ -12858,6 +13086,15 @@ __metadata:
languageName: node
linkType: hard
+"mapbox-gl-draw-snap-mode@npm:^0.4.0":
+ version: 0.4.0
+ resolution: "mapbox-gl-draw-snap-mode@npm:0.4.0"
+ dependencies:
+ "@turf/turf": "npm:^7.1.0"
+ checksum: 10c0/b8ecbedc4fa61e2de9cb76bff83e296689f68702957bbe0edd3e7d42ba7f2b65c749368157f68f2ae4374c6d3db5432fb37d1da8df8f11c104be787d3dfa626e
+ languageName: node
+ linkType: hard
+
"mapbox-gl-style-switcher@npm:^1.0.11":
version: 1.0.11
resolution: "mapbox-gl-style-switcher@npm:1.0.11"
@@ -12867,6 +13104,43 @@ __metadata:
languageName: node
linkType: hard
+"mapbox-gl@npm:*":
+ version: 3.12.0
+ resolution: "mapbox-gl@npm:3.12.0"
+ dependencies:
+ "@mapbox/jsonlint-lines-primitives": "npm:^2.0.2"
+ "@mapbox/mapbox-gl-supported": "npm:^3.0.0"
+ "@mapbox/point-geometry": "npm:^0.1.0"
+ "@mapbox/tiny-sdf": "npm:^2.0.6"
+ "@mapbox/unitbezier": "npm:^0.0.1"
+ "@mapbox/vector-tile": "npm:^1.3.1"
+ "@mapbox/whoots-js": "npm:^3.1.0"
+ "@types/geojson": "npm:^7946.0.16"
+ "@types/geojson-vt": "npm:^3.2.5"
+ "@types/mapbox__point-geometry": "npm:^0.1.4"
+ "@types/mapbox__vector-tile": "npm:^1.3.4"
+ "@types/pbf": "npm:^3.0.5"
+ "@types/supercluster": "npm:^7.1.3"
+ cheap-ruler: "npm:^4.0.0"
+ csscolorparser: "npm:~1.0.3"
+ earcut: "npm:^3.0.1"
+ geojson-vt: "npm:^4.0.2"
+ gl-matrix: "npm:^3.4.3"
+ grid-index: "npm:^1.1.0"
+ kdbush: "npm:^4.0.2"
+ martinez-polygon-clipping: "npm:^0.7.4"
+ murmurhash-js: "npm:^1.0.0"
+ pbf: "npm:^3.2.1"
+ potpack: "npm:^2.0.0"
+ quickselect: "npm:^3.0.0"
+ serialize-to-js: "npm:^3.1.2"
+ supercluster: "npm:^8.0.1"
+ tinyqueue: "npm:^3.0.0"
+ vt-pbf: "npm:^3.1.3"
+ checksum: 10c0/4457e001a9238b9ab88f3fc4efd4c22e4ada7a2237904d716c2ddc1f95298180ae6b35749631fbddc82d0083531e9fc46b4e09c346dbd29182dbe4c43b26d45e
+ languageName: node
+ linkType: hard
+
"mapbox-gl@npm:2.13.0":
version: 2.13.0
resolution: "mapbox-gl@npm:2.13.0"
@@ -12956,6 +13230,17 @@ __metadata:
languageName: node
linkType: hard
+"martinez-polygon-clipping@npm:^0.7.4":
+ version: 0.7.4
+ resolution: "martinez-polygon-clipping@npm:0.7.4"
+ dependencies:
+ robust-predicates: "npm:^2.0.4"
+ splaytree: "npm:^0.1.4"
+ tinyqueue: "npm:^1.2.0"
+ checksum: 10c0/0230c06056f2714994c0e65cb98cf487c45dfdffcef3b1c00e080577fdd52af80b71e87e7548283ac5cba7b2b2f9a5164e148fd88d17fd484e87f5d678bf199a
+ languageName: node
+ linkType: hard
+
"math-intrinsics@npm:^1.1.0":
version: 1.1.0
resolution: "math-intrinsics@npm:1.1.0"
@@ -13394,6 +13679,15 @@ __metadata:
languageName: node
linkType: hard
+"nanoid@npm:^5.0.9":
+ version: 5.1.5
+ resolution: "nanoid@npm:5.1.5"
+ bin:
+ nanoid: bin/nanoid.js
+ checksum: 10c0/e6004f1ad6c7123eeb037062c4441d44982037dc043aabb162457ef6986e99964ba98c63c975f96c547403beb0bf95bc537bd7bf9a09baf381656acdc2975c3c
+ languageName: node
+ linkType: hard
+
"napi-build-utils@npm:^1.0.1":
version: 1.0.2
resolution: "napi-build-utils@npm:1.0.2"
@@ -13588,6 +13882,67 @@ __metadata:
languageName: node
linkType: hard
+"next@npm:15.3.6":
+ version: 15.3.6
+ resolution: "next@npm:15.3.6"
+ dependencies:
+ "@next/env": "npm:15.3.6"
+ "@next/swc-darwin-arm64": "npm:15.3.5"
+ "@next/swc-darwin-x64": "npm:15.3.5"
+ "@next/swc-linux-arm64-gnu": "npm:15.3.5"
+ "@next/swc-linux-arm64-musl": "npm:15.3.5"
+ "@next/swc-linux-x64-gnu": "npm:15.3.5"
+ "@next/swc-linux-x64-musl": "npm:15.3.5"
+ "@next/swc-win32-arm64-msvc": "npm:15.3.5"
+ "@next/swc-win32-x64-msvc": "npm:15.3.5"
+ "@swc/counter": "npm:0.1.3"
+ "@swc/helpers": "npm:0.5.15"
+ busboy: "npm:1.6.0"
+ caniuse-lite: "npm:^1.0.30001579"
+ postcss: "npm:8.4.31"
+ sharp: "npm:^0.34.1"
+ styled-jsx: "npm:5.1.6"
+ peerDependencies:
+ "@opentelemetry/api": ^1.1.0
+ "@playwright/test": ^1.41.2
+ babel-plugin-react-compiler: "*"
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ dependenciesMeta:
+ "@next/swc-darwin-arm64":
+ optional: true
+ "@next/swc-darwin-x64":
+ optional: true
+ "@next/swc-linux-arm64-gnu":
+ optional: true
+ "@next/swc-linux-arm64-musl":
+ optional: true
+ "@next/swc-linux-x64-gnu":
+ optional: true
+ "@next/swc-linux-x64-musl":
+ optional: true
+ "@next/swc-win32-arm64-msvc":
+ optional: true
+ "@next/swc-win32-x64-msvc":
+ optional: true
+ sharp:
+ optional: true
+ peerDependenciesMeta:
+ "@opentelemetry/api":
+ optional: true
+ "@playwright/test":
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+ bin:
+ next: dist/bin/next
+ checksum: 10c0/98473d6c17fa7786cfa7178a301128161c40738514d29f22161083d1d178291f681fe72aed925e09bae0e1e865bb4f96ecc437e99d1a73d15b26e31b982103da
+ languageName: node
+ linkType: hard
+
"node-abi@npm:^3.3.0":
version: 3.67.0
resolution: "node-abi@npm:3.67.0"
@@ -14410,18 +14765,20 @@ __metadata:
languageName: node
linkType: hard
-"prisma@npm:5.21.1":
- version: 5.21.1
- resolution: "prisma@npm:5.21.1"
+"prisma@npm:6":
+ version: 6.9.0
+ resolution: "prisma@npm:6.9.0"
dependencies:
- "@prisma/engines": "npm:5.21.1"
- fsevents: "npm:2.3.3"
- dependenciesMeta:
- fsevents:
+ "@prisma/config": "npm:6.9.0"
+ "@prisma/engines": "npm:6.9.0"
+ peerDependencies:
+ typescript: ">=5.1.0"
+ peerDependenciesMeta:
+ typescript:
optional: true
bin:
prisma: build/index.js
- checksum: 10c0/14a276fb1891e1fec832da1632a4c3c9ae6909a094067d1ba94012fe2063b80f44680a0a373fc86e0e73b8a81455f20374303f70d93451060e06b93f0983b82b
+ checksum: 10c0/58217bca255ba01ecc23722b9f25f37e3c68780d3801ddd3438fc7987cd6abad5759b87d9a851725516ef035330e58ab3d56362c426f19cd22d2dccdb5bccb41
languageName: node
linkType: hard
@@ -14595,16 +14952,7 @@ __metadata:
languageName: node
linkType: hard
-"prosemirror-model@npm:^1.0.0, prosemirror-model@npm:^1.19.0, prosemirror-model@npm:^1.20.0, prosemirror-model@npm:^1.21.0":
- version: 1.22.3
- resolution: "prosemirror-model@npm:1.22.3"
- dependencies:
- orderedmap: "npm:^2.0.0"
- checksum: 10c0/c1405f9643a720fa65acb23f5f6c3980eb7df3ea6098828e3cfeab1b8d35e4952c96406dc25877f7ec02647970090ef96288776554b50be90ecac139a8ca268b
- languageName: node
- linkType: hard
-
-"prosemirror-model@npm:^1.23.0, prosemirror-model@npm:^1.24.1, prosemirror-model@npm:^1.25.0":
+"prosemirror-model@npm:1.25.0":
version: 1.25.0
resolution: "prosemirror-model@npm:1.25.0"
dependencies:
@@ -14671,16 +15019,7 @@ __metadata:
languageName: node
linkType: hard
-"prosemirror-transform@npm:^1.0.0, prosemirror-transform@npm:^1.1.0, prosemirror-transform@npm:^1.7.3":
- version: 1.10.0
- resolution: "prosemirror-transform@npm:1.10.0"
- dependencies:
- prosemirror-model: "npm:^1.21.0"
- checksum: 10c0/72c10b7689e0e7a575e2df66ee58980ae612f7bbad78b6781828276b5872763e05cb8fb1a92920f77e30ce5dd061d67c3de081dc4c060f23db5cbb2083100928
- languageName: node
- linkType: hard
-
-"prosemirror-transform@npm:^1.10.2":
+"prosemirror-transform@npm:1.10.2":
version: 1.10.2
resolution: "prosemirror-transform@npm:1.10.2"
dependencies:
@@ -14689,18 +15028,7 @@ __metadata:
languageName: node
linkType: hard
-"prosemirror-view@npm:^1.0.0, prosemirror-view@npm:^1.1.0, prosemirror-view@npm:^1.27.0, prosemirror-view@npm:^1.31.0":
- version: 1.34.2
- resolution: "prosemirror-view@npm:1.34.2"
- dependencies:
- prosemirror-model: "npm:^1.20.0"
- prosemirror-state: "npm:^1.0.0"
- prosemirror-transform: "npm:^1.1.0"
- checksum: 10c0/dd111994f6eb0a235aa01f511651e8317551cd4acb96de240fa4ebdeebb0ed8db44bc5e2f4a22e465e816b0f71d2220fd71d81b4f6451d27e4e3d36b3413b276
- languageName: node
- linkType: hard
-
-"prosemirror-view@npm:^1.37.0, prosemirror-view@npm:^1.37.2":
+"prosemirror-view@npm:1.39.1":
version: 1.39.1
resolution: "prosemirror-view@npm:1.39.1"
dependencies:
@@ -14821,6 +15149,13 @@ __metadata:
languageName: node
linkType: hard
+"quickselect@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "quickselect@npm:3.0.0"
+ checksum: 10c0/3a0d33b0ec06841d953accdfd735aa3d8b7922cddd12970544a2c4b0278871280d8f5ba496803600693c1e7b7b2fb57c31d2b14d99132f478888006a1be6e6b7
+ languageName: node
+ linkType: hard
+
"random-bytes@npm:~1.0.0":
version: 1.0.0
resolution: "random-bytes@npm:1.0.0"
@@ -14914,14 +15249,16 @@ __metadata:
languageName: node
linkType: hard
-"react-dropzone-esm@npm:15.2.0":
- version: 15.2.0
- resolution: "react-dropzone-esm@npm:15.2.0"
+"react-dropzone@npm:14.3.8":
+ version: 14.3.8
+ resolution: "react-dropzone@npm:14.3.8"
dependencies:
+ attr-accept: "npm:^2.2.4"
+ file-selector: "npm:^2.1.0"
prop-types: "npm:^15.8.1"
peerDependencies:
react: ">= 16.8 || 18.0.0"
- checksum: 10c0/a82dce547746f29214fa396e4060930250dd1b5f15c81e761035a05a09a1354493a90f79c1125c6341aee4b6226444bc7c7d29fc54ad266c149dfff170ef47a9
+ checksum: 10c0/e17b1832783cda7b8824fe9370e99185d1abbdd5e4980b2985d6321c5768c8de18ff7b9ad550c809ee9743269dea608ff74d5208062754ce8377ad022897b278
languageName: node
linkType: hard
@@ -15641,6 +15978,13 @@ __metadata:
languageName: node
linkType: hard
+"serialize-to-js@npm:^3.1.2":
+ version: 3.1.2
+ resolution: "serialize-to-js@npm:3.1.2"
+ checksum: 10c0/1aa5ba6f7a8e125e8d476b736ceae37e0cee6f45506ddabb04f95f31d7ad2260c922e17fa6da714e8ec84bbd491705b98974653928a57926d8b91f10de556617
+ languageName: node
+ linkType: hard
+
"serve-static@npm:1.16.2":
version: 1.16.2
resolution: "serve-static@npm:1.16.2"
@@ -16067,6 +16411,13 @@ __metadata:
languageName: node
linkType: hard
+"splaytree@npm:^0.1.4":
+ version: 0.1.4
+ resolution: "splaytree@npm:0.1.4"
+ checksum: 10c0/99c06883b832fd4725db790e736465eb1dafdc2dfa7faa238ebf45e718e0f66197e6aa6b229900496d5ab3529c8b151132e7956e34234956cbf63c7ae738faa7
+ languageName: node
+ linkType: hard
+
"splaytree@npm:^3.1.0":
version: 3.1.2
resolution: "splaytree@npm:3.1.2"
@@ -16442,6 +16793,15 @@ __metadata:
languageName: node
linkType: hard
+"supercluster@npm:^8.0.1":
+ version: 8.0.1
+ resolution: "supercluster@npm:8.0.1"
+ dependencies:
+ kdbush: "npm:^4.0.2"
+ checksum: 10c0/79121e6dbff67b3036ea6651f3baddb942478830ba3ecbc27455715ab5bd269de8381dc04618c0c15963346ea2ed0f81cd5f767c2978a63e2841807c73445d57
+ languageName: node
+ linkType: hard
+
"supports-color@npm:^5.3.0":
version: 5.5.0
resolution: "supports-color@npm:5.5.0"
@@ -16675,6 +17035,13 @@ __metadata:
languageName: node
linkType: hard
+"tinyqueue@npm:^1.2.0":
+ version: 1.2.3
+ resolution: "tinyqueue@npm:1.2.3"
+ checksum: 10c0/413b1ad64f700ef9d8d07242190ceb6ff921d0ab5b47f5b67ff7d3d2a394260914363b0e2c18039b900df793b26763fabc93da61e1b489f910795af6643b0355
+ languageName: node
+ linkType: hard
+
"tinyqueue@npm:^2.0.0, tinyqueue@npm:^2.0.3":
version: 2.0.3
resolution: "tinyqueue@npm:2.0.3"
@@ -16682,6 +17049,13 @@ __metadata:
languageName: node
linkType: hard
+"tinyqueue@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "tinyqueue@npm:3.0.0"
+ checksum: 10c0/edd6f1a6146aa3aa7bc85b44b3aacf44cddfa805b0901019272d7e9235894b4f28b2f9d09c1bce500ae48883b03708b6b871aa33920e895d2943720f7a9ad3ba
+ languageName: node
+ linkType: hard
+
"tippy.js@npm:^6.3.7":
version: 6.3.7
resolution: "tippy.js@npm:6.3.7"
@@ -16705,6 +17079,20 @@ __metadata:
languageName: node
linkType: hard
+"tiptap-markdown@npm:^0.9.0":
+ version: 0.9.0
+ resolution: "tiptap-markdown@npm:0.9.0"
+ dependencies:
+ "@types/markdown-it": "npm:^13.0.7"
+ markdown-it: "npm:^14.1.0"
+ markdown-it-task-lists: "npm:^2.1.1"
+ prosemirror-markdown: "npm:^1.11.1"
+ peerDependencies:
+ "@tiptap/core": ^3.0.1
+ checksum: 10c0/d599a93aac549963488394f1112937f9f2d268b1a48640ab5e1ba0b05590d184c26f0b5f714601a82c5437d77691819e1d790e0c14e6dc252a3476b6227b39a1
+ languageName: node
+ linkType: hard
+
"to-fast-properties@npm:^2.0.0":
version: 2.0.0
resolution: "to-fast-properties@npm:2.0.0"
@@ -16866,7 +17254,7 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.8.0, tslib@npm:^2.8.1":
+"tslib@npm:^2.7.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
@@ -16893,58 +17281,58 @@ __metadata:
languageName: node
linkType: hard
-"turbo-darwin-64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-darwin-64@npm:2.5.0"
+"turbo-darwin-64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-darwin-64@npm:2.5.4"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"turbo-darwin-arm64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-darwin-arm64@npm:2.5.0"
+"turbo-darwin-arm64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-darwin-arm64@npm:2.5.4"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"turbo-linux-64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-linux-64@npm:2.5.0"
+"turbo-linux-64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-linux-64@npm:2.5.4"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"turbo-linux-arm64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-linux-arm64@npm:2.5.0"
+"turbo-linux-arm64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-linux-arm64@npm:2.5.4"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
-"turbo-windows-64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-windows-64@npm:2.5.0"
+"turbo-windows-64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-windows-64@npm:2.5.4"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
-"turbo-windows-arm64@npm:2.5.0":
- version: 2.5.0
- resolution: "turbo-windows-arm64@npm:2.5.0"
+"turbo-windows-arm64@npm:2.5.4":
+ version: 2.5.4
+ resolution: "turbo-windows-arm64@npm:2.5.4"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"turbo@npm:^2.5.0":
- version: 2.5.0
- resolution: "turbo@npm:2.5.0"
- dependencies:
- turbo-darwin-64: "npm:2.5.0"
- turbo-darwin-arm64: "npm:2.5.0"
- turbo-linux-64: "npm:2.5.0"
- turbo-linux-arm64: "npm:2.5.0"
- turbo-windows-64: "npm:2.5.0"
- turbo-windows-arm64: "npm:2.5.0"
+"turbo@npm:^2.5.4":
+ version: 2.5.4
+ resolution: "turbo@npm:2.5.4"
+ dependencies:
+ turbo-darwin-64: "npm:2.5.4"
+ turbo-darwin-arm64: "npm:2.5.4"
+ turbo-linux-64: "npm:2.5.4"
+ turbo-linux-arm64: "npm:2.5.4"
+ turbo-windows-64: "npm:2.5.4"
+ turbo-windows-arm64: "npm:2.5.4"
dependenciesMeta:
turbo-darwin-64:
optional: true
@@ -16960,7 +17348,7 @@ __metadata:
optional: true
bin:
turbo: bin/turbo
- checksum: 10c0/df4ca3e8fd1121702b8f3a2f64a4a02ab886e01236a72fe54ebd1b4e33368a9dd3cec393d7eb143a6173f96c70f4dba4f7abfff84a0b5535076fc662a746e0ac
+ checksum: 10c0/81af22a24ca643d25f344184e4f37b8b70f118c9a3af13e6b6604b652eb0a7d98fdb03c195457ed1831ecf246ef06cb9566ae3e361d105fe89fbcdbb3f135c38
languageName: node
linkType: hard
@@ -17545,9 +17933,9 @@ __metadata:
dependencies:
"@repo/prettier-config": "npm:*"
"@repo/typescript-config": "npm:*"
- husky: "npm:^9.1.5"
+ husky: "npm:^9.1.7"
lint-staged: "npm:^15.2.10"
- turbo: "npm:^2.5.0"
+ turbo: "npm:^2.5.4"
languageName: unknown
linkType: soft
@@ -17857,3 +18245,24 @@ __metadata:
checksum: 10c0/6a56185ca67080c252dfe96039da02094cfd780bd7a45768708105f114dea39ae9abc80ffaa7f3f6104e6490db325bd443b857ab5eab8ebf9a697318cd163bb6
languageName: node
linkType: hard
+
+"zustand@npm:^5.0.4":
+ version: 5.0.4
+ resolution: "zustand@npm:5.0.4"
+ peerDependencies:
+ "@types/react": ">=18.0.0"
+ immer: ">=9.0.6"
+ react: ">=18.0.0"
+ use-sync-external-store: ">=1.2.0"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+ checksum: 10c0/5b6220f51b315cef3224a6517fcc00fa553df1af604009a9f02f8091727fe52e0499bc093be3efdd64b9fa4ad9238346aff21f34cdf79355207fcad097031596
+ languageName: node
+ linkType: hard