diff --git a/.nvmrc b/.nvmrc index aacb518..4a58985 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.17 +18.18 diff --git a/global.d.ts b/global.d.ts index b1db87e..1be2dc9 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,4 +1,337 @@ +import * as L from "leaflet"; + // Use type safe message keys with `next-intl` type Messages = typeof import("./src/messages/fr.json"); declare interface IntlMessages extends Messages {} + +// For some reason, @types/leaflet.markercluster isn't being recognized +// I'm including it here by hand +declare module "leaflet" { + class MarkerCluster extends Marker { + /* + * Recursively retrieve all child markers of this cluster. + */ + getAllChildMarkers(): Marker[]; + + /* + * Returns the count of how many child markers we have. + */ + getChildCount(): number; + + /* + * Zoom to the minimum of showing all of the child markers, or the extents of this cluster. + */ + zoomToBounds(options?: FitBoundsOptions): void; + + /* + * Returns the cluster bounds. + */ + getBounds(): LatLngBounds; + + /* + * Spiderfies the child markers of this cluster. + */ + spiderfy(): void; + + /* + * Unspiderfies a cluster (opposite of spiderfy). + */ + unspiderfy(): void; + } + + interface MarkerClusterGroupOptions extends LayerOptions { + /* + * The maximum radius that a cluster will cover from the central marker (in pixels). Default 80. + * Decreasing will make more, smaller clusters. You can also use a function that accepts + * the current map zoom and returns the maximum cluster radius in pixels + */ + maxClusterRadius?: number | ((zoom: number) => number) | undefined; + + /* + * Function used to create the cluster icon + */ + iconCreateFunction?: + | ((cluster: MarkerCluster) => Icon | DivIcon) + | undefined; + + /* + * Map pane where the cluster icons will be added. + * Defaults to L.Marker's default (currently 'markerPane') + */ + clusterPane?: string | undefined; + + /* + * When you click a cluster at any zoom level we spiderfy it + * so you can see all of its markers. + */ + spiderfyOnEveryZoom?: boolean | undefined; + + /* + * When you click a cluster at the bottom zoom level we spiderfy it + * so you can see all of its markers. + */ + spiderfyOnMaxZoom?: boolean | undefined; + + /* + * When you mouse over a cluster it shows the bounds of its markers. + */ + showCoverageOnHover?: boolean | undefined; + + /* + * When you click a cluster we zoom to its bounds. + */ + zoomToBoundsOnClick?: boolean | undefined; + + /* + * If set to true, overrides the icon for all added markers to make them appear as a 1 size cluster. + */ + singleMarkerMode?: boolean | undefined; + + /* + * If set, at this zoom level and below markers will not be clustered. This defaults to disabled. + */ + disableClusteringAtZoom?: number | undefined; + + /* + * Clusters and markers too far from the viewport are removed from the map + * for performance. + */ + removeOutsideVisibleBounds?: boolean | undefined; + + /* + * Smoothly split / merge cluster children when zooming and spiderfying. + * If L.DomUtil.TRANSITION is false, this option has no effect (no animation is possible). + */ + animate?: boolean | undefined; + + /* + * If set to true (and animate option is also true) then adding individual markers to the + * MarkerClusterGroup after it has been added to the map will add the marker and animate it + * into the cluster. Defaults to false as this gives better performance when bulk adding markers. + * addLayers does not support this, only addLayer with individual Markers. + */ + animateAddingMarkers?: boolean | undefined; + + /* + * Custom function to calculate spiderfy shape positions + */ + spiderfyShapePositions?: + | ((count: number, centerPoint: Point) => Point[]) + | undefined; + + /* + * Increase from 1 to increase the distance away from the center that spiderfied markers are placed. + * Use if you are using big marker icons (Default: 1). + */ + spiderfyDistanceMultiplier?: number | undefined; + + /* + * Allows you to specify PolylineOptions to style spider legs. + * By default, they are { weight: 1.5, color: '#222', opacity: 0.5 }. + */ + spiderLegPolylineOptions?: PolylineOptions | undefined; + + /* + * Boolean to split the addLayers processing in to small intervals so that the page does not freeze. + */ + chunkedLoading?: boolean | undefined; + + /* + * Time delay (in ms) between consecutive periods of processing for addLayers. Default to 50ms. + */ + chunkDelay?: number | undefined; + + /* + * Time interval (in ms) during which addLayers works before pausing to let the rest of the page process. + * In particular, this prevents the page from freezing while adding a lot of markers. Defaults to 200ms. + */ + chunkInterval?: number | undefined; + + /* + * Callback function that is called at the end of each chunkInterval. + * Typically used to implement a progress indicator. Defaults to null. + */ + chunkProgress?: + | (( + processedMarkers: number, + totalMarkers: number, + elapsedTime: number, + ) => void) + | undefined; + + /* + * Options to pass when creating the L.Polygon(points, options) to show the bounds of a cluster. + * Defaults to empty + */ + polygonOptions?: PolylineOptions | undefined; + } + + /* + * Cluster-related handler functions. + */ + type AnimationEndEventHandlerFn = (event: LeafletEvent) => void; + type SpiderfyEventHandlerFn = (event: MarkerClusterSpiderfyEvent) => void; + + /* + * Event fired on spiderfy cluster actions. + */ + interface MarkerClusterSpiderfyEvent extends LeafletEvent { + /* + * The cluster that fired the event. + */ + cluster: MarkerCluster; + + /* + * The markers in the cluster that fired the event. + */ + markers: Marker[]; + } + + /* + * Extend existing event handler function map to include cluster events. + */ + interface LeafletEventHandlerFnMap { + /* + * Fires when overlapping markers get spiderified. + */ + spiderfied?: SpiderfyEventHandlerFn | undefined; + + /* + * Fires when overlapping markers get unspiderified. + */ + unspiderfied?: SpiderfyEventHandlerFn | undefined; + + /* + * Fires when marker clustering/unclustering animation has completed. + */ + animationend?: AnimationEndEventHandlerFn | undefined; + } + + /* + * Extend Evented to include cluster events. + */ + interface Evented { + on( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + on( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + + off( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + off( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + + once( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + once( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + + addEventListener( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + addEventListener( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + + removeEventListener( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + removeEventListener( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + + addOneTimeEventListener( + type: "spiderfied" | "unspiderfied", + fn?: SpiderfyEventHandlerFn, + context?: any, + ): this; + addOneTimeEventListener( + type: "animationend", + fn?: AnimationEndEventHandlerFn, + context?: any, + ): this; + } + + class MarkerClusterGroup extends FeatureGroup { + constructor(options?: MarkerClusterGroupOptions); + + /* + * Bulk methods for adding and removing markers and should be favoured over the + * single versions when doing bulk addition/removal of markers. + */ + addLayers(layers: Layer[], skipLayerAddEvent?: boolean): this; + + removeLayers(layers: Layer[]): this; + + clearLayers(): this; + + /* + * If you have a marker in your MarkerClusterGroup and you want to get the visible + * parent of it + */ + getVisibleParent(marker: Marker): Marker; + + /* + * If you have customized the clusters icon to use some data from the contained markers, + * and later that data changes, use this method to force a refresh of the cluster icons. + */ + refreshClusters( + clusters?: Marker | Marker[] | LayerGroup | { [index: string]: Layer }, + ): this; + + /* + * Returns the total number of markers contained within that cluster. + */ + getChildCount(): number; + + /* + * Returns the array of total markers contained within that cluster. + */ + getAllChildMarkers(): Marker[]; + + /* + * Returns true if the given layer (marker) is in the cluster. + */ + hasLayer(layer: Layer): boolean; + + /* + * Zooms to show the given marker (spiderfying if required), + * calls the callback when the marker is visible on the map. + */ + zoomToShowLayer(layer: Layer, callback?: () => void): void; + } + + /* + * Create a marker cluster group, optionally given marker cluster group options. + */ + function markerClusterGroup( + options?: MarkerClusterGroupOptions, + ): MarkerClusterGroup; +} diff --git a/package.json b/package.json index a7a0cde..50a3e2a 100644 --- a/package.json +++ b/package.json @@ -12,20 +12,19 @@ "format:fix": "prettier --write \"**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@auth/mongodb-adapter": "^2.5.0", - "@headlessui-float/react": "^0.13.3", - "@headlessui/react": "^1.7.18", - "@headlessui/tailwindcss": "^0.2.0", - "@hookform/resolvers": "^3.3.3", + "@auth/mongodb-adapter": "^3.5.2", + "@headlessui-float/react": "^0.15.0", + "@headlessui/react": "^2.1.8", + "@headlessui/tailwindcss": "^0.2.1", + "@hookform/resolvers": "^3.9.0", "@infra-blocks/zod-utils": "^0.4.2", - "@kodingdotninja/use-tailwind-breakpoint": "^0.0.5", "@next/bundle-analyzer": "^14.0.4", "@tanstack/react-query": "^5.29.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@turf/boolean-point-in-polygon": "^7.0.0-alpha.114", - "@types/node": "^20.8.4", + "@types/node": "^22.7.2", "autoprefixer": "^10.4.16", - "date-fns": "^2.30.0", + "date-fns": "^4.1.0", "geojson": "^0.5.0", "jotai": "^2.6.1", "leaflet": "^1.9.4", @@ -33,22 +32,22 @@ "leaflet-gesture-handling": "^1.2.2", "leaflet-draw": "^1.0.4", "leaflet.markercluster": "^1.5.3", - "leaflet.smooth_marker_bouncing": "^3.0.3", + "leaflet.smooth_marker_bouncing": "3.0.3", "lodash": "^4.17.21", "mongodb": "^6.4.0", "next": "^14.0.4", "next-auth": "^5.0.0-beta.16", "next-intl": "^3.3.2", "next-pwa": "^5.6.0", - "next-safe-action": "^6.2.0", + "next-safe-action": "^7.9.3", "nodemailer": "^6.9.12", - "postcss": "8.4.33", + "postcss": "^8.4.47", "react": "^18.2.0", - "react-date-range": "^1.4.0", - "react-datepicker": "^4.18.0", + "react-date-range": "^2.0.1", + "react-datepicker": "^7.4.0", "react-dom": "^18.2.0", "react-hook-form": "^7.46.1", - "react-icons": "^4.10.1", + "react-icons": "^5.3.0", "react-input-mask": "^2.0.4", "react-leaflet": "^4.2.1", "react-leaflet-cluster": "^2.1.0", @@ -63,25 +62,23 @@ "zod": "^3.22.4" }, "devDependencies": { - "@swc-jotai/react-refresh": "^0.1.0", + "@swc-jotai/react-refresh": "^0.2.0", "@tailwindcss/forms": "^0.5.4", "@testing-library/jest-dom": "^6.1.2", - "@testing-library/react": "^14.0.0", - "@types/leaflet": "^1.9.3", - "@types/leaflet.markercluster": "^1.5.1", + "@types/leaflet": "^1.9.12", + "@types/leaflet.markercluster": "^1.5.4", "@types/lodash": "^4.14.195", "@types/nodemailer": "^6.4.8", "@types/react": "^18.2.27", "@types/react-date-range": "^1.4.9", - "@types/react-datepicker": "^4.15.0", "@types/react-dom": "^18.2.18", "@types/react-input-mask": "^3.0.2", - "eslint": "^8.54.0", + "eslint": "8", "eslint-config-next": "^14.0.4", "eslint-config-prettier": "^9.0.0", "eslint-plugin-react-hooks": "^5.0.0-canary-7118f5dd7-20230705", "prettier": "^3.1.1", - "prettier-plugin-tailwindcss": "^0.5.3" + "prettier-plugin-tailwindcss": "^0.6.8" }, "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" } diff --git a/src/app/[locale]/(main)/delete-account/page.tsx b/src/app/[locale]/(main)/delete-account/page.tsx index e4a1d03..de4bf38 100644 --- a/src/app/[locale]/(main)/delete-account/page.tsx +++ b/src/app/[locale]/(main)/delete-account/page.tsx @@ -23,7 +23,9 @@ export default function Contact() { setDeleting(true); const result = await deleteAccount(); - if (result.serverError) { + if (result?.validationErrors) { + throw new Error("ERR_VALIDATION"); + } else if (result?.serverError) { throw new Error(result.serverError); } @@ -68,7 +70,7 @@ export default function Contact() { diff --git a/src/app/[locale]/(main)/elo/page.tsx b/src/app/[locale]/(main)/elo/page.tsx index 4df5e43..55ec3f8 100644 --- a/src/app/[locale]/(main)/elo/page.tsx +++ b/src/app/[locale]/(main)/elo/page.tsx @@ -192,7 +192,7 @@ export default function Elo() { diff --git a/src/app/[locale]/(main)/zones/components/ZoneForm.tsx b/src/app/[locale]/(main)/zones/components/ZoneForm.tsx index cadff0c..fe69530 100644 --- a/src/app/[locale]/(main)/zones/components/ZoneForm.tsx +++ b/src/app/[locale]/(main)/zones/components/ZoneForm.tsx @@ -102,7 +102,7 @@ export const ZoneForm = ({