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>
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 à l'ancienneté</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é</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éé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ç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é {{ 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éés
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Price -->
|
||||||
|
<div class="flex items-center gap-3 justify-center">
|
||||||
|
<label class="gw-text text-sm whitespace-nowrap">Évaluation de ré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">Ğ1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Discount -->
|
||||||
|
<div class="flex items-center gap-3 justify-center">
|
||||||
|
<label class="gw-text text-sm whitespace-nowrap">Ré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">
|
||||||
|
Évaluation corrigé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>
|
||||||
@@ -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ç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é {{ 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éé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é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">Ğ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 × (1 + w × (1 − S / C))
|
||||||
|
</p>
|
||||||
|
<template v-if="seniority > 0">
|
||||||
|
<p class="gw-text text-xs font-mono mb-2">
|
||||||
|
{{ fr(v) }} × (1 + {{ fr(w) }} × (1 − {{ fr(s, 0) }} / {{ fr(seniority, 0) }}))
|
||||||
|
= {{ fr(v) }} × {{ fr(factor) }}
|
||||||
|
</p>
|
||||||
|
<p class="gw-title">= {{ fr(correctedValue) }} {{ currencyDisplay }}</p>
|
||||||
|
</template>
|
||||||
|
<p v-else class="gw-text text-xs">Renseigner l'ancienneté 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>
|
||||||
@@ -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">Ğ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é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é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) }} Ğ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é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é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 }} —
|
||||||
|
{{ 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érimè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éolocalisé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éolocalisé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és géolocalisé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érimè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>
|
||||||
@@ -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: '© <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érimètres sauvegardé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">📍</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)"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
export function useGrateWizard() {
|
export function useGrateWizard() {
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
function launch() {
|
|
||||||
const { url, popup } = appConfig.gratewizard as { url: string; popup: { width: number; height: number } }
|
const { url, popup } = appConfig.gratewizard as { url: string; popup: { width: number; height: number } }
|
||||||
|
|
||||||
|
function launch(e?: Event) {
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- Popup mode: standalone app, no Librodrome layout -->
|
||||||
|
<main v-if="isPopup" class="gw-app flex flex-col items-center sm:p-4 overflow-x-hidden h-screen">
|
||||||
|
<div class="sm:max-w-screen-sm w-full">
|
||||||
|
<div class="gw-card">
|
||||||
|
<GratewizardGwTabs />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Info mode: Librodrome layout with feature cards -->
|
||||||
|
<NuxtLayout v-else>
|
||||||
<div class="section-padding">
|
<div class="section-padding">
|
||||||
<div class="container-content max-w-3xl mx-auto">
|
<div class="container-content max-w-3xl mx-auto">
|
||||||
<!-- Back link -->
|
<!-- Back link -->
|
||||||
<UiScrollReveal>
|
<UiScrollReveal>
|
||||||
<NuxtLink to="/" class="inline-flex items-center gap-2 text-sm text-white/50 hover:text-white/80 mb-8 transition-colors">
|
<NuxtLink to="/" class="inline-flex items-center gap-2 text-sm text-white/50 hover:text-white/80 mb-8 transition-colors">
|
||||||
<div class="i-lucide-arrow-left h-4 w-4" />
|
<div class="i-lucide-arrow-left h-4 w-4" />
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</UiScrollReveal>
|
</UiScrollReveal>
|
||||||
|
|
||||||
@@ -47,7 +58,7 @@
|
|||||||
<p class="text-sm text-white/40 mb-4">
|
<p class="text-sm text-white/40 mb-4">
|
||||||
{{ content?.cta.note }}
|
{{ content?.cta.note }}
|
||||||
</p>
|
</p>
|
||||||
<UiBaseButton @click="launch">
|
<UiBaseButton :href="url" target="_blank" @click="launch">
|
||||||
<div class="i-lucide-external-link mr-2 h-5 w-5" />
|
<div class="i-lucide-external-link mr-2 h-5 w-5" />
|
||||||
{{ content?.cta.label }}
|
{{ content?.cta.label }}
|
||||||
</UiBaseButton>
|
</UiBaseButton>
|
||||||
@@ -55,19 +66,40 @@
|
|||||||
</UiScrollReveal>
|
</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>
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -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>>;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -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();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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,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}
|
|
||||||
@@ -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,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 @@
|
|||||||
[]
|
|
||||||
@@ -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 @@
|
|||||||
[]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
|
||||||
@@ -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}
|
|
||||||
@@ -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)))}]);
|
|
||||||
@@ -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)))}]);
|
|
||||||
@@ -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 @@
|
|||||||
[]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
|
||||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 696 B |
|
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 @@
|
|||||||
[]
|
|
||||||
@@ -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 @@
|
|||||||
[]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
|
||||||
@@ -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}
|
|
||||||
|
Before Width: | Height: | Size: 25 KiB |
@@ -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 +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 +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 |
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||