Migrate grateWizard from React/Next.js to native Nuxt integration

- Port all React components to Vue 3 (GwTabs, GwMN, GwCRA, GwCRS,
  GwMap, GwRelations, GwPerimeterList)
- Port hooks to Vue composables (useCesiumProfiles, useSavedPerimeters)
- Copy pure TS services and utils (duniter/, ss58, gratewizard utils)
- Add Leaflet + Geoman + MarkerCluster dependencies
- Serve grateWizard as popup via /gratewizard?popup (layout: false)
  and info page on /gratewizard (with Librodrome layout)
- Remove public/gratewizard-app/ static Next.js export
- Refine UI: compact tabs, buttons, inputs, cards, perimeter list
- Use Ğ1 breve everywhere, French locale for all dates and amounts
- Rename roles: vendeur→offre / acheteur→reçoit le produit ou service
- Rename prix→évaluation in all visible text
- Add calculated result column in CRA and CRS relation tables
- DU/Ğ1 selector uses toggle switch (same as role toggle)
- Auto-scroll to monetary data card on polygon selection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-21 15:33:01 +01:00
parent 524c7a0fc2
commit 2b5543791f
93 changed files with 2186 additions and 585 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ export default defineAppConfig({
], ],
}, },
gratewizard: { gratewizard: {
url: '/gratewizard-app/', url: '/gratewizard?popup',
popup: { popup: {
width: 420, width: 420,
height: 720, height: 720,
+1 -1
View File
@@ -37,7 +37,7 @@
</NuxtLink> </NuxtLink>
<NuxtLink to="/admin/pages/gratewizard" class="sidebar-link" active-class="sidebar-link--active"> <NuxtLink to="/admin/pages/gratewizard" class="sidebar-link" active-class="sidebar-link--active">
<div class="i-lucide-sparkles h-4 w-4" /> <div class="i-lucide-sparkles h-4 w-4" />
GrateWizard grateWizard
</NuxtLink> </NuxtLink>
<p class="sidebar-section">Livre</p> <p class="sidebar-section">Livre</p>
+194
View File
@@ -0,0 +1,194 @@
<template>
<div class="flex flex-col gap-5 pt-4">
<div class="card-surface">
<div class="flex flex-col items-center gap-3">
<p class="gw-metric">Coefficient relatif &agrave; l'anciennet&eacute;</p>
<!-- My seniority -->
<div class="flex flex-col items-center gap-1 w-full max-w-80">
<label class="gw-text text-sm">Mon anciennet&eacute;</label>
<input
v-model="myDate"
type="date"
:min="Block0Date"
:max="todayDate"
class="gw-input w-full"
/>
<p :class="['gw-text text-center text-sm', { invisible: !myDate }]">
{{ getDays(myDate) || 0 }} DUs cr&eacute;&eacute;s
</p>
</div>
<!-- Role -->
<div class="flex items-center justify-center gap-2 mt-2">
<span class="gw-text text-xs text-right">offre<br />le produit ou service</span>
<label class="gw-toggle shrink-0">
<input v-model="isSeller" type="checkbox" />
<span class="gw-toggle-slider" />
</label>
<span class="gw-text text-xs">re&ccedil;oit<br />le produit ou service</span>
</div>
<!-- Other party name + add button -->
<div class="flex items-center gap-4 w-full max-w-80">
<div class="flex-1">
<label class="gw-text text-sm">Nom {{ !isSeller ? 'du receveur' : "de l'offreur" }}</label>
<input
v-model="otherName"
type="text"
maxlength="25"
class="gw-input w-full"
/>
</div>
<button
v-if="!isBaseFriend"
class="gw-icon-btn self-end mb-0.5"
:disabled="!otherName || !otherDate || isFriend"
:title="friends.some((f) => f.name === otherName) ? 'Mettre \u00e0 jour' : 'Ajouter relation'"
@click="addFriend"
>
<svg v-if="friends.some((f) => f.name === otherName)" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16" /></svg>
<svg v-else class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg>
</button>
</div>
<!-- Other party date -->
<div class="flex flex-col items-center gap-1 w-full max-w-80">
<label class="gw-text text-sm">
Anciennet&eacute; {{ otherName ? 'de ' + otherName : !isSeller ? 'du receveur' : "de l'offreur" }}
</label>
<input
v-model="otherDate"
type="date"
:min="Block0Date"
:max="todayDate"
class="gw-input w-full max-w-64"
/>
<p :class="['gw-text text-center text-sm', { invisible: !otherDate }]">
{{ getDays(otherDate) || 0 }} DUs cr&eacute;&eacute;s
</p>
</div>
<!-- Price -->
<div class="flex items-center gap-3 justify-center">
<label class="gw-text text-sm whitespace-nowrap">&Eacute;valuation de r&eacute;f.</label>
<div class="relative">
<input
:value="price"
type="number"
min="0"
:step="Math.ceil(Number(price) / 10 / 2) || 1"
placeholder="0.00"
class="gw-input w-40 pr-14"
@input="price = Math.min(Number(($event.target as HTMLInputElement).value), 9999).toString()"
/>
<select
v-model="currency"
class="absolute right-2 top-1/2 -translate-y-1/2 bg-transparent text-white/40 text-sm cursor-pointer border-0 outline-none"
@change="price = '1'"
>
<option value="DU" class="bg-surface-100">DU</option>
<option value="G1" class="bg-surface-100">&#x11E;1</option>
</select>
</div>
</div>
<!-- Discount -->
<div class="flex items-center gap-3 justify-center">
<label class="gw-text text-sm whitespace-nowrap">R&eacute;duction newbie</label>
<div class="relative">
<input
:value="discount"
type="number"
min="0"
:step="Math.ceil(Number(discount) / 10 / 2) || 1"
placeholder="0"
class="gw-input w-40 pr-8"
@input="discount = Math.min(Number(($event.target as HTMLInputElement).value), 99).toString()"
/>
<span class="absolute right-2 top-1/2 -translate-y-1/2 text-white/40 text-sm pointer-events-none">%</span>
</div>
</div>
<!-- Corrected price -->
<p class="gw-title mt-4">
&Eacute;valuation corrig&eacute;e : {{ fr(getFinalPrice(isSeller ? myDate : otherDate)) }} {{ currencyDisplay }}
</p>
</div>
</div>
<!-- Relations table -->
<GratewizardGwRelations
:items="tableItems"
:base-friends="baseFriends"
result-label="ÉVAL."
@select="(f) => { otherName = f.name; otherDate = f.date; }"
@remove="removeFriend"
/>
</div>
</template>
<script setup lang="ts">
import { Block0Date, getDays, getRatio, dateToString, fr, type Friend, type TableFriend } from '~/utils/gratewizard';
const todayDate = dateToString(new Date());
const baseFriends: Friend[] = [
{ name: 'Bloc 0', date: Block0Date },
{ name: 'Newbie', date: todayDate },
];
const price = useLocalStorage('price', '1');
const discount = useLocalStorage('discount', '0');
const myDate = useLocalStorage<string | undefined>('myDate', undefined);
const isSeller = useLocalStorage('isSeller', true);
const currency = useLocalStorage('currency', 'DU');
const otherName = useLocalStorage('otherName', '');
const otherDate = useLocalStorage<string | undefined>('otherDate', undefined);
const friends = useLocalStorage<Friend[]>('friends', []);
const currencyDisplay = computed(() => currency.value === 'G1' ? '\u011e1' : 'DU');
function getFinalPrice(date: string | undefined): number {
if ((!date && !myDate.value) || (!date && !otherDate.value)) return Number(price.value);
const ratio = date
? myDate.value && (!isSeller.value || !otherDate.value)
? getRatio(date, myDate.value)
: getRatio(date, otherDate.value)
: 1;
const d = Number(discount.value) / 100;
const p = Number(price.value);
return (1 - d) * p + d * p * ratio;
}
function addFriend() {
if (!otherDate.value || !otherName.value) return;
if (friends.value.some((f) => f.name === otherName.value)) {
friends.value = friends.value.map((f) =>
f.name === otherName.value ? { ...f, date: otherDate.value! } : f
);
} else {
friends.value = [...friends.value, { name: otherName.value, date: otherDate.value }];
}
}
function removeFriend(name: string) {
friends.value = friends.value.filter((f) => f.name !== name);
}
const isBaseFriend = computed(() =>
baseFriends.some((f) => f.name === otherName.value && f.date === otherDate.value)
);
const isFriend = computed(() =>
friends.value.some((f) => f.name === otherName.value && f.date === otherDate.value)
);
const tableItems = computed<TableFriend[]>(() => {
return baseFriends.concat(friends.value).map((friend) => ({
...friend,
displayName: friend.name.substring(0, 10),
displayDate: new Date(friend.date).toLocaleDateString('fr-FR', { dateStyle: 'short' }),
result: fr(getFinalPrice(friend.date)),
du: getDays(friend.date),
}));
});
</script>
+208
View File
@@ -0,0 +1,208 @@
<template>
<div class="flex flex-col gap-5 pt-4">
<div class="card-surface">
<div class="flex flex-col items-center gap-3">
<p class="gw-metric">Coefficient relatif au solde net</p>
<!-- Buyer name + add button -->
<div class="flex items-center gap-4 w-full max-w-80">
<div class="flex-1">
<label class="gw-text text-sm">Qui re&ccedil;oit le produit ou service ?</label>
<input
v-model="otherName"
type="text"
maxlength="25"
class="gw-input w-full"
/>
</div>
<button
v-if="!isBaseFriend"
class="gw-icon-btn self-end mb-0.5"
:disabled="!otherName || !otherDate || isFriend"
:title="friends.some((f) => f.name === otherName) ? 'Mettre \u00e0 jour' : 'Ajouter relation'"
@click="addFriend"
>
<svg v-if="friends.some((f) => f.name === otherName)" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16" /></svg>
<svg v-else class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg>
</button>
</div>
<!-- Buyer seniority -->
<div class="flex flex-col items-center gap-1 w-full max-w-80">
<label class="gw-text text-sm">
Anciennet&eacute; {{ otherName ? 'de ' + otherName : 'du receveur' }}
</label>
<input
v-model="otherDate"
type="date"
:min="Block0Date"
:max="todayDate"
class="gw-input w-full max-w-64"
/>
<p :class="['gw-text text-center text-sm', { invisible: !otherDate }]">
{{ seniority || 0 }} DUs cr&eacute;&eacute;s
</p>
</div>
<!-- DU Balance -->
<div class="flex items-center gap-3 justify-center">
<label class="gw-text text-sm whitespace-nowrap">Solde DU{{ otherName ? ' de ' + otherName : ' du receveur' }}</label>
<div class="relative">
<input
:value="balance"
type="number"
min="0"
placeholder="0"
class="gw-input w-40 pr-10"
@input="balance = Math.max(0, Number(($event.target as HTMLInputElement).value)).toString()"
/>
<span class="absolute right-2 top-1/2 -translate-y-1/2 text-white/40 text-sm pointer-events-none">DU</span>
</div>
</div>
<!-- Net balance -->
<p v-if="seniority > 0" class="gw-text text-center">
Solde net : {{ fr(netBalance, 0) }} DU
{{ netBalance < 0
? ' (b\u00e9n\u00e9ficie de valeurs \u2192 majoration)'
: netBalance > 0
? ' (alimente en valeurs \u2192 minoration)'
: ' (\u00e9quilibr\u00e9)' }}
</p>
<!-- Reference value -->
<div class="flex items-center gap-3 justify-center">
<label class="gw-text text-sm whitespace-nowrap">Valeur de r&eacute;f.</label>
<div class="relative">
<input
:value="refValue"
type="number"
min="0"
:step="Math.ceil(v / 10 / 2) || 1"
placeholder="0.00"
class="gw-input w-40 pr-14"
@input="refValue = Math.min(Number(($event.target as HTMLInputElement).value), 9999).toString()"
/>
<select
v-model="currency"
class="absolute right-2 top-1/2 -translate-y-1/2 bg-transparent text-white/40 text-sm cursor-pointer border-0 outline-none"
@change="refValue = '1'"
>
<option value="DU" class="bg-surface-100">DU</option>
<option value="G1" class="bg-surface-100">&#x11E;1</option>
</select>
</div>
</div>
<!-- Weight slider -->
<div class="flex items-center gap-3 w-full max-w-80">
<span class="gw-text whitespace-nowrap text-sm">Poids</span>
<input
v-model="weight"
type="range"
min="0"
max="1"
step="0.01"
class="flex-1 h-2 rounded-lg appearance-none cursor-pointer accent-accent"
/>
<span class="gw-text text-sm w-10 text-right">{{ fr(w) }}</span>
</div>
<!-- Formula + result -->
<div class="w-full max-w-sm text-center p-4 rounded-lg bg-surface-200">
<p class="gw-text text-xs font-mono mb-1">
V &times; (1 + w &times; (1 &minus; S / C))
</p>
<template v-if="seniority > 0">
<p class="gw-text text-xs font-mono mb-2">
{{ fr(v) }} &times; (1 + {{ fr(w) }} &times; (1 &minus; {{ fr(s, 0) }} / {{ fr(seniority, 0) }}))
= {{ fr(v) }} &times; {{ fr(factor) }}
</p>
<p class="gw-title">= {{ fr(correctedValue) }} {{ currencyDisplay }}</p>
</template>
<p v-else class="gw-text text-xs">Renseigner l'anciennet&eacute; pour calculer</p>
</div>
</div>
</div>
<!-- Relations table (shared with CRA) -->
<GratewizardGwRelations
:items="tableItems"
:base-friends="baseFriends"
result-label="VALEUR"
@select="(f) => { otherName = f.name; otherDate = f.date; }"
@remove="removeFriend"
/>
</div>
</template>
<script setup lang="ts">
import { Block0Date, getDays, dateToString, fr, type Friend, type TableFriend } from '~/utils/gratewizard';
const todayDate = dateToString(new Date());
const baseFriends: Friend[] = [{ name: 'Bloc 0', date: Block0Date }];
// Shared state with CRA (same localStorage keys)
const currency = useLocalStorage('currency', 'DU');
const friends = useLocalStorage<Friend[]>('friends', []);
const otherName = useLocalStorage('otherName', '');
const otherDate = useLocalStorage<string | undefined>('otherDate', undefined);
// CRS-specific state
const refValue = useLocalStorage('crs-value', '1');
const balance = useLocalStorage('crs-balance', '0');
const weight = useLocalStorage('crs-weight', '0.50');
const currencyDisplay = computed(() => currency.value === 'G1' ? '\u011e1' : 'DU');
const seniority = computed(() => getDays(otherDate.value));
const s = computed(() => Number(balance.value));
const w = computed(() => Number(weight.value));
const v = computed(() => Number(refValue.value));
const netBalance = computed(() => s.value - seniority.value);
const correctedValue = computed(() =>
seniority.value > 0 ? v.value * (1 + w.value * (1 - s.value / seniority.value)) : v.value
);
const factor = computed(() =>
seniority.value > 0 ? 1 + w.value * (1 - s.value / seniority.value) : 1
);
// Friend management (shared with CRA)
function addFriend() {
if (!otherDate.value || !otherName.value) return;
if (friends.value.some((f) => f.name === otherName.value)) {
friends.value = friends.value.map((f) =>
f.name === otherName.value ? { ...f, date: otherDate.value! } : f
);
} else {
friends.value = [...friends.value, { name: otherName.value, date: otherDate.value }];
}
}
function removeFriend(name: string) {
friends.value = friends.value.filter((f) => f.name !== name);
}
const isBaseFriend = computed(() =>
baseFriends.some((f) => f.name === otherName.value && f.date === otherDate.value)
);
const isFriend = computed(() =>
friends.value.some((f) => f.name === otherName.value && f.date === otherDate.value)
);
function getCorrectedValueFor(friendDate: string): number {
const c = getDays(friendDate);
if (c <= 0) return v.value;
return v.value * (1 + w.value * (1 - s.value / c));
}
const tableItems = computed<TableFriend[]>(() => {
return baseFriends.concat(friends.value).map((f) => ({
...f,
displayName: f.name.substring(0, 10),
displayDate: new Date(f.date).toLocaleDateString('fr-FR', { dateStyle: 'short' }),
result: fr(getCorrectedValueFor(f.date)),
du: getDays(f.date),
}));
});
</script>
+272
View File
@@ -0,0 +1,272 @@
<template>
<div class="flex flex-col gap-5 pt-4">
<!-- Error state -->
<div v-if="error" class="p-8 flex flex-col gap-4">
<p class="gw-text">Erreur : {{ error }}</p>
</div>
<template v-else>
<!-- Currency selector -->
<div class="flex items-center justify-center gap-2">
<span class="gw-text text-xs">DU</span>
<label class="gw-toggle">
<input
type="checkbox"
:checked="unit === 'G1'"
@change="unit = ($event.target as HTMLInputElement).checked ? 'G1' : 'DU'"
/>
<span class="gw-toggle-slider" />
</label>
<span class="gw-text text-xs">&#x11E;1</span>
</div>
<!-- Monetary data card -->
<div ref="topRef" class="card-surface">
<div class="flex flex-col items-center gap-3">
<p class="gw-metric">Masse mon&eacute;taire / Membres</p>
<span v-if="polygon" class="gw-chip">
{{ activePerimeterName ?? 'S\u00e9lection manuelle' }}
</span>
<!-- Loading -->
<div v-if="loading" class="flex flex-col items-center gap-2 py-4">
<div class="gw-spinner" />
<p class="gw-text text-sm">Connexion au r&eacute;seau...</p>
</div>
<!-- Data grid -->
<div v-else-if="monetary" class="grid grid-cols-2 gap-2 w-full max-w-sm">
<div :class="['text-center p-2 rounded-md text-xs', polygon ? 'bg-accent/10' : 'bg-surface-200']">
<p class="gw-text">M (masse{{ polygon ? ' locale' : '' }})</p>
<p class="gw-title mt-1">
{{ mnIsLoading ? '...' : formatValue(displayM!, unit, duDaily) }}
</p>
</div>
<div :class="['text-center p-2 rounded-md text-xs', polygon ? 'bg-accent/10' : 'bg-surface-200']">
<p class="gw-text">N (membres{{ polygon ? ' locaux' : '' }})</p>
<p class="gw-title mt-1">{{ displayN.toLocaleString('fr-FR') }}</p>
</div>
<div :class="['text-center p-2 rounded-md text-xs', polygon ? 'bg-accent/10' : 'bg-surface-200']">
<p class="gw-text">M / N{{ polygon ? ' (local)' : '' }}</p>
<p class="gw-title mt-1">
{{ mnIsLoading ? '...' : formatValue(displayMN!, unit, duDaily) }}
</p>
</div>
<div class="text-center p-2 rounded-md text-xs bg-surface-200">
<p class="gw-text">DU journalier</p>
<p class="gw-title mt-1">{{ fr(duDaily) }} &#x11E;1</p>
</div>
<!-- Extra cells when polygon is active -->
<template v-if="polygon && nLocal > 0 && monetary">
<div class="text-center p-2 rounded-md text-xs bg-surface-200">
<p class="gw-text">M / N (r&eacute;seau)</p>
<p class="gw-title mt-1">{{ formatValue(mnG1, unit, duDaily) }}</p>
</div>
<div class="text-center p-2 rounded-md text-xs bg-accent/10">
<p class="gw-text">Part du r&eacute;seau</p>
<p class="gw-title mt-1">{{ fr(nLocal / monetary.membersCount * 100) }} %</p>
</div>
</template>
<div class="col-span-2 text-center">
<p class="gw-text">
Bloc #{{ monetary.blockNumber }} &mdash;
{{ new Date(monetary.timestamp).toLocaleDateString('fr-FR', { dateStyle: 'long' }) }}
</p>
</div>
</div>
<!-- Clear selection button -->
<div v-if="polygon" class="flex flex-col items-center gap-2">
<button
class="gw-btn gw-btn-danger"
@click="handleClearSelection"
>
P&eacute;rim&egrave;tre monde
</button>
</div>
<!-- Saved perimeters -->
<GratewizardGwPerimeterList
:perimeters="perimeters"
@load="handleLoadPerimeter"
@delete="deletePerimeter"
/>
</div>
</div>
<!-- Map card -->
<div class="card-surface">
<div class="flex flex-col items-center gap-3">
<p class="gw-metric">
Carte des membres g&eacute;olocalis&eacute;s
<span v-if="!geoLoading && v1Pubkeys" class="gw-chip ml-2">
{{ geoMembers.length.toLocaleString('fr-FR') }}
</span>
</p>
<p v-if="geoError" class="gw-text">Erreur Cesium+ : {{ geoError }}</p>
<!-- Loading map -->
<div v-if="geoLoading || !v1Pubkeys" class="flex flex-col items-center gap-2 py-4">
<div class="gw-spinner" />
<p class="gw-text text-sm">Chargement des profils g&eacute;olocalis&eacute;s...</p>
</div>
<!-- Map -->
<template v-else>
<GratewizardGwMap
:members="geoMembers"
:clear-trigger="clearTrigger"
:load-polygon="loadPolygonTrigger"
@polygon-change="handlePolygonChange"
/>
<p v-if="!loading && monetary" class="gw-text">
{{ geoMembers.length.toLocaleString('fr-FR') }} membres certifi&eacute;s g&eacute;olocalis&eacute;s sur
{{ monetary.membersCount.toLocaleString('fr-FR') }} membres au total
</p>
<!-- Save perimeter form -->
<div v-if="polygon" class="flex gap-2 items-center">
<input
v-model="perimeterName"
type="text"
placeholder="Nom du p&eacute;rim&egrave;tre"
class="gw-input"
@keydown.enter="handleSavePerimeter"
/>
<button
class="gw-btn gw-btn-accent"
:disabled="!perimeterName.trim()"
@click="handleSavePerimeter"
>
Sauvegarder
</button>
</div>
</template>
</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { duniter, type MonetaryData } from '~/services/duniter';
import { pointInPolygon, fr, formatValue, countUdSince, type CurrencyUnit } from '~/utils/gratewizard';
import type { GeoMember } from '~/composables/useCesiumProfiles';
import type { SavedPerimeter } from '~/composables/useSavedPerimeters';
const unit = useLocalStorage<CurrencyUnit>('gw-currency-unit', 'DU');
const monetary = ref<MonetaryData | null>(null);
const v1Pubkeys = ref<string[] | null>(null);
const loading = ref(true);
const error = ref<string | null>(null);
const polygon = ref<[number, number][] | null>(null);
const clearTrigger = ref(0);
const loadPolygonTrigger = ref<{ coords: [number, number][]; name: string } | null>(null);
const perimeterName = ref('');
const activePerimeterName = ref<string | null>(null);
const localMNg1 = ref<number | null>(null);
const mnLoading = ref(false);
const topRef = ref<HTMLDivElement | null>(null);
const { geoMembers, loading: geoLoading, error: geoError } = useCesiumProfiles(v1Pubkeys);
const { perimeters, savePerimeter, deletePerimeter } = useSavedPerimeters();
// Fetch monetary data on mount
onMounted(async () => {
try {
const [mon, pubkeys] = await Promise.all([
duniter.fetchMonetary(),
duniter.fetchMemberPubkeys(),
]);
monetary.value = mon;
v1Pubkeys.value = pubkeys;
} catch (e: any) {
error.value = e.message;
} finally {
loading.value = false;
}
});
const duDaily = computed(() => monetary.value ? Number(monetary.value.amount) / 100 : 1);
const massG1 = computed(() => monetary.value ? Number(monetary.value.monetaryMass) / 100 : 0);
const mnG1 = computed(() => monetary.value && monetary.value.membersCount ? massG1.value / monetary.value.membersCount : 0);
const localMembers = computed(() => {
if (!polygon.value) return [];
return geoMembers.value.filter((m) => pointInPolygon(m.lat, m.lon, polygon.value!));
});
const nLocal = computed(() => localMembers.value.length);
// Compute local M/N based on member seniority
watch([polygon, localMembers, monetary], async ([poly, members, mon]) => {
if (!poly || members.length === 0 || !mon) {
localMNg1.value = null;
return;
}
mnLoading.value = true;
try {
const pubkeys = members.map((m) => m.pubkey);
const joinBlocks = await duniter.fetchMemberJoinBlocks(pubkeys);
const udBlocks = mon.udBlockNumbers;
let totalSeniority = 0;
let validCount = 0;
for (const pk of pubkeys) {
const joinBlock = joinBlocks.get(pk);
if (joinBlock === undefined) continue;
totalSeniority += countUdSince(udBlocks, joinBlock);
validCount++;
}
if (validCount > 0) {
const localAvgSeniority = totalSeniority / validCount;
const globalAvgSeniority =
Number(mon.monetaryMass) / (Number(mon.amount) * mon.membersCount);
localMNg1.value = mnG1.value * (localAvgSeniority / globalAvgSeniority);
}
} catch (e) {
console.error('Failed to compute local M/N:', e);
} finally {
mnLoading.value = false;
}
});
const displayN = computed(() => polygon.value ? nLocal.value : monetary.value?.membersCount ?? 0);
const displayMN = computed(() => polygon.value ? localMNg1.value : mnG1.value);
const displayM = computed(() => polygon.value ? (localMNg1.value !== null ? localMNg1.value * nLocal.value : null) : massG1.value);
const mnIsLoading = computed(() => polygon.value && (mnLoading.value || localMNg1.value === null));
function handlePolygonChange(poly: [number, number][] | null, name?: string) {
polygon.value = poly;
activePerimeterName.value = name ?? null;
if (poly) nextTick(() => topRef.value?.scrollIntoView({ behavior: 'smooth' }));
}
function handleClearSelection() {
polygon.value = null;
activePerimeterName.value = null;
localMNg1.value = null;
clearTrigger.value++;
loadPolygonTrigger.value = null;
}
function handleSavePerimeter() {
if (!polygon.value || !perimeterName.value.trim()) return;
savePerimeter(perimeterName.value.trim(), polygon.value);
perimeterName.value = '';
}
function handleLoadPerimeter(p: SavedPerimeter) {
loadPolygonTrigger.value = { coords: p.polygon, name: p.name };
}
</script>
+146
View File
@@ -0,0 +1,146 @@
<template>
<div ref="containerRef" class="gw-map" />
</template>
<script setup lang="ts">
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet.markercluster';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import '@geoman-io/leaflet-geoman-free';
import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css';
import type { GeoMember } from '~/composables/useCesiumProfiles';
type LoadPolygon = { coords: [number, number][]; name: string } | null;
const props = defineProps<{
members: GeoMember[];
clearTrigger?: number;
loadPolygon?: LoadPolygon;
}>();
const emit = defineEmits<{
polygonChange: [polygon: [number, number][] | null, name?: string];
}>();
const containerRef = ref<HTMLDivElement | null>(null);
let map: L.Map | null = null;
let cluster: any = null;
let polygonLayer: L.Layer | null = null;
onMounted(() => {
if (!containerRef.value) return;
map = L.map(containerRef.value).setView([46.5, 2.5], 6);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(map);
cluster = (L as any).markerClusterGroup();
map.addLayer(cluster);
// Geoman controls
if (map.pm) {
map.pm.addControls({
position: 'topleft',
drawCircle: false,
drawCircleMarker: false,
drawMarker: false,
drawPolyline: false,
drawText: false,
editMode: false,
dragMode: false,
cutPolygon: false,
rotateMode: false,
removalMode: false,
});
map.pm.setLang('fr');
}
map.on('pm:create', (e: any) => {
if (polygonLayer) {
map!.removeLayer(polygonLayer);
}
polygonLayer = e.layer;
const latlngs = (e.layer as L.Polygon).getLatLngs()[0] as L.LatLng[];
emit('polygonChange', latlngs.map((ll) => [ll.lat, ll.lng]));
});
map.on('pm:remove', () => {
polygonLayer = null;
emit('polygonChange', null);
});
// Populate initial markers
updateMarkers(props.members);
});
onUnmounted(() => {
if (map) {
map.remove();
map = null;
cluster = null;
}
});
// Clear polygon when clearTrigger changes
watch(() => props.clearTrigger, () => {
if (!props.clearTrigger || !map) return;
if (polygonLayer) {
const center = map.getCenter();
const zoom = map.getZoom();
map.removeLayer(polygonLayer);
polygonLayer = null;
map.setView(center, zoom, { animate: false });
}
if (map.pm) {
map.pm.disableDraw();
}
});
// Load a saved polygon
watch(() => props.loadPolygon, (loadPolygon) => {
if (!map || !loadPolygon) return;
if (polygonLayer) {
map.removeLayer(polygonLayer);
polygonLayer = null;
}
const latlngs = loadPolygon.coords.map(([lat, lng]) => L.latLng(lat, lng));
const poly = L.polygon(latlngs, { color: '#3388ff' }).addTo(map);
polygonLayer = poly;
map.fitBounds(poly.getBounds(), { padding: [20, 20] });
emit('polygonChange', loadPolygon.coords, loadPolygon.name);
});
// Update markers when members change
function updateMarkers(members: GeoMember[]) {
if (!cluster) return;
cluster.clearLayers();
const markers = members.map((m) =>
L.circleMarker([m.lat, m.lon], {
radius: 6,
color: '#f59e0b',
fillColor: '#f59e0b',
fillOpacity: 0.7,
}).bindPopup(
`<strong>${m.title || m.pubkey.slice(0, 8)}</strong>${m.city ? '<br>' + m.city : ''}`
)
);
cluster.addLayers(markers);
}
watch(() => props.members, (members) => {
updateMarkers(members);
});
</script>
<style scoped>
.gw-map {
height: 500px;
width: 100%;
border-radius: 0.75rem;
}
</style>
@@ -0,0 +1,40 @@
<template>
<div v-if="perimeters.length > 0" class="w-full flex flex-col gap-1">
<p class="text-xs font-semibold text-white/50 uppercase tracking-wide">P&eacute;rim&egrave;tres sauvegard&eacute;s</p>
<div class="flex flex-col gap-0.5 w-full">
<div
v-for="p in perimeters"
:key="p.name"
class="gw-perimeter-item"
@click="emit('load', p)"
>
<div class="flex items-center gap-1.5 min-w-0">
<span class="text-accent text-xs shrink-0">&#x1F4CD;</span>
<span class="text-xs font-medium truncate">{{ p.name }}</span>
<span class="text-[10px] text-white/30 shrink-0">
{{ new Date(p.createdAt).toLocaleDateString('fr-FR', { dateStyle: 'short' }) }}
</span>
</div>
<button
class="gw-perimeter-delete"
@click.stop="emit('delete', p.name)"
>
&times;
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { SavedPerimeter } from '~/composables/useSavedPerimeters';
defineProps<{
perimeters: SavedPerimeter[];
}>();
const emit = defineEmits<{
load: [perimeter: SavedPerimeter];
delete: [name: string];
}>();
</script>
+131
View File
@@ -0,0 +1,131 @@
<template>
<div class="card-surface !p-0">
<div class="flex items-center justify-center py-3 px-4">
<p class="gw-metric text-sm">Mes relations</p>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-white/8">
<th
v-for="col in columns"
:key="col.key"
class="px-3 py-2 text-left text-xs font-semibold text-white/60 uppercase cursor-pointer select-none hover:text-white/80 transition-colors"
@click="col.sortable && toggleSort(col.key)"
>
<span class="inline-flex items-center gap-1">
{{ col.label }}
<span v-if="col.sortable && sortKey === col.key" class="text-accent">
{{ sortAsc ? '\u25B2' : '\u25BC' }}
</span>
</span>
</th>
<th class="px-3 py-2 w-8" />
</tr>
</thead>
<tbody>
<tr v-if="sortedItems.length === 0">
<td :colspan="columns.length + 1" class="px-3 py-3 text-center text-white/40 text-xs">
Aucune relation
</td>
</tr>
<tr
v-for="(friend, index) in sortedItems"
:key="friend.name"
class="cursor-pointer border-b border-white/5 transition-colors"
:class="index % 2 === 0 ? 'bg-surface-200/50' : ''"
@click="emit('select', friend)"
>
<td class="px-3 py-1.5">{{ friend.displayName }}</td>
<td v-if="resultLabel" class="px-3 py-1.5">{{ friend.result }}</td>
<td class="px-3 py-1.5">{{ friend.displayDate }}</td>
<td class="px-3 py-1.5">{{ friend.du }}</td>
<td class="px-3 py-1.5 w-8">
<button
v-if="!isBaseFriend(friend)"
class="text-red-400 hover:text-red-300 transition-colors"
title="Effacer relation"
@click.stop="emit('remove', friend.name)"
>
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<path d="M15 9l-6 6M9 9l6 6" />
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { Block0Date, type TableFriend } from '~/utils/gratewizard';
const props = defineProps<{
items: TableFriend[];
baseFriends: { name: string; date: string }[];
resultLabel?: string;
}>();
const emit = defineEmits<{
select: [friend: TableFriend];
remove: [name: string];
}>();
const sortKey = ref<string>('name');
const sortAsc = ref(true);
const columns = computed(() => {
const cols = [
{ key: 'name', label: 'NOM', sortable: true },
];
if (props.resultLabel) {
cols.push({ key: 'result', label: props.resultLabel, sortable: true });
}
cols.push(
{ key: 'date', label: 'DATE', sortable: true },
{ key: 'du', label: 'DU', sortable: true },
);
return cols;
});
function toggleSort(key: string) {
if (sortKey.value === key) {
sortAsc.value = !sortAsc.value;
} else {
sortKey.value = key;
sortAsc.value = true;
}
}
const sortedItems = computed(() => {
const items = [...props.items];
const key = sortKey.value;
const dir = sortAsc.value ? 1 : -1;
return items.sort((a, b) => {
const first = a[key];
const second = b[key];
let cmp = 0;
switch (key) {
case 'name':
cmp = String(first).localeCompare(String(second));
break;
case 'result':
case 'du':
cmp = Number(first) - Number(second);
break;
case 'date':
cmp = new Date(a.date).getTime() - new Date(b.date).getTime();
break;
}
return cmp * dir;
});
});
function isBaseFriend(friend: TableFriend): boolean {
return props.baseFriends.some(({ name, date }) => name === friend.name && date === friend.date);
}
</script>
+41
View File
@@ -0,0 +1,41 @@
<template>
<div class="flex flex-col items-center">
<p class="gw-metric pb-3" style="font-size: 1.75rem">grateWizard</p>
<!-- Tab buttons -->
<div class="flex gap-0.5 p-0.5 rounded-full bg-surface-200">
<button
v-for="tab in tabs"
:key="tab.key"
class="gw-tab-btn"
:class="activeTab === tab.key
? 'gw-tab-active'
: 'gw-tab-inactive'"
:disabled="tab.disabled"
@click="activeTab = tab.key"
>
{{ tab.label }}
</button>
</div>
<!-- Tab content with KeepAlive -->
<div class="w-full">
<KeepAlive>
<GratewizardGwMN v-if="activeTab === '1'" />
<GratewizardGwCRA v-else-if="activeTab === '2'" />
<GratewizardGwCRS v-else-if="activeTab === '3'" />
</KeepAlive>
</div>
</div>
</template>
<script setup lang="ts">
const tabs = [
{ key: '1', label: 'M/N local', disabled: false },
{ key: '2', label: "A l'anciennet\u00e9", disabled: false },
{ key: '3', label: 'Au solde', disabled: false },
{ key: '4', label: 'Au volume', disabled: true },
];
const activeTab = ref('1');
</script>
+2 -2
View File
@@ -24,7 +24,7 @@
<!-- CTAs --> <!-- CTAs -->
<div class="shrink-0 flex flex-col gap-2"> <div class="shrink-0 flex flex-col gap-2">
<UiBaseButton @click="launch"> <UiBaseButton :href="url" target="_blank" @click="launch">
<div class="i-lucide-external-link mr-2 h-4 w-4" /> <div class="i-lucide-external-link mr-2 h-4 w-4" />
{{ content?.grateWizardTeaser.cta.launch }} {{ content?.grateWizardTeaser.cta.launch }}
</UiBaseButton> </UiBaseButton>
@@ -41,7 +41,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const { launch } = useGrateWizard() const { url, launch } = useGrateWizard()
const { data: content } = await usePageContent('home') const { data: content } = await usePageContent('home')
</script> </script>
+97
View File
@@ -0,0 +1,97 @@
const CESIUM_PODS = [
'https://g1.data.brussels.ovh/user/profile/_search',
'https://g1.data.le-sou.org/user/profile/_search',
'https://g1.data.e-is.pro/user/profile/_search',
];
const BATCH_SIZE = 500;
export type GeoMember = {
pubkey: string;
title: string;
city: string;
lat: number;
lon: number;
};
/** Find the first Cesium+ pod that responds successfully. */
async function findWorkingPod(): Promise<string> {
for (const url of CESIUM_PODS) {
try {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ size: 0, query: { match_all: {} } }),
});
if (res.ok) return url;
} catch {
// try next pod
}
}
throw new Error('Aucun pod Cesium+ disponible');
}
/**
* Fetch Cesium+ profiles for a given list of v1 pubkeys.
* Uses Elasticsearch `ids` query with batches of 500, filtered to geolocated profiles only.
* Pass `null` to skip fetching (e.g. while pubkeys are still loading).
*/
export function useCesiumProfiles(v1Pubkeys: Ref<string[] | null>) {
const geoMembers = ref<GeoMember[]>([]);
const loading = ref(true);
const error = ref<string | null>(null);
watch(v1Pubkeys, async (pubkeys) => {
if (pubkeys === null) return;
loading.value = true;
error.value = null;
try {
const podUrl = await findWorkingPod();
const allMembers: GeoMember[] = [];
for (let i = 0; i < pubkeys.length; i += BATCH_SIZE) {
const batch = pubkeys.slice(i, i + BATCH_SIZE);
const res = await fetch(podUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
size: batch.length,
_source: ['title', 'city', 'geoPoint'],
query: {
bool: {
must: [
{ ids: { values: batch } },
{ exists: { field: 'geoPoint' } },
],
},
},
}),
});
if (!res.ok) throw new Error(`Cesium+ HTTP ${res.status}`);
const json = await res.json();
for (const hit of json.hits?.hits ?? []) {
const s = hit._source;
if (!s?.geoPoint?.lat || !s?.geoPoint?.lon) continue;
allMembers.push({
pubkey: hit._id,
title: s.title || '',
city: s.city || '',
lat: s.geoPoint.lat,
lon: s.geoPoint.lon,
});
}
}
geoMembers.value = allMembers;
} catch (e: any) {
error.value = e.message;
} finally {
loading.value = false;
}
}, { immediate: true });
return { geoMembers, loading, error };
}
+6 -5
View File
@@ -1,16 +1,17 @@
export function useGrateWizard() { export function useGrateWizard() {
const appConfig = useAppConfig() const appConfig = useAppConfig()
const { url, popup } = appConfig.gratewizard as { url: string; popup: { width: number; height: number } }
function launch() { function launch(e?: Event) {
const { url, popup } = appConfig.gratewizard as { url: string; popup: { width: number; height: number } }
const left = Math.round((window.screen.width - popup.width) / 2) const left = Math.round((window.screen.width - popup.width) / 2)
const top = Math.round((window.screen.height - popup.height) / 2) const top = Math.round((window.screen.height - popup.height) / 2)
window.open( const win = window.open(
url, url,
'GrateWizard', 'grateWizard',
`width=${popup.width},height=${popup.height},left=${left},top=${top},scrollbars=yes,resizable=yes`, `width=${popup.width},height=${popup.height},left=${left},top=${top},scrollbars=yes,resizable=yes`,
) )
if (win) e?.preventDefault()
} }
return { launch } return { url, launch }
} }
+24
View File
@@ -0,0 +1,24 @@
export type SavedPerimeter = {
name: string;
polygon: [number, number][];
createdAt: string;
};
const STORAGE_KEY = 'gw-saved-perimeters';
export function useSavedPerimeters() {
const perimeters = useLocalStorage<SavedPerimeter[]>(STORAGE_KEY, []);
function savePerimeter(name: string, polygon: [number, number][]) {
perimeters.value = [
...perimeters.value.filter((p) => p.name !== name),
{ name, polygon, createdAt: new Date().toISOString() },
];
}
function deletePerimeter(name: string) {
perimeters.value = perimeters.value.filter((p) => p.name !== name);
}
return { perimeters, savePerimeter, deletePerimeter };
}
+1 -1
View File
@@ -65,7 +65,7 @@
</AdminFieldList> </AdminFieldList>
</AdminFormSection> </AdminFormSection>
<AdminFormSection title="GrateWizard"> <AdminFormSection title="grateWizard">
<AdminFieldText v-model="data.gratewizard.url" label="URL de l'application" /> <AdminFieldText v-model="data.gratewizard.url" label="URL de l'application" />
</AdminFormSection> </AdminFormSection>
</template> </template>
+382 -52
View File
@@ -1,73 +1,105 @@
<template> <template>
<div class="section-padding"> <!-- Popup mode: standalone app, no Librodrome layout -->
<div class="container-content max-w-3xl mx-auto"> <main v-if="isPopup" class="gw-app flex flex-col items-center sm:p-4 overflow-x-hidden h-screen">
<!-- Back link --> <div class="sm:max-w-screen-sm w-full">
<UiScrollReveal> <div class="gw-card">
<NuxtLink to="/" class="inline-flex items-center gap-2 text-sm text-white/50 hover:text-white/80 mb-8 transition-colors"> <GratewizardGwTabs />
<div class="i-lucide-arrow-left h-4 w-4" /> </div>
Retour à l'accueil </div>
</NuxtLink> </main>
</UiScrollReveal>
<!-- Header --> <!-- Info mode: Librodrome layout with feature cards -->
<UiScrollReveal> <NuxtLayout v-else>
<div class="text-center mb-12"> <div class="section-padding">
<span class="inline-block mb-3 rounded-full bg-amber-400/15 px-3 py-0.5 font-mono text-xs tracking-widest text-amber-400 uppercase"> <div class="container-content max-w-3xl mx-auto">
{{ content?.kicker }} <!-- Back link -->
</span> <UiScrollReveal>
<h1 class="page-title font-display font-bold text-white"> <NuxtLink to="/" class="inline-flex items-center gap-2 text-sm text-white/50 hover:text-white/80 mb-8 transition-colors">
{{ content?.title }} <div class="i-lucide-arrow-left h-4 w-4" />
</h1> Retour &agrave; l'accueil
<p class="mt-4 text-lg text-white/60 leading-relaxed max-w-xl mx-auto"> </NuxtLink>
{{ content?.description }} </UiScrollReveal>
</p>
</div>
</UiScrollReveal>
<!-- Explanation cards --> <!-- Header -->
<div class="grid gap-6 md:grid-cols-2 mb-12"> <UiScrollReveal>
<UiScrollReveal <div class="text-center mb-12">
v-for="(feature, i) in content?.features" <span class="inline-block mb-3 rounded-full bg-amber-400/15 px-3 py-0.5 font-mono text-xs tracking-widest text-amber-400 uppercase">
:key="i" {{ content?.kicker }}
:delay="(i + 1) * 100" </span>
> <h1 class="page-title font-display font-bold text-white">
<div class="gw-feature-card"> {{ content?.title }}
<div :class="`i-lucide-${feature.icon}`" class="h-6 w-6 text-amber-400 mb-3" /> </h1>
<h3 class="font-display text-lg font-semibold text-white mb-2">{{ feature.title }}</h3> <p class="mt-4 text-lg text-white/60 leading-relaxed max-w-xl mx-auto">
<p class="text-sm text-white/60 leading-relaxed"> {{ content?.description }}
{{ feature.description }}
</p> </p>
</div> </div>
</UiScrollReveal> </UiScrollReveal>
</div>
<!-- CTA --> <!-- Explanation cards -->
<UiScrollReveal :delay="500"> <div class="grid gap-6 md:grid-cols-2 mb-12">
<div class="text-center"> <UiScrollReveal
<p class="text-sm text-white/40 mb-4"> v-for="(feature, i) in content?.features"
{{ content?.cta.note }} :key="i"
</p> :delay="(i + 1) * 100"
<UiBaseButton @click="launch"> >
<div class="i-lucide-external-link mr-2 h-5 w-5" /> <div class="gw-feature-card">
{{ content?.cta.label }} <div :class="`i-lucide-${feature.icon}`" class="h-6 w-6 text-amber-400 mb-3" />
</UiBaseButton> <h3 class="font-display text-lg font-semibold text-white mb-2">{{ feature.title }}</h3>
<p class="text-sm text-white/60 leading-relaxed">
{{ feature.description }}
</p>
</div>
</UiScrollReveal>
</div> </div>
</UiScrollReveal>
<!-- CTA -->
<UiScrollReveal :delay="500">
<div class="text-center">
<p class="text-sm text-white/40 mb-4">
{{ content?.cta.note }}
</p>
<UiBaseButton :href="url" target="_blank" @click="launch">
<div class="i-lucide-external-link mr-2 h-5 w-5" />
{{ content?.cta.label }}
</UiBaseButton>
</div>
</UiScrollReveal>
</div>
</div> </div>
</div> </NuxtLayout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
definePageMeta({ layout: false })
const route = useRoute()
const isPopup = computed(() => route.query.popup !== undefined)
const { data: content } = await usePageContent('gratewizard') const { data: content } = await usePageContent('gratewizard')
useHead({ useHead(isPopup.value
title: content.value?.meta?.title ?? 'GrateWizard Coefficients relatifs', ? {
}) title: 'grateWizard',
htmlAttrs: { lang: 'fr' },
meta: [{ name: 'color-scheme', content: 'dark' }],
link: [
{ rel: 'preconnect', href: 'https://fonts.bunny.net' },
{
rel: 'stylesheet',
href: 'https://fonts.bunny.net/css?family=space-grotesk:300,400,500,600,700',
},
],
}
: {
title: content.value?.meta?.title ?? 'grateWizard \u2014 Coefficients relatifs',
},
)
const { launch } = useGrateWizard() const { url, launch } = useGrateWizard()
</script> </script>
<style scoped> <style scoped>
/* Info page styles */
.page-title { .page-title {
font-size: clamp(2rem, 5vw, 2.75rem); font-size: clamp(2rem, 5vw, 2.75rem);
} }
@@ -93,3 +125,301 @@ code {
background: hsl(40 80% 50% / 0.1); background: hsl(40 80% 50% / 0.1);
} }
</style> </style>
<style>
/* Standalone popup app styles — unscoped so child components inherit */
.gw-app {
font-family: 'Space Grotesk', sans-serif;
background-color: hsl(20 8% 3.5%);
color: white;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 0.875rem;
}
.gw-app button,
.gw-app select {
border: 0;
}
.gw-card {
border-radius: 0.75rem;
border: 1px solid hsl(20 8% 18% / 0.5);
background: hsl(20 8% 8%);
padding: 1rem 1.25rem;
}
/* --- Typography --- */
.gw-metric {
font-weight: 600;
text-align: center;
font-size: 1rem;
color: white;
}
.gw-title {
font-weight: 500;
font-size: 0.875rem;
color: white;
}
.gw-text {
font-size: 0.8125rem;
color: hsl(0 0% 75%);
}
.gw-chip {
display: inline-block;
padding: 0.0625rem 0.375rem;
border-radius: 9999px;
font-size: 0.6875rem;
font-weight: 500;
background: hsl(36 80% 52% / 0.15);
color: hsl(36 80% 52%);
}
/* --- Tab buttons --- */
.gw-tab-btn {
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
transition: all 0.2s ease;
white-space: nowrap;
}
.gw-tab-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.gw-tab-active {
background: hsl(36 80% 52%);
color: hsl(20 8% 3.5%);
box-shadow: 0 1px 3px hsl(36 80% 52% / 0.3);
}
.gw-tab-inactive {
color: hsl(0 0% 100% / 0.5);
}
.gw-tab-inactive:not(:disabled):hover {
color: hsl(0 0% 100% / 0.8);
background: hsl(0 0% 100% / 0.05);
}
/* --- Buttons --- */
.gw-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.3125rem 0.875rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
transition: all 0.2s ease;
cursor: pointer;
border: none;
outline: none;
}
.gw-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.gw-btn-accent {
background: hsl(36 80% 52%);
color: hsl(20 8% 3.5%);
box-shadow: 0 1px 2px hsl(36 80% 52% / 0.25);
}
.gw-btn-accent:not(:disabled):hover {
background: hsl(36 80% 58%);
box-shadow: 0 2px 6px hsl(36 80% 52% / 0.3);
}
.gw-btn-accent:not(:disabled):active {
background: hsl(36 80% 46%);
transform: scale(0.97);
}
.gw-btn-danger {
background: hsl(0 72% 50%);
color: white;
box-shadow: 0 1px 2px hsl(0 72% 50% / 0.25);
}
.gw-btn-danger:not(:disabled):hover {
background: hsl(0 72% 56%);
box-shadow: 0 2px 6px hsl(0 72% 50% / 0.3);
}
.gw-btn-danger:not(:disabled):active {
background: hsl(0 72% 44%);
transform: scale(0.97);
}
/* --- Inputs --- */
.gw-input {
padding: 0.3125rem 0.625rem;
font-size: 0.8125rem;
border-radius: 0.375rem;
border: 1px solid hsl(20 8% 22%);
background: hsl(20 8% 6%);
color: white;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
}
.gw-input:focus {
border-color: hsl(36 80% 52%);
box-shadow: 0 0 0 2px hsl(36 80% 52% / 0.12);
}
/* --- Icon button --- */
.gw-icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.75rem;
height: 1.75rem;
border-radius: 9999px;
background: hsl(36 80% 52%);
color: hsl(20 8% 3.5%);
transition: all 0.2s ease;
border: none;
cursor: pointer;
}
.gw-icon-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.gw-icon-btn:not(:disabled):hover {
background: hsl(36 80% 58%);
box-shadow: 0 2px 6px hsl(36 80% 52% / 0.3);
}
.gw-icon-btn:not(:disabled):active {
transform: scale(0.92);
}
/* --- Toggle --- */
.gw-toggle {
position: relative;
display: inline-block;
width: 2.25rem;
height: 1.25rem;
}
.gw-toggle input {
opacity: 0;
width: 0;
height: 0;
}
.gw-toggle-slider {
position: absolute;
inset: 0;
border-radius: 9999px;
background: hsl(20 8% 22%);
transition: background 0.2s;
cursor: pointer;
}
.gw-toggle-slider::before {
content: '';
position: absolute;
height: 0.875rem;
width: 0.875rem;
left: 0.1875rem;
bottom: 0.1875rem;
background: white;
border-radius: 50%;
transition: transform 0.2s;
}
.gw-toggle input:checked + .gw-toggle-slider {
background: hsl(36 80% 52%);
}
.gw-toggle input:checked + .gw-toggle-slider::before {
transform: translateX(1rem);
}
/* --- Spinner --- */
.gw-spinner {
width: 1.5rem;
height: 1.5rem;
border: 2px solid hsl(20 8% 22%);
border-top-color: hsl(36 80% 52%);
border-radius: 50%;
animation: gw-spin 0.8s linear infinite;
}
@keyframes gw-spin {
to { transform: rotate(360deg); }
}
/* --- Card surface --- */
.card-surface {
border-radius: 0.625rem;
background: hsl(20 8% 7%);
border: 1px solid hsl(0 0% 100% / 0.06);
padding: 1rem;
transition: all 0.3s;
}
/* --- Perimeter list items --- */
.gw-perimeter-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
padding: 0.375rem 0.5rem;
border-radius: 0.375rem;
background: hsl(20 8% 12%);
cursor: pointer;
transition: background 0.15s ease;
}
.gw-perimeter-item:hover {
background: hsl(20 8% 16%);
}
.gw-perimeter-delete {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.25rem;
height: 1.25rem;
border-radius: 9999px;
font-size: 0.875rem;
line-height: 1;
color: hsl(0 60% 60%);
background: hsl(0 60% 50% / 0.1);
border: none;
cursor: pointer;
transition: all 0.15s ease;
flex-shrink: 0;
}
.gw-perimeter-delete:hover {
background: hsl(0 60% 50% / 0.2);
color: hsl(0 60% 70%);
}
/* --- Scrollbar --- */
.gw-app ::-webkit-scrollbar {
width: 5px;
}
.gw-app ::-webkit-scrollbar-track {
background: transparent;
}
.gw-app ::-webkit-scrollbar-thumb {
background: hsl(0 0% 100% / 0.12);
border-radius: 3px;
}
</style>
+4
View File
@@ -0,0 +1,4 @@
// Changer cette ligne pour switcher v1 → v2 après mars 2026 :
import { v1Adapter as duniter } from './v1';
export { duniter };
export type { DuniterAdapter, MonetaryData } from './types';
+14
View File
@@ -0,0 +1,14 @@
export type MonetaryData = {
monetaryMass: string;
membersCount: number;
amount: string;
timestamp: string;
blockNumber: number;
udBlockNumbers: number[];
};
export interface DuniterAdapter {
fetchMonetary(): Promise<MonetaryData>;
fetchMemberPubkeys(): Promise<string[]>;
fetchMemberJoinBlocks(pubkeys: string[]): Promise<Map<string, number>>;
}
+83
View File
@@ -0,0 +1,83 @@
import type { DuniterAdapter, MonetaryData } from './types';
const BMA_URL = 'https://g1.duniter.org';
async function bmaGet<T>(path: string): Promise<T> {
const res = await fetch(`${BMA_URL}${path}`);
if (!res.ok) throw new Error(`BMA ${path}: ${res.status}`);
return res.json();
}
const joinBlockCache = new Map<string, number>();
export const v1Adapter: DuniterAdapter = {
async fetchMonetary(): Promise<MonetaryData> {
const [current, udBlocks] = await Promise.all([
bmaGet<{
monetaryMass: number;
membersCount: number;
number: number;
medianTime: number;
}>('/blockchain/current'),
bmaGet<{ result: { blocks: number[] } }>('/blockchain/with/ud'),
]);
const udBlockNumbers = udBlocks.result.blocks;
const lastUdBlock = udBlockNumbers[udBlockNumbers.length - 1];
const udBlock = await bmaGet<{ dividend: number }>(`/blockchain/block/${lastUdBlock}`);
return {
monetaryMass: String(current.monetaryMass),
membersCount: current.membersCount,
amount: String(udBlock.dividend),
timestamp: new Date(current.medianTime * 1000).toISOString(),
blockNumber: current.number,
udBlockNumbers,
};
},
async fetchMemberPubkeys(): Promise<string[]> {
const data = await bmaGet<{ results: { pubkey: string }[] }>('/wot/members');
return data.results.map((m) => m.pubkey);
},
async fetchMemberJoinBlocks(pubkeys: string[]): Promise<Map<string, number>> {
const result = new Map<string, number>();
const toFetch: string[] = [];
for (const pk of pubkeys) {
const cached = joinBlockCache.get(pk);
if (cached !== undefined) {
result.set(pk, cached);
} else {
toFetch.push(pk);
}
}
const CONCURRENT = 10;
for (let i = 0; i < toFetch.length; i += CONCURRENT) {
const batch = toFetch.slice(i, i + CONCURRENT);
await Promise.all(
batch.map(async (pk) => {
try {
const data = await bmaGet<{
results: { uids: { meta: { timestamp: string } }[] }[];
}>(`/wot/lookup/${encodeURIComponent(pk)}`);
const ts = data.results?.[0]?.uids?.[0]?.meta?.timestamp;
if (ts) {
const blockNum = parseInt(ts.split('-')[0], 10);
if (!isNaN(blockNum)) {
joinBlockCache.set(pk, blockNum);
result.set(pk, blockNum);
}
}
} catch {
// Skip members we can't look up
}
})
);
}
return result;
},
};
+68
View File
@@ -0,0 +1,68 @@
import { ss58ToV1Pubkey } from '~/utils/ss58';
import type { DuniterAdapter, MonetaryData } from './types';
const SQUID_URL = 'https://gt-squid.axiom-team.fr/v1/graphql';
async function gql<T>(query: string): Promise<T> {
const res = await fetch(SQUID_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
});
const json = await res.json();
if (json.errors) throw new Error(json.errors[0].message);
return json.data;
}
export const v2Adapter: DuniterAdapter = {
async fetchMonetary(): Promise<MonetaryData> {
const data = await gql<{
universalDividends: { nodes: (Omit<MonetaryData, 'udBlockNumbers'> & Record<string, unknown>)[] };
}>(`{
universalDividends(first: 1, orderBy: BLOCK_NUMBER_DESC) {
nodes { monetaryMass membersCount amount timestamp blockNumber }
}
}`);
return { ...data.universalDividends.nodes[0], udBlockNumbers: [] };
},
async fetchMemberPubkeys(): Promise<string[]> {
const accountIds: string[] = [];
let offset = 0;
const pageSize = 1000;
while (true) {
const data = await gql<{
identities: { nodes: { accountId: string }[] };
}>(`{
identities(first: ${pageSize}, offset: ${offset}, filter: { isMember: { equalTo: true } }) {
nodes { accountId }
}
}`);
const nodes = data.identities.nodes;
for (const node of nodes) {
accountIds.push(node.accountId);
}
if (nodes.length < pageSize) break;
offset += pageSize;
}
// Convert SS58 accountIds to Cesium+ v1 base58 pubkeys
const pubkeys: string[] = [];
for (const id of accountIds) {
try {
pubkeys.push(ss58ToV1Pubkey(id));
} catch {
// Skip invalid addresses
}
}
return pubkeys;
},
async fetchMemberJoinBlocks(_pubkeys: string[]): Promise<Map<string, number>> {
// TODO: implement using squid GraphQL after v2 migration
return new Map();
},
};
+69
View File
@@ -0,0 +1,69 @@
/** Ray-casting algorithm to test if a point is inside a polygon */
export function pointInPolygon(lat: number, lng: number, polygon: [number, number][]): boolean {
let inside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const [yi, xi] = polygon[i];
const [yj, xj] = polygon[j];
if ((yi > lat) !== (yj > lat) && lng < ((xj - xi) * (lat - yi)) / (yj - yi) + xi) {
inside = !inside;
}
}
return inside;
}
/** Format a number in French locale */
export const fr = (n: number, decimals = 2) =>
n.toLocaleString('fr-FR', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
export type CurrencyUnit = 'DU' | 'G1';
/** Format a G1 value in the given unit, with k/M suffix */
export function formatValue(g1Value: number, unit: CurrencyUnit, duDaily: number): string {
const val = unit === 'DU' ? g1Value / duDaily : g1Value;
const suffix = unit === 'DU' ? 'DU' : '\u011e1';
if (val >= 1_000_000) return fr(val / 1_000_000) + ' M' + suffix;
if (val >= 1_000) return fr(val / 1_000) + ' k' + suffix;
return fr(val) + ' ' + suffix;
}
/** Binary-search count of udBlocks entries >= joinBlock (udBlocks is sorted ascending). */
export function countUdSince(udBlocks: number[], joinBlock: number): number {
let lo = 0, hi = udBlocks.length;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (udBlocks[mid] < joinBlock) lo = mid + 1;
else hi = mid;
}
return udBlocks.length - lo;
}
/** Date to ISO-like string (yyyy-mm-dd) */
export const dateToString = (date: Date) =>
date.getFullYear() + '-' + ('0' + (date.getMonth() + 1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2);
/** Number of days between a date and today */
export const getDays = (date: string | undefined) => {
if (!date) return 0;
const d = new Date(date);
const today = new Date();
return Math.floor(Math.abs(d.getTime() - today.getTime()) / (1000 * 3600 * 24));
};
/** Seniority ratio between two dates (days from today) */
export const getRatio = (date1: string | undefined, date2: string | undefined) => {
return getDays(date1) / Math.max(getDays(date2), 1);
};
export const Block0Date = '2017-03-08';
export type Friend = {
name: string;
date: string;
};
export type TableFriend = Friend & {
[key: string]: string | number;
displayName: string;
displayDate: string;
du: number;
};
+109
View File
@@ -0,0 +1,109 @@
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const ALPHABET_MAP = new Map<string, number>();
for (let i = 0; i < BASE58_ALPHABET.length; i++) {
ALPHABET_MAP.set(BASE58_ALPHABET[i], i);
}
export function base58Decode(str: string): Uint8Array {
if (str.length === 0) return new Uint8Array(0);
// Count leading '1's (zero bytes)
let leadingZeros = 0;
while (leadingZeros < str.length && str[leadingZeros] === '1') {
leadingZeros++;
}
// Decode base58 to big integer (stored as byte array)
const size = Math.ceil(str.length * 733 / 1000) + 1; // log(58) / log(256)
const bytes = new Uint8Array(size);
for (let i = leadingZeros; i < str.length; i++) {
const val = ALPHABET_MAP.get(str[i]);
if (val === undefined) throw new Error(`Invalid base58 character: ${str[i]}`);
let carry = val;
for (let j = size - 1; j >= 0; j--) {
carry += 256 * bytes[j];
bytes[j] = carry % 256;
carry = Math.floor(carry / 256);
}
}
// Skip leading zeros in the decoded bytes
let start = 0;
while (start < size && bytes[start] === 0) {
start++;
}
const result = new Uint8Array(leadingZeros + (size - start));
// Leading zeros from '1' characters
for (let i = 0; i < leadingZeros; i++) {
result[i] = 0;
}
// Decoded bytes
for (let i = start; i < size; i++) {
result[leadingZeros + (i - start)] = bytes[i];
}
return result;
}
export function base58Encode(bytes: Uint8Array): string {
if (bytes.length === 0) return '';
// Count leading zero bytes
let leadingZeros = 0;
while (leadingZeros < bytes.length && bytes[leadingZeros] === 0) {
leadingZeros++;
}
// Encode to base58
const size = Math.ceil(bytes.length * 138 / 100) + 1; // log(256) / log(58)
const digits = new Uint8Array(size);
for (let i = leadingZeros; i < bytes.length; i++) {
let carry = bytes[i];
for (let j = size - 1; j >= 0; j--) {
carry += 256 * digits[j];
digits[j] = carry % 58;
carry = Math.floor(carry / 58);
}
}
// Skip leading zeros in base58 output
let start = 0;
while (start < size && digits[start] === 0) {
start++;
}
let result = '1'.repeat(leadingZeros);
for (let i = start; i < size; i++) {
result += BASE58_ALPHABET[digits[i]];
}
return result;
}
/**
* Convert an SS58 address to a base58-encoded raw pubkey (Cesium+ v1 format).
*
* SS58 layout: [prefix (1 or 2 bytes)] [32 bytes pubkey] [2 bytes checksum]
* - If first byte has bit 6 set (& 0x40), prefix is 2 bytes
* - Otherwise prefix is 1 byte
*/
export function ss58ToV1Pubkey(ss58: string): string {
const raw = base58Decode(ss58);
// Determine prefix length
const prefixLen = (raw[0] & 0x40) ? 2 : 1;
// Extract 32-byte pubkey (skip prefix, drop 2-byte checksum)
const pubkey = raw.slice(prefixLen, prefixLen + 32);
if (pubkey.length !== 32) {
throw new Error(`Invalid SS58 address: expected 32-byte pubkey, got ${pubkey.length}`);
}
return base58Encode(pubkey);
}
+6 -2
View File
@@ -10,20 +10,24 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@geoman-io/leaflet-geoman-free": "^2.19.2",
"@nuxt/content": "^3.11.2", "@nuxt/content": "^3.11.2",
"@nuxt/image": "^2.0.0", "@nuxt/image": "^2.0.0",
"@pinia/nuxt": "^0.11.3", "@pinia/nuxt": "^0.11.3",
"@unocss/nuxt": "^66.6.0", "@unocss/nuxt": "^66.6.0",
"@vueuse/nuxt": "^14.2.1", "@vueuse/nuxt": "^14.2.1",
"better-sqlite3": "^12.6.2", "better-sqlite3": "^12.6.2",
"leaflet": "^1.9.4",
"leaflet.markercluster": "^1.5.3",
"nuxt": "^4.3.1", "nuxt": "^4.3.1",
"yaml": "^2.8.2",
"vue": "^3.5.28", "vue": "^3.5.28",
"vue-router": "^4.6.4" "vue-router": "^4.6.4",
"yaml": "^2.8.2"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/lucide": "^1.2.91", "@iconify-json/lucide": "^1.2.91",
"@iconify-json/ph": "^1.2.2", "@iconify-json/ph": "^1.2.2",
"@types/leaflet": "^1.9.21",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"unocss": "^66.6.0" "unocss": "^66.6.0"
}, },
+271 -2
View File
@@ -8,6 +8,9 @@ importers:
.: .:
dependencies: dependencies:
'@geoman-io/leaflet-geoman-free':
specifier: ^2.19.2
version: 2.19.2(leaflet@1.9.4)
'@nuxt/content': '@nuxt/content':
specifier: ^3.11.2 specifier: ^3.11.2
version: 3.11.2(better-sqlite3@12.6.2)(magicast@0.5.2) version: 3.11.2(better-sqlite3@12.6.2)(magicast@0.5.2)
@@ -26,6 +29,12 @@ importers:
better-sqlite3: better-sqlite3:
specifier: ^12.6.2 specifier: ^12.6.2
version: 12.6.2 version: 12.6.2
leaflet:
specifier: ^1.9.4
version: 1.9.4
leaflet.markercluster:
specifier: ^1.5.3
version: 1.5.3(leaflet@1.9.4)
nuxt: nuxt:
specifier: ^4.3.1 specifier: ^4.3.1
version: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.2.3)(@vue/compiler-sfc@3.5.28)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.9.3)(magicast@0.5.2)(rollup@4.57.1)(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2) version: 4.3.1(@parcel/watcher@2.5.6)(@types/node@25.2.3)(@vue/compiler-sfc@3.5.28)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.9.3)(magicast@0.5.2)(rollup@4.57.1)(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2)
@@ -45,6 +54,9 @@ importers:
'@iconify-json/ph': '@iconify-json/ph':
specifier: ^1.2.2 specifier: ^1.2.2
version: 1.2.2 version: 1.2.2
'@types/leaflet':
specifier: ^1.9.21
version: 1.9.21
typescript: typescript:
specifier: ^5.9.3 specifier: ^5.9.3
version: 5.9.3 version: 5.9.3
@@ -388,6 +400,12 @@ packages:
'@fastify/accept-negotiator@2.0.1': '@fastify/accept-negotiator@2.0.1':
resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==} resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==}
'@geoman-io/leaflet-geoman-free@2.19.2':
resolution: {integrity: sha512-FYqLCFjCWLc1c5vel83i2ON77zPugH9qfxzLxTt+SiFiMgHjO1dSS59qz23aLLQ0hRWTQdycnxXGNmT+4OC9sg==}
engines: {node: '>=18.0.0'}
peerDependencies:
leaflet: ^1.2.0
'@iconify-json/lucide@1.2.91': '@iconify-json/lucide@1.2.91':
resolution: {integrity: sha512-8fuRiK+HiNRgCKMspn9UPsDpBw0TqVTIY0LOiDbMnFxOBwAulMXIl+SVOtp4LzxNvCXB5ofYffiiFIFDitqo7w==} resolution: {integrity: sha512-8fuRiK+HiNRgCKMspn9UPsDpBw0TqVTIY0LOiDbMnFxOBwAulMXIl+SVOtp4LzxNvCXB5ofYffiiFIFDitqo7w==}
@@ -1475,6 +1493,51 @@ packages:
'@standard-schema/spec@1.1.0': '@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@turf/bbox@7.3.4':
resolution: {integrity: sha512-D5ErVWtfQbEPh11yzI69uxqrcJmbPU/9Y59f1uTapgwAwQHQztDWgsYpnL3ns8r1GmPWLP8sGJLVTIk2TZSiYA==}
'@turf/boolean-contains@7.3.4':
resolution: {integrity: sha512-AJMGbtC6HiXgHvq0RNlTfsDB58Qf9Js45MP/APbhGTH4AiLZ8VMDISywVFNd7qN6oppNlDd3xApVR28+ti8bNg==}
'@turf/boolean-point-in-polygon@7.3.4':
resolution: {integrity: sha512-v/4hfyY90Vz9cDgs2GwjQf+Lft8o7mNCLJOTz/iv8SHAIgMMX0czEoIaNVOJr7tBqPqwin1CGwsncrkf5C9n8Q==}
'@turf/boolean-point-on-line@7.3.4':
resolution: {integrity: sha512-70gm5x6YQOZKcw0b/O4jjMwVWnFj+Zb6TXozLgZFDZShc8pgTQtZku7K+HKZ7Eya+7usHIB4IimZauomOMa+iw==}
'@turf/distance@7.3.4':
resolution: {integrity: sha512-9drWgd46uHPPyzgrcRQLgSvdS/SjVlQ6ZIBoRQagS5P2kSjUbcOXHIMeOSPwfxwlKhEtobLyr+IiR2ns1TfF8w==}
'@turf/geojson-rbush@7.3.4':
resolution: {integrity: sha512-aDG/5mMCgKduqBwZ3XpLOdlE2hizV3fM+5dHCWyrBepCQLeM/QRvvpBDCdQKDWKpoIBmrGGYDNiOofnf3QmGhg==}
'@turf/helpers@7.3.4':
resolution: {integrity: sha512-U/S5qyqgx3WTvg4twaH0WxF3EixoTCfDsmk98g1E3/5e2YKp7JKYZdz0vivsS5/UZLJeZDEElOSFH4pUgp+l7g==}
'@turf/invariant@7.3.4':
resolution: {integrity: sha512-88Eo4va4rce9sNZs6XiMJowWkikM3cS2TBhaCKlU+GFHdNf8PFEpiU42VDU8q5tOF6/fu21Rvlke5odgOGW4AQ==}
'@turf/kinks@7.3.4':
resolution: {integrity: sha512-LZTKELWxvXl0vc9ZxVgi0v07fO9+2FrZOam2B10fz/eGjy3oKNazU5gjggbnc499wEIcJS4hN+VyjQZrmsJAdQ==}
'@turf/line-intersect@7.3.4':
resolution: {integrity: sha512-XygbTvHa6A+v6l2ZKYtS8AAWxwmrPxKxfBbdH75uED1JvdytSLWYTKGlcU3soxd9sYb4x/g9sDvRIVyU6Lucrg==}
'@turf/line-segment@7.3.4':
resolution: {integrity: sha512-UeISzf/JHoWEY5yeoyvKwA5epWcvJMCpCwbIMolvfTC5pp+IVozjHPVCRvRWuzmbmAvetcW0unL5bjqi0ADmuQ==}
'@turf/line-split@7.3.4':
resolution: {integrity: sha512-l1zmCSUnGsiN4gf22Aw91a2VnYs5DZS67FdkYqKgr+wPEAL/gpQgIBBWSTmhwY8zb3NEqty+f/gMEe8EJAWYng==}
'@turf/meta@7.3.4':
resolution: {integrity: sha512-tlmw9/Hs1p2n0uoHVm1w3ugw1I6L8jv9YZrcdQa4SH5FX5UY0ATrKeIvfA55FlL//PGuYppJp+eyg/0eb4goqw==}
'@turf/nearest-point-on-line@7.3.4':
resolution: {integrity: sha512-DQrP3lRju83rIXFN68tUEpc7ki/eRwdwBkK2CTT4RAcyCxbcH2NGJPQv8dYiww/Ar77u1WLVn+aINXZH904dWw==}
'@turf/truncate@7.3.4':
resolution: {integrity: sha512-VPXdae9+RLLM19FMrJgt7QANBikm7DxPbfp/dXgzE4Ca7v+mJ4T1fYc7gCZDaqOrWMccHKbvv4iSuW7YZWdIIA==}
'@tybys/wasm-util@0.10.1': '@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
@@ -1490,12 +1553,18 @@ packages:
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hast@3.0.4': '@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/leaflet@1.9.21':
resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==}
'@types/mdast@4.0.4': '@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -1951,6 +2020,9 @@ packages:
resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==} resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
bindings@1.5.0: bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -2992,6 +3064,14 @@ packages:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'} engines: {node: '>= 0.6.3'}
leaflet.markercluster@1.5.3:
resolution: {integrity: sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==}
peerDependencies:
leaflet: ^1.3.1
leaflet@1.9.4:
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
lilconfig@3.1.3: lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'} engines: {node: '>=14'}
@@ -3532,6 +3612,12 @@ packages:
pkg-types@2.3.0: pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
point-in-polygon-hao@1.2.4:
resolution: {integrity: sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==}
polyclip-ts@0.16.8:
resolution: {integrity: sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==}
possible-typed-array-names@1.1.0: possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3747,6 +3833,9 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
quickselect@2.0.0:
resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==}
radix3@1.1.2: radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
@@ -3757,6 +3846,9 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
rbush@3.0.1:
resolution: {integrity: sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==}
rc9@2.1.2: rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
@@ -3874,6 +3966,9 @@ packages:
rfdc@1.4.1: rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
rollup-plugin-visualizer@6.0.5: rollup-plugin-visualizer@6.0.5:
resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -4046,6 +4141,9 @@ packages:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
splaytree-ts@1.0.2:
resolution: {integrity: sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==}
srvx@0.11.5: srvx@0.11.5:
resolution: {integrity: sha512-MbQgu/gbLcXjg1bhUhPXXOpeMfmDMTGSKPWeht5acXnlQNldD925eS4+bIH/qESecSkP71dU3Fmvunlai1+yzw==} resolution: {integrity: sha512-MbQgu/gbLcXjg1bhUhPXXOpeMfmDMTGSKPWeht5acXnlQNldD925eS4+bIH/qESecSkP71dU3Fmvunlai1+yzw==}
engines: {node: '>=20.16.0'} engines: {node: '>=20.16.0'}
@@ -4130,6 +4228,9 @@ packages:
engines: {node: '>=16'} engines: {node: '>=16'}
hasBin: true hasBin: true
sweepline-intersections@1.5.0:
resolution: {integrity: sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==}
system-architecture@0.1.0: system-architecture@0.1.0:
resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -4191,6 +4292,9 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
tinyqueue@2.0.3:
resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==}
to-buffer@1.2.2: to-buffer@1.2.2:
resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -5039,6 +5143,16 @@ snapshots:
'@fastify/accept-negotiator@2.0.1': '@fastify/accept-negotiator@2.0.1':
optional: true optional: true
'@geoman-io/leaflet-geoman-free@2.19.2(leaflet@1.9.4)':
dependencies:
'@turf/boolean-contains': 7.3.4
'@turf/kinks': 7.3.4
'@turf/line-intersect': 7.3.4
'@turf/line-split': 7.3.4
leaflet: 1.9.4
lodash: 4.17.23
polyclip-ts: 0.16.8
'@iconify-json/lucide@1.2.91': '@iconify-json/lucide@1.2.91':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
@@ -6118,6 +6232,123 @@ snapshots:
'@standard-schema/spec@1.1.0': {} '@standard-schema/spec@1.1.0': {}
'@turf/bbox@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/meta': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/boolean-contains@7.3.4':
dependencies:
'@turf/bbox': 7.3.4
'@turf/boolean-point-in-polygon': 7.3.4
'@turf/boolean-point-on-line': 7.3.4
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@turf/line-split': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/boolean-point-in-polygon@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@types/geojson': 7946.0.16
point-in-polygon-hao: 1.2.4
tslib: 2.8.1
'@turf/boolean-point-on-line@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/distance@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/geojson-rbush@7.3.4':
dependencies:
'@turf/bbox': 7.3.4
'@turf/helpers': 7.3.4
'@turf/meta': 7.3.4
'@types/geojson': 7946.0.16
rbush: 3.0.1
tslib: 2.8.1
'@turf/helpers@7.3.4':
dependencies:
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/invariant@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/kinks@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/line-intersect@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@types/geojson': 7946.0.16
sweepline-intersections: 1.5.0
tslib: 2.8.1
'@turf/line-segment@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@turf/meta': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/line-split@7.3.4':
dependencies:
'@turf/bbox': 7.3.4
'@turf/geojson-rbush': 7.3.4
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@turf/line-intersect': 7.3.4
'@turf/line-segment': 7.3.4
'@turf/meta': 7.3.4
'@turf/nearest-point-on-line': 7.3.4
'@turf/truncate': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/meta@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/nearest-point-on-line@7.3.4':
dependencies:
'@turf/distance': 7.3.4
'@turf/helpers': 7.3.4
'@turf/invariant': 7.3.4
'@turf/meta': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@turf/truncate@7.3.4':
dependencies:
'@turf/helpers': 7.3.4
'@turf/meta': 7.3.4
'@types/geojson': 7946.0.16
tslib: 2.8.1
'@tybys/wasm-util@0.10.1': '@tybys/wasm-util@0.10.1':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -6139,12 +6370,18 @@ snapshots:
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/geojson@7946.0.16': {}
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/leaflet@1.9.21':
dependencies:
'@types/geojson': 7946.0.16
'@types/mdast@4.0.4': '@types/mdast@4.0.4':
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
@@ -6793,6 +7030,8 @@ snapshots:
bindings: 1.5.0 bindings: 1.5.0
prebuild-install: 7.1.3 prebuild-install: 7.1.3
bignumber.js@9.3.1: {}
bindings@1.5.0: bindings@1.5.0:
dependencies: dependencies:
file-uri-to-path: 1.0.0 file-uri-to-path: 1.0.0
@@ -7915,6 +8154,12 @@ snapshots:
dependencies: dependencies:
readable-stream: 2.3.8 readable-stream: 2.3.8
leaflet.markercluster@1.5.3(leaflet@1.9.4):
dependencies:
leaflet: 1.9.4
leaflet@1.9.4: {}
lilconfig@3.1.3: {} lilconfig@3.1.3: {}
listhen@1.9.0: listhen@1.9.0:
@@ -8886,6 +9131,15 @@ snapshots:
exsolve: 1.0.8 exsolve: 1.0.8
pathe: 2.0.3 pathe: 2.0.3
point-in-polygon-hao@1.2.4:
dependencies:
robust-predicates: 3.0.2
polyclip-ts@0.16.8:
dependencies:
bignumber.js: 9.3.1
splaytree-ts: 1.0.2
possible-typed-array-names@1.1.0: {} possible-typed-array-names@1.1.0: {}
postcss-calc@10.1.1(postcss@8.5.6): postcss-calc@10.1.1(postcss@8.5.6):
@@ -9091,6 +9345,8 @@ snapshots:
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
quickselect@2.0.0: {}
radix3@1.1.2: {} radix3@1.1.2: {}
randombytes@2.1.0: randombytes@2.1.0:
@@ -9099,6 +9355,10 @@ snapshots:
range-parser@1.2.1: {} range-parser@1.2.1: {}
rbush@3.0.1:
dependencies:
quickselect: 2.0.0
rc9@2.1.2: rc9@2.1.2:
dependencies: dependencies:
defu: 6.1.4 defu: 6.1.4
@@ -9294,6 +9554,8 @@ snapshots:
rfdc@1.4.1: {} rfdc@1.4.1: {}
robust-predicates@3.0.2: {}
rollup-plugin-visualizer@6.0.5(rollup@4.57.1): rollup-plugin-visualizer@6.0.5(rollup@4.57.1):
dependencies: dependencies:
open: 8.4.2 open: 8.4.2
@@ -9535,6 +9797,8 @@ snapshots:
speakingurl@14.0.1: {} speakingurl@14.0.1: {}
splaytree-ts@1.0.2: {}
srvx@0.11.5: {} srvx@0.11.5: {}
standard-as-callback@2.1.0: {} standard-as-callback@2.1.0: {}
@@ -9623,6 +9887,10 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
sax: 1.4.4 sax: 1.4.4
sweepline-intersections@1.5.0:
dependencies:
tinyqueue: 2.0.3
system-architecture@0.1.0: {} system-architecture@0.1.0: {}
tagged-tag@1.0.0: {} tagged-tag@1.0.0: {}
@@ -9692,6 +9960,8 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3 picomatch: 4.0.3
tinyqueue@2.0.3: {}
to-buffer@1.2.2: to-buffer@1.2.2:
dependencies: dependencies:
isarray: 2.0.5 isarray: 2.0.5
@@ -9714,8 +9984,7 @@ snapshots:
trough@2.2.0: {} trough@2.2.0: {}
tslib@2.8.1: tslib@2.8.1: {}
optional: true
tunnel-agent@0.6.0: tunnel-agent@0.6.0:
dependencies: dependencies:
File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
1:"$Sreact.fragment"
2:I[47257,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ClientPageRoot"]
3:I[31713,["/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js"],"default"]
6:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
7:"$Sreact.suspense"
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","async":true}],["$","script","script-1",{"src":"/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","async":true}],["$","script","script-2",{"src":"/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
4:{}
5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
8:null
-20
View File
@@ -1,20 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
4:I[47257,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ClientPageRoot"]
5:I[31713,["/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js"],"default"]
8:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
9:"$Sreact.suspense"
b:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
d:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
f:I[68027,[],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
:HL["/gratewizard-app/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
0:{"P":null,"b":"qdiAZtzGemrTuAwJHoG52","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
6:{}
7:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
10:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
a:null
e:[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L10","3",{}]]
-6
View File
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
3:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
4:"$Sreact.suspense"
5:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
-6
View File
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]]}],"loading":[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],"isPartial":false}
-4
View File
@@ -1,4 +0,0 @@
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
:HL["/gratewizard-app/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/2c55a0e60120577a-s.2a48534a.woff2)format("woff2");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/9c72aa0f40e4eef8-s.18a48cbc.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/ad66f9afd8947f86-s.7a40eb73.woff2)format("woff2");unicode-range:U+1F??}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/5476f68d60460930-s.c995e352.woff2)format("woff2");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/2bbe8d2671613f1f-s.76dcb0b2.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/1bffadaabf893a1e-s.7cd81963.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/83afe278b6a6bb3c-s.p.3a6ba036.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter Fallback;src:local(Arial);ascent-override:90.44%;descent-override:22.52%;line-gap-override:0.0%;size-adjust:107.12%}.inter_5972bc34-module__OU16Qa__className{font-family:Inter,Inter Fallback;font-style:normal}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,31761,c=>{c.v(s=>Promise.all(["static/chunks/d34030f2c8f04cfc.js"].map(s=>c.l(s))).then(()=>s(36969)))},17629,c=>{c.v(s=>Promise.all(["static/chunks/f7eb72bed109bc88.js","static/chunks/4106ddfc45f7925e.css"].map(s=>c.l(s))).then(()=>s(5618)))}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,36969,t=>{"use strict";var o=t.i(62607).domAnimation;t.s(["default",()=>o])}]);
@@ -1 +0,0 @@
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,31761,c=>{c.v(s=>Promise.all(["static/chunks/d34030f2c8f04cfc.js"].map(s=>c.l(s))).then(()=>s(36969)))},17629,c=>{c.v(s=>Promise.all(["static/chunks/d3337d279a7174ed.js","static/chunks/4106ddfc45f7925e.css"].map(s=>c.l(s))).then(()=>s(5618)))}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
File diff suppressed because one or more lines are too long
-15
View File
@@ -1,15 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
4:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
5:"$Sreact.suspense"
7:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
9:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
b:I[68027,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
0:{"P":null,"b":"qdiAZtzGemrTuAwJHoG52","c":["","_not-found"],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
c:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
6:null
a:[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$Lc","3",{}]]
@@ -1,15 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
4:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
5:"$Sreact.suspense"
7:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
9:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
b:I[68027,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
0:{"P":null,"b":"qdiAZtzGemrTuAwJHoG52","c":["","_not-found"],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false]},[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
c:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
6:null
a:[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$Lc","3",{}]]
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
3:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
4:"$Sreact.suspense"
5:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]]}],"loading":[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],"isPartial":false}
@@ -1,5 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
3:"$Sreact.suspense"
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
4:null
@@ -1,4 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
@@ -1,3 +0,0 @@
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
0:{"buildId":"qdiAZtzGemrTuAwJHoG52","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long
-20
View File
@@ -1,20 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
3:I[37457,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"default"]
4:I[47257,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ClientPageRoot"]
5:I[31713,["/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js"],"default"]
8:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"OutletBoundary"]
9:"$Sreact.suspense"
b:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"ViewportBoundary"]
d:I[97367,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"MetadataBoundary"]
f:I[68027,[],"default"]
:HL["/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","style"]
:HL["/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","style"]
:HL["/gratewizard-app/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
0:{"P":null,"b":"qdiAZtzGemrTuAwJHoG52","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/4e20891f2fd03463.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/gratewizard-app/_next/static/chunks/6aa743261c1ef51d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"className":"antialiased","lang":"fr","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/gratewizard-app/_next/static/chunks/8b5fadbc8bf897a0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/gratewizard-app/_next/static/chunks/093aca5759c9b5bd.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/gratewizard-app/_next/static/chunks/ffaecfd52c45f81a.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,false]},[["$","div","l",{"className":"inset-0 absolute justify-center items-center flex h-full w-full text-center","style":{"background":"inherit"},"children":["$","div",null,{"className":"h-4 w-28 flex relative","children":[["$","span",null,{"className":"animate-grow mr-8 left-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-move mr-[30px] h-4 w-4 rounded-full bg-primary"}],["$","span",null,{"className":"animate-growReverse mr-0 right-0 top-0 absolute h-4 w-4 rounded-full bg-primary"}]]}]}],[],[]],false,false],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[]],"S":true}
6:{}
7:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
10:I[27201,["/gratewizard-app/_next/static/chunks/4392189f08a7af14.js"],"IconMark"]
a:null
e:[["$","title","0",{"children":"GrateWizard"}],["$","meta","1",{"name":"description","content":"Calculateur de coefficients relatifs pour une économie du don"}],["$","link","2",{"rel":"icon","href":"/gratewizard-app/favicon.ico?favicon.0b3bf435.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L10","3",{}]]
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

+4 -4
View File
@@ -1,8 +1,8 @@
kicker: Prochain projet kicker: Prochain projet
title: GrateWizard title: grateWizard
description: Un outil de calcul de coefficients relatifs pour estimer les valeurs dans une économie du don. description: Un outil de calcul de coefficients relatifs pour estimer les valeurs dans une économie du don.
meta: meta:
title: GrateWizard — Coefficients relatifs title: grateWizard — Coefficients relatifs
features: features:
- icon: clock - icon: clock
title: Ancienneté title: Ancienneté
@@ -15,8 +15,8 @@ features:
title: Moyenne (M / N) title: Moyenne (M / N)
description: Obtenir l'indicateur M/N dans un bassin de vie ou un périmètre qui fait sens pour vous. description: Obtenir l'indicateur M/N dans un bassin de vie ou un périmètre qui fait sens pour vous.
- icon: heart-handshake - icon: heart-handshake
title: Prix juste title: Évaluation juste
description: La formule P' = (1-d) × P + d × P × ratio permet de doser la part du don dans chaque échange. description: La formule P' = (1-d) × P + d × P × ratio permet de doser la part du don dans chaque échange.
cta: cta:
note: Le prototype est disponible en tant qu'application autonome. note: Le prototype est disponible en tant qu'application autonome.
label: Lancer GrateWizard label: Lancer grateWizard
+11 -13
View File
@@ -9,7 +9,7 @@ hero:
label: En savoir plus sur le projet label: En savoir plus sur le projet
to: /a-propos to: /a-propos
book: book:
kicker: Le livre kicker: Modèle économique
title: Une économie du don — enfin concevable title: Une économie du don — enfin concevable
description: Un livre et quelques chansons pour une proposition de modèle économique fondé sur le don. Le livre est description: Un livre et quelques chansons pour une proposition de modèle économique fondé sur le don. Le livre est
accompagné de chansons qui le racontent, un peu autrement. accompagné de chansons qui le racontent, un peu autrement.
@@ -22,17 +22,16 @@ bookPresentation:
kicker: Le livre kicker: Le livre
title: Une économie du don — enfin concevable title: Une économie du don — enfin concevable
description: description:
- Ce livre explore les fondements d'une économie fondée sur le don. À travers 11 chapitres, il déploie une vision où - Ce livre explore les fondements d'une économie fondée sur le don.
la richesse se mesure à ce que l'on donne, pas à ce que l'on accumule. - Chaque chapitre est accompagné d'une chanson qui en prolonge le propos, créant une invitation inédite et
- Chaque chapitre est accompagné d'une chanson qui en prolonge le propos, créant une expérience de lecture unique et immersive à la lecture.
immersive.
cta: cta:
label: Découvrir le sommaire label: Sommaire
to: /lire to: /lire
songs: songs:
kicker: Les chansons kicker: Les chansons
title: 9 chansons qui racontent le livre title: 9 chansons qui racontent le livre
description: Chaque chanson est un prolongement musical d'un ou deux chapitres. Écoutez-les en lisant, ou savourez-les librement. description: Chaque chanson est un prolongement musical d'un ou deux chapitres.
cta: cta:
label: Voir toutes les chansons label: Voir toutes les chansons
to: /ecouter to: /ecouter
@@ -42,17 +41,16 @@ cooperative:
title: Une plateforme coopérative title: Une plateforme coopérative
description: description:
- Le Librodrome est le premier pas vers une plateforme de productions collectives. Un espace où les créateurs, - Le Librodrome est le premier pas vers une plateforme de productions collectives. Un espace où les créateurs,
lecteurs et auditeurs contribuent ensemble à faire émerger des oeuvres nouvelles. producteurs et personnes mobilisées, contribuent ensemble à faire émerger des projets de production.
- Ce projet est ouvert. Chaque contribution enrichit l'ensemble. Rejoignez-nous pour construire ensemble l'économie - Ce projet est ouvert. Chaque contribution enrichit l'ensemble. Rejoignez-nous pour construire une autonomie collective à l'échelle des bassins de vie.
du don.
cta: cta:
label: En savoir plus label: En savoir plus
to: /a-propos to: /a-propos
grateWizardTeaser: grateWizardTeaser:
kicker: Prochain projet kicker: Estimer les valeurs en DU - Les coefficients relatifs
title: GrateWizard title: grateWizard
description: Une webapp pour calculer des coefficients relatifs et estimer les valeurs dans une économie du don. description: Une webapp pour calculer des coefficients relatifs et estimer les valeurs dans une économie du don.
Comparez l'ancienneté, la quantité ou la moyenne pour fixer un prix juste. Relatif à la moyenne, à l'ancienneté, au solde net, au volume.
cta: cta:
launch: Lancer l'appli launch: Lancer l'appli
more: more:
+1 -1
View File
@@ -17,7 +17,7 @@ footer:
- label: Mentions légales - label: Mentions légales
to: /mentions-legales to: /mentions-legales
gratewizard: gratewizard:
url: /gratewizard-app/ url: /gratewizard?popup
popup: popup:
width: 420 width: 420
height: 720 height: 720