diff --git a/package.json b/package.json
index b620d3e4..c622f191 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"date-fns": "^4.1.0",
"email-validator": "^2.0.4",
"i18next": "^25.3.2",
- "need4deed-sdk": "0.0.139",
+ "need4deed-sdk": "0.0.145",
"next": "15.3.8",
"react": "^19.0.0",
"react-day-picker": "^9.13.0",
diff --git a/public/locales/de/translations.json b/public/locales/de/translations.json
index 95b0c789..e429cd67 100644
--- a/public/locales/de/translations.json
+++ b/public/locales/de/translations.json
@@ -830,7 +830,21 @@
"confirmMessage": "Dadurch wird \"{{opportunityName}}\" zu {{agentName}} verschoben. Die Kontaktdaten der antragstellenden Person bleiben gleich; zugewiesene Freiwillige werden mit übertragen.",
"confirmButton": "Übertragen",
"success": "Möglichkeit erfolgreich übertragen"
- }
+ },
+ "createAgent": {
+ "button": "Neue NGO hinzufügen",
+ "title": "Neue NGO hinzufügen",
+ "name": "Name der Organisation",
+ "addressStreet": "Straße",
+ "addressPostcode": "Postleitzahl",
+ "cancel": "Abbrechen",
+ "submit": "Erstellen",
+ "success": "NGO erfolgreich erstellt",
+ "validation": {
+ "titleRequired": "Name der Organisation ist erforderlich"
+ }
+ },
+ "unclaimed": "Nicht übernommen"
},
"opportunities": {
"opportunities": "Ehrenamtsbedarfe",
@@ -1077,7 +1091,9 @@
"new": "Neu",
"active": "Aktiv",
"unresponsive": "Nicht erreichbar",
- "inactive": "Inaktiv"
+ "inactive": "Inaktiv",
+ "incontact": "In Kontakt",
+ "triedToContact": "Kontaktversuch unternommen"
},
"volunteerSearch": {
"notNeeded": "Nicht benötigt",
@@ -1109,7 +1125,9 @@
"unresponsive": "Nicht erreichbar",
"unresponsive_description": "Diese Einrichtung/dieses Projekt antwortet nicht.",
"inactive": "Inaktiv",
- "inactive_description": "Diese Einrichtung/dieses Projekt arbeitet nicht mehr mit der Organisation zusammen."
+ "inactive_description": "Diese Einrichtung/dieses Projekt arbeitet nicht mehr mit der Organisation zusammen.",
+ "incontact_description": "Diese Einrichtung/dieses Projekt ist in Kontakt mit der Organisation.",
+ "triedToContact_description": "Es wurde versucht, diese Einrichtung/dieses Projekt zu kontaktieren."
},
"optionsVolSearch": {
"agent-not-needed": "Keine Ehrenamtlichen mehr benötigt",
diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json
index 75219285..d0496c07 100644
--- a/public/locales/en/translations.json
+++ b/public/locales/en/translations.json
@@ -863,7 +863,21 @@
"confirmMessage": "This will move \"{{opportunityName}}\" to {{agentName}}. The applicant's contact details stay the same; assigned volunteers move with it.",
"confirmButton": "Transfer",
"success": "Opportunity transferred successfully"
- }
+ },
+ "createAgent": {
+ "button": "Add a new NGO",
+ "title": "Add a new NGO",
+ "name": "Organization name",
+ "addressStreet": "Street address",
+ "addressPostcode": "Postcode",
+ "cancel": "Cancel",
+ "submit": "Create",
+ "success": "NGO created successfully",
+ "validation": {
+ "titleRequired": "Organization name is required"
+ }
+ },
+ "unclaimed": "Unclaimed"
},
"opportunities": {
"opportunities": "Opportunities",
@@ -1065,7 +1079,9 @@
"new": "New",
"active": "Active",
"unresponsive": "Unresponsive",
- "inactive": "Inactive"
+ "inactive": "Inactive",
+ "incontact": "In contact",
+ "triedToContact": "Tried to contact"
},
"volunteerSearch": {
"notNeeded": "Not needed",
@@ -1097,7 +1113,9 @@
"unresponsive": "Unresponsive",
"unresponsive_description": "This NGO does not respond.",
"inactive": "Inactive",
- "inactive_description": "This NGO does not work with the organization anymore."
+ "inactive_description": "This NGO does not work with the organization anymore.",
+ "incontact_description": "This NGO is in contact with the organization.",
+ "triedToContact_description": "We tried to contact this NGO but they did not respond."
},
"optionsVolSearch": {
"agent-not-needed": "No longer need volunteers",
@@ -1145,7 +1163,6 @@
"confirmText": "Delete",
"success": "Contact deleted successfully"
},
-
"validation": {
"nameRequired": "Name is required",
"districtRequired": "District is required",
diff --git a/src/components/Dashboard/Agents/AgentCard.tsx b/src/components/Dashboard/Agents/AgentCard.tsx
index a76f8b48..49d73724 100644
--- a/src/components/Dashboard/Agents/AgentCard.tsx
+++ b/src/components/Dashboard/Agents/AgentCard.tsx
@@ -17,6 +17,7 @@ import { useUpdateAgentStatus } from "@/hooks";
import { getNormalizedAgent } from "./helpers";
import { createVolunteerSearchMap } from "./constants";
import { StatusBadge } from "../common/StatusBadge";
+import { UnclaimedBadge } from "../common/UnclaimedBadge";
import { Card, CardDetailsInfo, CardHeader, CardHeaderInfo, DistrictContainer, DistrictDiv } from "./styles";
interface Props {
@@ -28,7 +29,7 @@ interface Props {
export const AgentCard = ({ agent, districtsList, onSelect }: Props) => {
const { t, i18n } = useTranslation();
- const { id, title, district, volunteerSearch, services, type, trustLevel } = getNormalizedAgent(agent);
+ const { id, title, district, volunteerSearch, services, type, trustLevel, unclaimed } = getNormalizedAgent(agent);
const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
const lang = i18n.language as Lang;
@@ -63,6 +64,7 @@ export const AgentCard = ({ agent, districtsList, onSelect }: Props) => {
{title}
+ {unclaimed && }
diff --git a/src/components/Dashboard/Agents/AgentTableRow.tsx b/src/components/Dashboard/Agents/AgentTableRow.tsx
index 3d45c82a..d41a08f7 100644
--- a/src/components/Dashboard/Agents/AgentTableRow.tsx
+++ b/src/components/Dashboard/Agents/AgentTableRow.tsx
@@ -7,6 +7,7 @@ import { AGENT_COL_WIDTHS } from "./agentsTableColumns";
import { ClickableRow, TableCell } from "@/components/core/common/Table";
import { WrapAnywhereCell } from "../common/EntityTableList/styles";
import { CopyEmail } from "../common/CopyEmail";
+import { UnclaimedBadge } from "../common/UnclaimedBadge";
import { extractOptionTitle } from "@/components/Dashboard/Profile/sections/OpportunityDetails/formatters";
interface TableRowProps {
@@ -20,7 +21,7 @@ interface TableRowProps {
export function AgentTableRow({ agent, isLast, searchLabels, districtsList, onSelect }: TableRowProps) {
const { i18n } = useTranslation();
- const { id, title, type, volunteerSearch, district, activeVolunteers, numOpportunities, email } = agent;
+ const { id, title, type, volunteerSearch, district, activeVolunteers, numOpportunities, email, unclaimed } = agent;
const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
const lang = i18n.language as Lang;
@@ -44,6 +45,7 @@ export function AgentTableRow({ agent, isLast, searchLabels, districtsList, onSe
{title}
+ {unclaimed && }
{extractOptionTitle(type, lang)}
diff --git a/src/components/Dashboard/Agents/Agents.tsx b/src/components/Dashboard/Agents/Agents.tsx
index b4e78231..d3de3db7 100644
--- a/src/components/Dashboard/Agents/Agents.tsx
+++ b/src/components/Dashboard/Agents/Agents.tsx
@@ -1,9 +1,11 @@
"use client";
+import { PlusIcon } from "@phosphor-icons/react";
import { DashboardLayout } from "@/components/Layout";
import { AgentListController } from "./AgentListController";
+import { CreateAgentDialog } from "./CreateAgentDialog/CreateAgentDialog";
import { PendingMemberships } from "./PendingMemberships";
-import { AgentsContainer, ContentRow } from "./styles";
+import { AgentsContainer, ContentRow, CreateAgentButton } from "./styles";
import CardsHeader from "../common/CardsHeader/CardsHeader";
import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react";
@@ -27,9 +29,11 @@ import { ConfirmationDialog } from "../Profile/sections/shared/ConfirmationDialo
export const Agents = () => {
const user = useCurrentUser(true);
const isAgent = user?.role === UserRole.AGENT;
+ const canCreateAgent = user?.role === UserRole.COORDINATOR || user?.role === UserRole.ADMIN;
const { t, i18n } = useTranslation();
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
+ const [isCreateAgentOpen, setIsCreateAgentOpen] = useState(false);
const [sortOrder, setSortOrder] = useState(SortOrder.NewToOld);
const [numOfAgents, setNumOfAgents] = useState(0);
const [cardsFilter, setCardsFilter] = useState(defaultAgentCardsFilter);
@@ -131,6 +135,17 @@ export const Agents = () => {
activeFilters={activeFilters}
onClearAllFilters={handleClearAllFilters}
onClearFilter={handleClearFilter}
+ headerAction={
+ canCreateAgent && (
+ setIsCreateAgentOpen(true)}
+ aria-label={t("dashboard.agents.createAgent.button")}
+ >
+
+
+ )
+ }
/>
@@ -161,6 +176,7 @@ export const Agents = () => {
onConfirm={handleTransferConfirm}
/>
)}
+ {canCreateAgent && setIsCreateAgentOpen(false)} />}
);
};
diff --git a/src/components/Dashboard/Agents/CreateAgentDialog/CreateAgentDialog.tsx b/src/components/Dashboard/Agents/CreateAgentDialog/CreateAgentDialog.tsx
new file mode 100644
index 00000000..2e3ed02b
--- /dev/null
+++ b/src/components/Dashboard/Agents/CreateAgentDialog/CreateAgentDialog.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import Button from "@/components/core/button/Button/Button";
+import { Modal } from "@/components/core/modal/Modal";
+import { EditableField } from "@/components/EditableField/EditableField";
+import { useCreateAgent } from "@/hooks/useCreateAgent";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Controller, ControllerRenderProps, useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import { ButtonRow, FormDetails } from "../../Profile/sections/shared/styles";
+import { CreateAgentFormData, createAgentFormSchema } from "./createAgentFormSchema";
+import { DialogTitle } from "./styles";
+
+type Props = {
+ isOpen: boolean;
+ onClose: () => void;
+};
+
+const emptyValues: CreateAgentFormData = { title: "", addressStreet: "", addressPostcode: "" };
+
+// Coordinator/admin-only "+" flow from the Agents table header (fe#911): a
+// bare Agent with no linked Person/User, for an NGO the coordinator already
+// has details for before it has self-registered. Deliberately minimal — just
+// enough to create the placeholder record; richer fields (type, services,
+// languages, about) are filled in later via the agent's own profile page,
+// same as any other agent.
+export const CreateAgentDialog = ({ isOpen, onClose }: Props) => {
+ const { t } = useTranslation();
+ const { mutate: createAgent, isPending } = useCreateAgent();
+
+ const schema = createAgentFormSchema(t);
+ const {
+ control,
+ handleSubmit,
+ reset,
+ formState: { errors, isValid },
+ } = useForm({
+ resolver: zodResolver(schema),
+ mode: "onChange",
+ defaultValues: emptyValues,
+ });
+
+ const handleClose = () => {
+ reset(emptyValues);
+ onClose();
+ };
+
+ const onSubmit = (values: CreateAgentFormData) => {
+ createAgent(
+ {
+ title: values.title,
+ addressStreet: values.addressStreet || undefined,
+ addressPostcode: values.addressPostcode || undefined,
+ },
+ { onSuccess: handleClose },
+ );
+ };
+
+ if (!isOpen) return null;
+
+ return (
+
+ {t("dashboard.agents.createAgent.title")}
+
+ }) => (
+
+ )}
+ />
+ }) => (
+
+ )}
+ />
+ }) => (
+
+ )}
+ />
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/Dashboard/Agents/CreateAgentDialog/createAgentFormSchema.ts b/src/components/Dashboard/Agents/CreateAgentDialog/createAgentFormSchema.ts
new file mode 100644
index 00000000..e94f218d
--- /dev/null
+++ b/src/components/Dashboard/Agents/CreateAgentDialog/createAgentFormSchema.ts
@@ -0,0 +1,11 @@
+import { z } from "zod";
+
+export const createAgentFormSchema = (t: (key: string) => string) => {
+ return z.object({
+ title: z.string().min(1, t("dashboard.agents.createAgent.validation.titleRequired")),
+ addressStreet: z.string().optional().or(z.literal("")),
+ addressPostcode: z.string().optional().or(z.literal("")),
+ });
+};
+
+export type CreateAgentFormData = z.infer>;
diff --git a/src/components/Dashboard/Agents/CreateAgentDialog/styles.ts b/src/components/Dashboard/Agents/CreateAgentDialog/styles.ts
new file mode 100644
index 00000000..7586fe8a
--- /dev/null
+++ b/src/components/Dashboard/Agents/CreateAgentDialog/styles.ts
@@ -0,0 +1,6 @@
+import styled from "styled-components";
+import { Heading3 } from "@/components/styled/text";
+
+export const DialogTitle = styled(Heading3)`
+ margin-bottom: 16px;
+`;
diff --git a/src/components/Dashboard/Agents/styles.ts b/src/components/Dashboard/Agents/styles.ts
index 4c8cd5bb..278bf9fa 100644
--- a/src/components/Dashboard/Agents/styles.ts
+++ b/src/components/Dashboard/Agents/styles.ts
@@ -1,4 +1,5 @@
import { BaseCard } from "@/components/styled/container";
+import { circleIconButtonStyles } from "@/components/styled/mixins";
import styled from "styled-components";
export const AgentsContainer = styled.div`
@@ -7,6 +8,13 @@ export const AgentsContainer = styled.div`
gap: var(--dashboard-volunteers-container-gap);
`;
+// Coordinator/admin-only "create agent" button next to the Agents table
+// header (fe#911) — same circular icon-button treatment as the agent
+// profile's "add contact" button.
+export const CreateAgentButton = styled.button`
+ ${circleIconButtonStyles}
+`;
+
export const ContentRow = styled.div`
display: flex;
flex-direction: row;
diff --git a/src/components/Dashboard/Profile/sections/ContactDetails/agent/styles.ts b/src/components/Dashboard/Profile/sections/ContactDetails/agent/styles.ts
index acf3cfbd..a6e7912c 100644
--- a/src/components/Dashboard/Profile/sections/ContactDetails/agent/styles.ts
+++ b/src/components/Dashboard/Profile/sections/ContactDetails/agent/styles.ts
@@ -1,25 +1,8 @@
import styled from "styled-components";
-
-const iconButtonBase = `
- display: flex;
- align-items: center;
- justify-content: center;
- width: 32px;
- height: 32px;
- border-radius: 50%;
- border: var(--border-width-thin) solid var(--color-aubergine);
- background-color: var(--color-white);
- color: var(--color-aubergine);
- cursor: pointer;
- flex-shrink: 0;
-
- &:hover {
- background-color: var(--color-aubergine-subtle);
- }
-`;
+import { circleIconButtonStyles } from "@/components/styled/mixins";
export const EditIconButton = styled.button`
- ${iconButtonBase}
+ ${circleIconButtonStyles}
`;
export const AddContactRow = styled.div`
@@ -29,5 +12,5 @@ export const AddContactRow = styled.div`
`;
export const AddContactButton = styled.button`
- ${iconButtonBase}
+ ${circleIconButtonStyles}
`;
diff --git a/src/components/Dashboard/Profile/sections/ProfileHeader/agent/constants.ts b/src/components/Dashboard/Profile/sections/ProfileHeader/agent/constants.ts
index 31ae25e3..2445243f 100644
--- a/src/components/Dashboard/Profile/sections/ProfileHeader/agent/constants.ts
+++ b/src/components/Dashboard/Profile/sections/ProfileHeader/agent/constants.ts
@@ -7,9 +7,16 @@ export const createEngagementStatusLabelMap = (t: TFunction): Record = {
[AgentEngagementStatus.UNRESPONSIVE]: "unresponsive_description",
[AgentEngagementStatus.INACTIVE]: "inactive_description",
+ [AgentEngagementStatus.INCONTACT]: "incontact_description",
+ [AgentEngagementStatus.TRIED_TO_CONTACT]: "triedToContact_description",
};
export const createAgentDialogOptions = (t: TFunction) => {
diff --git a/src/components/Dashboard/common/CardsHeader/CardsHeader.tsx b/src/components/Dashboard/common/CardsHeader/CardsHeader.tsx
index f5ece629..8f09299f 100644
--- a/src/components/Dashboard/common/CardsHeader/CardsHeader.tsx
+++ b/src/components/Dashboard/common/CardsHeader/CardsHeader.tsx
@@ -1,3 +1,4 @@
+import { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Paragraph } from "../../../styled/text";
@@ -13,6 +14,7 @@ import {
HeaderContainer,
HeaderFilterItem,
HeaderFilterItemContainer,
+ HeaderTitleRow,
HyphenatedHeading2,
SearchBarSectionContainer,
TabHeading,
@@ -46,6 +48,10 @@ type Props = {
onClearAllFilters?: () => void;
onClearFilter: (filter: string, parentKey?: string) => void;
entityFilter?: EntityFilter;
+ // Optional slot next to the header title — e.g. a coordinator-only "+"
+ // button to create a record directly (fe#911). No consumer needs it yet
+ // beyond Agents, so it's a generic slot rather than a named prop per action.
+ headerAction?: ReactNode;
};
export default function CardsHeader({
@@ -66,12 +72,16 @@ export default function CardsHeader({
onClearAllFilters,
onClearFilter,
entityFilter,
+ headerAction,
}: Props) {
const { t } = useTranslation();
return (
- {header}
+
+ {header}
+ {headerAction}
+
diff --git a/src/components/Dashboard/common/CardsHeader/styles.ts b/src/components/Dashboard/common/CardsHeader/styles.ts
index dea53967..9e347dc8 100644
--- a/src/components/Dashboard/common/CardsHeader/styles.ts
+++ b/src/components/Dashboard/common/CardsHeader/styles.ts
@@ -82,6 +82,12 @@ export const HyphenatedHeading2 = styled(Heading2)`
${hyphenationStyles}
`;
+export const HeaderTitleRow = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 12px;
+`;
+
export const ClearAllButton = styled.button`
display: flex;
align-items: center;
diff --git a/src/components/Dashboard/common/UnclaimedBadge.tsx b/src/components/Dashboard/common/UnclaimedBadge.tsx
new file mode 100644
index 00000000..bc6e2d53
--- /dev/null
+++ b/src/components/Dashboard/common/UnclaimedBadge.tsx
@@ -0,0 +1,15 @@
+import { useTranslation } from "react-i18next";
+import { StyledBadge } from "./styles";
+
+// A coordinator-created agent with no linked Person/User yet (fe#911) — not a
+// StatusValue (statusMaps.ts), just a boolean flag, so this stays a small
+// standalone badge rather than being forced into that enum-keyed system.
+export const UnclaimedBadge = () => {
+ const { t } = useTranslation();
+
+ return (
+
+ {t("dashboard.agents.unclaimed")}
+
+ );
+};
diff --git a/src/components/Dashboard/common/statusMaps.ts b/src/components/Dashboard/common/statusMaps.ts
index f33beb84..9b62cb46 100644
--- a/src/components/Dashboard/common/statusMaps.ts
+++ b/src/components/Dashboard/common/statusMaps.ts
@@ -20,6 +20,8 @@ export const statusColorMap: Record = {
[AgentEngagementStatusType.UNRESPONSIVE]: "var(--color-grey-50)",
[AgentEngagementStatusType.INACTIVE]: "var(--color-grey-50)",
[AgentEngagementStatusType.NEW]: "var(--color-green-100)",
+ [AgentEngagementStatusType.INCONTACT]: "var(--color-green-100)",
+ [AgentEngagementStatusType.TRIED_TO_CONTACT]: "var(--color-green-100)",
[AgentVolunteerSearchType.NOT_NEEDED]: "var(--color-grey-50)",
[AgentVolunteerSearchType.VOLUNTEERS_FOUND]: "var(--color-green-100)",
[AgentVolunteerSearchType.SEARCHING]: "var(--color-red-50)",
@@ -35,6 +37,8 @@ export const statusIconMap: Record = {
[AgentEngagementStatusType.UNRESPONSIVE]: PhoneXIcon,
[AgentEngagementStatusType.INACTIVE]: StopCircleIcon,
[AgentEngagementStatusType.NEW]: SparkleIcon,
+ [AgentEngagementStatusType.INCONTACT]: SparkleIcon,
+ [AgentEngagementStatusType.TRIED_TO_CONTACT]: SparkleIcon,
[AgentVolunteerSearchType.NOT_NEEDED]: HandPalmIcon,
[AgentVolunteerSearchType.VOLUNTEERS_FOUND]: CheckCircleIcon,
[AgentVolunteerSearchType.SEARCHING]: BinocularsIcon,
diff --git a/src/components/styled/mixins.ts b/src/components/styled/mixins.ts
index 1c89040b..6288abc2 100644
--- a/src/components/styled/mixins.ts
+++ b/src/components/styled/mixins.ts
@@ -1,5 +1,26 @@
import { css } from "styled-components";
+// Small circular "+"/pencil icon-button treatment shared by the agent
+// contact list's add/edit buttons and the Agents table header's "create
+// agent" button (fe#911).
+export const circleIconButtonStyles = css`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ border: var(--border-width-thin) solid var(--color-aubergine);
+ background-color: var(--color-white);
+ color: var(--color-aubergine);
+ cursor: pointer;
+ flex-shrink: 0;
+
+ &:hover {
+ background-color: var(--color-aubergine-subtle);
+ }
+`;
+
export const hyphenationStyles = css`
white-space: normal; /* Allows text to wrap */
overflow-wrap: break-word; /* Ensures long words break to fit */
diff --git a/src/hooks/useCreateAgent.ts b/src/hooks/useCreateAgent.ts
new file mode 100644
index 00000000..d3629787
--- /dev/null
+++ b/src/hooks/useCreateAgent.ts
@@ -0,0 +1,12 @@
+import { apiPathAgent } from "@/config/constants";
+import { useMutationQuery } from "@/hooks";
+import { ApiAgentCreateResponse, ApiAgentRegisterNew } from "need4deed-sdk";
+
+export const useCreateAgent = () => {
+ return useMutationQuery({
+ apiPath: apiPathAgent,
+ method: "post",
+ successMessage: "dashboard.agents.createAgent.success",
+ queryKeyToInvalidate: ["agents"],
+ });
+};
diff --git a/yarn.lock b/yarn.lock
index 41ba0761..69cdad33 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2412,10 +2412,10 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
-need4deed-sdk@0.0.139:
- version "0.0.139"
- resolved "https://registry.yarnpkg.com/need4deed-sdk/-/need4deed-sdk-0.0.139.tgz#34ca63577a104dcaa3353d3b1c6c9661da182c40"
- integrity sha512-gU3wlLF4sUxz9azKUBcceuiOgUmt1Q6Km6sXvVWzSDbZa7BieXu9Xr2Lz7NQDVZxMM817nDt5y8CLfrKBexvrw==
+need4deed-sdk@0.0.145:
+ version "0.0.145"
+ resolved "https://registry.yarnpkg.com/need4deed-sdk/-/need4deed-sdk-0.0.145.tgz#79c2e4ffefbe2bed5520dd356e644f7b572862fe"
+ integrity sha512-xLfKK9W9AvpwAoq95c/ifBTBGGU9ndALyGLpiUAp7DNLnWjyQjLghhVVFTwyKygKSxY04p6h/hvY0G7amsJSKg==
next@15.3.8:
version "15.3.8"