-
-
{$t("project.tabs.budget.campaingState")}
+
+
+ {$t("project.tabs.budget.total")}:
+ {formatCurrency(totalBudget)}
+
+
+
+ {#each legendEntries as [key, color]}
+
+
+
{$t(`project.tabs.budget.typeBudget.${key}`)}
+
+ {/each}
diff --git a/src/components/project/ProgressChart.svelte b/src/components/project/ProgressChart.svelte
index 0a455752..b87a3e37 100644
--- a/src/components/project/ProgressChart.svelte
+++ b/src/components/project/ProgressChart.svelte
@@ -14,9 +14,9 @@
Accounting,
ApiAccountingBalancePointsGetCollectionData,
Project,
- ProjectCalendar,
} from "../../openapi/client/index";
import { formatCurrency } from "../../utils/currencies";
+ import { t } from "../../i18n/store";
export let accounting: Accounting;
export let project: Project;
@@ -33,36 +33,55 @@
let optimal = formatAmount(project.budget?.optimum?.money?.amount);
let canvas: HTMLCanvasElement | null = null;
- let data = Array.isArray(balancePoints)
- ? balancePoints.map((point, i) => {
- return {
- x: i,
- y: formatAmount(point.balance.amount),
- };
- })
- : [];
+ const marginDays = 4;
- let maxValue = minimal;
+ let data: { x: number; y: number }[] = [];
+ let sortedBalancePoints: any[] = [];
+ let firstDateMs: number | null = null;
+ let lastDay = 0;
- if (received > minimal) {
- maxValue = optimal;
+ if (Array.isArray(balancePoints) && balancePoints.length > 0) {
+ sortedBalancePoints = [...balancePoints].sort(
+ (a, b) => new Date(a.start).getTime() - new Date(b.start).getTime(),
+ );
+
+ firstDateMs = new Date(sortedBalancePoints[0].start).getTime();
+
+ data = sortedBalancePoints.map((point) => {
+ const pointMs = new Date(point.start).getTime();
+ const daysSinceFirst = Math.round((pointMs - firstDateMs!) / 86400000);
+ return { x: daysSinceFirst + marginDays, y: formatAmount(point.balance.amount) };
+ });
+
+ const lastDateMs = new Date(
+ sortedBalancePoints[sortedBalancePoints.length - 1].start,
+ ).getTime();
+ lastDay = Math.round((lastDateMs - firstDateMs) / 86400000) + marginDays;
+
+ for (let i = marginDays - 1; i >= 0; i--) {
+ data.unshift({ x: i, y: 0 });
+ }
+ } else {
+ for (let i = marginDays - 1; i >= 0; i--) {
+ data.unshift({ x: i, y: 0 });
+ }
+ firstDateMs = null;
+ lastDay = marginDays;
}
- if (received > optimal) {
- maxValue = received;
+ let maxValue = Math.max(minimal, optimal, received) * 1.05;
+
+ const hasSecondRound = project.deadline == "optimum";
+
+ let secondRoundStartDay = -1;
+ if (hasSecondRound && firstDateMs !== null) {
+ const minimumDate = new Date(project.calendar!.minimum!).getTime();
+ const diffDays = Math.round((minimumDate - firstDateMs) / 86400000);
+ if (diffDays >= 0) {
+ secondRoundStartDay = diffDays + marginDays;
+ }
}
- maxValue = maxValue + maxValue / 20;
-
- function calcDaysForMinimum(calendar: ProjectCalendar): number {
- const release = new Date(calendar.release!).getTime();
- const minimum = new Date(calendar.minimum!).getTime();
-
- return Math.round(Math.abs((release - minimum) / 86400000));
- }
-
- const daysForMinimum = calcDaysForMinimum(project.calendar!);
-
onMount(() => {
Chart.register(
LineController,
@@ -75,59 +94,74 @@
);
Chart.register({
- id: "twoSectionLines",
+ id: "guidelines",
afterDatasetDraw(chart) {
const {
ctx,
- chartArea: { left, right },
- scales: { y },
+ chartArea: { top, left, right, bottom },
+ scales: { y, x },
} = chart;
ctx.save();
- const drawLine = (value: number, color: string) => {
+ const drawHorizontal = (value: any) => {
const yPos = y.getPixelForValue(value);
ctx.beginPath();
ctx.moveTo(left, yPos);
ctx.lineTo(right, yPos);
ctx.lineWidth = 1;
- ctx.strokeStyle = color;
+ ctx.strokeStyle = "#000";
ctx.setLineDash([2, 3]);
ctx.stroke();
};
- drawLine(minimal, "text-black");
- drawLine(optimal, "text-black");
+ drawHorizontal(minimal);
+ drawHorizontal(optimal);
+
+ if (hasSecondRound && secondRoundStartDay !== -1) {
+ const xPos = x.getPixelForValue(secondRoundStartDay);
+ const color = "#462949";
+
+ const badgeWidth = 76;
+ const badgeHeight = 26;
+ const badgeY = top + 8;
+ const badgeX = xPos - badgeWidth / 2;
+
+ ctx.fillStyle = color;
+ if (typeof ctx.roundRect === "function") {
+ ctx.beginPath();
+ ctx.roundRect(badgeX, badgeY, badgeWidth, badgeHeight, 6);
+ ctx.fill();
+ } else {
+ ctx.beginPath();
+ ctx.fillRect(badgeX, badgeY, badgeWidth, badgeHeight);
+ }
+
+ ctx.font = "12px sans-serif";
+ ctx.fillStyle = "#fff";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText($t("budget.chart.secondRound"), xPos, badgeY + badgeHeight / 2);
+
+ const lineStartY = badgeY + badgeHeight + 4;
+ ctx.beginPath();
+ ctx.moveTo(xPos, lineStartY);
+ ctx.lineTo(xPos, bottom);
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = color;
+ ctx.setLineDash([]);
+ ctx.stroke();
+
+ const pointY = lineStartY + 4;
+ ctx.beginPath();
+ ctx.arc(xPos, pointY, 4, 0, 2 * Math.PI);
+ ctx.fillStyle = color;
+ ctx.fill();
+ }
ctx.restore();
},
});
- // Chart.register({
- // id: "deadlineLine",
- // afterDatasetDraw(chart) {
- // const {
- // ctx,
- // chartArea: { top, bottom },
- // scales: { x },
- // } = chart;
-
- // ctx.save();
-
- // const xPos = x.getPixelForValue(daysForMinimum);
-
- // // Draw vertical line
- // ctx.beginPath();
- // ctx.moveTo(xPos, top - 100);
- // ctx.lineTo(xPos, bottom + 100);
- // ctx.lineWidth = 2;
- // ctx.strokeStyle = "rgba(239, 68, 68, 0.5)";
- // ctx.setLineDash([]);
- // ctx.stroke();
-
- // ctx.restore();
- // },
- // });
-
if (!canvas) return;
new Chart(canvas, {
@@ -136,12 +170,12 @@
datasets: [
{
data,
- borderColor: "rgba(94, 234, 212, 1)",
- backgroundColor: "rgba(94, 234, 212, 1)",
+ borderColor: "#59E9D3",
+ backgroundColor: "#59E9D3",
borderWidth: 1,
fill: "start",
tension: 0.25,
- pointRadius: 0.5,
+ pointRadius: 0,
},
],
},
@@ -151,9 +185,13 @@
scales: {
x: {
type: "linear",
- display: true,
+ display: false,
beginAtZero: true,
- max: data.length - 1,
+ min: 0,
+ max:
+ hasSecondRound && secondRoundStartDay > lastDay
+ ? secondRoundStartDay + 5
+ : lastDay,
grid: { display: false },
},
y: {
@@ -167,10 +205,12 @@
tooltip: {
callbacks: {
title: (items) => {
- // @ts-ignore
- const point = balancePoints[items[0].dataIndex];
-
- return new Date(point.start).toLocaleDateString();
+ const dataIndex = items[0].dataIndex;
+ const bpIndex = dataIndex - marginDays;
+ const point = sortedBalancePoints[bpIndex];
+ return point
+ ? new Date(point.start).toLocaleDateString()
+ : $t("budget.chart.start");
},
},
},
@@ -180,6 +220,6 @@
});
-
-
+
+
diff --git a/src/components/project/ProjectBudget.svelte b/src/components/project/ProjectBudget.svelte
index a9c5bd6f..e5b95cce 100644
--- a/src/components/project/ProjectBudget.svelte
+++ b/src/components/project/ProjectBudget.svelte
@@ -75,7 +75,7 @@
-
+
{$t("project.tabs.budget.minimum")}:
{formatCurrency(
project.budget?.minimum?.money?.amount,
@@ -96,13 +96,13 @@
class="flex w-full flex-col justify-between gap-6 rounded-4xl bg-white p-6 font-bold"
>
-
{item.title}
-
+
{item.title}
+
{item.description}
-
+
{formatCurrency(item.money.amount, item.money.currency)}
@@ -119,7 +119,7 @@
-
+
{$t("project.tabs.budget.optimal")}:
{formatCurrency(
@@ -141,13 +141,13 @@
class="flex w-full flex-col justify-between gap-6 rounded-4xl bg-white p-6 font-bold"
>
-
{item.title}
-
+
{item.title}
+
{item.description}
-
+
{formatCurrency(item.money.amount, item.money.currency)}
diff --git a/src/components/project/ProjectCommunity.svelte b/src/components/project/ProjectCommunity.svelte
index 3b2c6f41..d88bf23a 100644
--- a/src/components/project/ProjectCommunity.svelte
+++ b/src/components/project/ProjectCommunity.svelte
@@ -89,7 +89,7 @@
{:else}
-
+
{$t("project.tabs.community.content.title")}
@@ -121,7 +121,7 @@
@@ -132,10 +132,10 @@
😀
-
+
{$t("project.tabs.community.contribution")}
-
+
{formatCurrency(
selectedProjectSupport.money?.amount ?? 0,
selectedProjectSupport.money?.currency ?? "undefined",
@@ -143,10 +143,10 @@
-
+
{selectedProjectSupport.displayName}
-
+
{selectedProjectSupport.message}
diff --git a/src/components/project/ProjectCommunityAnonymous.svelte b/src/components/project/ProjectCommunityAnonymous.svelte
index 3f9fa2a1..03f29af3 100644
--- a/src/components/project/ProjectCommunityAnonymous.svelte
+++ b/src/components/project/ProjectCommunityAnonymous.svelte
@@ -33,7 +33,7 @@
/>
-
+
{$t("project.tabs.community.matchfunding.contribution")}
-
+
{$t("project.tabs.community.anonymous.title")}
-
+
{$t("project.tabs.community.anonymous.description")}
diff --git a/src/components/project/ProjectCommunityMatchfunding.svelte b/src/components/project/ProjectCommunityMatchfunding.svelte
index e6f5356c..b1dafb3e 100644
--- a/src/components/project/ProjectCommunityMatchfunding.svelte
+++ b/src/components/project/ProjectCommunityMatchfunding.svelte
@@ -14,7 +14,7 @@
>
😀
-
+
{$t("project.tabs.community.matchfunding.contribution")}
@@ -26,7 +26,7 @@
)}
-
+
{$t("project.tabs.community.matchfunding.up_to")}
{formatCurrency(item.money?.amount ?? 0, item.money?.currency ?? "")}
@@ -34,10 +34,10 @@
-
+
{item.displayName}
-
+
{item.message}
diff --git a/src/components/project/ProjectCommunityMessage.svelte b/src/components/project/ProjectCommunityMessage.svelte
index d522ca40..e007eccf 100644
--- a/src/components/project/ProjectCommunityMessage.svelte
+++ b/src/components/project/ProjectCommunityMessage.svelte
@@ -15,18 +15,18 @@
😀
-
+
{$t("project.tabs.community.contribution")}
-
+
{formatCurrency(item.money?.amount ?? 0, item.money?.currency ?? "")}
-
+
{item.displayName}
-
+
{#if item.message}
{item.message}
{:else}
diff --git a/src/components/project/ProjectRewards.svelte b/src/components/project/ProjectRewards.svelte
index 73d306e3..438551f4 100644
--- a/src/components/project/ProjectRewards.svelte
+++ b/src/components/project/ProjectRewards.svelte
@@ -8,6 +8,7 @@
import { cart } from "../../stores/cart";
import { getUnit } from "../../utils/currencies";
import { extractId } from "../../utils/extractId";
+ import Button from "../library/Button.svelte";
import Reward from "../Reward.svelte";
let {
@@ -73,17 +74,17 @@
-
+
{$t("rewards.title")}
-
-
+
{$t("rewards.donation-free.title")}
@@ -97,16 +98,14 @@
placeholder={$t("rewards.donation-free.placeholder")}
bind:value={freeAmount}
/>
-
+
{#each rewards as reward}
diff --git a/src/components/project/ProjectUpdate.svelte b/src/components/project/ProjectUpdate.svelte
index 542ea10b..90353198 100644
--- a/src/components/project/ProjectUpdate.svelte
+++ b/src/components/project/ProjectUpdate.svelte
@@ -9,6 +9,7 @@
import { apiProjectUpdatesGetCollection } from "../../openapi/client/index";
import Carousel from "../Carousel.svelte";
import { renderMarkdown } from "../../utils/renderMarkdown";
+ import Button from "../library/Button.svelte";
let {
lang = $bindable(),
@@ -103,7 +104,7 @@
-
+
{$t("project.tabs.updates.content.title")}
@@ -120,7 +121,7 @@
class="flex w-full flex-col justify-between gap-6 rounded-4xl bg-white p-6 font-bold"
>
-
+
{formatDate(update.date ?? "")}
@@ -134,22 +135,22 @@
{/if}
-
{update.title}
+
{update.title}
-
{update.subtitle}
-
{update.body}
+
{update.subtitle}
+
{update.body}
-
+
{/each}
@@ -157,17 +158,17 @@
{#if selected}
{#if shouldShowHeader(selected.date)}
-
+
{$t("project.tabs.updates.modal-title")}
{/if}
-
+
{selected?.title}
@@ -177,12 +178,10 @@
-
{/if}
diff --git a/src/components/project/ResumeBudget.svelte b/src/components/project/ResumeBudget.svelte
index 9cb54e6c..6ab6834d 100644
--- a/src/components/project/ResumeBudget.svelte
+++ b/src/components/project/ResumeBudget.svelte
@@ -2,7 +2,6 @@
import BudgetBar from "./BudgetBar.svelte";
import LegendBudgetBar from "./LegendBudgetBar.svelte";
import { t } from "../../i18n/store";
- import { formatCurrency } from "../../utils/currencies";
import type { Project, Accounting } from "../../openapi/client/index";
let { project, accounting } = $props<{
@@ -12,40 +11,14 @@
-
+
{$t("project.tabs.budget.chart-title")}
-
-
-
- {$t("project.tabs.budget.minimum")}:
-
-
- {formatCurrency(
- project.budget.minimum.money.amount,
- project.budget.minimum.money.currency,
- )}
-
-
-
-
-
-
-
-
- {$t("project.tabs.budget.optimal")}:
-
-
- {formatCurrency(
- project.budget.optimum.money.amount,
- project.budget.optimum.money.currency,
- )}
-
-
-
-
-
-
+
+
+
diff --git a/src/components/project/Sharebutton.svelte b/src/components/project/Sharebutton.svelte
index 1e7d3fdb..38048990 100644
--- a/src/components/project/Sharebutton.svelte
+++ b/src/components/project/Sharebutton.svelte
@@ -59,7 +59,7 @@
(openModal = true)}
- class="text-tertiary flex cursor-pointer flex-row items-center gap-2 p-2 font-bold"
+ class="text-secondary flex cursor-pointer flex-row items-center gap-2 p-2 font-bold"
>
{$t("project.actions.share")}
@@ -67,17 +67,17 @@
{$t("project.share.modal.description")}
{$t("project.share.iframe-modal.description")}
@@ -149,9 +149,9 @@
{
navigator.clipboard.writeText(iframeCode);
copied = true;
diff --git a/src/components/project/SingleProject.svelte b/src/components/project/SingleProject.svelte
index 5ac875fd..65de6f63 100644
--- a/src/components/project/SingleProject.svelte
+++ b/src/components/project/SingleProject.svelte
@@ -21,6 +21,7 @@
import RememberIcon from "../../svgs/RememberIcon.svelte";
import { getDefaultLanguage } from "../../utils/consts";
import TopRewards from "./TopRewards.svelte";
+ import Button from "../library/Button.svelte";
let {
lang = $bindable(),
@@ -39,11 +40,8 @@
} = $props();
let poster = { src: project.video?.thumbnail || "", alt: "Miniatura del video" };
- let showFull = $state(false);
- let paragraphRef: HTMLParagraphElement;
- let showToggle = $state(false);
- const countdownEnd = project.calendar?.optimum ? new Date(project.calendar.optimum) : undefined;
+ const countdownEnd = getCurrentDeadline(project);
async function getProjectData(code?: string) {
lang = code ? code : getDefaultLanguage();
@@ -58,46 +56,61 @@
project = data!;
}
- onMount(() => {
- const twoLinesHeight = parseFloat(getComputedStyle(paragraphRef).lineHeight) * 2;
- if (paragraphRef.scrollHeight > twoLinesHeight + 1) {
- showToggle = true;
+ let tabsComponent: any;
+
+ function scrollToRewards() {
+ if (tabsComponent?.activateRewardsTab) {
+ tabsComponent.activateRewardsTab();
}
- });
+ setTimeout(() => {
+ const rewardsElement = document.getElementById("tab-rewards");
+ if (rewardsElement) {
+ rewardsElement.scrollIntoView({
+ behavior: "smooth",
+ block: "start",
+ });
+ }
+ }, 100);
+ }
+
+ function getCurrentDeadline(project: Project): Date | undefined {
+ const now = new Date();
+
+ const minimum = new Date(project.calendar?.minimum!);
+ if (now < minimum) {
+ return minimum;
+ }
+
+ if (!project.calendar?.optimum) {
+ return undefined;
+ }
+
+ const optimum = new Date(project.calendar?.optimum);
+ if (now < optimum) {
+ return optimum;
+ }
+
+ return undefined;
+ }
-
+
{$t("project.owner")}
- {ownerName}
+ {ownerName}
-
+
{project.title}
-
+
{project.subtitle}
-
- {#if showToggle}
-
(showFull = !showFull)}
- >
- {showFull ? $t("project.actions.viewLess") : $t("project.actions.viewMore")}
-
- {/if}
@@ -129,39 +142,40 @@
-
+
-
+
-
- {$t("project.actions.remember")}
+
+
+ {$t("project.actions.remember")}
+
-
+
{$t("reward.trending")}
-
+
{$t("reward.showAll")}
-
+
-
+
{$t("reward.showAll")}
-
+
-
+
diff --git a/src/components/project/Slider.svelte b/src/components/project/Slider.svelte
index ef846e8c..0107660c 100644
--- a/src/components/project/Slider.svelte
+++ b/src/components/project/Slider.svelte
@@ -66,22 +66,22 @@
{#if !isLoaded}
- Cargando...
+ Cargando...
{/if}
{#each slides as { title, subtitle, date, body, cover }}
-
{date}
+
{date}
{#if cover}

{/if}
-
{title}
-
{subtitle}
-
{body}
+
{title}
+
{subtitle}
+
{body}
{/each}
diff --git a/src/components/project/Tabs.svelte b/src/components/project/Tabs.svelte
index b6bfe2c8..5252330c 100644
--- a/src/components/project/Tabs.svelte
+++ b/src/components/project/Tabs.svelte
@@ -19,14 +19,14 @@
accounting: Accounting;
}>();
- let activeTab = $state("rewards");
+ let activeTab = $state("project");
let tabsContainer: HTMLDivElement;
let canScrollLeft = $state(false);
let canScrollRight = $state(true);
const tabs = [
- { id: "rewards", label: $t("project.tabs.rewards") },
{ id: "project", label: $t("project.tabs.project") },
+ { id: "rewards", label: $t("project.tabs.rewards") },
{ id: "budget", label: $t("project.tabs.budget.title") },
{ id: "updates", label: $t("project.tabs.updates.title") },
{ id: "community", label: $t("project.tabs.community.title") },
@@ -36,6 +36,10 @@
activeTab = tabId;
}
+ export function activateRewardsTab() {
+ activeTab = "rewards";
+ }
+
function updateScrollButtons() {
if (!tabsContainer) return;
@@ -67,7 +71,7 @@
scrollTabs("left")}
- class="absolute top-5 left-0 z-10 h-8 w-8 -translate-y-1/2 rounded-r-sm bg-[#e6e5f7] p-2 shadow-md lg:hidden"
+ class="bg-variant1 absolute top-5 left-0 z-10 h-8 w-8 -translate-y-1/2 rounded-r-sm p-2 shadow-md lg:hidden"
class:opacity-50={!canScrollLeft}
class:pointer-events-none={!canScrollLeft}
aria-label="Scroll tabs left"
@@ -89,8 +93,8 @@
aria-selected={activeTab === tab.id}
aria-controls={`tab-${tab.id}`}
id={`tab-button-${tab.id}`}
- class="text-tertiary inline-flex flex-shrink-0 items-center rounded-t-lg px-6 py-2 font-bold whitespace-nowrap transition-colors duration-100 ease-in-out lg:border-t-1 lg:border-r-1 lg:border-l-1 lg:border-[#E6E5F7]"
- class:bg-[#E6E5F7]={activeTab === tab.id}
+ class="text-secondary lg:border-variant1 inline-flex flex-shrink-0 items-center rounded-t-lg px-6 py-2 font-bold whitespace-nowrap transition-colors duration-100 ease-in-out lg:border-t-1 lg:border-r-1 lg:border-l-1"
+ class:bg-variant1={activeTab === tab.id}
onclick={() => selectTab(tab.id)}
>
{tab.label}
@@ -100,7 +104,7 @@
scrollTabs("right")}
- class="absolute top-5 right-0 z-10 h-8 w-8 -translate-y-1/2 rounded-l-sm bg-[#e6e5f7] p-2 shadow-md lg:hidden"
+ class="bg-variant1 absolute top-5 right-0 z-10 h-8 w-8 -translate-y-1/2 rounded-l-sm p-2 shadow-md lg:hidden"
class:opacity-50={!canScrollRight}
class:pointer-events-none={!canScrollRight}
aria-label="Scroll tabs right"
@@ -117,7 +121,7 @@
}
-
+
{#if activeTab === "rewards"}
+
+
+
+ {#if showLabel}
+
+ {$t("search.categoryLabel")}
+
+ {/if}
+
+ {#await categories then categories}
+
s.id)}
+ options={categories.map((c) => mapCategoryToOption(c))}
+ onchange={(selected) => onCategoryChange?.(selected.map((o) => `${o.id}`))}
+ />
+ {/await}
+
+ {#if selected.length > 0}
+
+ {$t("search.selectedCategories", { count: selected.length })}
+
+ {/if}
+
diff --git a/src/components/search/FilterDropdown.svelte b/src/components/search/FilterDropdown.svelte
new file mode 100644
index 00000000..c445d6ee
--- /dev/null
+++ b/src/components/search/FilterDropdown.svelte
@@ -0,0 +1,155 @@
+
+
+
+
+ {#if label}
+
+ {/if}
+
+
+
+ {getDisplayText()}
+
+
+
+
+ {#if isOpen}
+
+
+ {#each options as option}
+ selectOption(option)}
+ role="option"
+ aria-selected={selectedOption?.value === option.value}
+ data-testid={`${props["data-testid"] || "filter"}-option-${option.value}`}
+ >
+ {option.translationKey
+ ? $t(option.translationKey)
+ : option.label || option.value}
+
+ {/each}
+
+
+ {/if}
+
+
+
+
{
+ const target = e.target as Element;
+ const dropdown =
+ target?.closest?.('[role="listbox"]') ||
+ target?.closest?.('button[aria-haspopup="listbox"]');
+ if (!dropdown && isOpen) {
+ isOpen = false;
+ }
+ }}
+/>
diff --git a/src/components/search/LoadMoreButton.svelte b/src/components/search/LoadMoreButton.svelte
new file mode 100644
index 00000000..a28b742a
--- /dev/null
+++ b/src/components/search/LoadMoreButton.svelte
@@ -0,0 +1,100 @@
+
+
+
+
+{#if hasMore}
+
+
+ {#if isLoading}
+
+
+ {$t("search.loadMore.loading")}
+
+ {:else}
+ {$t("search.loadMore.button")}
+ {/if}
+
+
+
+
+ {$t("search.results.showing", { count: loadedCount })}
+
+
+
+
+
+ {#if justLoaded}
+ {$t("search.loadMore.announcement", {
+ count: newResultsCount,
+ loaded: loadedCount,
+ })}
+ {/if}
+
+{/if}
+
+
diff --git a/src/components/search/LoadingSpinner.svelte b/src/components/search/LoadingSpinner.svelte
new file mode 100644
index 00000000..6b03af88
--- /dev/null
+++ b/src/components/search/LoadingSpinner.svelte
@@ -0,0 +1,57 @@
+
+
+
+
+
+
diff --git a/src/components/search/SearchButton.svelte b/src/components/search/SearchButton.svelte
new file mode 100644
index 00000000..62777ec0
--- /dev/null
+++ b/src/components/search/SearchButton.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+ {#if loading}
+
+ {/if}
+ {@render children?.()}
+
diff --git a/src/components/search/SearchErrorAlert.svelte b/src/components/search/SearchErrorAlert.svelte
new file mode 100644
index 00000000..c9b90aed
--- /dev/null
+++ b/src/components/search/SearchErrorAlert.svelte
@@ -0,0 +1,80 @@
+
+
+
+{#if error}
+
+
+
+
+
+
+
+
+ {$t("search.error.title", "Search Error")}
+
+
+ {error}
+
+
+
+ {#if showRetry && onRetry}
+
+
+ {$t("search.error.retry", "Try Again")}
+
+ {/if}
+
+
+
+{/if}
diff --git a/src/components/search/SearchFilters.svelte b/src/components/search/SearchFilters.svelte
new file mode 100644
index 00000000..0bd3fff2
--- /dev/null
+++ b/src/components/search/SearchFilters.svelte
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+ updateFilters({ query })}
+ onEnter={handleSearch}
+ onClear={handleSearch}
+ placeholder={$t("search.placeholder")}
+ data-testid="search-input"
+ />
+
+
+
+
+ {$t("search.searchButton")}
+
+
+
+
+ {#if !filtersOpen}
+
+
+ {$t("search.showFilters")}
+
+ {/if}
+
+
+
+ {#if filtersOpen}
+
+
+ updateFilters({ statusFilter: value })}
+ data-testid="status-filter"
+ />
+
+
+
+
+ updateFilters({ categories })}
+ data-testid="category-filter"
+ />
+
+
+
+
+
+
+ {$t("search.applyFilters")}
+
+
+
+
+
+ {$t("search.closeFilters")}
+
+
+ {/if}
+
diff --git a/src/components/search/SearchInput.svelte b/src/components/search/SearchInput.svelte
new file mode 100644
index 00000000..6960fc7e
--- /dev/null
+++ b/src/components/search/SearchInput.svelte
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+ {#if searchQuery}
+
+
+
+ {/if}
+
+
+
+
+ {$t("search.label")}
+
+
+
+
+
diff --git a/src/components/search/SearchPagination.svelte b/src/components/search/SearchPagination.svelte
new file mode 100644
index 00000000..f1012419
--- /dev/null
+++ b/src/components/search/SearchPagination.svelte
@@ -0,0 +1,81 @@
+
+
+
+{#if $hasPrevPage || $hasNextPage}
+
+{/if}
diff --git a/src/components/search/SearchResultsClient.svelte b/src/components/search/SearchResultsClient.svelte
new file mode 100644
index 00000000..e0b7349c
--- /dev/null
+++ b/src/components/search/SearchResultsClient.svelte
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+ {#if $searchError}
+
+
+
+ {/if}
+
+
+
+
+ {#if ($isSearching || isTransforming) && campaigns.length === 0}
+
+
+
+ {/if}
+
+
+ {#if campaigns.length > 0}
+
+ {#each campaigns as campaign}
+
+
+
+
+ {/each}
+
+ {/if}
+
+
+
+ {#if $hasActualSearchResults && !isTransforming}
+
+ searchStore.loadMoreResults()}
+ isLoading={$isSearching}
+ hasMore={$hasNextPage}
+ loadedCount={$searchResults.length}
+ />
+
+ {/if}
+
+ {#if $isEmpty && !$isSearching && !isTransforming}
+
+
+
+
+ {$t("search.empty.title")}
+
+
+ {$t("search.empty.description")}
+
+
+ {$t("search.results.empty.clearFilters")}
+
+
+ {/if}
+
+ {#if !$hasSearchResults && !$isEmpty && !$isSearching && !isTransforming}
+
+
+
+ {$t("search.initial.title")}
+
+
{$t("search.initial.description")}
+
+ {/if}
+
+
+
diff --git a/src/i18n/locales/ca.json b/src/i18n/locales/ca.json
index 24b08d39..e9b5126e 100644
--- a/src/i18n/locales/ca.json
+++ b/src/i18n/locales/ca.json
@@ -31,7 +31,9 @@
"chart": {
"minimum": "Mínim",
"optimal": "Òptim",
- "progress": "Estat de la campanya"
+ "progress": "Estat de la campanya",
+ "secondRound": "2a Ronda",
+ "start": "Inici"
}
},
"project": {
@@ -126,10 +128,148 @@
},
"navigation": {
"search": {
- "placeholder": "Cerca la teva campanya"
+ "placeholder": "Cerca la teva campanya",
+ "label": "Cercar campanyes",
+ "clearSearch": "Esborrar cerca",
+ "pagination": {
+ "page": "Pàgina {{page}}",
+ "currentPage": "Pàgina {{page}}",
+ "previous": "Anterior",
+ "next": "Següent"
+ }
},
"categories": "Categories"
},
+ "search": {
+ "title": "Cercar campanyes - Goteo",
+ "description": "Explora el nostre catàleg de campanyes i troba la que més t'inspiri per fer la teva donació",
+ "heading": "Troba la campanya que busques",
+ "subtitle": "Explora el nostre catàleg de campanyes i tria la que més t'inspiri per fer la teva donació",
+ "topResults": "Campanyes més cercades",
+ "showing_top_results": "Mostrant les {{count}} campanyes més populars",
+ "placeholder": "Cercar",
+ "label": "Cercar campanyes",
+ "clearSearch": "Esborrar cerca",
+ "searchButton": "Cercar",
+ "closeFilters": "Tancar filtres",
+ "showFilters": "Mostrar filtres",
+ "hideFilters": "Amagar filtres",
+ "applyFilters": "Aplicar filtres",
+ "categoryLabel": "Tria una categoria",
+ "selectCategory": "Seleccionar categoria",
+ "deselectCategory": "Desseleccionar categoria",
+ "selectedCategories": "{{count}} categories seleccionades",
+ "clearFilters": "Esborrar filtres",
+ "loading": "Cercant campanyes...",
+ "results": {
+ "count": "{{count}} resultats trobats",
+ "found": "{{count}} resultats trobats",
+ "noResults": "No s'han trobat resultats",
+ "regionLabel": "Resultats de cerca",
+ "showing": "Mostrant {{count}} resultats",
+ "empty": {
+ "title": "No s'han trobat resultats",
+ "description": "No hi ha campanyes que coincideixin amb la teva cerca actual.",
+ "clearFilters": "Esborrar filtres"
+ }
+ },
+ "initial": {
+ "title": "Cerca la teva campanya ideal",
+ "description": "Utilitza els filtres de dalt per trobar campanyes que t'inspirin."
+ },
+ "filters": {
+ "clear": "Esborrar filtres"
+ },
+ "errors": {
+ "searchFailed": "No s'ha pogut realitzar la cerca. Torna-ho a provar.",
+ "api": {
+ "invalidToken": "Autenticació fallida. Si us plau, verifica les teves credencials.",
+ "expiredToken": "La teva sessió ha expirat. Si us plau, inicia sessió de nou.",
+ "missingToken": "Es requereix autenticació. Si us plau, inicia sessió.",
+ "networkError": "Error de connexió. Verifica la teva connexió a internet.",
+ "serverError": "Error del servidor. Si us plau, torna-ho a provar més tard."
+ }
+ },
+ "empty": {
+ "title": "No s'han trobat campanyes",
+ "description": "No hi ha campanyes que coincideixin amb la teva cerca actual. Verifica l'ortografia o prova amb paraules clau diferents o termes més generals.",
+ "suggestions": {
+ "title": "Suggeriments per millorar la teva cerca:",
+ "checkSpelling": "Verifica l'ortografia de les teves paraules clau",
+ "tryDifferentKeywords": "Prova amb paraules clau diferents",
+ "removeFilters": "Treu alguns filtres per ampliar la cerca",
+ "broadenSearch": "Usa termes més generals"
+ }
+ },
+ "accessibility": {
+ "resultsFound": "S'han trobat {{count}} campanyes"
+ },
+ "loadMore": {
+ "button": "Carregar més",
+ "loading": "Carregant més resultats...",
+ "announcement": "S'han carregat {{count}} resultats més. Mostrant {{loaded}} resultats en total."
+ },
+ "pagination": {
+ "summary": "Pàgina {{page}} de {{totalPages}} ({{totalCount}} resultats en total)",
+ "ariaLabel": "Navegació de pàgines de resultats",
+ "page": "Pàgina {{page}}",
+ "currentPage": "Pàgina {{page}}",
+ "previous": "Anterior",
+ "next": "Següent",
+ "loading": "Carregant...",
+ "loadMore": "Carregar més resultats",
+ "noMoreResults": "No hi ha més resultats"
+ },
+ "error": {
+ "title": "Error al cercar",
+ "retry": "Tornar a provar",
+ "network": {
+ "title": "Sense connexió a internet",
+ "description": "No es pot connectar amb el servidor. Verifica la teva connexió a internet i torna-ho a provar."
+ },
+ "timeout": {
+ "title": "La cerca està trigant massa",
+ "description": "El servidor no respon. Torna-ho a provar o utilitza les dades locals."
+ },
+ "server": {
+ "title": "Error del servidor",
+ "description": "Hi ha un problema amb el servidor. Torna-ho a provar més tard o utilitza les dades locals."
+ },
+ "unknown": {
+ "title": "Error inesperat",
+ "description": "Ha ocorregut un error inesperat. Torna-ho a provar."
+ },
+ "actions": {
+ "retry": "Reintentar",
+ "showHelp": "Mostrar ajuda"
+ },
+ "technicalDetails": "Detalls tècnics"
+ }
+ },
+ "filters": {
+ "selectOption": "Seleccionar opció",
+ "timePeriod": "Període de temps",
+ "campaignStatus": "Estat de la campanya",
+ "location": "Ubicació",
+ "time": {
+ "all": "Qualsevol moment",
+ "week": "Última setmana",
+ "month": "Últim mes",
+ "year": "Últim any"
+ },
+ "status": {
+ "all": "Tots els estats",
+ "funding": "En finançament",
+ "successful": "Exitoses",
+ "completed": "Completades"
+ },
+ "locationOptions": {
+ "all": "Qualsevol ubicació",
+ "spain": "Espanya",
+ "europe": "Europa",
+ "global": "Global"
+ }
+ },
"categories": {
"solidary": "Solidari",
"libre-software": "Programari Lliure",
@@ -316,7 +456,9 @@
"title": "Hola!",
"header": {
"login": "Iniciar Sessió",
- "btnCart": "Anar al checkout"
+ "btnCart": "Anar al checkout",
+ "search": "Cercar",
+ "createProject": "Crea la teva campanya"
},
"checkout": {
"title": "Resum de les teves donacions seleccionades",
@@ -343,7 +485,9 @@
"resume": {
"donationsPrefix": "de donacions",
"foundationPrefix": "d’aportació a la fundació"
- }
+ },
+ "show_more": "Mostra'n més",
+ "hide": "Amagar"
},
"cart": {
"freeDonation": {
@@ -369,6 +513,22 @@
}
},
"payment": {
+ "postPayment": {
+ "title": "Enhorabona! Gràcies per la teva donació",
+ "emailSent": "T'hem enviat un resum a:",
+ "certificate": {
+ "title": "Com sol·licito el meu certificat?",
+ "description": "El certificat es genera de forma automàtica al final de l'any fiscal (desembre). T'enviarem un correu una vegada hàgim presentat la documentació a Hisenda. En cas que trobem algun problema et contactarem per correu."
+ },
+ "donatedProjects": {
+ "title": "Has donat als següents projectes",
+ "empty": "No s'han trobat projectes donats"
+ },
+ "suggestedProjects": {
+ "title": "Altres projectes que necessiten la teva ajuda",
+ "empty": "No hi ha projectes disponibles en aquest moment"
+ }
+ },
"page": {
"title": "Indica’ns les teves dades de pagament",
"description": "Ara necessitem que ens diguis com vols pagar.",
@@ -609,8 +769,18 @@
"viewAll": "Veure totes",
"obtained": "Obtingut",
"minimum": "Mínim",
+ "optimum": "Òptim",
"daysRemaining": "Queden {{days}} dies",
- "matchfunding": "Matchfunding"
+ "matchfunding": "Matchfunding",
+ "status": {
+ "minimumReached": "Mínim aconseguit!",
+ "goForMinimum": "A pel mínim!"
+ },
+ "remaining": {
+ "toOptimum": "Falta per òptim",
+ "toMinimum": "Falta pel mínim"
+ },
+ "userDonations": "Les teves donacions:"
},
"blog": {
"title": "S'aveïnen grans canvis… ¡no et perdis res!",
@@ -641,6 +811,7 @@
}
},
"stats": {
+ "title": "Estadístiques de Goteo",
"theoryToAction": {
"title": "De la teoria a l'acció",
"subtitle": "Les xifres que revelen la nostra empremta. Dades actualitzades del 2024.",
@@ -776,5 +947,82 @@
"description": "Continua donant per pujar de nivell"
}
}
+ },
+ "me": {
+ "page": {
+ "title": "Activitat",
+ "subtitle": "Consulta la teva activitat a Goteo"
+ },
+ "period": {
+ "label": "Període",
+ "year": "Any {{year}}"
+ },
+ "donations": {
+ "title": "Les meves donacions",
+ "empty": "Encara no has donat a cap projecte",
+ "explore": "Explora projectes",
+ "illustration": "/images/profile/ilustration-donations.png",
+ "ctaLink": "/ca/projects",
+ "count": "Donacions",
+ "total": "Total donat",
+ "recent": "Últimes donacions",
+ "viewAll": "Veure totes",
+ "certificate": "Sol·licitar certificat"
+ },
+ "projects": {
+ "title": "Projectes impulsats",
+ "empty": "Encara no has impulsat cap projecte",
+ "create": "Impulsa el teu projecte",
+ "illustration": "/images/profile/ilustration-project.png",
+ "ctaLink": "/ca/projects/new",
+ "count": "Projectes",
+ "raised": "Recaptat",
+ "recent": "Últims projectes impulsats",
+ "viewAll": "Veure tots",
+ "createNew": "Crear un projecte"
+ },
+ "ownedProjects": {
+ "title": "Els Teus Projectes Actius",
+ "messageToDonatorsButton": "Missatge a donants",
+ "uploadNewsButton": "Pujar novetat"
+ },
+ "donatedProjects": {
+ "title": "Projectes actius on has donat"
+ },
+ "cta": {
+ "discover": {
+ "title": "Descobreix, connecta i dona",
+ "description": "Imagina un lloc on centenars de projectes socials esperen el teu suport. A la nostra plataforma, explorar és fàcil: navega per causes comunitàries, ambientals o educatives. Perquè aquí, tu decideixes quines històries s'escriuen.",
+ "button": "Dona a un projecte"
+ },
+ "matchfunding": {
+ "title": "Donacions que brillen. Projectes que inspiren.",
+ "description": "Goteo és pionera en matchfunding. Tant si ets un donant que busca potenciar la teva contribució a projectes, un impulsor que vol augmentar el seu pressupost o una entitat que busca donar suport a projectes i impulsors a través de reforços en el donatiu... el nostre programa de matchfunding et permet multiplicar l'impuls pel canvi",
+ "learnMore": "Descobreix com funciona el matchfunding",
+ "becomeMatcher": "Fes-te Matcher"
+ }
+ },
+ "matchfunding": {
+ "card": {
+ "title": "Matchfunding",
+ "calls": "Convocatòries",
+ "donated": "Donat",
+ "recent": "Últimes convocatòries",
+ "viewAll": "Veure convocatòries",
+ "create": "Crear convocatòria"
+ },
+ "section": {
+ "title": "Les Teves Convocatòries de Matchfunding Actives",
+ "empty": "No tens convocatòries actives",
+ "error": "Error en carregar les convocatòries"
+ },
+ "callCard": {
+ "donationCall": "Dotació convocatòria",
+ "participatingProjects": "Projectes participants",
+ "successfulProjects": "Projectes acollits amb èxit",
+ "projects": "projectes",
+ "viewCall": "Anar a la convocatòria"
+ }
+ }
}
}
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index d1c8615a..c7fdfa8c 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -31,7 +31,9 @@
"chart": {
"minimum": "Minimum",
"optimal": "Optimum",
- "progress": "Campaign"
+ "progress": "Campaign",
+ "secondRound": "2nd Round",
+ "start": "Start"
}
},
"project": {
@@ -130,6 +132,136 @@
},
"categories": "Categories"
},
+ "search": {
+ "title": "Search campaigns - Goteo",
+ "description": "Explore our catalog of campaigns and find the one that inspires you most to make your donation",
+ "heading": "Find the campaign you're looking for",
+ "subtitle": "Explore our catalog of campaigns and choose the one that inspires you most to make your donation",
+ "topResults": "Most searched campaigns",
+ "showing_top_results": "Showing the top {{count}} popular campaigns",
+ "placeholder": "Search",
+ "label": "Search campaigns",
+ "clearSearch": "Clear search",
+ "searchButton": "Search",
+ "closeFilters": "Close filters",
+ "showFilters": "Show filters",
+ "hideFilters": "Hide filters",
+ "applyFilters": "Apply filters",
+ "categoryLabel": "Choose a category",
+ "selectCategory": "Select category",
+ "deselectCategory": "Deselect category",
+ "selectedCategories": "{{count}} categories selected",
+ "clearFilters": "Clear filters",
+ "loading": "Searching campaigns...",
+ "results": {
+ "count": "{{count}} results found",
+ "found": "{{count}} results found",
+ "noResults": "No results found",
+ "regionLabel": "Search results",
+ "showing": "Showing {{count}} results",
+ "empty": {
+ "title": "No results found",
+ "description": "No campaigns match your current search.",
+ "clearFilters": "Clear filters"
+ }
+ },
+ "initial": {
+ "title": "Find your ideal campaign",
+ "description": "Use the filters above to find campaigns that inspire you."
+ },
+ "filters": {
+ "clear": "Clear filters"
+ },
+ "errors": {
+ "searchFailed": "Could not perform search. Please try again.",
+ "api": {
+ "invalidToken": "Authentication failed. Please check your credentials.",
+ "expiredToken": "Your session has expired. Please log in again.",
+ "missingToken": "Authentication required. Please log in.",
+ "networkError": "Connection error. Check your internet connection.",
+ "serverError": "Server error. Please try again later."
+ }
+ },
+ "empty": {
+ "title": "No campaigns found",
+ "description": "No campaigns match your current search. Try different terms or filters.",
+ "suggestions": {
+ "title": "Suggestions to improve your search:",
+ "checkSpelling": "Check the spelling of your keywords",
+ "tryDifferentKeywords": "Try different keywords",
+ "removeFilters": "Remove some filters to broaden the search",
+ "broadenSearch": "Use more general terms"
+ }
+ },
+ "accessibility": {
+ "resultsFound": "Found {{count}} campaigns"
+ },
+ "loadMore": {
+ "button": "Load More",
+ "loading": "Loading more results...",
+ "announcement": "Loaded {{count}} more results. Showing {{loaded}} results total."
+ },
+ "pagination": {
+ "summary": "Page {{page}} of {{totalPages}} ({{totalCount}} results total)",
+ "ariaLabel": "Results pagination navigation",
+ "page": "Page {{page}}",
+ "currentPage": "Page {{page}}",
+ "previous": "Previous",
+ "next": "Next",
+ "loading": "Loading...",
+ "loadMore": "Load more results",
+ "noMoreResults": "No more results"
+ },
+ "error": {
+ "title": "Search error",
+ "retry": "Try again",
+ "network": {
+ "title": "No internet connection",
+ "description": "Unable to connect to the server. Check your internet connection and try again."
+ },
+ "timeout": {
+ "title": "Search is taking too long",
+ "description": "The server is not responding. Try again or use local data."
+ },
+ "server": {
+ "title": "Server error",
+ "description": "There's a problem with the server. Try again later or use local data."
+ },
+ "unknown": {
+ "title": "Unexpected error",
+ "description": "An unexpected error occurred. Please try again."
+ },
+ "actions": {
+ "retry": "Retry",
+ "showHelp": "Show help"
+ },
+ "technicalDetails": "Technical details"
+ }
+ },
+ "filters": {
+ "selectOption": "Select option",
+ "timePeriod": "Time period",
+ "campaignStatus": "Campaign status",
+ "location": "Location",
+ "time": {
+ "all": "Any time",
+ "week": "Last week",
+ "month": "Last month",
+ "year": "Last year"
+ },
+ "status": {
+ "all": "All statuses",
+ "funding": "Funding",
+ "successful": "Successful",
+ "completed": "Completed"
+ },
+ "locationOptions": {
+ "all": "Any location",
+ "spain": "Spain",
+ "europe": "Europe",
+ "global": "Global"
+ }
+ },
"categories": {
"solidary": "Solidarity",
"libre-software": "Free Software",
@@ -316,7 +448,9 @@
"title": "Hello!",
"header": {
"login": "Login",
- "btnCart": "Go to checkout"
+ "btnCart": "Go to checkout",
+ "search": "Search",
+ "createProject": "Create your campaign"
},
"checkout": {
"title": "Summary of your selected donations",
@@ -343,7 +477,9 @@
"resume": {
"donationsPrefix": "of donations",
"foundationPrefix": "of contribution to the foundation"
- }
+ },
+ "show_more": "Show more",
+ "hide": "Hide"
},
"cart": {
"freeDonation": {
@@ -369,6 +505,22 @@
}
},
"payment": {
+ "postPayment": {
+ "title": "Congratulations! Thank you for your donation",
+ "emailSent": "We have sent you a summary to:",
+ "certificate": {
+ "title": "How do I request my certificate?",
+ "description": "The certificate is automatically generated at the end of the fiscal year (December). We will send you an email once we have submitted the documentation to the Tax Agency. If we encounter any problems, we will contact you by email."
+ },
+ "donatedProjects": {
+ "title": "You have donated to the following projects",
+ "empty": "No donated projects found"
+ },
+ "suggestedProjects": {
+ "title": "Other projects that need your help",
+ "empty": "No projects available at this time"
+ }
+ },
"page": {
"title": "Tell us your payment data",
"description": "Now we need to tell us how you want to pay.",
@@ -609,8 +761,18 @@
"viewAll": "See all",
"obtained": "Obtained",
"minimum": "Minimum",
+ "optimum": "Optimum",
"daysRemaining": "{{days}} days left",
- "matchfunding": "Matchfunding"
+ "matchfunding": "Matchfunding",
+ "status": {
+ "minimumReached": "Minimum reached!",
+ "goForMinimum": "Go for minimum!"
+ },
+ "remaining": {
+ "toOptimum": "To optimum",
+ "toMinimum": "To minimum"
+ },
+ "userDonations": "Your donations:"
},
"blog": {
"title": "Big changes are coming… don't miss anything!",
@@ -641,6 +803,7 @@
}
},
"stats": {
+ "title": "Goteo Statistics",
"theoryToAction": {
"title": "From theory to action",
"subtitle": "The numbers that reveal our footprint. Updated data for 2024.",
@@ -776,5 +939,82 @@
"description": "Keep donating to level up"
}
}
+ },
+ "me": {
+ "page": {
+ "title": "Activity",
+ "subtitle": "Check your activity on Goteo"
+ },
+ "period": {
+ "label": "Period",
+ "year": "Year {{year}}"
+ },
+ "donations": {
+ "title": "My donations",
+ "empty": "You haven't donated to any project yet",
+ "explore": "Explore projects",
+ "illustration": "/images/profile/ilustration-donations.png",
+ "ctaLink": "/en/projects",
+ "count": "Donations",
+ "total": "Total donated",
+ "recent": "Recent donations",
+ "viewAll": "View all",
+ "certificate": "Request certificate"
+ },
+ "projects": {
+ "title": "Promoted projects",
+ "empty": "You haven't promoted any projects yet",
+ "create": "Promote your project",
+ "illustration": "/images/profile/ilustration-project.png",
+ "ctaLink": "/en/projects/new",
+ "count": "Projects",
+ "raised": "Raised",
+ "recent": "Recent promoted projects",
+ "viewAll": "View all",
+ "createNew": "Create a project"
+ },
+ "ownedProjects": {
+ "title": "Your Active Projects",
+ "messageToDonatorsButton": "Message donors",
+ "uploadNewsButton": "Post update"
+ },
+ "donatedProjects": {
+ "title": "Active projects you donated to"
+ },
+ "cta": {
+ "discover": {
+ "title": "Discover, connect and donate",
+ "description": "Imagine a place where hundreds of social projects await your support. On our platform, exploring is easy: browse through community, environmental, or educational causes. Because here, you decide which stories get written.",
+ "button": "Donate to a project"
+ },
+ "matchfunding": {
+ "title": "Donations that shine. Projects that inspire.",
+ "description": "Goteo is a pioneer in matchfunding. Whether you're a donor looking to boost your contribution to projects, a promoter wanting to increase your budget, or an entity seeking to support projects and promoters through donation reinforcements... our matchfunding program allows you to multiply the momentum for change",
+ "learnMore": "Discover how matchfunding works",
+ "becomeMatcher": "Become a Matcher"
+ }
+ },
+ "matchfunding": {
+ "card": {
+ "title": "Matchfunding",
+ "calls": "Calls",
+ "donated": "Donated",
+ "recent": "Recent calls",
+ "viewAll": "View calls",
+ "create": "Create call"
+ },
+ "section": {
+ "title": "Your Active Matchfunding Calls",
+ "empty": "You don't have active calls",
+ "error": "Error loading calls"
+ },
+ "callCard": {
+ "donationCall": "Call allocation",
+ "participatingProjects": "Participating projects",
+ "successfulProjects": "Successfully accepted projects",
+ "projects": "projects",
+ "viewCall": "Go to call"
+ }
+ }
}
}
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json
index 10d3fb1b..4ea4741e 100644
--- a/src/i18n/locales/es.json
+++ b/src/i18n/locales/es.json
@@ -31,7 +31,9 @@
"chart": {
"minimum": "Mínimo",
"optimal": "Óptimo",
- "progress": "Estado de la campaña"
+ "progress": "Estado de la campaña",
+ "secondRound": "2ª Ronda",
+ "start": "Inicio"
}
},
"project": {
@@ -39,10 +41,12 @@
"tabs": {
"project": "Información de campaña",
"budget": {
- "chart-title": "Este es el presupuesto necesario para hacer realidad esta campaña y descubre cómo tu apoyo puede llevarnos al éxito.",
+ "chart-title": "Este es el presupuesto para esta campaña. Descubre cómo tu apoyo puede llevarnos al éxito.",
"title": "Presupuesto",
"minimum": "Mínimo",
"optimal": "Óptimo",
+ "raised": "Obtenido",
+ "total": "Total",
"typeBudget": {
"infrastructure": "Infraestructura",
"material": "Material",
@@ -130,19 +134,146 @@
},
"categories": "Categorías"
},
+ "search": {
+ "title": "Buscar campañas - Goteo",
+ "description": "Explora nuestro catálogo de campañas y encuentra la que más te inspire para hacer tu donación",
+ "heading": "Encuentra la campaña que buscas",
+ "subtitle": "Explora nuestro catálogo de campañas y elige la que más te inspire para hacer tu donación",
+ "topResults": "Campañas más buscadas",
+ "showing_top_results": "Mostrando las {{count}} campañas más populares",
+ "placeholder": "Buscar",
+ "label": "Buscar campañas",
+ "clearSearch": "Limpiar búsqueda",
+ "searchButton": "Buscar",
+ "closeFilters": "Cerrar filtros",
+ "showFilters": "Mostrar filtros",
+ "hideFilters": "Ocultar filtros",
+ "applyFilters": "Aplicar filtros",
+ "categoryLabel": "Elige una categoría",
+ "selectCategory": "Seleccionar categoría",
+ "deselectCategory": "Deseleccionar categoría",
+ "selectedCategories": "{{count}} categorías seleccionadas",
+ "clearFilters": "Limpiar filtros",
+ "loading": "Buscando campañas...",
+ "results": {
+ "count": "{{count}} resultados encontrados",
+ "found": "{{count}} resultados encontrados",
+ "noResults": "No se encontraron resultados",
+ "regionLabel": "Resultados de búsqueda",
+ "showing": "Mostrando {{count}} resultados",
+ "empty": {
+ "title": "No se encontraron resultados",
+ "description": "No hay campañas que coincidan con tu búsqueda actual.",
+ "clearFilters": "Limpiar filtros"
+ }
+ },
+ "initial": {
+ "title": "Busca tu campaña ideal",
+ "description": "Utiliza los filtros de arriba para encontrar campañas que te inspiren."
+ },
+ "filters": {
+ "clear": "Limpiar filtros"
+ },
+ "errors": {
+ "searchFailed": "No se pudo realizar la búsqueda. Intenta de nuevo.",
+ "api": {
+ "invalidToken": "Autenticación fallida. Por favor, verifica tus credenciales.",
+ "expiredToken": "Tu sesión ha expirado. Por favor, inicia sesión de nuevo.",
+ "missingToken": "Se requiere autenticación. Por favor, inicia sesión.",
+ "networkError": "Error de conexión. Verifica tu conexión a internet.",
+ "serverError": "Error del servidor. Por favor, inténtalo de nuevo más tarde."
+ }
+ },
+ "empty": {
+ "title": "No se encontraron campañas",
+ "description": "No hay campañas que coincidan con tu búsqueda actual. Verifica la ortografía o prueba con palabras clave diferentes o términos más generales.",
+ "suggestions": {
+ "title": "Sugerencias para mejorar tu búsqueda:",
+ "checkSpelling": "Verifica la ortografía de tus palabras clave",
+ "tryDifferentKeywords": "Prueba con palabras clave diferentes",
+ "removeFilters": "Quita algunos filtros para ampliar la búsqueda",
+ "broadenSearch": "Usa términos más generales"
+ }
+ },
+ "accessibility": {
+ "resultsFound": "Se encontraron {{count}} campañas"
+ },
+ "loadMore": {
+ "button": "Cargar más",
+ "loading": "Cargando más resultados...",
+ "announcement": "Se cargaron {{count}} resultados más. Mostrando {{loaded}} resultados en total."
+ },
+ "pagination": {
+ "summary": "Página {{page}} de {{totalPages}} ({{totalCount}} resultados en total)",
+ "ariaLabel": "Navegación de páginas de resultados",
+ "page": "Página {{page}}",
+ "currentPage": "Página {{page}}",
+ "previous": "Anterior",
+ "next": "Siguiente",
+ "loading": "Cargando...",
+ "loadMore": "Cargar más resultados",
+ "noMoreResults": "No hay más resultados"
+ },
+ "error": {
+ "title": "Error al buscar",
+ "retry": "Intentar de nuevo",
+ "network": {
+ "title": "Sin conexión a internet",
+ "description": "No se puede conectar con el servidor. Verifica tu conexión a internet e inténtalo de nuevo."
+ },
+ "timeout": {
+ "title": "La búsqueda está tardando demasiado",
+ "description": "El servidor no responde. Inténtalo de nuevo o usa los datos locales."
+ },
+ "server": {
+ "title": "Error del servidor",
+ "description": "Hay un problema con el servidor. Inténtalo más tarde o usa los datos locales."
+ },
+ "unknown": {
+ "title": "Error inesperado",
+ "description": "Ha ocurrido un error inesperado. Inténtalo de nuevo."
+ },
+ "actions": {
+ "retry": "Reintentar",
+ "showHelp": "Mostrar ayuda"
+ },
+ "technicalDetails": "Detalles técnicos"
+ }
+ },
+ "filters": {
+ "selectOption": "Seleccionar opción",
+ "timePeriod": "Periodo de tiempo",
+ "campaignStatus": "Estado de la campaña",
+ "location": "Ubicación",
+ "time": {
+ "all": "Cualquier momento",
+ "week": "Última semana",
+ "month": "Último mes",
+ "year": "Último año"
+ },
+ "status": {
+ "all": "Todos los estados",
+ "funding": "En financiación",
+ "successful": "Exitosas",
+ "completed": "Completadas"
+ },
+ "locationOptions": {
+ "all": "Cualquier ubicación",
+ "spain": "España",
+ "europe": "Europa",
+ "global": "Global"
+ }
+ },
"categories": {
- "solidary": "Solidario",
- "libre-software": "Software Libre",
- "employment": "Generar empleo",
- "design": "Desde el diseño",
+ "culture": "Cultura",
+ "rights": "Derechos",
+ "education": "Educación",
"journalism": "Periodismo independiente",
- "education": "Educativo",
- "culture": "Crea cultura",
- "ecology": "Ecológico",
- "health-and-cares": "Salud y cuidados",
- "open-data": "Datos abiertos",
- "democracy": "Democracia y participación",
- "equity": "Igualdad de género"
+ "health": "Salud",
+ "ecological_transition": "Transición ecológica",
+ "social_economy": "Economía social",
+ "rural": "Rural",
+ "science": "Ciencia"
},
"ShareButton": {
"title": "Difunde este proyecto por el mundo",
@@ -316,7 +447,9 @@
"title": "Hola!",
"header": {
"login": "Iniciar Sesión",
- "btnCart": "Ir al checkout"
+ "btnCart": "Ir al checkout",
+ "search": "Buscar",
+ "createProject": "Crea tu proyecto"
},
"checkout": {
"title": "Resumen de tus donaciones seleccionadas",
@@ -343,7 +476,13 @@
"resume": {
"donationsPrefix": "de donaciones",
"foundationPrefix": "de aporte a la fundación"
- }
+ },
+ "show_details": "Ver detalle",
+ "hide_details": "Ocultar desglose",
+ "show_more": "Ver más",
+ "hide": "Ocultar",
+ "donations": "Donaciones",
+ "foundation": "Fundación Platoniq"
},
"cart": {
"freeDonation": {
@@ -369,6 +508,22 @@
}
},
"payment": {
+ "postPayment": {
+ "title": "¡Enhorabuena! Gracias por tu donación",
+ "emailSent": "Te hemos enviado un resumen a:",
+ "certificate": {
+ "title": "¿Cómo solicito mi certificado?",
+ "description": "El certificado se genera de forma automática al final del año fiscal (diciembre). Te mandaremos un correo una vez hayamos presentado la documentación a Hacienda. En caso de que encontremos algún problema te contactaremos por correo."
+ },
+ "donatedProjects": {
+ "title": "Has donado a los siguientes proyectos",
+ "empty": "No se encontraron proyectos donados"
+ },
+ "suggestedProjects": {
+ "title": "Otros proyectos que requieren de tu ayuda",
+ "empty": "No hay proyectos disponibles en este momento"
+ }
+ },
"page": {
"title": "Indícanos tus datos de pago",
"description": "Ahora necesitamos que nos digas cómo quieres pagar.",
@@ -610,8 +765,18 @@
"viewAll": "Ver todas",
"obtained": "Obtenido",
"minimum": "Mínimo",
+ "optimum": "Óptimo",
"daysRemaining": "Quedan {{days}} días",
- "matchfunding": "Matchfunding"
+ "matchfunding": "Matchfunding",
+ "status": {
+ "minimumReached": "¡Mínimo conseguido!",
+ "goForMinimum": "¡A por el mínimo!"
+ },
+ "remaining": {
+ "toOptimum": "Falta para óptimo",
+ "toMinimum": "Falta para mínimo"
+ },
+ "userDonations": "Tus donaciones:"
},
"blog": {
"title": "Se avecinan grandes cambios… ¡no te pierdas nada!",
@@ -642,6 +807,7 @@
}
},
"stats": {
+ "title": "Estadísticas de Goteo",
"theoryToAction": {
"title": "De la teoría a la acción",
"subtitle": "Las cifras que revelan nuestra huella real. Datos a fecha de diciembre de 2024.",
@@ -777,5 +943,82 @@
"description": "Sigue donando para subir de nivel"
}
}
+ },
+ "me": {
+ "page": {
+ "title": "Actividad",
+ "subtitle": "Consulta tu actividad en Goteo"
+ },
+ "period": {
+ "label": "Periodo",
+ "year": "Año {{year}}"
+ },
+ "donations": {
+ "title": "Mis donaciones",
+ "empty": "Aún no has donado a ningún proyecto",
+ "explore": "Explora proyectos",
+ "illustration": "/images/profile/ilustration-donations.png",
+ "ctaLink": "/projects",
+ "count": "Donaciones",
+ "total": "Total donado",
+ "recent": "Últimas donaciones",
+ "viewAll": "Ver todas",
+ "certificate": "Solicitar certificado"
+ },
+ "projects": {
+ "title": "Proyectos impulsados",
+ "empty": "Aún no has impulsado ningún proyecto",
+ "create": "Impulsa tu proyecto",
+ "illustration": "/images/profile/ilustration-project.png",
+ "ctaLink": "/projects/new",
+ "count": "Proyectos",
+ "raised": "Recaudado",
+ "recent": "Últimos proyectos impulsados",
+ "viewAll": "Ver todos",
+ "createNew": "Crear un proyecto"
+ },
+ "ownedProjects": {
+ "title": "Tus Proyectos Activos",
+ "messageToDonatorsButton": "Mensaje a donantes",
+ "uploadNewsButton": "Subir novedad"
+ },
+ "donatedProjects": {
+ "title": "Proyectos activos donde donaste"
+ },
+ "cta": {
+ "discover": {
+ "title": "Descubre, conecta y dona",
+ "description": "Imagina un lugar donde cientos de proyectos sociales esperan tu apoyo. En nuestra plataforma, explorar es fácil: navega por causas comunitarias, ambientales o educativas. Porque aquí, tú decides qué historias se escriben.",
+ "button": "Dona a un proyecto"
+ },
+ "matchfunding": {
+ "title": "Donaciones que brillan. Proyectos que inspiran.",
+ "description": "Goteo es pionera en matchfunding. Ya seas un donante que buscas potenciar tu contribución a proyectos, un impulsor que quiere aumentar su presupuesto o una entidad que busca apoyar a proyectos e impulsores a través de refuerzos en el donativo... nuestro programa de matchfunding te permite multiplicar el impulso para el cambio",
+ "learnMore": "Descubre cómo funciona el matchfunding",
+ "becomeMatcher": "Hazte Matcher"
+ }
+ },
+ "matchfunding": {
+ "card": {
+ "title": "Matchfunding",
+ "calls": "Convocatorias",
+ "donated": "Donado",
+ "recent": "Últimas convocatorias",
+ "viewAll": "Ver convocatorias",
+ "create": "Crear convocatoria"
+ },
+ "section": {
+ "title": "Tus convocatorias de Matchfunding activas",
+ "empty": "No tienes convocatorias activas",
+ "error": "Error al cargar las convocatorias"
+ },
+ "callCard": {
+ "donationCall": "Dotación convocatoria",
+ "participatingProjects": "Proyectos participantes",
+ "successfulProjects": "Proyectos acogidos con éxito",
+ "projects": "proyectos",
+ "viewCall": "Ir a la convocatoria"
+ }
+ }
}
}
diff --git a/src/middleware/utils.ts b/src/middleware/utils.ts
index 69d15a05..108e4f64 100644
--- a/src/middleware/utils.ts
+++ b/src/middleware/utils.ts
@@ -70,7 +70,7 @@ export function handleProtectedRoutes(context: APIContext, lang: string): string
return isLangInPath ? `/${currentLang}/payment` : `/payment`;
}
- const protectedRoutes = ["payment", "admin"];
+ const protectedRoutes = ["me", "payment", "admin"];
const isProtected = protectedRoutes.includes(nextSegment);
if (!accessToken && isProtected) {
diff --git a/src/openapi/api.ts b/src/openapi/api.ts
index 8c47e4d1..e9b8a9b3 100644
--- a/src/openapi/api.ts
+++ b/src/openapi/api.ts
@@ -2,7 +2,104 @@ import { getBaseUrl } from "../utils/consts";
import type { CreateClientConfig } from "./client/client.gen";
-export const createClientConfig: CreateClientConfig = (config) => ({
- ...config,
- baseUrl: getBaseUrl(),
-});
+// Authentication error types for better error handling
+export enum AuthErrorType {
+ INVALID_TOKEN = "INVALID_TOKEN",
+ EXPIRED_TOKEN = "EXPIRED_TOKEN",
+ MISSING_TOKEN = "MISSING_TOKEN",
+ NETWORK_ERROR = "NETWORK_ERROR",
+ SERVER_ERROR = "SERVER_ERROR",
+}
+
+export interface AuthError {
+ type: AuthErrorType;
+ status?: number;
+ originalError?: Error;
+ shouldRetry?: boolean;
+}
+
+/**
+ * Creates authentication error from various error sources.
+ * Note: This is an API-layer utility. Components should map the error type
+ * to user-facing translated messages using the i18n system.
+ */
+export function createAuthError(error: unknown): AuthError {
+ if (error instanceof Response) {
+ switch (error.status) {
+ case 401:
+ return {
+ type: AuthErrorType.INVALID_TOKEN,
+ status: error.status,
+ shouldRetry: false,
+ };
+ case 403:
+ return {
+ type: AuthErrorType.EXPIRED_TOKEN,
+ status: error.status,
+ shouldRetry: true,
+ };
+ case 0:
+ return {
+ type: AuthErrorType.NETWORK_ERROR,
+ status: error.status,
+ shouldRetry: true,
+ };
+ default:
+ return {
+ type: AuthErrorType.SERVER_ERROR,
+ status: error.status,
+ shouldRetry: true,
+ };
+ }
+ }
+
+ if (error instanceof Error) {
+ if (error.name === "AbortError") {
+ return {
+ type: AuthErrorType.NETWORK_ERROR,
+ originalError: error,
+ shouldRetry: false,
+ };
+ }
+
+ if (error.message.includes("fetch")) {
+ return {
+ type: AuthErrorType.NETWORK_ERROR,
+ originalError: error,
+ shouldRetry: true,
+ };
+ }
+ }
+
+ return {
+ type: AuthErrorType.SERVER_ERROR,
+ originalError: error instanceof Error ? error : new Error(String(error)),
+ shouldRetry: true,
+ };
+}
+
+/**
+ * Get translation key for error type.
+ * Components should use this with their i18n system to display user-facing messages.
+ * Example: const message = t(getErrorTranslationKey(error.type));
+ */
+export function getErrorTranslationKey(errorType: AuthErrorType): string {
+ const keyMap: Record = {
+ [AuthErrorType.INVALID_TOKEN]: "errors.api.invalidToken",
+ [AuthErrorType.EXPIRED_TOKEN]: "errors.api.expiredToken",
+ [AuthErrorType.MISSING_TOKEN]: "errors.api.missingToken",
+ [AuthErrorType.NETWORK_ERROR]: "errors.api.networkError",
+ [AuthErrorType.SERVER_ERROR]: "errors.api.serverError",
+ };
+ return keyMap[errorType];
+}
+
+/**
+ * Simple client configuration
+ */
+export const createClientConfig: CreateClientConfig = (config) => {
+ return {
+ ...config,
+ baseUrl: getBaseUrl(),
+ };
+};
diff --git a/src/openapi/client/paths.gen.ts b/src/openapi/client/paths.gen.ts
index 9684fab3..c33438a3 100644
--- a/src/openapi/client/paths.gen.ts
+++ b/src/openapi/client/paths.gen.ts
@@ -12,6 +12,10 @@ export const apiAccountingTransactionsGetCollectionUrl = '/v4/accounting_transac
export const apiAccountingTransactionsIdGetUrl = '/v4/accounting_transactions/{id}';
+export const apiCategoriesGetCollectionUrl = '/v4/categories';
+
+export const apiCategoriesIdGetUrl = '/v4/categories/{id}';
+
export const apiGatewaysGetCollectionUrl = '/v4/gateways';
export const apiGatewaysNameGetUrl = '/v4/gateways/{name}';
@@ -74,6 +78,8 @@ export const apiUsersIdpersonGetUrl = '/v4/users/{id}/person';
export const apiUsersIdpersonPatchUrl = '/v4/users/{id}/person';
+export const apiPingGetUrl = '/v4/ping';
+
export const apiProjectsGetCollectionUrl = '/v4/projects';
export const apiProjectsPostUrl = '/v4/projects';
@@ -94,6 +100,16 @@ export const apiProjectBudgetItemsIdGetUrl = '/v4/project_budget_items/{id}';
export const apiProjectBudgetItemsIdPatchUrl = '/v4/project_budget_items/{id}';
+export const apiProjectCollaborationsGetCollectionUrl = '/v4/project_collaborations';
+
+export const apiProjectCollaborationsPostUrl = '/v4/project_collaborations';
+
+export const apiProjectCollaborationsIdDeleteUrl = '/v4/project_collaborations/{id}';
+
+export const apiProjectCollaborationsIdGetUrl = '/v4/project_collaborations/{id}';
+
+export const apiProjectCollaborationsIdPatchUrl = '/v4/project_collaborations/{id}';
+
export const apiProjectRewardsGetCollectionUrl = '/v4/project_rewards';
export const apiProjectRewardsPostUrl = '/v4/project_rewards';
diff --git a/src/openapi/client/sdk.gen.ts b/src/openapi/client/sdk.gen.ts
index 6a7a0ef6..e8fedfe4 100644
--- a/src/openapi/client/sdk.gen.ts
+++ b/src/openapi/client/sdk.gen.ts
@@ -2,7 +2,7 @@
import { client as _heyApiClient } from './client.gen';
-import type { ApiAccountingsGetCollectionData, ApiAccountingsGetCollectionResponses, ApiAccountingsIdGetData, ApiAccountingsIdGetResponses, ApiAccountingsIdGetErrors, ApiAccountingsIdPatchData, ApiAccountingsIdPatchResponses, ApiAccountingsIdPatchErrors, ApiAccountingBalancePointsGetCollectionData, ApiAccountingBalancePointsGetCollectionResponses, ApiAccountingTransactionsGetCollectionData, ApiAccountingTransactionsGetCollectionResponses, ApiAccountingTransactionsIdGetData, ApiAccountingTransactionsIdGetResponses, ApiAccountingTransactionsIdGetErrors, ApiGatewaysGetCollectionData, ApiGatewaysGetCollectionResponses, ApiGatewaysNameGetData, ApiGatewaysNameGetResponses, ApiGatewaysNameGetErrors, ApiGatewayChargesGetCollectionData, ApiGatewayChargesGetCollectionResponses, ApiGatewayChargesIdGetData, ApiGatewayChargesIdGetResponses, ApiGatewayChargesIdGetErrors, ApiGatewayChargesIdPatchData, ApiGatewayChargesIdPatchResponses, ApiGatewayChargesIdPatchErrors, ApiGatewayCheckoutsGetCollectionData, ApiGatewayCheckoutsGetCollectionResponses, ApiGatewayCheckoutsGetCollectionErrors, ApiGatewayCheckoutsPostData, ApiGatewayCheckoutsPostResponses, ApiGatewayCheckoutsPostErrors, ApiGatewayCheckoutsIdGetData, ApiGatewayCheckoutsIdGetResponses, ApiGatewayCheckoutsIdGetErrors, ApiGatewayCheckoutsIdPatchData, ApiGatewayCheckoutsIdPatchResponses, ApiGatewayCheckoutsIdPatchErrors, ApiMatchCallsGetCollectionData, ApiMatchCallsGetCollectionResponses, ApiMatchCallsPostData, ApiMatchCallsPostResponses, ApiMatchCallsPostErrors, ApiMatchCallsIdDeleteData, ApiMatchCallsIdDeleteResponses, ApiMatchCallsIdDeleteErrors, ApiMatchCallsIdGetData, ApiMatchCallsIdGetResponses, ApiMatchCallsIdGetErrors, ApiMatchCallsIdPatchData, ApiMatchCallsIdPatchResponses, ApiMatchCallsIdPatchErrors, ApiMatchCallSubmissionsGetCollectionData, ApiMatchCallSubmissionsGetCollectionResponses, ApiMatchCallSubmissionsPostData, ApiMatchCallSubmissionsPostResponses, ApiMatchCallSubmissionsPostErrors, ApiMatchCallSubmissionsIdGetData, ApiMatchCallSubmissionsIdGetResponses, ApiMatchCallSubmissionsIdGetErrors, ApiMatchCallSubmissionsIdPatchData, ApiMatchCallSubmissionsIdPatchResponses, ApiMatchCallSubmissionsIdPatchErrors, ApiMatchFormulasGetCollectionData, ApiMatchFormulasGetCollectionResponses, ApiMatchFormulasNameGetData, ApiMatchFormulasNameGetResponses, ApiMatchFormulasNameGetErrors, ApiMatchRulesGetCollectionData, ApiMatchRulesGetCollectionResponses, ApiMatchRulesNameGetData, ApiMatchRulesNameGetResponses, ApiMatchRulesNameGetErrors, ApiMatchStrategiesGetCollectionData, ApiMatchStrategiesGetCollectionResponses, ApiMatchStrategiesPostData, ApiMatchStrategiesPostResponses, ApiMatchStrategiesPostErrors, ApiMatchStrategiesIdDeleteData, ApiMatchStrategiesIdDeleteResponses, ApiMatchStrategiesIdDeleteErrors, ApiMatchStrategiesIdGetData, ApiMatchStrategiesIdGetResponses, ApiMatchStrategiesIdGetErrors, ApiMatchStrategiesIdPatchData, ApiMatchStrategiesIdPatchResponses, ApiMatchStrategiesIdPatchErrors, ApiUsersIdorganizationGetData, ApiUsersIdorganizationGetResponses, ApiUsersIdorganizationGetErrors, ApiUsersIdorganizationPatchData, ApiUsersIdorganizationPatchResponses, ApiUsersIdorganizationPatchErrors, ApiUsersIdpersonGetData, ApiUsersIdpersonGetResponses, ApiUsersIdpersonGetErrors, ApiUsersIdpersonPatchData, ApiUsersIdpersonPatchResponses, ApiUsersIdpersonPatchErrors, ApiProjectsGetCollectionData, ApiProjectsGetCollectionResponses, ApiProjectsPostData, ApiProjectsPostResponses, ApiProjectsPostErrors, ApiProjectsIdOrSlugGetData, ApiProjectsIdOrSlugGetResponses, ApiProjectsIdOrSlugGetErrors, ApiProjectsIdDeleteData, ApiProjectsIdDeleteResponses, ApiProjectsIdDeleteErrors, ApiProjectsIdPatchData, ApiProjectsIdPatchResponses, ApiProjectsIdPatchErrors, ApiProjectBudgetItemsGetCollectionData, ApiProjectBudgetItemsGetCollectionResponses, ApiProjectBudgetItemsPostData, ApiProjectBudgetItemsPostResponses, ApiProjectBudgetItemsPostErrors, ApiProjectBudgetItemsIdDeleteData, ApiProjectBudgetItemsIdDeleteResponses, ApiProjectBudgetItemsIdDeleteErrors, ApiProjectBudgetItemsIdGetData, ApiProjectBudgetItemsIdGetResponses, ApiProjectBudgetItemsIdGetErrors, ApiProjectBudgetItemsIdPatchData, ApiProjectBudgetItemsIdPatchResponses, ApiProjectBudgetItemsIdPatchErrors, ApiProjectRewardsGetCollectionData, ApiProjectRewardsGetCollectionResponses, ApiProjectRewardsPostData, ApiProjectRewardsPostResponses, ApiProjectRewardsPostErrors, ApiProjectRewardsIdDeleteData, ApiProjectRewardsIdDeleteResponses, ApiProjectRewardsIdDeleteErrors, ApiProjectRewardsIdGetData, ApiProjectRewardsIdGetResponses, ApiProjectRewardsIdGetErrors, ApiProjectRewardsIdPatchData, ApiProjectRewardsIdPatchResponses, ApiProjectRewardsIdPatchErrors, ApiProjectRewardClaimsGetCollectionData, ApiProjectRewardClaimsGetCollectionResponses, ApiProjectRewardClaimsPostData, ApiProjectRewardClaimsPostResponses, ApiProjectRewardClaimsPostErrors, ApiProjectRewardClaimsIdDeleteData, ApiProjectRewardClaimsIdDeleteResponses, ApiProjectRewardClaimsIdDeleteErrors, ApiProjectRewardClaimsIdGetData, ApiProjectRewardClaimsIdGetResponses, ApiProjectRewardClaimsIdGetErrors, ApiProjectSupportsGetCollectionData, ApiProjectSupportsGetCollectionResponses, ApiProjectSupportsmoneyTotalGetCollectionData, ApiProjectSupportsmoneyTotalGetCollectionResponses, ApiProjectSupportsIdGetData, ApiProjectSupportsIdGetResponses, ApiProjectSupportsIdGetErrors, ApiProjectSupportsIdPatchData, ApiProjectSupportsIdPatchResponses, ApiProjectSupportsIdPatchErrors, ApiProjectUpdatesGetCollectionData, ApiProjectUpdatesGetCollectionResponses, ApiProjectUpdatesPostData, ApiProjectUpdatesPostResponses, ApiProjectUpdatesPostErrors, ApiProjectUpdatesIdDeleteData, ApiProjectUpdatesIdDeleteResponses, ApiProjectUpdatesIdDeleteErrors, ApiProjectUpdatesIdGetData, ApiProjectUpdatesIdGetResponses, ApiProjectUpdatesIdGetErrors, ApiProjectUpdatesIdPatchData, ApiProjectUpdatesIdPatchResponses, ApiProjectUpdatesIdPatchErrors, ApiTipjarsGetCollectionData, ApiTipjarsGetCollectionResponses, ApiTipjarsPostData, ApiTipjarsPostResponses, ApiTipjarsPostErrors, ApiTipjarsIdDeleteData, ApiTipjarsIdDeleteResponses, ApiTipjarsIdDeleteErrors, ApiTipjarsIdGetData, ApiTipjarsIdGetResponses, ApiTipjarsIdGetErrors, ApiTipjarsIdPatchData, ApiTipjarsIdPatchResponses, ApiTipjarsIdPatchErrors, ApiUsersGetCollectionData, ApiUsersGetCollectionResponses, ApiUsersPostData, ApiUsersPostResponses, ApiUsersPostErrors, ApiUsersIdDeleteData, ApiUsersIdDeleteResponses, ApiUsersIdDeleteErrors, ApiUsersIdGetData, ApiUsersIdGetResponses, ApiUsersIdGetErrors, ApiUsersIdPatchData, ApiUsersIdPatchResponses, ApiUsersIdPatchErrors, ApiUserTokensPostData, ApiUserTokensPostResponses, ApiUserTokensPostErrors, ApiUserTokensIdDeleteData, ApiUserTokensIdDeleteResponses, ApiUserTokensIdDeleteErrors, ApiUserTokensIdGetData, ApiUserTokensIdGetResponses, ApiUserTokensIdGetErrors, ApiVersionsGetCollectionData, ApiVersionsGetCollectionResponses, ApiVersionsIdGetData, ApiVersionsIdGetResponses, ApiVersionsIdGetErrors } from './types.gen';
+import type { ApiAccountingsGetCollectionData, ApiAccountingsGetCollectionResponses, ApiAccountingsIdGetData, ApiAccountingsIdGetResponses, ApiAccountingsIdGetErrors, ApiAccountingsIdPatchData, ApiAccountingsIdPatchResponses, ApiAccountingsIdPatchErrors, ApiAccountingBalancePointsGetCollectionData, ApiAccountingBalancePointsGetCollectionResponses, ApiAccountingTransactionsGetCollectionData, ApiAccountingTransactionsGetCollectionResponses, ApiAccountingTransactionsIdGetData, ApiAccountingTransactionsIdGetResponses, ApiAccountingTransactionsIdGetErrors, ApiCategoriesGetCollectionData, ApiCategoriesGetCollectionResponses, ApiCategoriesIdGetData, ApiCategoriesIdGetResponses, ApiCategoriesIdGetErrors, ApiGatewaysGetCollectionData, ApiGatewaysGetCollectionResponses, ApiGatewaysNameGetData, ApiGatewaysNameGetResponses, ApiGatewaysNameGetErrors, ApiGatewayChargesGetCollectionData, ApiGatewayChargesGetCollectionResponses, ApiGatewayChargesIdGetData, ApiGatewayChargesIdGetResponses, ApiGatewayChargesIdGetErrors, ApiGatewayChargesIdPatchData, ApiGatewayChargesIdPatchResponses, ApiGatewayChargesIdPatchErrors, ApiGatewayCheckoutsGetCollectionData, ApiGatewayCheckoutsGetCollectionResponses, ApiGatewayCheckoutsGetCollectionErrors, ApiGatewayCheckoutsPostData, ApiGatewayCheckoutsPostResponses, ApiGatewayCheckoutsPostErrors, ApiGatewayCheckoutsIdGetData, ApiGatewayCheckoutsIdGetResponses, ApiGatewayCheckoutsIdGetErrors, ApiGatewayCheckoutsIdPatchData, ApiGatewayCheckoutsIdPatchResponses, ApiGatewayCheckoutsIdPatchErrors, ApiMatchCallsGetCollectionData, ApiMatchCallsGetCollectionResponses, ApiMatchCallsPostData, ApiMatchCallsPostResponses, ApiMatchCallsPostErrors, ApiMatchCallsIdDeleteData, ApiMatchCallsIdDeleteResponses, ApiMatchCallsIdDeleteErrors, ApiMatchCallsIdGetData, ApiMatchCallsIdGetResponses, ApiMatchCallsIdGetErrors, ApiMatchCallsIdPatchData, ApiMatchCallsIdPatchResponses, ApiMatchCallsIdPatchErrors, ApiMatchCallSubmissionsGetCollectionData, ApiMatchCallSubmissionsGetCollectionResponses, ApiMatchCallSubmissionsPostData, ApiMatchCallSubmissionsPostResponses, ApiMatchCallSubmissionsPostErrors, ApiMatchCallSubmissionsIdGetData, ApiMatchCallSubmissionsIdGetResponses, ApiMatchCallSubmissionsIdGetErrors, ApiMatchCallSubmissionsIdPatchData, ApiMatchCallSubmissionsIdPatchResponses, ApiMatchCallSubmissionsIdPatchErrors, ApiMatchFormulasGetCollectionData, ApiMatchFormulasGetCollectionResponses, ApiMatchFormulasNameGetData, ApiMatchFormulasNameGetResponses, ApiMatchFormulasNameGetErrors, ApiMatchRulesGetCollectionData, ApiMatchRulesGetCollectionResponses, ApiMatchRulesNameGetData, ApiMatchRulesNameGetResponses, ApiMatchRulesNameGetErrors, ApiMatchStrategiesGetCollectionData, ApiMatchStrategiesGetCollectionResponses, ApiMatchStrategiesPostData, ApiMatchStrategiesPostResponses, ApiMatchStrategiesPostErrors, ApiMatchStrategiesIdDeleteData, ApiMatchStrategiesIdDeleteResponses, ApiMatchStrategiesIdDeleteErrors, ApiMatchStrategiesIdGetData, ApiMatchStrategiesIdGetResponses, ApiMatchStrategiesIdGetErrors, ApiMatchStrategiesIdPatchData, ApiMatchStrategiesIdPatchResponses, ApiMatchStrategiesIdPatchErrors, ApiUsersIdorganizationGetData, ApiUsersIdorganizationGetResponses, ApiUsersIdorganizationGetErrors, ApiUsersIdorganizationPatchData, ApiUsersIdorganizationPatchResponses, ApiUsersIdorganizationPatchErrors, ApiUsersIdpersonGetData, ApiUsersIdpersonGetResponses, ApiUsersIdpersonGetErrors, ApiUsersIdpersonPatchData, ApiUsersIdpersonPatchResponses, ApiUsersIdpersonPatchErrors, ApiPingGetData, ApiPingGetResponses, ApiPingGetErrors, ApiProjectsGetCollectionData, ApiProjectsGetCollectionResponses, ApiProjectsPostData, ApiProjectsPostResponses, ApiProjectsPostErrors, ApiProjectsIdOrSlugGetData, ApiProjectsIdOrSlugGetResponses, ApiProjectsIdOrSlugGetErrors, ApiProjectsIdDeleteData, ApiProjectsIdDeleteResponses, ApiProjectsIdDeleteErrors, ApiProjectsIdPatchData, ApiProjectsIdPatchResponses, ApiProjectsIdPatchErrors, ApiProjectBudgetItemsGetCollectionData, ApiProjectBudgetItemsGetCollectionResponses, ApiProjectBudgetItemsPostData, ApiProjectBudgetItemsPostResponses, ApiProjectBudgetItemsPostErrors, ApiProjectBudgetItemsIdDeleteData, ApiProjectBudgetItemsIdDeleteResponses, ApiProjectBudgetItemsIdDeleteErrors, ApiProjectBudgetItemsIdGetData, ApiProjectBudgetItemsIdGetResponses, ApiProjectBudgetItemsIdGetErrors, ApiProjectBudgetItemsIdPatchData, ApiProjectBudgetItemsIdPatchResponses, ApiProjectBudgetItemsIdPatchErrors, ApiProjectCollaborationsGetCollectionData, ApiProjectCollaborationsGetCollectionResponses, ApiProjectCollaborationsPostData, ApiProjectCollaborationsPostResponses, ApiProjectCollaborationsPostErrors, ApiProjectCollaborationsIdDeleteData, ApiProjectCollaborationsIdDeleteResponses, ApiProjectCollaborationsIdDeleteErrors, ApiProjectCollaborationsIdGetData, ApiProjectCollaborationsIdGetResponses, ApiProjectCollaborationsIdGetErrors, ApiProjectCollaborationsIdPatchData, ApiProjectCollaborationsIdPatchResponses, ApiProjectCollaborationsIdPatchErrors, ApiProjectRewardsGetCollectionData, ApiProjectRewardsGetCollectionResponses, ApiProjectRewardsPostData, ApiProjectRewardsPostResponses, ApiProjectRewardsPostErrors, ApiProjectRewardsIdDeleteData, ApiProjectRewardsIdDeleteResponses, ApiProjectRewardsIdDeleteErrors, ApiProjectRewardsIdGetData, ApiProjectRewardsIdGetResponses, ApiProjectRewardsIdGetErrors, ApiProjectRewardsIdPatchData, ApiProjectRewardsIdPatchResponses, ApiProjectRewardsIdPatchErrors, ApiProjectRewardClaimsGetCollectionData, ApiProjectRewardClaimsGetCollectionResponses, ApiProjectRewardClaimsPostData, ApiProjectRewardClaimsPostResponses, ApiProjectRewardClaimsPostErrors, ApiProjectRewardClaimsIdDeleteData, ApiProjectRewardClaimsIdDeleteResponses, ApiProjectRewardClaimsIdDeleteErrors, ApiProjectRewardClaimsIdGetData, ApiProjectRewardClaimsIdGetResponses, ApiProjectRewardClaimsIdGetErrors, ApiProjectSupportsGetCollectionData, ApiProjectSupportsGetCollectionResponses, ApiProjectSupportsmoneyTotalGetCollectionData, ApiProjectSupportsmoneyTotalGetCollectionResponses, ApiProjectSupportsIdGetData, ApiProjectSupportsIdGetResponses, ApiProjectSupportsIdGetErrors, ApiProjectSupportsIdPatchData, ApiProjectSupportsIdPatchResponses, ApiProjectSupportsIdPatchErrors, ApiProjectUpdatesGetCollectionData, ApiProjectUpdatesGetCollectionResponses, ApiProjectUpdatesPostData, ApiProjectUpdatesPostResponses, ApiProjectUpdatesPostErrors, ApiProjectUpdatesIdDeleteData, ApiProjectUpdatesIdDeleteResponses, ApiProjectUpdatesIdDeleteErrors, ApiProjectUpdatesIdGetData, ApiProjectUpdatesIdGetResponses, ApiProjectUpdatesIdGetErrors, ApiProjectUpdatesIdPatchData, ApiProjectUpdatesIdPatchResponses, ApiProjectUpdatesIdPatchErrors, ApiTipjarsGetCollectionData, ApiTipjarsGetCollectionResponses, ApiTipjarsPostData, ApiTipjarsPostResponses, ApiTipjarsPostErrors, ApiTipjarsIdDeleteData, ApiTipjarsIdDeleteResponses, ApiTipjarsIdDeleteErrors, ApiTipjarsIdGetData, ApiTipjarsIdGetResponses, ApiTipjarsIdGetErrors, ApiTipjarsIdPatchData, ApiTipjarsIdPatchResponses, ApiTipjarsIdPatchErrors, ApiUsersGetCollectionData, ApiUsersGetCollectionResponses, ApiUsersPostData, ApiUsersPostResponses, ApiUsersPostErrors, ApiUsersIdDeleteData, ApiUsersIdDeleteResponses, ApiUsersIdDeleteErrors, ApiUsersIdGetData, ApiUsersIdGetResponses, ApiUsersIdGetErrors, ApiUsersIdPatchData, ApiUsersIdPatchResponses, ApiUsersIdPatchErrors, ApiUserTokensPostData, ApiUserTokensPostResponses, ApiUserTokensPostErrors, ApiUserTokensIdDeleteData, ApiUserTokensIdDeleteResponses, ApiUserTokensIdDeleteErrors, ApiUserTokensIdGetData, ApiUserTokensIdGetResponses, ApiUserTokensIdGetErrors, ApiVersionsGetCollectionData, ApiVersionsGetCollectionResponses, ApiVersionsIdGetData, ApiVersionsIdGetResponses, ApiVersionsIdGetErrors } from './types.gen';
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
export type Options = ClientOptions & {
@@ -125,6 +125,40 @@ export const apiAccountingTransactionsIdGet = (options?: Options) => {
+ return (options?.client ?? _heyApiClient).get({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/categories',
+ ...options
+ });
+};
+
+/**
+ * Retrieves a Category resource.
+ * Retrieves one Category resource.
+ */
+export const apiCategoriesIdGet = (options: Options) => {
+ return (options.client ?? _heyApiClient).get({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/categories/{id}',
+ ...options
+ });
+};
+
/**
* Retrieves the collection of Gateway resources.
* Retrieves the collection of Gateway resources.
@@ -702,6 +736,23 @@ export const apiUsersIdpersonPatch = (opti
});
};
+/**
+ * Retrieves a Ping resource.
+ * Retrieves one Ping resource.
+ */
+export const apiPingGet = (options?: Options) => {
+ return (options?.client ?? _heyApiClient).get({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/ping',
+ ...options
+ });
+};
+
/**
* Retrieves the collection of Project resources.
* Retrieves the collection of Project resources.
@@ -888,6 +939,99 @@ export const apiProjectBudgetItemsIdPatch = (options?: Options) => {
+ return (options?.client ?? _heyApiClient).get({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/project_collaborations',
+ ...options
+ });
+};
+
+/**
+ * Creates a ProjectCollaboration resource.
+ * Creates a ProjectCollaboration resource.
+ */
+export const apiProjectCollaborationsPost = (options: Options) => {
+ return (options.client ?? _heyApiClient).post({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/project_collaborations',
+ ...options,
+ headers: {
+ 'Content-Type': 'application/json',
+ ...options.headers
+ }
+ });
+};
+
+/**
+ * Removes the ProjectCollaboration resource.
+ * Removes the ProjectCollaboration resource.
+ */
+export const apiProjectCollaborationsIdDelete = (options: Options) => {
+ return (options.client ?? _heyApiClient).delete({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/project_collaborations/{id}',
+ ...options
+ });
+};
+
+/**
+ * Retrieves a ProjectCollaboration resource.
+ * Retrieves one ProjectCollaboration resource.
+ */
+export const apiProjectCollaborationsIdGet = (options: Options) => {
+ return (options.client ?? _heyApiClient).get({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/project_collaborations/{id}',
+ ...options
+ });
+};
+
+/**
+ * Updates the ProjectCollaboration resource.
+ * Updates the ProjectCollaboration resource.
+ */
+export const apiProjectCollaborationsIdPatch = (options: Options) => {
+ return (options.client ?? _heyApiClient).patch({
+ security: [
+ {
+ scheme: 'bearer',
+ type: 'http'
+ }
+ ],
+ url: '/v4/project_collaborations/{id}',
+ ...options,
+ headers: {
+ 'Content-Type': 'application/merge-patch+json',
+ ...options.headers
+ }
+ });
+};
+
/**
* Retrieves the collection of ProjectReward resources.
* Retrieves the collection of ProjectReward resources.
diff --git a/src/openapi/client/types.gen.ts b/src/openapi/client/types.gen.ts
index d9d6ce52..6a9732c9 100644
--- a/src/openapi/client/types.gen.ts
+++ b/src/openapi/client/types.gen.ts
@@ -14,12 +14,13 @@ export type Accounting = {
* IRI of the resource owning this Accounting.
*/
owner?: string;
+ ownerObject?: string;
/**
* The preferred currency for monetary operations.\
* 3-letter ISO 4217 currency code.
*/
currency?: string;
- balance?: ApiMoney;
+ balance?: MoneyWithConversion;
};
/**
@@ -43,12 +44,13 @@ export type AccountingJsonld = {
* IRI of the resource owning this Accounting.
*/
owner?: string;
+ ownerObject?: string;
/**
* The preferred currency for monetary operations.\
* 3-letter ISO 4217 currency code.
*/
currency?: string;
- balance?: ApiMoneyJsonld;
+ balance?: MoneyWithConversionJsonld;
};
/**
@@ -69,7 +71,7 @@ export type AccountingBalancePoint = {
/**
* Resulting balance for items in this point.
*/
- balance?: ApiMoney;
+ balance?: MoneyWithConversion;
/**
* The number of items aggregated in this point.
*/
@@ -96,7 +98,7 @@ export type AccountingBalancePointJsonld = {
/**
* Resulting balance for items in this point.
*/
- balance?: ApiMoneyJsonld;
+ balance?: MoneyWithConversionJsonld;
/**
* The number of items aggregated in this point.
*/
@@ -117,7 +119,7 @@ export type AccountingTransaction = {
/**
* The monetary value received at target and issued at origin.
*/
- money?: ApiMoney;
+ money?: MoneyWithConversion;
/**
* The Accounting from which the Transaction comes from.
*/
@@ -149,7 +151,7 @@ export type AccountingTransactionJsonld = {
/**
* The monetary value received at target and issued at origin.
*/
- money?: ApiMoneyJsonld;
+ money?: MoneyWithConversionJsonld;
/**
* The Accounting from which the Transaction comes from.
*/
@@ -160,45 +162,6 @@ export type AccountingTransactionJsonld = {
target?: string;
};
-export type ApiMoney = {
- /**
- * An amount of currency.\
- * Expressed as the minor unit, e.g: cents, pennies, etc.
- */
- amount: number;
- /**
- * 3-letter ISO 4217 currency code.
- */
- currency: string;
- /**
- * Conversion metadata.
- */
- conversion?: Conversion | null;
-};
-
-export type ApiMoneyJsonld = {
- '@context'?: string | {
- '@vocab': string;
- hydra: 'http://www.w3.org/ns/hydra/core#';
- [key: string]: unknown | string | 'http://www.w3.org/ns/hydra/core#';
- };
- readonly '@id'?: string;
- readonly '@type'?: string;
- /**
- * An amount of currency.\
- * Expressed as the minor unit, e.g: cents, pennies, etc.
- */
- amount: number;
- /**
- * 3-letter ISO 4217 currency code.
- */
- currency: string;
- /**
- * Conversion metadata.
- */
- conversion?: ConversionJsonld | null;
-};
-
export type Budget = {
/**
* A summary of the minimum budget. As described by items with specified minimum money.
@@ -232,19 +195,19 @@ export type BudgetSummary = {
/**
* The total money by the included items.
*/
- money?: ApiMoney;
+ money?: MoneyWithConversion;
/**
* The total money of type 'task'.
*/
- task?: ApiMoney;
+ task?: MoneyWithConversion;
/**
* The total money of type 'material'.
*/
- material?: ApiMoney;
+ material?: MoneyWithConversion;
/**
* The total money of type 'infrastructure'.
*/
- infra?: ApiMoney;
+ infra?: MoneyWithConversion;
};
export type BudgetSummaryJsonld = {
@@ -258,19 +221,34 @@ export type BudgetSummaryJsonld = {
/**
* The total money by the included items.
*/
- money?: ApiMoneyJsonld;
+ money?: MoneyWithConversionJsonld;
/**
* The total money of type 'task'.
*/
- task?: ApiMoneyJsonld;
+ task?: MoneyWithConversionJsonld;
/**
* The total money of type 'material'.
*/
- material?: ApiMoneyJsonld;
+ material?: MoneyWithConversionJsonld;
/**
* The total money of type 'infrastructure'.
*/
- infra?: ApiMoneyJsonld;
+ infra?: MoneyWithConversionJsonld;
+};
+
+export type Category = {
+ id?: string;
+};
+
+export type CategoryJsonld = {
+ '@context'?: string | {
+ '@vocab': string;
+ hydra: 'http://www.w3.org/ns/hydra/core#';
+ [key: string]: unknown | string | 'http://www.w3.org/ns/hydra/core#';
+ };
+ readonly '@id'?: string;
+ readonly '@type'?: string;
+ id?: string;
};
/**
@@ -466,7 +444,7 @@ export type GatewayCharge = {
/**
* The money to-be-paid for this item at the Gateway.
*/
- money: ApiMoney;
+ money: MoneyWithConversion;
/**
* The status of the charge item with the Gateway.
*/
@@ -529,7 +507,7 @@ export type GatewayChargeJsonld = {
/**
* The money to-be-paid for this item at the Gateway.
*/
- money: ApiMoneyJsonld;
+ money: MoneyWithConversionJsonld;
/**
* The status of the charge item with the Gateway.
*/
@@ -977,7 +955,7 @@ export type MatchStrategy = {
/**
* The assigned maximum amount of funding that will be given by the MatchFormula per operation.
*/
- limit: ApiMoney;
+ limit: MoneyWithConversion;
/**
* The `x` factor used to calculate the resulting match of funds with the MatchFormula.
*/
@@ -1031,7 +1009,7 @@ export type MatchStrategyJsonld = {
/**
* The assigned maximum amount of funding that will be given by the MatchFormula per operation.
*/
- limit: ApiMoneyJsonld;
+ limit: MoneyWithConversionJsonld;
/**
* The `x` factor used to calculate the resulting match of funds with the MatchFormula.
*/
@@ -1064,6 +1042,45 @@ export type MoneyJsonld = {
conversion?: ConversionJsonld | null;
};
+export type MoneyWithConversion = {
+ /**
+ * An amount of currency.\
+ * Expressed as the minor unit, e.g: cents, pennies, etc.
+ */
+ amount: number;
+ /**
+ * 3-letter ISO 4217 currency code.
+ */
+ currency: string;
+ /**
+ * Conversion metadata.
+ */
+ conversion?: Conversion | null;
+};
+
+export type MoneyWithConversionJsonld = {
+ '@context'?: string | {
+ '@vocab': string;
+ hydra: 'http://www.w3.org/ns/hydra/core#';
+ [key: string]: unknown | string | 'http://www.w3.org/ns/hydra/core#';
+ };
+ readonly '@id'?: string;
+ readonly '@type'?: string;
+ /**
+ * An amount of currency.\
+ * Expressed as the minor unit, e.g: cents, pennies, etc.
+ */
+ amount: number;
+ /**
+ * 3-letter ISO 4217 currency code.
+ */
+ currency: string;
+ /**
+ * Conversion metadata.
+ */
+ conversion?: ConversionJsonld | null;
+};
+
/**
* Sometimes an Organization is behind a User account,
* we keep their data separated from the User record to allow for other User types,
@@ -1186,6 +1203,29 @@ export type PersonJsonld = {
lastName?: string;
};
+export type Ping = {
+ /**
+ * The req-res time difference in milliseconds.
+ */
+ time?: number;
+ message?: string;
+};
+
+export type PingJsonld = {
+ '@context'?: string | {
+ '@vocab': string;
+ hydra: 'http://www.w3.org/ns/hydra/core#';
+ [key: string]: unknown | string | 'http://www.w3.org/ns/hydra/core#';
+ };
+ readonly '@id'?: string;
+ readonly '@type'?: string;
+ /**
+ * The req-res time difference in milliseconds.
+ */
+ time?: number;
+ message?: string;
+};
+
/**
* Projects describe a User-owned, community-led event that is to be discovered, developed and funded by the community.
*/
@@ -1222,9 +1262,9 @@ export type Project = {
*/
calendar?: ProjectCalendar;
/**
- * One of the available categories.
+ * A list of the available categories most relevant to this Project.
*/
- category: 'solidary' | 'libre-software' | 'employment' | 'design' | 'journalism' | 'education' | 'culture' | 'ecology' | 'health-and-cares' | 'open-data' | 'democracy' | 'equity';
+ categories: Array;
/**
* ISO 3166 data about the Project's territory of interest.
*/
@@ -1240,7 +1280,7 @@ export type Project = {
/**
* The status of a Project represents how far it is in it's life-cycle.
*/
- status?: 'in_draft' | 'to_review' | 'in_review' | 'in_editing' | 'rejected' | 'to_campaign' | 'in_campaign' | 'unfunded' | 'in_funding' | 'funded';
+ status?: 'in_draft' | 'to_campaign_review' | 'in_campaign_review' | 'in_campaign_review.request_change' | 'campaign_review.rejected' | 'to_campaign' | 'in_campaign' | 'campaign.failed' | 'to_funding_review' | 'in_funding_review' | 'in_funding_review.request_change' | 'funding_review.rejected' | 'to_funding' | 'in_funding' | 'funding.paid';
/**
* List of the ProjectRewards this Project offers.
*/
@@ -1257,10 +1297,13 @@ export type Project = {
* A list of the ProjectUpdates this Project has.
*/
readonly updates?: Array;
+ readonly matchCallSubmissions?: Array;
/**
* List of the available content locales.
*/
readonly locales?: Array;
+ readonly dateCreated?: string;
+ readonly dateUpdated?: string;
};
/**
@@ -1278,25 +1321,13 @@ export type ProjectProjectCreationDto = {
/**
* One of the available categories.
*/
- category: 'solidary' | 'libre-software' | 'employment' | 'design' | 'journalism' | 'education' | 'culture' | 'ecology' | 'health-and-cares' | 'open-data' | 'democracy' | 'equity';
+ categories: Array