diff --git a/PostFundManagement.ConsoleUI/.env.example b/PostFundManagement.ConsoleUI/.env.example index d2c8f7c..0f6d60e 100644 --- a/PostFundManagement.ConsoleUI/.env.example +++ b/PostFundManagement.ConsoleUI/.env.example @@ -17,6 +17,12 @@ VITE_OIDC_CLIENT_SECRET="secret_71f88570a46eb22eb5270b47f69e3332c84d8db85d008f02 # PFM Backend API Base URL VITE_API_BASE_URL="https://api.ifri.khongisa.co.za/" +# Protocol / SSL Settings for local development +# HTTPS is enabled by default when running locally via @vitejs/plugin-basic-ssl. +# Set to 'false' if you ever need to run local dev over plain HTTP. +HTTPS="true" +VITE_ENABLE_HTTPS="true" + # HasherSettings for HashIds compatibility with backend pfm-api VITE_HASHER_MIN_LENGTH="11" VITE_HASHER_SALT="TGlnaHQgQ2hhcm1zLCBNaWRyYW5kQm9va3MgbWFrZSBsb3RzIG9mIG1vbmV5IGFuZCBhcmUgYSBzdWNjZXNzZnVsIHZpcmFsIHN0b3J5IGluIFNvdXRoIEFmcmljYQ==" diff --git a/PostFundManagement.ConsoleUI/all_endpoints.json b/PostFundManagement.ConsoleUI/all_endpoints.json new file mode 100644 index 0000000..1b2763e --- /dev/null +++ b/PostFundManagement.ConsoleUI/all_endpoints.json @@ -0,0 +1,3268 @@ +[ + { + "tag": "Users", + "method": "GET", + "path": "/v1/api/users/{userId}", + "opId": "getuser", + "desc": "Gets the metadata of a single user.", + "params": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Users", + "method": "POST", + "path": "/v1/api/users/{page}/{pageSize}", + "opId": "getusers", + "desc": "Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UserFilterRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Users", + "method": "POST", + "path": "/v1/api/integration/sync", + "opId": "syncuser", + "desc": "Trigger integration and data synchronization in the background", + "params": [], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Users", + "method": "GET", + "path": "/v1/api/users/{userId}/status/{status}", + "opId": "updateuserstatus", + "desc": "Changes the Activity status of the user in the system", + "params": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "status", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/ActivityStatus" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "POST", + "path": "/v1/api/projects/activities", + "opId": "createactivity", + "desc": "Add a new activity under project (M04)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateActivityRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "POST", + "path": "/v1/api/projects/programme", + "opId": "createprogramme", + "desc": "Create programme", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateProgrammeRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "PUT", + "path": "/v1/api/projects/programme", + "opId": "updateprogramme", + "desc": "Update existing programme", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateProgrammeRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "POST", + "path": "/v1/api/projects", + "opId": "createproject", + "desc": "Create a new project (M04)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateProjectRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects", + "opId": "getprojects", + "desc": "Get a list of projects", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "PUT", + "path": "/v1/api/projects", + "opId": "updateproject", + "desc": "Update project details", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateProjectRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/{projectId}/activities", + "opId": "getactivities", + "desc": "Get a list of activities for a project", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Activity" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/activities/{activityId}", + "opId": "getactivity", + "desc": "Gets a specific activity by it's id", + "params": [ + { + "name": "activityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Activity" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/milestones/{milestoneId}", + "opId": "getmilestone", + "desc": "Gets a specific milestone", + "params": [ + { + "name": "milestoneId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Milestone" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/milestones/{page}/{pageSize}", + "opId": "getmilestones", + "desc": "Get a list of all milestones", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Milestone" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/programme/{programmeId}", + "opId": "getprogramme", + "desc": "Get specific programme", + "params": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Programme" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/{programmeId}/programme", + "opId": "getprogrammeprojects", + "desc": "Get a list of all projects under a programme", + "params": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/programme/{page}/{pageSize}", + "opId": "getprogrammesprojects", + "desc": "Get a list of all programmes", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/{projectId}", + "opId": "getproject", + "desc": "Get project by ID", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/indicators/project/{projectId}", + "opId": "getprojectindicators", + "desc": "Get get a detailed list of project indicators", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Indicator" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/{projectId}/milestones", + "opId": "getprojectmilestones", + "desc": "Get a list of milestones for a project", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Milestone" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "GET", + "path": "/v1/api/projects/{projectId}/risks", + "opId": "getprojectrisks", + "desc": "Get a list of risks for a project", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Risk" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "PUT", + "path": "/v1/api/projects/milestone", + "opId": "updatemilestone", + "desc": "Update project milestone details", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateMilestoneRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Projects", + "method": "PUT", + "path": "/v1/api/projects/status", + "opId": "updateprojectstatus", + "desc": "Update project status (M04)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateProjectStatusRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "POST", + "path": "/v1/api/grants/awards", + "opId": "createaward", + "desc": "Register an approved funding award (M01)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateAwardRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/awards", + "opId": "getawards", + "desc": "Get a list of awards", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 10 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Award" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "POST", + "path": "/v1/api/grants/budgets", + "opId": "createbudget", + "desc": "Create a new budget line (M05)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateBudgetRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/budgets", + "opId": "getbudgets", + "desc": "Get all budgets", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Budget" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "POST", + "path": "/v1/api/grants/contracts", + "opId": "createcontract", + "desc": "Create a contract record from an approved award (M02)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateContractRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/contracts", + "opId": "getcontracts", + "desc": "Get all contracts", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Contract" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "PUT", + "path": "/v1/api/grants/contracts", + "opId": "updatecontract", + "desc": "Updates a specific contract", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateContractRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "POST", + "path": "/v1/api/grants/contracts/execute", + "opId": "executecontract", + "desc": "Execute the contract and lock the contractual version", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/ExecuteContractRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/awards/{awardId}", + "opId": "getaward", + "desc": "Get award by ID", + "params": [ + { + "name": "awardId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Award" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/budgets/{budgetId}", + "opId": "getbudget", + "desc": "Get budget by ID", + "params": [ + { + "name": "budgetId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Budget" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/contracts/{contractId}", + "opId": "getcontract", + "desc": "Get contract by ID", + "params": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Contract" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "POST", + "path": "/v1/api/grants/contracts/{contractId}", + "opId": "uploadcontract", + "desc": "Uploads a contract file securely", + "params": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/grants/contracts/{contractId}/securelink", + "opId": "getcontracturl", + "desc": "Gets a secure download URL, has expiry period (default 15min)", + "params": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "POST", + "path": "/v1/api/governance/organisations", + "opId": "createorganisation", + "desc": "Create a new organization profile", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateOrganisationRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/organisations", + "opId": "getorganisations", + "desc": "Get a paginated directory of organizations", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Organisation" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "PUT", + "path": "/v1/api/governance/organisations", + "opId": "updateorganisation", + "desc": "Update an existing organization profile", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateOrganisationRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "POST", + "path": "/v1/api/governance/organisations/portfolio", + "opId": "createportfolio", + "desc": "Create a new organization portfolio", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreatePortfolioRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "PUT", + "path": "/v1/api/governance/organisations/portfolio", + "opId": "updateportfolio", + "desc": "Update an existing organization portfolio", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdatePortfolioRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/dashboard", + "opId": "dashboard", + "desc": "Get executive portfolio dashboard metrics and KPIs", + "params": [], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Dashboard" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/instruments/{programmeId}", + "opId": "getinstruments", + "desc": "Get all funding instruments", + "params": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Instrument" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/organisations/{organisationId}", + "opId": "getorganisation", + "desc": "Get organization profile by ID", + "params": [ + { + "name": "organisationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Organisation" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/portfolios/{portfolioId}", + "opId": "getportfolio", + "desc": "Get a specific portfolios", + "params": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/portfolios/{portfolioId}/programmes", + "opId": "getportfolioprogrammes", + "desc": "Get a programme's portfolios", + "params": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/organisations/{organisationId}/portfolio", + "opId": "getportfolios", + "desc": "Get all portfolios", + "params": [ + { + "name": "organisationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Governance", + "method": "GET", + "path": "/v1/api/governance/programmes/{portfolioId}", + "opId": "getprogrammes", + "desc": "Get all programmes", + "params": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "POST", + "path": "/v1/api/execution/disbursements/{disbursementId}/approve", + "opId": "approvedisbursement", + "desc": "Approve disbursement and release payment (M06)", + "params": [ + { + "name": "disbursementId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "POST", + "path": "/v1/api/execution/disbursements", + "opId": "createdisbursement", + "desc": "Create a new disbursement schedule (M06)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateDisbursementRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "GET", + "path": "/v1/api/execution/disbursements", + "opId": "getdisbursements", + "desc": "Get a list of disbursements", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 10 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Disbursement" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "PUT", + "path": "/v1/api/execution/disbursements", + "opId": "updatedisbursement", + "desc": "Update the details of a specific disbursement", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateDisbursementRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "POST", + "path": "/v1/api/execution/risks", + "opId": "createrisk", + "desc": "Add risk to risk register (M09)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateRiskRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "GET", + "path": "/v1/api/execution/risks", + "opId": "getrisks", + "desc": "Get a list of all risks", + "params": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "PUT", + "path": "/v1/api/execution/risks", + "opId": "updaterisk", + "desc": "Update the details of a specific risk", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateRiskRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "GET", + "path": "/v1/api/execution/disbursements/{disbursementId}", + "opId": "getdisbursement", + "desc": "Get a specific disbursement", + "params": [ + { + "name": "disbursementId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Disbursement" + } + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Execution", + "method": "GET", + "path": "/v1/api/execution/risks/{riskId}", + "opId": "getrisk", + "desc": "Get a specific risk", + "params": [ + { + "name": "riskId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Risk" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "PUT", + "path": "/v1/api/evidence/file", + "opId": "createevidence", + "desc": "Upload supporting evidence file to S3 and register metadata (M14)", + "params": [], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "POST", + "path": "/v1/api/evidence/indicators", + "opId": "createindicator", + "desc": "Define indicators and baselines/targets (M08)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateIndicatorRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "POST", + "path": "/v1/api/evidence/milestones", + "opId": "createmilestonerequest", + "desc": "Define contractual milestones (M07)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/CreateMilestoneRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/{evidenceId}", + "opId": "getevidence", + "desc": "Get a specific evidence record and assiciated file URLs", + "params": [ + { + "name": "evidenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Evidence" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/files/{page}/{pageSize}", + "opId": "getevidencefiles", + "desc": "Get a list of all uploaded evidence files metadata", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Grants", + "method": "GET", + "path": "/v1/api/evidence/{evidenceId}/securelink", + "opId": "getevidenceurl", + "desc": "Gets a secure download URL of the evidence file, URL expires (default: 15min)", + "params": [ + { + "name": "evidenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": { + "type": "string" + } + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/indicators/{indicatorId}", + "opId": "getindicator", + "desc": "Get a specific indicator.", + "params": [ + { + "name": "indicatorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Indicator" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/indicators/{page}/{pageSize}", + "opId": "getindicators", + "desc": "Get a list of indicators", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Indicator" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/milestone/{milestoneId}", + "opId": "getmilestoneevidence", + "desc": "Get a list of all uploaded evidence metadata for a specific mliestone", + "params": [ + { + "name": "milestoneId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "GET", + "path": "/v1/api/evidence/{projectId}", + "opId": "getprojectevidence", + "desc": "Get a list of all uploaded evidence metadata for a specific project", + "params": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "POST", + "path": "/v1/api/evidence/indicators/{indicatorId}/actuals/{actualAmount}", + "opId": "recordindicatoramount", + "desc": "Record actual values for indicators (M08)", + "params": [ + { + "name": "indicatorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "actualAmount", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": "number", + "format": "double" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Evidence & M&E", + "method": "PUT", + "path": "/v1/api/evidence", + "opId": "updateevidence", + "desc": "Updates a specific evidence record metadata", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/UpdateEvidenceRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": null + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "403": { + "desc": "Forbidden", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Communications", + "method": "GET", + "path": "/v1/api/communications/invites/applicant/{applicantUserId}", + "opId": "getapplicantinvite", + "desc": "Gets an applicant's invite", + "params": [ + { + "name": "applicantUserId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Invite" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Communications", + "method": "GET", + "path": "/v1/api/communications/invites/{applicantUserId}", + "opId": "getapplicantinvites", + "desc": "Gets an applicant's invite list", + "params": [ + { + "name": "applicantUserId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Invite" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "404": { + "desc": "Not Found", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Communications", + "method": "GET", + "path": "/v1/api/communications/invites/{page}/{pageSize}", + "opId": "getinvites", + "desc": "Get a list of sent invitations", + "params": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "reqBody": null, + "responses": { + "200": { + "desc": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Invite" + } + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + { + "tag": "Communications", + "method": "POST", + "path": "/v1/api/communications/invite", + "opId": "sendinvite", + "desc": "Trigger an invite to an organization or candidate (specifying NotificationPlatform)", + "params": [], + "reqBody": { + "required": true, + "contentType": "application/json; version=1", + "schema": { + "$ref": "#/components/schemas/InviteRequest" + } + }, + "responses": { + "200": { + "desc": "OK", + "schema": { + "$ref": "#/components/schemas/Invite" + } + }, + "400": { + "desc": "Bad Request", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "401": { + "desc": "Unauthorized", + "schema": null + }, + "500": { + "desc": "Internal Server Error", + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } +] \ No newline at end of file diff --git a/PostFundManagement.ConsoleUI/bun.lock b/PostFundManagement.ConsoleUI/bun.lock index 98b5beb..da15f2a 100644 --- a/PostFundManagement.ConsoleUI/bun.lock +++ b/PostFundManagement.ConsoleUI/bun.lock @@ -28,6 +28,7 @@ "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^22.14.0", + "@vitejs/plugin-basic-ssl": "^2.3.0", "autoprefixer": "^10.4.21", "esbuild": "^0.25.0", "tsx": "^4.21.0", @@ -282,6 +283,8 @@ "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + "@vitejs/plugin-basic-ssl": ["@vitejs/plugin-basic-ssl@2.3.0", "", { "peerDependencies": { "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], diff --git a/PostFundManagement.ConsoleUI/openapi.json b/PostFundManagement.ConsoleUI/openapi.json new file mode 100644 index 0000000..423173e --- /dev/null +++ b/PostFundManagement.ConsoleUI/openapi.json @@ -0,0 +1,6077 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "PostFundManagement.Api | v1", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://api.ifri.khongisa.co.za/" + } + ], + "paths": { + "/v1/api/users/{userId}": { + "get": { + "tags": [ + "Users" + ], + "description": "Gets the metadata of a single user.", + "operationId": "getuser", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/users/{page}/{pageSize}": { + "post": { + "tags": [ + "Users" + ], + "description": "Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting", + "operationId": "getusers", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UserFilterRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/integration/sync": { + "post": { + "tags": [ + "Users" + ], + "description": "Trigger integration and data synchronization in the background", + "operationId": "syncuser", + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/users/{userId}/status/{status}": { + "get": { + "tags": [ + "Users" + ], + "description": "Changes the Activity status of the user in the system", + "operationId": "updateuserstatus", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "status", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/ActivityStatus" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/tools/hashid/{id64}": { + "get": { + "tags": [ + "Tools" + ], + "description": "Hashes an int64 identifier", + "operationId": "encodeid", + "parameters": [ + { + "name": "id64", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/activities": { + "post": { + "tags": [ + "Projects" + ], + "description": "Add a new activity under project (M04)", + "operationId": "createactivity", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateActivityRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/programme": { + "post": { + "tags": [ + "Projects" + ], + "description": "Create programme", + "operationId": "createprogramme", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateProgrammeRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Projects" + ], + "description": "Update existing programme", + "operationId": "updateprogramme", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateProgrammeRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects": { + "post": { + "tags": [ + "Projects" + ], + "description": "Create a new project (M04)", + "operationId": "createproject", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateProjectRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of projects", + "operationId": "getprojects", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Projects" + ], + "description": "Update project details", + "operationId": "updateproject", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateProjectRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/{projectId}/activities": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of activities for a project", + "operationId": "getactivities", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Activity" + } + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/activities/{activityId}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Gets a specific activity by it's id", + "operationId": "getactivity", + "parameters": [ + { + "name": "activityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Activity" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/milestones/{milestoneId}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Gets a specific milestone", + "operationId": "getmilestone", + "parameters": [ + { + "name": "milestoneId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Milestone" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/milestones/{page}/{pageSize}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of all milestones", + "operationId": "getmilestones", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Milestone" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/programme/{programmeId}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get specific programme", + "operationId": "getprogramme", + "parameters": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Programme" + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/{programmeId}/programme": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of all projects under a programme", + "operationId": "getprogrammeprojects", + "parameters": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/programme/{page}/{pageSize}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of all programmes", + "operationId": "getprogrammesprojects", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/{projectId}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get project by ID", + "operationId": "getproject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/indicators/project/{projectId}": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get get a detailed list of project indicators", + "operationId": "getprojectindicators", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Indicator" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/{projectId}/milestones": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of milestones for a project", + "operationId": "getprojectmilestones", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Milestone" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/{projectId}/risks": { + "get": { + "tags": [ + "Projects" + ], + "description": "Get a list of risks for a project", + "operationId": "getprojectrisks", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Risk" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/milestone": { + "put": { + "tags": [ + "Projects" + ], + "description": "Update project milestone details", + "operationId": "updatemilestone", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateMilestoneRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/projects/status": { + "put": { + "tags": [ + "Projects" + ], + "description": "Update project status (M04)", + "operationId": "updateprojectstatus", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateProjectStatusRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/awards": { + "post": { + "tags": [ + "Grants" + ], + "description": "Register an approved funding award (M01)", + "operationId": "createaward", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateAwardRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Grants" + ], + "description": "Get a list of awards", + "operationId": "getawards", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Award" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/budgets": { + "post": { + "tags": [ + "Grants" + ], + "description": "Create a new budget line (M05)", + "operationId": "createbudget", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateBudgetRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Grants" + ], + "description": "Get all budgets", + "operationId": "getbudgets", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Budget" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/contracts": { + "post": { + "tags": [ + "Grants" + ], + "description": "Create a contract record from an approved award (M02)", + "operationId": "createcontract", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateContractRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Grants" + ], + "description": "Get all contracts", + "operationId": "getcontracts", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Contract" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Grants" + ], + "description": "Updates a specific contract", + "operationId": "updatecontract", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateContractRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/contracts/execute": { + "post": { + "tags": [ + "Grants" + ], + "description": "Execute the contract and lock the contractual version", + "operationId": "executecontract", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ExecuteContractRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/awards/{awardId}": { + "get": { + "tags": [ + "Grants" + ], + "description": "Get award by ID", + "operationId": "getaward", + "parameters": [ + { + "name": "awardId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Award" + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/budgets/{budgetId}": { + "get": { + "tags": [ + "Grants" + ], + "description": "Get budget by ID", + "operationId": "getbudget", + "parameters": [ + { + "name": "budgetId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Budget" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/contracts/{contractId}": { + "get": { + "tags": [ + "Grants" + ], + "description": "Get contract by ID", + "operationId": "getcontract", + "parameters": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Contract" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "post": { + "tags": [ + "Grants" + ], + "description": "Uploads a contract file securely", + "operationId": "uploadcontract", + "parameters": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/grants/contracts/{contractId}/securelink": { + "get": { + "tags": [ + "Grants" + ], + "description": "Gets a secure download URL, has expiry period (default 15min)", + "operationId": "getcontracturl", + "parameters": [ + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "string" + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/organisations": { + "post": { + "tags": [ + "Governance" + ], + "description": "Create a new organization profile", + "operationId": "createorganisation", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateOrganisationRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Governance" + ], + "description": "Get a paginated directory of organizations", + "operationId": "getorganisations", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Organisation" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Governance" + ], + "description": "Update an existing organization profile", + "operationId": "updateorganisation", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateOrganisationRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/organisations/portfolio": { + "post": { + "tags": [ + "Governance" + ], + "description": "Create a new organization portfolio", + "operationId": "createportfolio", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreatePortfolioRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Governance" + ], + "description": "Update an existing organization portfolio", + "operationId": "updateportfolio", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdatePortfolioRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/dashboard": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get executive portfolio dashboard metrics and KPIs", + "operationId": "dashboard", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Dashboard" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/instruments/{programmeId}": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get all funding instruments", + "operationId": "getinstruments", + "parameters": [ + { + "name": "programmeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Instrument" + } + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/organisations/{organisationId}": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get organization profile by ID", + "operationId": "getorganisation", + "parameters": [ + { + "name": "organisationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Organisation" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/portfolios/{portfolioId}": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get a specific portfolios", + "operationId": "getportfolio", + "parameters": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/portfolios/{portfolioId}/programmes": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get a programme's portfolios", + "operationId": "getportfolioprogrammes", + "parameters": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/organisations/{organisationId}/portfolio": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get all portfolios", + "operationId": "getportfolios", + "parameters": [ + { + "name": "organisationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Portfolio" + } + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/governance/programmes/{portfolioId}": { + "get": { + "tags": [ + "Governance" + ], + "description": "Get all programmes", + "operationId": "getprogrammes", + "parameters": [ + { + "name": "portfolioId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Programme" + } + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/execution/disbursements/{disbursementId}/approve": { + "post": { + "tags": [ + "Execution" + ], + "description": "Approve disbursement and release payment (M06)", + "operationId": "approvedisbursement", + "parameters": [ + { + "name": "disbursementId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/execution/disbursements": { + "post": { + "tags": [ + "Execution" + ], + "description": "Create a new disbursement schedule (M06)", + "operationId": "createdisbursement", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateDisbursementRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Execution" + ], + "description": "Get a list of disbursements", + "operationId": "getdisbursements", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Disbursement" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Execution" + ], + "description": "Update the details of a specific disbursement", + "operationId": "updatedisbursement", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateDisbursementRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/execution/risks": { + "post": { + "tags": [ + "Execution" + ], + "description": "Add risk to risk register (M09)", + "operationId": "createrisk", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateRiskRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "get": { + "tags": [ + "Execution" + ], + "description": "Get a list of all risks", + "operationId": "getrisks", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + }, + "put": { + "tags": [ + "Execution" + ], + "description": "Update the details of a specific risk", + "operationId": "updaterisk", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateRiskRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/execution/disbursements/{disbursementId}": { + "get": { + "tags": [ + "Execution" + ], + "description": "Get a specific disbursement", + "operationId": "getdisbursement", + "parameters": [ + { + "name": "disbursementId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Disbursement" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/execution/risks/{riskId}": { + "get": { + "tags": [ + "Execution" + ], + "description": "Get a specific risk", + "operationId": "getrisk", + "parameters": [ + { + "name": "riskId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Risk" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/file": { + "put": { + "tags": [ + "Evidence & M&E" + ], + "description": "Upload supporting evidence file to S3 and register metadata (M14)", + "operationId": "createevidence", + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/indicators": { + "post": { + "tags": [ + "Evidence & M&E" + ], + "description": "Define indicators and baselines/targets (M08)", + "operationId": "createindicator", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateIndicatorRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/milestones": { + "post": { + "tags": [ + "Evidence & M&E" + ], + "description": "Define contractual milestones (M07)", + "operationId": "createmilestonerequest", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/CreateMilestoneRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/{evidenceId}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a specific evidence record and assiciated file URLs", + "operationId": "getevidence", + "parameters": [ + { + "name": "evidenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Evidence" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/files/{page}/{pageSize}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a list of all uploaded evidence files metadata", + "operationId": "getevidencefiles", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/{evidenceId}/securelink": { + "get": { + "tags": [ + "Grants" + ], + "description": "Gets a secure download URL of the evidence file, URL expires (default: 15min)", + "operationId": "getevidenceurl", + "parameters": [ + { + "name": "evidenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json; version=1": { + "schema": { + "type": "string" + } + } + } + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/indicators/{indicatorId}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a specific indicator.", + "operationId": "getindicator", + "parameters": [ + { + "name": "indicatorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Indicator" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/indicators/{page}/{pageSize}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a list of indicators", + "operationId": "getindicators", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Indicator" + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/milestone/{milestoneId}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a list of all uploaded evidence metadata for a specific mliestone", + "operationId": "getmilestoneevidence", + "parameters": [ + { + "name": "milestoneId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/{projectId}": { + "get": { + "tags": [ + "Evidence & M&E" + ], + "description": "Get a list of all uploaded evidence metadata for a specific project", + "operationId": "getprojectevidence", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Evidence" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence/indicators/{indicatorId}/actuals/{actualAmount}": { + "post": { + "tags": [ + "Evidence & M&E" + ], + "description": "Record actual values for indicators (M08)", + "operationId": "recordindicatoramount", + "parameters": [ + { + "name": "indicatorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "actualAmount", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": "number", + "format": "double" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/evidence": { + "put": { + "tags": [ + "Evidence & M&E" + ], + "description": "Updates a specific evidence record metadata", + "operationId": "updateevidence", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/UpdateEvidenceRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found" + }, + "403": { + "description": "Forbidden" + }, + "401": { + "description": "Unauthorized" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/communications/invites/applicant/{applicantUserId}": { + "get": { + "tags": [ + "Communications" + ], + "description": "Gets an applicant's invite", + "operationId": "getapplicantinvite", + "parameters": [ + { + "name": "applicantUserId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Invite" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/communications/invites/{applicantUserId}": { + "get": { + "tags": [ + "Communications" + ], + "description": "Gets an applicant's invite list", + "operationId": "getapplicantinvites", + "parameters": [ + { + "name": "applicantUserId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Invite" + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/communications/invites/{page}/{pageSize}": { + "get": { + "tags": [ + "Communications" + ], + "description": "Get a list of sent invitations", + "operationId": "getinvites", + "parameters": [ + { + "name": "page", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "path", + "required": true, + "schema": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Invite" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { } + ] + } + }, + "/v1/api/communications/invite": { + "post": { + "tags": [ + "Communications" + ], + "description": "Trigger an invite to an organization or candidate (specifying NotificationPlatform)", + "operationId": "sendinvite", + "requestBody": { + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/InviteRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/Invite" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json; version=1": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { } + ] + } + } + }, + "components": { + "schemas": { + "Activity": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "approvedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "category": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "title": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "performedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "ActivityStatus": { + "type": "integer" + }, + "ApprovalStatus": { + "type": "integer" + }, + "Award": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "approvedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "Budget": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "approvedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "Contract": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "awardId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "externalSignatureId": { + "type": [ + "null", + "string" + ] + }, + "signedDocumentUrl": { + "type": [ + "null", + "string" + ] + }, + "effectiveAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "expiresAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "totalValue": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/ContractStatus" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "ContractStatus": { + "type": "integer" + }, + "CreateActivityRequest": { + "required": [ + "projectId", + "milestoneId", + "category", + "amount", + "title", + "description", + "performedAt" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "category": { + "type": "string" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "performedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "CreateAwardRequest": { + "required": [ + "organisationId", + "programmeId", + "amount" + ], + "type": "object", + "properties": { + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CreateBudgetRequest": { + "required": [ + "projectId", + "name", + "amount" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CreateContractRequest": { + "required": [ + "awardId", + "totalValue", + "effectiveAt", + "expiresAt" + ], + "type": "object", + "properties": { + "awardId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "totalValue": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "effectiveAt": { + "type": "string", + "format": "date-time" + }, + "expiresAt": { + "type": "string", + "format": "date-time" + } + } + }, + "CreateDisbursementRequest": { + "required": [ + "projectId", + "milestoneId", + "amount" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CreateIndicatorRequest": { + "required": [ + "projectId", + "name", + "unitOfMeasure", + "baselineAmount", + "targetAmount" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "unitOfMeasure": { + "$ref": "#/components/schemas/UnitOfMeasure" + }, + "baselineAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "targetAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "CreateMilestoneRequest": { + "required": [ + "projectId", + "dueAt", + "name", + "priority" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "dueAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string" + }, + "priority": { + "$ref": "#/components/schemas/Priority" + } + } + }, + "CreateOrganisationRequest": { + "required": [ + "name", + "registrationNo", + "email" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "registrationNo": { + "type": "string" + }, + "email": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/OrganisationType" + } + } + }, + "CreatePortfolioRequest": { + "required": [ + "name", + "description", + "organisationId", + "ownerId" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "ownerId": { + "type": "string", + "format": "uuid" + } + } + }, + "CreateProgrammeRequest": { + "required": [ + "portfolioId", + "name", + "ownerId" + ], + "type": "object", + "properties": { + "portfolioId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "ownerId": { + "type": "string", + "format": "uuid" + } + } + }, + "CreateProjectRequest": { + "required": [ + "programmeId", + "name", + "description", + "sector", + "projectType" + ], + "type": "object", + "properties": { + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "sector": { + "$ref": "#/components/schemas/IndustrySector" + }, + "projectType": { + "$ref": "#/components/schemas/ProjectType" + } + } + }, + "CreateRiskRequest": { + "required": [ + "projectId", + "likelihood", + "impact", + "name", + "description" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "likelihood": { + "$ref": "#/components/schemas/RiskLikelihood" + }, + "impact": { + "$ref": "#/components/schemas/RiskImpact" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "Dashboard": { + "required": [ + "portfolioValue", + "activeAwards", + "disbursed", + "onTrackPercentage", + "atRiskCount", + "beneficiariesReached" + ], + "type": "object", + "properties": { + "portfolioValue": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "activeAwards": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "disbursed": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "onTrackPercentage": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "atRiskCount": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "beneficiariesReached": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + } + } + }, + "Disbursement": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "Evidence": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "indicatorId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "activityId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "version": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "type": { + "$ref": "#/components/schemas/EvidenceType" + }, + "documentUrl": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "EvidenceType": { + "type": "integer" + }, + "ExecuteContractRequest": { + "required": [ + "contractId", + "externalSignatureId", + "signedDocumentUrl" + ], + "type": "object", + "properties": { + "contractId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "externalSignatureId": { + "type": "string" + }, + "signedDocumentUrl": { + "type": "string" + } + } + }, + "Indicator": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "unitOfMeasure": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/UnitOfMeasure" + } + ] + }, + "baselineAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "targetAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + }, + "actualAmount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "IndustrySector": { + "type": "integer" + }, + "Instrument": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "code": { + "type": [ + "null", + "string" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ActivityStatus" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "Invite": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "invitedOrganisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "awardId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "contractId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "recipient": { + "type": "string", + "format": "uuid" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "InviteRequest": { + "required": [ + "organisationId", + "invitedOrganisationId", + "awardId", + "contractId", + "recipient", + "message", + "platform" + ], + "type": "object", + "properties": { + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "invitedOrganisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "awardId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "contractId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "recipient": { + "type": "string", + "format": "uuid" + }, + "message": { + "type": "string" + }, + "platform": { + "$ref": "#/components/schemas/NotificationPlatform" + } + } + }, + "Milestone": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "dueAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + }, + "priority": { + "$ref": "#/components/schemas/Priority" + } + } + }, + "NotificationPlatform": { + "type": "integer" + }, + "Organisation": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "registrationNo": { + "type": [ + "null", + "string" + ] + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "type": { + "$ref": "#/components/schemas/OrganisationType" + }, + "status": { + "$ref": "#/components/schemas/ActivityStatus" + } + } + }, + "OrganisationType": { + "type": "integer", + "default": 5 + }, + "Portfolio": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "ownedBy": { + "type": "string", + "format": "uuid" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + } + } + }, + "Priority": { + "type": "integer" + }, + "ProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": [ + "null", + "string" + ] + }, + "title": { + "type": [ + "null", + "string" + ] + }, + "status": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int32" + }, + "detail": { + "type": [ + "null", + "string" + ] + }, + "instance": { + "type": [ + "null", + "string" + ] + } + } + }, + "Programme": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "portfolioId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "ownedBy": { + "type": "string", + "format": "uuid" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "Project": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "updatedBy": { + "type": [ + "null", + "string" + ], + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "startedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "endedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "sector": { + "$ref": "#/components/schemas/IndustrySector" + }, + "projectType": { + "$ref": "#/components/schemas/ProjectType" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "ProjectType": { + "type": "integer" + }, + "Risk": { + "type": "object", + "properties": { + "id": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "createdBy": { + "type": "string", + "format": "uuid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "likelihood": { + "$ref": "#/components/schemas/RiskLikelihood" + }, + "impact": { + "$ref": "#/components/schemas/RiskImpact" + }, + "name": { + "type": [ + "null", + "string" + ] + }, + "description": { + "type": [ + "null", + "string" + ] + } + } + }, + "RiskImpact": { + "type": "integer" + }, + "RiskLikelihood": { + "type": "integer" + }, + "UnitOfMeasure": { + "type": "integer" + }, + "UpdateContractRequest": { + "required": [ + "contractId", + "status" + ], + "type": "object", + "properties": { + "contractId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/ContractStatus" + } + } + }, + "UpdateDisbursementRequest": { + "required": [ + "disbursementId", + "milestoneId", + "amount" + ], + "type": "object", + "properties": { + "disbursementId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "null", + "integer", + "string" + ], + "format": "int64" + }, + "amount": { + "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$", + "type": [ + "number", + "string" + ], + "format": "double" + } + } + }, + "UpdateEvidenceRequest": { + "required": [ + "evidenceId", + "version", + "evidenceType", + "status" + ], + "type": "object", + "properties": { + "evidenceId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "version": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int32" + }, + "evidenceType": { + "$ref": "#/components/schemas/EvidenceType" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "UpdateMilestoneRequest": { + "required": [ + "milestoneId", + "dueAt", + "name", + "priority", + "status" + ], + "type": "object", + "properties": { + "milestoneId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "dueAt": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string" + }, + "priority": { + "$ref": "#/components/schemas/Priority" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "UpdateOrganisationRequest": { + "required": [ + "organisationId", + "name", + "registrationNo", + "email", + "type" + ], + "type": "object", + "properties": { + "organisationId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "registrationNo": { + "type": "string" + }, + "email": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/OrganisationType" + } + } + }, + "UpdatePortfolioRequest": { + "required": [ + "portfolioId", + "name", + "description", + "ownerId" + ], + "type": "object", + "properties": { + "portfolioId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "ownerId": { + "type": "string", + "format": "uuid" + } + } + }, + "UpdateProgrammeRequest": { + "required": [ + "programmeId", + "name", + "ownerId", + "status" + ], + "type": "object", + "properties": { + "programmeId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "ownerId": { + "type": "string", + "format": "uuid" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "UpdateProjectRequest": { + "required": [ + "projectId", + "name", + "description", + "startedAt", + "endedAt", + "sector", + "projectType" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "startedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "endedAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "sector": { + "$ref": "#/components/schemas/IndustrySector" + }, + "projectType": { + "$ref": "#/components/schemas/ProjectType" + } + } + }, + "UpdateProjectStatusRequest": { + "required": [ + "projectId", + "status" + ], + "type": "object", + "properties": { + "projectId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "UpdateRiskRequest": { + "required": [ + "riskId", + "likelihood", + "impact", + "name", + "description" + ], + "type": "object", + "properties": { + "riskId": { + "pattern": "^-?(?:0|[1-9]\\d*)$", + "type": [ + "integer", + "string" + ], + "format": "int64" + }, + "likelihood": { + "$ref": "#/components/schemas/RiskLikelihood" + }, + "impact": { + "$ref": "#/components/schemas/RiskImpact" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "lastLoginAt": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/components/schemas/ActivityStatus" + } + } + }, + "UserFilterRequest": { + "type": "object", + "properties": { + "status": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ActivityStatus" + } + ] + }, + "email": { + "type": [ + "null", + "string" + ] + }, + "sortBy": { + "type": [ + "null", + "string" + ] + }, + "sortDescending": { + "type": "boolean", + "default": false + } + } + } + }, + "securitySchemes": { + "Bearer": { + "type": "http", + "description": "JWT Authorization header using the Bearer scheme", + "scheme": "bearer" + }, + "OAuth2": { + "type": "oauth2", + "description": "Interactive OAuth2 flow for authentication and authorization", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://sts.security.khongisa.co.za/connect/authorize", + "tokenUrl": "https://sts.security.khongisa.co.za/connect/token", + "scopes": { + "openid": "OpenID Connect", + "profile": "User Profile", + "roles": "User Roles", + "email": "User Email", + "address": "User Address", + "pfm-api-dev": "PFM API Scope" + } + } + } + } + } + }, + "tags": [ + { + "name": "Users" + }, + { + "name": "Tools" + }, + { + "name": "Projects" + }, + { + "name": "Grants" + }, + { + "name": "Governance" + }, + { + "name": "Execution" + }, + { + "name": "Evidence & M&E" + }, + { + "name": "Communications" + } + ] +} \ No newline at end of file diff --git a/PostFundManagement.ConsoleUI/openapi_models.txt b/PostFundManagement.ConsoleUI/openapi_models.txt new file mode 100644 index 0000000..21575ba --- /dev/null +++ b/PostFundManagement.ConsoleUI/openapi_models.txt @@ -0,0 +1,434 @@ +=== ALL SCHEMAS WITH PROPERTIES === + +Model: Activity + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: null,integer,string (format: int64, nullable: false) + milestoneId: null,integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + approvedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + category: null,string (format: -, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + amount: number,string (format: double, nullable: false) + title: null,string (format: -, nullable: false) + description: null,string (format: -, nullable: false) + performedAt: null,string (format: date-time, nullable: false) + +Model: Award + Required: [] + id: integer,string (format: int64, nullable: false) + organisationId: integer,string (format: int64, nullable: false) + programmeId: integer,string (format: int64, nullable: false) + projectId: null,integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + approvedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + amount: number,string (format: double, nullable: false) + +Model: Budget + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + approvedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + name: null,string (format: -, nullable: false) + amount: number,string (format: double, nullable: false) + +Model: Contract + Required: [] + id: integer,string (format: int64, nullable: false) + awardId: integer,string (format: int64, nullable: false) + externalSignatureId: null,string (format: -, nullable: false) + signedDocumentUrl: null,string (format: -, nullable: false) + effectiveAt: null,string (format: date-time, nullable: false) + expiresAt: null,string (format: date-time, nullable: false) + totalValue: number,string (format: double, nullable: false) + status: ContractStatus (format: -, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + +Model: CreateActivityRequest + Required: [ + 'projectId', + 'milestoneId', + 'category', + 'amount', + 'title', + 'description', + 'performedAt' +] + projectId*: integer,string (format: int64, nullable: false) + milestoneId*: null,integer,string (format: int64, nullable: false) + category*: string (format: -, nullable: false) + amount*: number,string (format: double, nullable: false) + title*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + performedAt*: null,string (format: date-time, nullable: false) + +Model: CreateAwardRequest + Required: [ 'organisationId', 'programmeId', 'amount' ] + organisationId*: integer,string (format: int64, nullable: false) + programmeId*: integer,string (format: int64, nullable: false) + amount*: number,string (format: double, nullable: false) + +Model: CreateBudgetRequest + Required: [ 'projectId', 'name', 'amount' ] + projectId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + amount*: number,string (format: double, nullable: false) + +Model: CreateContractRequest + Required: [ 'awardId', 'totalValue', 'effectiveAt', 'expiresAt' ] + awardId*: integer,string (format: int64, nullable: false) + totalValue*: number,string (format: double, nullable: false) + effectiveAt*: string (format: date-time, nullable: false) + expiresAt*: string (format: date-time, nullable: false) + +Model: CreateDisbursementRequest + Required: [ 'projectId', 'milestoneId', 'amount' ] + projectId*: integer,string (format: int64, nullable: false) + milestoneId*: null,integer,string (format: int64, nullable: false) + amount*: number,string (format: double, nullable: false) + +Model: CreateIndicatorRequest + Required: [ + 'projectId', + 'name', + 'unitOfMeasure', + 'baselineAmount', + 'targetAmount' +] + projectId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + unitOfMeasure*: UnitOfMeasure (format: -, nullable: false) + baselineAmount*: number,string (format: double, nullable: false) + targetAmount*: number,string (format: double, nullable: false) + +Model: CreateMilestoneRequest + Required: [ 'projectId', 'dueAt', 'name', 'priority' ] + projectId*: integer,string (format: int64, nullable: false) + dueAt*: string (format: date-time, nullable: false) + name*: string (format: -, nullable: false) + priority*: Priority (format: -, nullable: false) + +Model: CreateOrganisationRequest + Required: [ 'name', 'registrationNo', 'email' ] + name*: string (format: -, nullable: false) + registrationNo*: string (format: -, nullable: false) + email*: string (format: -, nullable: false) + type: OrganisationType (format: -, nullable: false) + +Model: CreatePortfolioRequest + Required: [ 'name', 'description', 'organisationId', 'ownerId' ] + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + organisationId*: integer,string (format: int64, nullable: false) + ownerId*: string (format: uuid, nullable: false) + +Model: CreateProgrammeRequest + Required: [ 'portfolioId', 'name', 'ownerId' ] + portfolioId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + ownerId*: string (format: uuid, nullable: false) + +Model: CreateProjectRequest + Required: [ 'programmeId', 'name', 'description', 'sector', 'projectType' ] + programmeId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + sector*: IndustrySector (format: -, nullable: false) + projectType*: ProjectType (format: -, nullable: false) + +Model: CreateRiskRequest + Required: [ 'projectId', 'likelihood', 'impact', 'name', 'description' ] + projectId*: integer,string (format: int64, nullable: false) + likelihood*: RiskLikelihood (format: -, nullable: false) + impact*: RiskImpact (format: -, nullable: false) + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + +Model: Dashboard + Required: [ + 'portfolioValue', + 'activeAwards', + 'disbursed', + 'onTrackPercentage', + 'atRiskCount', + 'beneficiariesReached' +] + portfolioValue*: number,string (format: double, nullable: false) + activeAwards*: integer,string (format: int32, nullable: false) + disbursed*: number,string (format: double, nullable: false) + onTrackPercentage*: number,string (format: double, nullable: false) + atRiskCount*: integer,string (format: int32, nullable: false) + beneficiariesReached*: integer,string (format: int32, nullable: false) + +Model: Disbursement + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + milestoneId: null,integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + amount: number,string (format: double, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + +Model: Evidence + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + milestoneId: integer,string (format: int64, nullable: false) + indicatorId: integer,string (format: int64, nullable: false) + activityId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + version: integer,string (format: int32, nullable: false) + type: EvidenceType (format: -, nullable: false) + documentUrl: null,string (format: -, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + +Model: ExecuteContractRequest + Required: [ 'contractId', 'externalSignatureId', 'signedDocumentUrl' ] + contractId*: integer,string (format: int64, nullable: false) + externalSignatureId*: string (format: -, nullable: false) + signedDocumentUrl*: string (format: -, nullable: false) + +Model: Indicator + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + name: null,string (format: -, nullable: false) + unitOfMeasure: any (format: -, nullable: false) + baselineAmount: number,string (format: double, nullable: false) + targetAmount: number,string (format: double, nullable: false) + actualAmount: number,string (format: double, nullable: false) + +Model: Instrument + Required: [] + id: integer,string (format: int64, nullable: false) + programmeId: integer,string (format: int64, nullable: false) + code: null,string (format: -, nullable: false) + name: null,string (format: -, nullable: false) + description: null,string (format: -, nullable: false) + status: ActivityStatus (format: -, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + +Model: Invite + Required: [] + id: integer,string (format: int64, nullable: false) + organisationId: integer,string (format: int64, nullable: false) + invitedOrganisationId: integer,string (format: int64, nullable: false) + awardId: integer,string (format: int64, nullable: false) + contractId: integer,string (format: int64, nullable: false) + recipient: string (format: uuid, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + +Model: InviteRequest + Required: [ + 'organisationId', + 'invitedOrganisationId', + 'awardId', + 'contractId', + 'recipient', + 'message', + 'platform' +] + organisationId*: integer,string (format: int64, nullable: false) + invitedOrganisationId*: integer,string (format: int64, nullable: false) + awardId*: integer,string (format: int64, nullable: false) + contractId*: integer,string (format: int64, nullable: false) + recipient*: string (format: uuid, nullable: false) + message*: string (format: -, nullable: false) + platform*: NotificationPlatform (format: -, nullable: false) + +Model: Milestone + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + dueAt: string (format: date-time, nullable: false) + name: null,string (format: -, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + priority: Priority (format: -, nullable: false) + +Model: Organisation + Required: [] + id: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + registrationNo: null,string (format: -, nullable: false) + name: null,string (format: -, nullable: false) + email: null,string (format: -, nullable: false) + type: OrganisationType (format: -, nullable: false) + status: ActivityStatus (format: -, nullable: false) + +Model: Portfolio + Required: [] + id: integer,string (format: int64, nullable: false) + organisationId: integer,string (format: int64, nullable: false) + ownedBy: string (format: uuid, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + name: null,string (format: -, nullable: false) + description: null,string (format: -, nullable: false) + +Model: ProblemDetails + Required: [] + type: null,string (format: -, nullable: false) + title: null,string (format: -, nullable: false) + status: null,integer,string (format: int32, nullable: false) + detail: null,string (format: -, nullable: false) + instance: null,string (format: -, nullable: false) + +Model: Programme + Required: [] + id: integer,string (format: int64, nullable: false) + portfolioId: integer,string (format: int64, nullable: false) + ownedBy: string (format: uuid, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + name: null,string (format: -, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + +Model: Project + Required: [] + id: integer,string (format: int64, nullable: false) + programmeId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + updatedBy: null,string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + updatedAt: null,string (format: date-time, nullable: false) + startedAt: null,string (format: date-time, nullable: false) + endedAt: null,string (format: date-time, nullable: false) + sector: IndustrySector (format: -, nullable: false) + projectType: ProjectType (format: -, nullable: false) + name: null,string (format: -, nullable: false) + description: null,string (format: -, nullable: false) + status: ApprovalStatus (format: -, nullable: false) + +Model: Risk + Required: [] + id: integer,string (format: int64, nullable: false) + projectId: integer,string (format: int64, nullable: false) + createdBy: string (format: uuid, nullable: false) + createdAt: string (format: date-time, nullable: false) + likelihood: RiskLikelihood (format: -, nullable: false) + impact: RiskImpact (format: -, nullable: false) + name: null,string (format: -, nullable: false) + description: null,string (format: -, nullable: false) + +Model: UpdateContractRequest + Required: [ 'contractId', 'status' ] + contractId*: integer,string (format: int64, nullable: false) + status*: ContractStatus (format: -, nullable: false) + +Model: UpdateDisbursementRequest + Required: [ 'disbursementId', 'milestoneId', 'amount' ] + disbursementId*: integer,string (format: int64, nullable: false) + milestoneId*: null,integer,string (format: int64, nullable: false) + amount*: number,string (format: double, nullable: false) + +Model: UpdateEvidenceRequest + Required: [ 'evidenceId', 'version', 'evidenceType', 'status' ] + evidenceId*: integer,string (format: int64, nullable: false) + version*: integer,string (format: int32, nullable: false) + evidenceType*: EvidenceType (format: -, nullable: false) + status*: ApprovalStatus (format: -, nullable: false) + +Model: UpdateMilestoneRequest + Required: [ 'milestoneId', 'dueAt', 'name', 'priority', 'status' ] + milestoneId*: integer,string (format: int64, nullable: false) + dueAt*: string (format: date-time, nullable: false) + name*: string (format: -, nullable: false) + priority*: Priority (format: -, nullable: false) + status*: ApprovalStatus (format: -, nullable: false) + +Model: UpdateOrganisationRequest + Required: [ 'organisationId', 'name', 'registrationNo', 'email', 'type' ] + organisationId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + registrationNo*: string (format: -, nullable: false) + email*: string (format: -, nullable: false) + type*: OrganisationType (format: -, nullable: false) + +Model: UpdatePortfolioRequest + Required: [ 'portfolioId', 'name', 'description', 'ownerId' ] + portfolioId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + ownerId*: string (format: uuid, nullable: false) + +Model: UpdateProgrammeRequest + Required: [ 'programmeId', 'name', 'ownerId', 'status' ] + programmeId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + ownerId*: string (format: uuid, nullable: false) + status*: ApprovalStatus (format: -, nullable: false) + +Model: UpdateProjectRequest + Required: [ + 'projectId', + 'name', + 'description', + 'startedAt', + 'endedAt', + 'sector', + 'projectType' +] + projectId*: integer,string (format: int64, nullable: false) + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + startedAt*: null,string (format: date-time, nullable: false) + endedAt*: null,string (format: date-time, nullable: false) + sector*: IndustrySector (format: -, nullable: false) + projectType*: ProjectType (format: -, nullable: false) + +Model: UpdateProjectStatusRequest + Required: [ 'projectId', 'status' ] + projectId*: integer,string (format: int64, nullable: false) + status*: ApprovalStatus (format: -, nullable: false) + +Model: UpdateRiskRequest + Required: [ 'riskId', 'likelihood', 'impact', 'name', 'description' ] + riskId*: integer,string (format: int64, nullable: false) + likelihood*: RiskLikelihood (format: -, nullable: false) + impact*: RiskImpact (format: -, nullable: false) + name*: string (format: -, nullable: false) + description*: string (format: -, nullable: false) + +Model: User + Required: [] + id: string (format: uuid, nullable: false) + lastLoginAt: null,string (format: date-time, nullable: false) + email: null,string (format: -, nullable: false) + status: ActivityStatus (format: -, nullable: false) + +Model: UserFilterRequest + Required: [] + status: any (format: -, nullable: false) + email: null,string (format: -, nullable: false) + sortBy: null,string (format: -, nullable: false) + sortDescending: boolean (format: -, nullable: false) diff --git a/PostFundManagement.ConsoleUI/openapi_summary.txt b/PostFundManagement.ConsoleUI/openapi_summary.txt new file mode 100644 index 0000000..c5a262b --- /dev/null +++ b/PostFundManagement.ConsoleUI/openapi_summary.txt @@ -0,0 +1,919 @@ +=== COMPONENT SCHEMAS === +Activity, ActivityStatus, ApprovalStatus, Award, Budget, Contract, ContractStatus, CreateActivityRequest, CreateAwardRequest, CreateBudgetRequest, CreateContractRequest, CreateDisbursementRequest, CreateIndicatorRequest, CreateMilestoneRequest, CreateOrganisationRequest, CreatePortfolioRequest, CreateProgrammeRequest, CreateProjectRequest, CreateRiskRequest, Dashboard, Disbursement, Evidence, EvidenceType, ExecuteContractRequest, Indicator, IndustrySector, Instrument, Invite, InviteRequest, Milestone, NotificationPlatform, Organisation, OrganisationType, Portfolio, Priority, ProblemDetails, Programme, Project, ProjectType, Risk, RiskImpact, RiskLikelihood, UnitOfMeasure, UpdateContractRequest, UpdateDisbursementRequest, UpdateEvidenceRequest, UpdateMilestoneRequest, UpdateOrganisationRequest, UpdatePortfolioRequest, UpdateProgrammeRequest, UpdateProjectRequest, UpdateProjectStatusRequest, UpdateRiskRequest, User, UserFilterRequest + +=== DETAILED ENDPOINTS (EXCLUDING TOOLS) === + +------------------------------------------------------------ +GET /v1/api/users/{userId} [Tag: Users] +OperationId: getuser +Description: Gets the metadata of a single user. +Parameters: + - userId (in: path, required: true) schema: {"type":"string","format":"uuid"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/User"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/users/{page}/{pageSize} [Tag: Users] +OperationId: getusers +Description: Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UserFilterRequest"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/User"}} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/integration/sync [Tag: Users] +OperationId: syncuser +Description: Trigger integration and data synchronization in the background +Responses: + [200] (No content) + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/users/{userId}/status/{status} [Tag: Users] +OperationId: updateuserstatus +Description: Changes the Activity status of the user in the system +Parameters: + - userId (in: path, required: true) schema: {"type":"string","format":"uuid"} + - status (in: path, required: true) schema: {"$ref":"#/components/schemas/ActivityStatus"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/projects/activities [Tag: Projects] +OperationId: createactivity +Description: Add a new activity under project (M04) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateActivityRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/projects/programme [Tag: Projects] +OperationId: createprogramme +Description: Create programme +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateProgrammeRequest"} +Responses: + [200] (No content) + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/projects/programme [Tag: Projects] +OperationId: updateprogramme +Description: Update existing programme +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateProgrammeRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/projects [Tag: Projects] +OperationId: createproject +Description: Create a new project (M04) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateProjectRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects [Tag: Projects] +OperationId: getprojects +Description: Get a list of projects +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Project"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/projects [Tag: Projects] +OperationId: updateproject +Description: Update project details +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateProjectRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/{projectId}/activities [Tag: Projects] +OperationId: getactivities +Description: Get a list of activities for a project +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Activity"}} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/activities/{activityId} [Tag: Projects] +OperationId: getactivity +Description: Gets a specific activity by it's id +Parameters: + - activityId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Activity"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/milestones/{milestoneId} [Tag: Projects] +OperationId: getmilestone +Description: Gets a specific milestone +Parameters: + - milestoneId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Milestone"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/milestones/{page}/{pageSize} [Tag: Projects] +OperationId: getmilestones +Description: Get a list of all milestones +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Milestone"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/programme/{programmeId} [Tag: Projects] +OperationId: getprogramme +Description: Get specific programme +Parameters: + - programmeId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Programme"} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/{programmeId}/programme [Tag: Projects] +OperationId: getprogrammeprojects +Description: Get a list of all projects under a programme +Parameters: + - programmeId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Project"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/programme/{page}/{pageSize} [Tag: Projects] +OperationId: getprogrammesprojects +Description: Get a list of all programmes +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Programme"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/{projectId} [Tag: Projects] +OperationId: getproject +Description: Get project by ID +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Project"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/indicators/project/{projectId} [Tag: Projects] +OperationId: getprojectindicators +Description: Get get a detailed list of project indicators +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Indicator"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/{projectId}/milestones [Tag: Projects] +OperationId: getprojectmilestones +Description: Get a list of milestones for a project +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Milestone"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/projects/{projectId}/risks [Tag: Projects] +OperationId: getprojectrisks +Description: Get a list of risks for a project +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Risk"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/projects/milestone [Tag: Projects] +OperationId: updatemilestone +Description: Update project milestone details +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateMilestoneRequest"} +Responses: + [200] (No content) + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/projects/status [Tag: Projects] +OperationId: updateprojectstatus +Description: Update project status (M04) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateProjectStatusRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/grants/awards [Tag: Grants] +OperationId: createaward +Description: Register an approved funding award (M01) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateAwardRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/awards [Tag: Grants] +OperationId: getawards +Description: Get a list of awards +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":10} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Award"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/grants/budgets [Tag: Grants] +OperationId: createbudget +Description: Create a new budget line (M05) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateBudgetRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/budgets [Tag: Grants] +OperationId: getbudgets +Description: Get all budgets +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Budget"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/grants/contracts [Tag: Grants] +OperationId: createcontract +Description: Create a contract record from an approved award (M02) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateContractRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/contracts [Tag: Grants] +OperationId: getcontracts +Description: Get all contracts +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Contract"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/grants/contracts [Tag: Grants] +OperationId: updatecontract +Description: Updates a specific contract +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateContractRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/grants/contracts/execute [Tag: Grants] +OperationId: executecontract +Description: Execute the contract and lock the contractual version +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/ExecuteContractRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/awards/{awardId} [Tag: Grants] +OperationId: getaward +Description: Get award by ID +Parameters: + - awardId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Award"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/budgets/{budgetId} [Tag: Grants] +OperationId: getbudget +Description: Get budget by ID +Parameters: + - budgetId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Budget"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/contracts/{contractId} [Tag: Grants] +OperationId: getcontract +Description: Get contract by ID +Parameters: + - contractId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Contract"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/grants/contracts/{contractId} [Tag: Grants] +OperationId: uploadcontract +Description: Uploads a contract file securely +Parameters: + - contractId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] (No content) + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/grants/contracts/{contractId}/securelink [Tag: Grants] +OperationId: getcontracturl +Description: Gets a secure download URL, has expiry period (default 15min) +Parameters: + - contractId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"string"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/governance/organisations [Tag: Governance] +OperationId: createorganisation +Description: Create a new organization profile +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateOrganisationRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/organisations [Tag: Governance] +OperationId: getorganisations +Description: Get a paginated directory of organizations +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Organisation"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/governance/organisations [Tag: Governance] +OperationId: updateorganisation +Description: Update an existing organization profile +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateOrganisationRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/governance/organisations/portfolio [Tag: Governance] +OperationId: createportfolio +Description: Create a new organization portfolio +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreatePortfolioRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [404] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/governance/organisations/portfolio [Tag: Governance] +OperationId: updateportfolio +Description: Update an existing organization portfolio +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdatePortfolioRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/dashboard [Tag: Governance] +OperationId: dashboard +Description: Get executive portfolio dashboard metrics and KPIs +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Dashboard"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/instruments/{programmeId} [Tag: Governance] +OperationId: getinstruments +Description: Get all funding instruments +Parameters: + - programmeId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Instrument"}} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/organisations/{organisationId} [Tag: Governance] +OperationId: getorganisation +Description: Get organization profile by ID +Parameters: + - organisationId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Organisation"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/portfolios/{portfolioId} [Tag: Governance] +OperationId: getportfolio +Description: Get a specific portfolios +Parameters: + - portfolioId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Portfolio"}} + [401] (No content) + [403] (No content) + [404] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Portfolio"}} + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/portfolios/{portfolioId}/programmes [Tag: Governance] +OperationId: getportfolioprogrammes +Description: Get a programme's portfolios +Parameters: + - portfolioId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Programme"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/organisations/{organisationId}/portfolio [Tag: Governance] +OperationId: getportfolios +Description: Get all portfolios +Parameters: + - organisationId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Portfolio"}} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/governance/programmes/{portfolioId} [Tag: Governance] +OperationId: getprogrammes +Description: Get all programmes +Parameters: + - portfolioId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Programme"}} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/execution/disbursements/{disbursementId}/approve [Tag: Execution] +OperationId: approvedisbursement +Description: Approve disbursement and release payment (M06) +Parameters: + - disbursementId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/execution/disbursements [Tag: Execution] +OperationId: createdisbursement +Description: Create a new disbursement schedule (M06) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateDisbursementRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/execution/disbursements [Tag: Execution] +OperationId: getdisbursements +Description: Get a list of disbursements +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":10} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Disbursement"}} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/execution/disbursements [Tag: Execution] +OperationId: updatedisbursement +Description: Update the details of a specific disbursement +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateDisbursementRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/execution/risks [Tag: Execution] +OperationId: createrisk +Description: Add risk to risk register (M09) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateRiskRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/execution/risks [Tag: Execution] +OperationId: getrisks +Description: Get a list of all risks +Parameters: + - page (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":1} + - pageSize (in: query, required: false) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":["integer","string"],"format":"int32","default":100} +Responses: + [200] (No content) + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/execution/risks [Tag: Execution] +OperationId: updaterisk +Description: Update the details of a specific risk +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateRiskRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/execution/disbursements/{disbursementId} [Tag: Execution] +OperationId: getdisbursement +Description: Get a specific disbursement +Parameters: + - disbursementId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Disbursement"} + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/execution/risks/{riskId} [Tag: Execution] +OperationId: getrisk +Description: Get a specific risk +Parameters: + - riskId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Risk"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/evidence/file [Tag: Evidence & M&E] +OperationId: createevidence +Description: Upload supporting evidence file to S3 and register metadata (M14) +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/evidence/indicators [Tag: Evidence & M&E] +OperationId: createindicator +Description: Define indicators and baselines/targets (M08) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateIndicatorRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/evidence/milestones [Tag: Evidence & M&E] +OperationId: createmilestonerequest +Description: Define contractual milestones (M07) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/CreateMilestoneRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/{evidenceId} [Tag: Evidence & M&E] +OperationId: getevidence +Description: Get a specific evidence record and assiciated file URLs +Parameters: + - evidenceId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Evidence"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/files/{page}/{pageSize} [Tag: Evidence & M&E] +OperationId: getevidencefiles +Description: Get a list of all uploaded evidence files metadata +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Evidence"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/{evidenceId}/securelink [Tag: Grants] +OperationId: getevidenceurl +Description: Gets a secure download URL of the evidence file, URL expires (default: 15min) +Parameters: + - evidenceId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"string"} + [401] (No content) + [403] (No content) + [404] application/json; version=1 -> {"type":"string"} + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/indicators/{indicatorId} [Tag: Evidence & M&E] +OperationId: getindicator +Description: Get a specific indicator. +Parameters: + - indicatorId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Indicator"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/indicators/{page}/{pageSize} [Tag: Evidence & M&E] +OperationId: getindicators +Description: Get a list of indicators +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Indicator"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/milestone/{milestoneId} [Tag: Evidence & M&E] +OperationId: getmilestoneevidence +Description: Get a list of all uploaded evidence metadata for a specific mliestone +Parameters: + - milestoneId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Evidence"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/evidence/{projectId} [Tag: Evidence & M&E] +OperationId: getprojectevidence +Description: Get a list of all uploaded evidence metadata for a specific project +Parameters: + - projectId (in: path, required: true) schema: {"type":"string"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Evidence"}} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/evidence/indicators/{indicatorId}/actuals/{actualAmount} [Tag: Evidence & M&E] +OperationId: recordindicatoramount +Description: Record actual values for indicators (M08) +Parameters: + - indicatorId (in: path, required: true) schema: {"type":"string"} + - actualAmount (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$","type":"number","format":"double"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +PUT /v1/api/evidence [Tag: Evidence & M&E] +OperationId: updateevidence +Description: Updates a specific evidence record metadata +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/UpdateEvidenceRequest"} +Responses: + [200] (No content) + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [403] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/communications/invites/applicant/{applicantUserId} [Tag: Communications] +OperationId: getapplicantinvite +Description: Gets an applicant's invite +Parameters: + - applicantUserId (in: path, required: true) schema: {"type":"string","format":"uuid"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Invite"} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/communications/invites/{applicantUserId} [Tag: Communications] +OperationId: getapplicantinvites +Description: Gets an applicant's invite list +Parameters: + - applicantUserId (in: path, required: true) schema: {"type":"string","format":"uuid"} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Invite"}} + [401] (No content) + [404] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +GET /v1/api/communications/invites/{page}/{pageSize} [Tag: Communications] +OperationId: getinvites +Description: Get a list of sent invitations +Parameters: + - page (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":1} + - pageSize (in: path, required: true) schema: {"pattern":"^-?(?:0|[1-9]\\d*)$","type":"integer","format":"int32","default":100} +Responses: + [200] application/json; version=1 -> {"type":"array","items":{"$ref":"#/components/schemas/Invite"}} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + +------------------------------------------------------------ +POST /v1/api/communications/invite [Tag: Communications] +OperationId: sendinvite +Description: Trigger an invite to an organization or candidate (specifying NotificationPlatform) +Request Body: required=true + Content-Type: application/json; version=1 -> Schema: {"$ref":"#/components/schemas/InviteRequest"} +Responses: + [200] application/json; version=1 -> {"$ref":"#/components/schemas/Invite"} + [400] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} + [401] (No content) + [500] application/json; version=1 -> {"$ref":"#/components/schemas/ProblemDetails"} diff --git a/PostFundManagement.ConsoleUI/package-lock.json b/PostFundManagement.ConsoleUI/package-lock.json new file mode 100644 index 0000000..c6aa72f --- /dev/null +++ b/PostFundManagement.ConsoleUI/package-lock.json @@ -0,0 +1,2882 @@ +{ + "name": "react-example", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "react-example", + "version": "0.0.0", + "dependencies": { + "@google/genai": "^2.4.0", + "@hookform/resolvers": "^5.9.1", + "@tailwindcss/vite": "^4.1.14", + "@tanstack/react-query": "^5.102.8", + "@vitejs/plugin-basic-ssl": "^2.3.0", + "@vitejs/plugin-react": "^5.0.4", + "axios": "^1.20.0", + "dotenv": "^17.2.3", + "express": "^4.21.2", + "hashids": "^2.3.0", + "lucide-react": "^0.546.0", + "motion": "^12.23.24", + "oidc-client-ts": "^3.5.0", + "react": "^19.0.1", + "react-dom": "^19.0.1", + "react-hook-form": "^7.87.0", + "react-router-dom": "^7.18.3", + "tailwindcss": "^4.1.14", + "vite": "^6.2.3", + "zod": "^3.25.76" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.14.0", + "autoprefixer": "^10.4.21", + "esbuild": "^0.25.0", + "tsx": "^4.21.0", + "typescript": "~5.8.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "2.21.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.9.1", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "@sinclair/typebox": ">=0.25.24", + "@standard-schema/spec": "^1.0.0", + "@typeschema/main": ">=0.13.7", + "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", + "ajv": "^8.12.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "arktype": "^2.0.0", + "ata-validator": "^1.2.0", + "class-transformer": ">=0.4.0", + "class-validator": ">=0.12.0", + "computed-types": "^1.0.0", + "effect": "^3.10.3", + "fluentvalidation-ts": "^3.0.0", + "fp-ts": "^2.7.0", + "io-ts": "^2.0.0", + "joi": "^17.0.0 || ^18.0.0", + "nope-validator": ">=0.12.0", + "react-hook-form": "^7.55.0", + "superstruct": ">=0.12.0", + "typanion": "^3.3.2", + "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", + "vest": ">=6.0.0", + "yup": "^1.0.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@sinclair/typebox": { + "optional": true + }, + "@standard-schema/spec": { + "optional": true + }, + "@typeschema/main": { + "optional": true + }, + "@vinejs/vine": { + "optional": true + }, + "ajv": { + "optional": true + }, + "ajv-errors": { + "optional": true + }, + "ajv-formats": { + "optional": true + }, + "arktype": { + "optional": true + }, + "ata-validator": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "computed-types": { + "optional": true + }, + "effect": { + "optional": true + }, + "fluentvalidation-ts": { + "optional": true + }, + "fp-ts": { + "optional": true + }, + "io-ts": { + "optional": true + }, + "joi": { + "optional": true + }, + "nope-validator": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "typanion": { + "optional": true + }, + "valibot": { + "optional": true + }, + "vest": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "license": "MIT" + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.1", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.1", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.102.8", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.102.8", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.102.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/body-parser/node_modules/@types/node": { + "version": "26.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/body-parser/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect/node_modules/@types/node": { + "version": "26.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/connect/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.25", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express-serve-static-core/node_modules/@types/node": { + "version": "26.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/express-serve-static-core/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/send/node_modules/@types/node": { + "version": "26.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/send/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/node": { + "version": "26.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/serve-static/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.3.0", + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.5.5", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.9", + "caniuse-lite": "^1.0.30001810", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.20.0", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.21", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "1.20.6", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.422", + "license": "ISC" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.43.0", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.43.0", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.3.1", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent/node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/google-auth-library": { + "version": "10.9.1", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hashids": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/hasown": { + "version": "2.0.4", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/long": { + "version": "5.3.2", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.546.0", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/motion": { + "version": "12.43.0", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.43.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.43.0", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.54", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oidc-client-ts": { + "version": "3.5.0", + "license": "Apache-2.0", + "dependencies": { + "jwt-decode": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.6.6", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs/node_modules/@types/node": { + "version": "26.4.1", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/protobufjs/node_modules/@types/node/node_modules/undici-types": { + "version": "8.3.0", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "19.2.8", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-hook-form": { + "version": "7.87.0", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.18.3", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.3", + "license": "MIT", + "dependencies": { + "react-router": "7.18.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rollup": { + "version": "4.63.1", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.23.13", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/tsx/node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.2", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.21.3", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/PostFundManagement.ConsoleUI/package.json b/PostFundManagement.ConsoleUI/package.json index bf986c9..000793a 100644 --- a/PostFundManagement.ConsoleUI/package.json +++ b/PostFundManagement.ConsoleUI/package.json @@ -5,6 +5,8 @@ "type": "module", "scripts": { "dev": "vite --port=3000 --host=0.0.0.0", + "dev:https": "HTTPS=true vite --port=3000 --host=0.0.0.0", + "dev:http": "HTTPS=false vite --port=3000 --host=0.0.0.0", "build": "vite build", "preview": "vite preview", "clean": "rm -rf dist server.js", @@ -15,6 +17,7 @@ "@hookform/resolvers": "^5.9.1", "@tailwindcss/vite": "^4.1.14", "@tanstack/react-query": "^5.102.8", + "@vitejs/plugin-basic-ssl": "^2.3.0", "@vitejs/plugin-react": "^5.0.4", "axios": "^1.20.0", "dotenv": "^17.2.3", @@ -29,14 +32,14 @@ "react-router-dom": "^7.18.3", "tailwindcss": "^4.1.14", "vite": "^6.2.3", - "zod": "^4.5.4" + "zod": "^3.25.76" }, "devDependencies": { + "@types/express": "^4.17.21", "@types/node": "^22.14.0", "autoprefixer": "^10.4.21", "esbuild": "^0.25.0", "tsx": "^4.21.0", - "typescript": "~5.8.2", - "@types/express": "^4.17.21" + "typescript": "~5.8.2" } } diff --git a/PostFundManagement.ConsoleUI/schemas_summary.txt b/PostFundManagement.ConsoleUI/schemas_summary.txt new file mode 100644 index 0000000..4f4f073 --- /dev/null +++ b/PostFundManagement.ConsoleUI/schemas_summary.txt @@ -0,0 +1,469 @@ + +=== SCHEMA: Activity === +Required: [] + - id: integer,string (format: int64) + - projectId: null,integer,string (format: int64) + - milestoneId: null,integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - approvedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - category: null,string (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + - amount: number,string (format: double) + - title: null,string (format: none) + - description: null,string (format: none) + - performedAt: null,string (format: date-time) + +=== SCHEMA: ActivityStatus === +Other: { type: 'integer' } + +=== SCHEMA: ApprovalStatus === +Other: { type: 'integer' } + +=== SCHEMA: Award === +Required: [] + - id: integer,string (format: int64) + - organisationId: integer,string (format: int64) + - programmeId: integer,string (format: int64) + - projectId: null,integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - approvedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - status: #/components/schemas/ApprovalStatus (format: none) + - amount: number,string (format: double) + +=== SCHEMA: Budget === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - createdBy: string (format: uuid) + - approvedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - name: null,string (format: none) + - amount: number,string (format: double) + +=== SCHEMA: Contract === +Required: [] + - id: integer,string (format: int64) + - awardId: integer,string (format: int64) + - externalSignatureId: null,string (format: none) + - signedDocumentUrl: null,string (format: none) + - effectiveAt: null,string (format: date-time) + - expiresAt: null,string (format: date-time) + - totalValue: number,string (format: double) + - status: #/components/schemas/ContractStatus (format: none) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + +=== SCHEMA: ContractStatus === +Other: { type: 'integer' } + +=== SCHEMA: CreateActivityRequest === +Required: [ + 'projectId', + 'milestoneId', + 'category', + 'amount', + 'title', + 'description', + 'performedAt' +] + - projectId: integer,string (format: int64) + - milestoneId: null,integer,string (format: int64) + - category: string (format: none) + - amount: number,string (format: double) + - title: string (format: none) + - description: string (format: none) + - performedAt: null,string (format: date-time) + +=== SCHEMA: CreateAwardRequest === +Required: [ 'organisationId', 'programmeId', 'amount' ] + - organisationId: integer,string (format: int64) + - programmeId: integer,string (format: int64) + - amount: number,string (format: double) + +=== SCHEMA: CreateBudgetRequest === +Required: [ 'projectId', 'name', 'amount' ] + - projectId: integer,string (format: int64) + - name: string (format: none) + - amount: number,string (format: double) + +=== SCHEMA: CreateContractRequest === +Required: [ 'awardId', 'totalValue', 'effectiveAt', 'expiresAt' ] + - awardId: integer,string (format: int64) + - totalValue: number,string (format: double) + - effectiveAt: string (format: date-time) + - expiresAt: string (format: date-time) + +=== SCHEMA: CreateDisbursementRequest === +Required: [ 'projectId', 'milestoneId', 'amount' ] + - projectId: integer,string (format: int64) + - milestoneId: null,integer,string (format: int64) + - amount: number,string (format: double) + +=== SCHEMA: CreateIndicatorRequest === +Required: [ + 'projectId', + 'name', + 'unitOfMeasure', + 'baselineAmount', + 'targetAmount' +] + - projectId: integer,string (format: int64) + - name: string (format: none) + - unitOfMeasure: #/components/schemas/UnitOfMeasure (format: none) + - baselineAmount: number,string (format: double) + - targetAmount: number,string (format: double) + +=== SCHEMA: CreateMilestoneRequest === +Required: [ 'projectId', 'dueAt', 'name', 'priority' ] + - projectId: integer,string (format: int64) + - dueAt: string (format: date-time) + - name: string (format: none) + - priority: #/components/schemas/Priority (format: none) + +=== SCHEMA: CreateOrganisationRequest === +Required: [ 'name', 'registrationNo', 'email' ] + - name: string (format: none) + - registrationNo: string (format: none) + - email: string (format: none) + - type: #/components/schemas/OrganisationType (format: none) + +=== SCHEMA: CreatePortfolioRequest === +Required: [ 'name', 'description', 'organisationId', 'ownerId' ] + - name: string (format: none) + - description: string (format: none) + - organisationId: integer,string (format: int64) + - ownerId: string (format: uuid) + +=== SCHEMA: CreateProgrammeRequest === +Required: [ 'portfolioId', 'name', 'ownerId' ] + - portfolioId: integer,string (format: int64) + - name: string (format: none) + - ownerId: string (format: uuid) + +=== SCHEMA: CreateProjectRequest === +Required: [ 'programmeId', 'name', 'description', 'sector', 'projectType' ] + - programmeId: integer,string (format: int64) + - name: string (format: none) + - description: string (format: none) + - sector: #/components/schemas/IndustrySector (format: none) + - projectType: #/components/schemas/ProjectType (format: none) + +=== SCHEMA: CreateRiskRequest === +Required: [ 'projectId', 'likelihood', 'impact', 'name', 'description' ] + - projectId: integer,string (format: int64) + - likelihood: #/components/schemas/RiskLikelihood (format: none) + - impact: #/components/schemas/RiskImpact (format: none) + - name: string (format: none) + - description: string (format: none) + +=== SCHEMA: Dashboard === +Required: [ + 'portfolioValue', + 'activeAwards', + 'disbursed', + 'onTrackPercentage', + 'atRiskCount', + 'beneficiariesReached' +] + - portfolioValue: number,string (format: double) + - activeAwards: integer,string (format: int32) + - disbursed: number,string (format: double) + - onTrackPercentage: number,string (format: double) + - atRiskCount: integer,string (format: int32) + - beneficiariesReached: integer,string (format: int32) + +=== SCHEMA: Disbursement === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - milestoneId: null,integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - amount: number,string (format: double) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: Evidence === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - milestoneId: integer,string (format: int64) + - indicatorId: integer,string (format: int64) + - activityId: integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - version: integer,string (format: int32) + - type: #/components/schemas/EvidenceType (format: none) + - documentUrl: null,string (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: EvidenceType === +Other: { type: 'integer' } + +=== SCHEMA: ExecuteContractRequest === +Required: [ 'contractId', 'externalSignatureId', 'signedDocumentUrl' ] + - contractId: integer,string (format: int64) + - externalSignatureId: string (format: none) + - signedDocumentUrl: string (format: none) + +=== SCHEMA: Indicator === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + - name: null,string (format: none) + - unitOfMeasure: any (format: none) + - baselineAmount: number,string (format: double) + - targetAmount: number,string (format: double) + - actualAmount: number,string (format: double) + +=== SCHEMA: IndustrySector === +Other: { type: 'integer' } + +=== SCHEMA: Instrument === +Required: [] + - id: integer,string (format: int64) + - programmeId: integer,string (format: int64) + - code: null,string (format: none) + - name: null,string (format: none) + - description: null,string (format: none) + - status: #/components/schemas/ActivityStatus (format: none) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + +=== SCHEMA: Invite === +Required: [] + - id: integer,string (format: int64) + - organisationId: integer,string (format: int64) + - invitedOrganisationId: integer,string (format: int64) + - awardId: integer,string (format: int64) + - contractId: integer,string (format: int64) + - recipient: string (format: uuid) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + +=== SCHEMA: InviteRequest === +Required: [ + 'organisationId', + 'invitedOrganisationId', + 'awardId', + 'contractId', + 'recipient', + 'message', + 'platform' +] + - organisationId: integer,string (format: int64) + - invitedOrganisationId: integer,string (format: int64) + - awardId: integer,string (format: int64) + - contractId: integer,string (format: int64) + - recipient: string (format: uuid) + - message: string (format: none) + - platform: #/components/schemas/NotificationPlatform (format: none) + +=== SCHEMA: Milestone === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - dueAt: string (format: date-time) + - name: null,string (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + - priority: #/components/schemas/Priority (format: none) + +=== SCHEMA: NotificationPlatform === +Other: { type: 'integer' } + +=== SCHEMA: Organisation === +Required: [] + - id: integer,string (format: int64) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + - registrationNo: null,string (format: none) + - name: null,string (format: none) + - email: null,string (format: none) + - type: #/components/schemas/OrganisationType (format: none) + - status: #/components/schemas/ActivityStatus (format: none) + +=== SCHEMA: OrganisationType === +Other: { type: 'integer', default: 5 } + +=== SCHEMA: Portfolio === +Required: [] + - id: integer,string (format: int64) + - organisationId: integer,string (format: int64) + - ownedBy: string (format: uuid) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + - name: null,string (format: none) + - description: null,string (format: none) + +=== SCHEMA: Priority === +Other: { type: 'integer' } + +=== SCHEMA: ProblemDetails === +Required: [] + - type: null,string (format: none) + - title: null,string (format: none) + - status: null,integer,string (format: int32) + - detail: null,string (format: none) + - instance: null,string (format: none) + +=== SCHEMA: Programme === +Required: [] + - id: integer,string (format: int64) + - portfolioId: integer,string (format: int64) + - ownedBy: string (format: uuid) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - name: null,string (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: Project === +Required: [] + - id: integer,string (format: int64) + - programmeId: integer,string (format: int64) + - createdBy: string (format: uuid) + - updatedBy: null,string (format: uuid) + - createdAt: string (format: date-time) + - updatedAt: null,string (format: date-time) + - startedAt: null,string (format: date-time) + - endedAt: null,string (format: date-time) + - sector: #/components/schemas/IndustrySector (format: none) + - projectType: #/components/schemas/ProjectType (format: none) + - name: null,string (format: none) + - description: null,string (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: ProjectType === +Other: { type: 'integer' } + +=== SCHEMA: Risk === +Required: [] + - id: integer,string (format: int64) + - projectId: integer,string (format: int64) + - createdBy: string (format: uuid) + - createdAt: string (format: date-time) + - likelihood: #/components/schemas/RiskLikelihood (format: none) + - impact: #/components/schemas/RiskImpact (format: none) + - name: null,string (format: none) + - description: null,string (format: none) + +=== SCHEMA: RiskImpact === +Other: { type: 'integer' } + +=== SCHEMA: RiskLikelihood === +Other: { type: 'integer' } + +=== SCHEMA: UnitOfMeasure === +Other: { type: 'integer' } + +=== SCHEMA: UpdateContractRequest === +Required: [ 'contractId', 'status' ] + - contractId: integer,string (format: int64) + - status: #/components/schemas/ContractStatus (format: none) + +=== SCHEMA: UpdateDisbursementRequest === +Required: [ 'disbursementId', 'milestoneId', 'amount' ] + - disbursementId: integer,string (format: int64) + - milestoneId: null,integer,string (format: int64) + - amount: number,string (format: double) + +=== SCHEMA: UpdateEvidenceRequest === +Required: [ 'evidenceId', 'version', 'evidenceType', 'status' ] + - evidenceId: integer,string (format: int64) + - version: integer,string (format: int32) + - evidenceType: #/components/schemas/EvidenceType (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: UpdateMilestoneRequest === +Required: [ 'milestoneId', 'dueAt', 'name', 'priority', 'status' ] + - milestoneId: integer,string (format: int64) + - dueAt: string (format: date-time) + - name: string (format: none) + - priority: #/components/schemas/Priority (format: none) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: UpdateOrganisationRequest === +Required: [ 'organisationId', 'name', 'registrationNo', 'email', 'type' ] + - organisationId: integer,string (format: int64) + - name: string (format: none) + - registrationNo: string (format: none) + - email: string (format: none) + - type: #/components/schemas/OrganisationType (format: none) + +=== SCHEMA: UpdatePortfolioRequest === +Required: [ 'portfolioId', 'name', 'description', 'ownerId' ] + - portfolioId: integer,string (format: int64) + - name: string (format: none) + - description: string (format: none) + - ownerId: string (format: uuid) + +=== SCHEMA: UpdateProgrammeRequest === +Required: [ 'programmeId', 'name', 'ownerId', 'status' ] + - programmeId: integer,string (format: int64) + - name: string (format: none) + - ownerId: string (format: uuid) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: UpdateProjectRequest === +Required: [ + 'projectId', + 'name', + 'description', + 'startedAt', + 'endedAt', + 'sector', + 'projectType' +] + - projectId: integer,string (format: int64) + - name: string (format: none) + - description: string (format: none) + - startedAt: null,string (format: date-time) + - endedAt: null,string (format: date-time) + - sector: #/components/schemas/IndustrySector (format: none) + - projectType: #/components/schemas/ProjectType (format: none) + +=== SCHEMA: UpdateProjectStatusRequest === +Required: [ 'projectId', 'status' ] + - projectId: integer,string (format: int64) + - status: #/components/schemas/ApprovalStatus (format: none) + +=== SCHEMA: UpdateRiskRequest === +Required: [ 'riskId', 'likelihood', 'impact', 'name', 'description' ] + - riskId: integer,string (format: int64) + - likelihood: #/components/schemas/RiskLikelihood (format: none) + - impact: #/components/schemas/RiskImpact (format: none) + - name: string (format: none) + - description: string (format: none) + +=== SCHEMA: User === +Required: [] + - id: string (format: uuid) + - lastLoginAt: null,string (format: date-time) + - email: null,string (format: none) + - status: #/components/schemas/ActivityStatus (format: none) + +=== SCHEMA: UserFilterRequest === +Required: [] + - status: any (format: none) + - email: null,string (format: none) + - sortBy: null,string (format: none) + - sortDescending: boolean (format: none) diff --git a/PostFundManagement.ConsoleUI/src/App.tsx b/PostFundManagement.ConsoleUI/src/App.tsx index 0189855..084a573 100644 --- a/PostFundManagement.ConsoleUI/src/App.tsx +++ b/PostFundManagement.ConsoleUI/src/App.tsx @@ -1,17 +1,27 @@ -import React from 'react'; +import React, { Suspense, lazy } from 'react'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { QueryClientProvider } from '@tanstack/react-query'; import { queryClient } from './lib/queryClient'; import { AuthProvider, ProtectedRoute } from './auth/AuthProvider'; import { ToastProvider } from './components/ui/Toast'; import { AppLayout } from './components/layout/AppLayout'; -import { LandingPage } from './pages/LandingPage'; -import { ExecutiveDashboard } from './pages/ExecutiveDashboard'; -import { GovernancePage } from './pages/GovernancePage'; -import { ProjectsPage } from './pages/ProjectsPage'; -import { GrantsPage } from './pages/GrantsPage'; -import { MonitoringPage } from './pages/MonitoringPage'; -import { SettingsPage } from './pages/SettingsPage'; + +const LandingPage = lazy(() => import('./pages/LandingPage').then(m => ({ default: m.LandingPage }))); +const ExecutiveDashboard = lazy(() => import('./pages/ExecutiveDashboard').then(m => ({ default: m.ExecutiveDashboard }))); +const GovernancePage = lazy(() => import('./pages/GovernancePage').then(m => ({ default: m.GovernancePage }))); +const ProjectsPage = lazy(() => import('./pages/ProjectsPage').then(m => ({ default: m.ProjectsPage }))); +const GrantsPage = lazy(() => import('./pages/GrantsPage').then(m => ({ default: m.GrantsPage }))); +const MonitoringPage = lazy(() => import('./pages/MonitoringPage').then(m => ({ default: m.MonitoringPage }))); +const SettingsPage = lazy(() => import('./pages/SettingsPage').then(m => ({ default: m.SettingsPage }))); + +const RouteLoading = () => ( +
+
+
+ Loading module... +
+
+); export default function App() { return ( @@ -19,30 +29,32 @@ export default function App() { - - {/* Public OIDC Login / Landing Page */} - } /> - } /> + }> + + {/* Public OIDC Login / Landing Page */} + } /> + } /> - {/* Protected Backoffice Application Shell */} - - - - } - > - } /> - } /> - } /> - } /> - } /> - } /> - + {/* Protected Backoffice Application Shell */} + + + + } + > + } /> + } /> + } /> + } /> + } /> + } /> + - {/* Fallback Redirect */} - } /> - + {/* Fallback Redirect */} + } /> + + diff --git a/PostFundManagement.ConsoleUI/src/auth/AuthProvider.tsx b/PostFundManagement.ConsoleUI/src/auth/AuthProvider.tsx index c833437..6be0a35 100644 --- a/PostFundManagement.ConsoleUI/src/auth/AuthProvider.tsx +++ b/PostFundManagement.ConsoleUI/src/auth/AuthProvider.tsx @@ -131,7 +131,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => // Subscribe to global HTTP 401 session expiration events useEffect(() => { const unsubscribe = onSessionExpired((reason) => { - // Subordination: If account activation lockout is active, session expiration is completely avoided and bypassed + // If account activation is pending, suppress session expiration modal if (isPendingActivation || isActivationPending()) { return; } @@ -148,8 +148,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => }; }, [accessToken, isPendingActivation]); - // Priority Subordination: When activation process is active, dismiss all other modals, - // cancel session expiration and avoid its processes so that there is no overlapping. + // When account activation is pending, ensure secondary dialogs are closed useEffect(() => { if (isPendingActivation) { setIsSessionExpired(false); @@ -159,7 +158,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => } }, [isPendingActivation]); - // When session expiration modal is displayed, dismiss all other subordinates + // When session expiration modal is displayed, dismiss background modals useEffect(() => { if (isSessionExpired) { dismissSubordinateModals(); @@ -221,10 +220,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => // Clean up code and state from URL window.history.replaceState({}, document.title, window.location.pathname); - // Execute user sync endpoint upon login. - // If HTTP 401 -> routes to landing page - // If non-401 failure -> displays non-dismissable activation modal - // If success -> saved in browser to prevent endpoint hotspot + // Synchronize authenticated user profile with backend directory try { const syncResult = await syncUserAccount(userSub, token, profile, true); if (syncResult.isUnauthorized) { diff --git a/PostFundManagement.ConsoleUI/src/components/auth/AccountActivationModal.tsx b/PostFundManagement.ConsoleUI/src/components/auth/AccountActivationModal.tsx index fbdf998..36add5a 100644 --- a/PostFundManagement.ConsoleUI/src/components/auth/AccountActivationModal.tsx +++ b/PostFundManagement.ConsoleUI/src/components/auth/AccountActivationModal.tsx @@ -38,8 +38,7 @@ export const AccountActivationModal: React.FC = ({ const [errorMessage, setErrorMessage] = useState(initialError || null); const [syncAttemptCount, setSyncAttemptCount] = useState(0); - // Activation process dismisses all other modals so that it is displayed without overlapping. - // By extension, any active session expiration modal is dismissed and its processes cancelled. + // Clear any existing session prompt and close background dialogs useEffect(() => { cancelSessionExpiration(); dismissSubordinateModals(); diff --git a/PostFundManagement.ConsoleUI/src/components/auth/SessionExpiredModal.tsx b/PostFundManagement.ConsoleUI/src/components/auth/SessionExpiredModal.tsx index a2711c4..7523ca8 100644 --- a/PostFundManagement.ConsoleUI/src/components/auth/SessionExpiredModal.tsx +++ b/PostFundManagement.ConsoleUI/src/components/auth/SessionExpiredModal.tsx @@ -21,14 +21,14 @@ export const SessionExpiredModal: React.FC = ({ const totalDurationMs = countdownSeconds * 1000; const isClearingRef = useRef(false); - // When session expiration modal is displayed, dismiss all subordinate modals + // Close background modals when session expires useEffect(() => { if (isOpen) { dismissSubordinateModals(); } }, [isOpen]); - // Listen for cancellation event (e.g., when activation lockout takes priority) + // Reset state if session expiration is cancelled useEffect(() => { const handleCancelled = () => { setTimeLeftMs(countdownSeconds * 1000); diff --git a/PostFundManagement.ConsoleUI/src/components/layout/AppLayout.tsx b/PostFundManagement.ConsoleUI/src/components/layout/AppLayout.tsx index b7a0da8..32afa5a 100644 --- a/PostFundManagement.ConsoleUI/src/components/layout/AppLayout.tsx +++ b/PostFundManagement.ConsoleUI/src/components/layout/AppLayout.tsx @@ -18,6 +18,7 @@ import { } from 'lucide-react'; import { useAuth } from '../../auth/AuthProvider'; import { BrandLogo } from '../ui/BrandLogo'; +import { HelpTooltip } from '../ui/HelpTooltip'; import { useQuery } from '@tanstack/react-query'; import { getUsers } from '../../services/api'; import { getUserEmail, registerUserEmail } from '../../lib/formatters'; @@ -27,31 +28,37 @@ const navItems = [ name: 'Executive Dashboard', path: '/dashboard', icon: LayoutDashboard, + help: 'Strategic macro metrics, portfolio value, disbursed funding, ISO 31000 risk heatmap, and projects requiring immediate intervention.', }, { name: 'Governance & Structure', path: '/governance', icon: Building2, + help: 'Master records for legal entities, holding structures, investment portfolios, and thematic programmes funded under statutory mandates.', }, { name: 'Projects & Milestones', path: '/projects', icon: FolderKanban, + help: 'Project Work Breakdown Structure (WBS), contractual deliverables, scheduled milestones, evidence verification, and execution tracking.', }, { name: 'Grant & Financials', path: '/grants', icon: Coins, + help: 'Financial management including statutory awards, multi-year budget line allocations, tranches, and scheduled payment disbursements.', }, { name: 'M&E, Evidence & Risks', path: '/monitoring', icon: ShieldAlert, + help: 'Monitoring & Evaluation (M&E) framework, indicator tracking, ISO 31000 risk registers, mitigation plans, and compliance audits.', }, { name: 'Settings & Communications', path: '/settings', icon: Settings, + help: 'User account provisioning, statutory sync, tenant environment parameters, and communication invite templates.', }, ]; @@ -109,7 +116,7 @@ export const AppLayout: React.FC = () => { sidebarOpen ? 'w-64' : 'w-20' } shrink-0 bg-[#0b101b]/95 text-slate-200 border-r border-slate-800/80 transition-all duration-300 flex flex-col z-30 shadow-2xl backdrop-blur-md`} > - {/* Brand Header — Clickable link navigating to auth-aware Home page */} + {/* Brand Header */}
{ {navItems.map((item) => { const Icon = item.icon; return ( - - `flex items-center gap-3 px-3.5 py-2.5 rounded-xl text-xs font-medium transition-all ${ - isActive - ? 'bg-gradient-to-r from-blue-600 to-indigo-600 text-white shadow-md shadow-blue-600/25 font-semibold' - : 'text-slate-400 hover:text-slate-100 hover:bg-slate-800/60' - }` - } - > - - {sidebarOpen && {item.name}} - +
+ + `flex items-center gap-3 px-3.5 py-2.5 rounded-xl text-xs font-medium transition-all flex-1 min-w-0 ${ + isActive + ? 'bg-gradient-to-r from-blue-600 to-indigo-600 text-white shadow-md shadow-blue-600/25 font-semibold' + : 'text-slate-400 hover:text-slate-100 hover:bg-slate-800/60' + }` + } + > + + {sidebarOpen && {item.name}} + + {sidebarOpen && ( +
+ +
+ )} +
); })} - {/* User Card with Token-derived info & Identity Server Logout */} + {/* User Card */}
@@ -192,7 +210,7 @@ export const AppLayout: React.FC = () => { {/* Top Header */}
- {/* Top Bar Brand Icon & Direct Portal Link */} + {/* Brand Icon */} { - {/* Clickable Home Link */} + {/* Home Navigation */} { {tenantName} +
- - - Authenticated Session - +
+ + + Authenticated Session + + +
- {/* User Name & Email derived from token, plus Identity Server signout */} + {/* User Profile and Controls */}

@@ -292,7 +324,7 @@ export const AppLayout: React.FC = () => {

- {/* Viewport with Landing Page ambient dot grid and glow */} + {/* Main Content Area */}
void; + isCreateModalOpenExternal?: boolean; + onCloseCreateModalExternal?: () => void; +} + +export const ContractsManagement: React.FC = ({ + isCreateModalOpenExternal, + onCloseCreateModalExternal, +}) => { + const queryClient = useQueryClient(); + const { success, error: toastError, info } = useToast(); + + // Component state + const [internalCreateModalOpen, setInternalCreateModalOpen] = useState(false); + const isCreateModalOpen = isCreateModalOpenExternal ?? internalCreateModalOpen; + const setCreateModalOpen = (open: boolean) => { + if (onCloseCreateModalExternal && !open) { + onCloseCreateModalExternal(); + } + setInternalCreateModalOpen(open); + }; + + const [statusFilter, setStatusFilter] = useState('all'); + const [searchTerm, setSearchTerm] = useState(''); + + // Selected contract for status update + const [contractToUpdate, setContractToUpdate] = useState(null); + const [newStatus, setNewStatus] = useState(ContractStatus.Active); + + // Selected contract for file upload / replace + const [contractForFileUpload, setContractForFileUpload] = useState(null); + const [selectedUploadFile, setSelectedUploadFile] = useState(null); + const fileInputRef = useRef(null); + + // Create contract form state + const [selectedAwardId, setSelectedAwardId] = useState(''); + const [totalValueInput, setTotalValueInput] = useState(0); + const [effectiveAtInput, setEffectiveAtInput] = useState( + new Date().toISOString().split('T')[0] + ); + const [expiresAtInput, setExpiresAtInput] = useState(() => { + const d = new Date(); + d.setFullYear(d.getFullYear() + 1); + return d.toISOString().split('T')[0]; + }); + const [contractPdfFile, setContractPdfFile] = useState(null); + const [onlyApprovedAwards, setOnlyApprovedAwards] = useState(true); + + // API Error states for modal banner display + const [createApiError, setCreateApiError] = useState(null); + const [uploadApiError, setUploadApiError] = useState(null); + const [updateApiError, setUpdateApiError] = useState(null); + + // Loading indicator for secure download + const [downloadingContractId, setDownloadingContractId] = useState(null); + + // Queries + const { + data: contracts = [], + isLoading: loadingContracts, + refetch: refetchContracts, + isRefetching: refetchingContracts, + } = useQuery({ + queryKey: ['contracts'], + queryFn: () => getContracts(1, 100), + }); + + const { data: awards = [], isLoading: loadingAwards } = useQuery({ + queryKey: ['awards'], + queryFn: () => getAwards(1, 100), + }); + + const { data: organisations = [] } = useQuery({ + queryKey: ['organisations'], + queryFn: () => getOrganisations(1, 100), + }); + + const { data: programmes = [] } = useQuery({ + queryKey: ['all-programmes'], + queryFn: () => getAllProgrammes(1, 100), + }); + + const { data: projects = [] } = useQuery({ + queryKey: ['projects'], + queryFn: () => getProjects(), + }); + + // Helper to match award and its related metadata + const getAwardForContract = (contract: Contract): Award | undefined => { + return awards.find( + (a) => + String(a.id) === String(contract.awardId) || + toolsService.encodeIdSync(a.id) === String(contract.awardId) + ); + }; + + const getOrganisationForAward = (award?: Award): Organisation | undefined => { + if (!award) return undefined; + return organisations.find( + (o) => + String(o.id) === String(award.organisationId) || + toolsService.encodeIdSync(o.id) === String(award.organisationId) + ); + }; + + const getProgrammeForAward = (award?: Award): Programme | undefined => { + if (!award) return undefined; + return programmes.find( + (p) => + String(p.id) === String(award.programmeId) || + toolsService.encodeIdSync(p.id) === String(award.programmeId) + ); + }; + + const getProjectForAward = (award?: Award, prog?: Programme): Project | undefined => { + if (!award) return undefined; + // Match by direct project ID on award or by programme link + return projects.find( + (pr) => + (award.projectId && (String(pr.id) === String(award.projectId) || toolsService.encodeIdSync(pr.id) === String(award.projectId))) || + (award.programmeId && String(pr.programmeId) === String(award.programmeId)) || + (prog && String(pr.programmeId) === String(prog.id)) + ); + }; + + // Filter awards for dropdown: Approved status or all + const filteredAwardsForDropdown = awards.filter((a) => { + if (onlyApprovedAwards) { + return Number(a.status) === ApprovalStatus.Approved; + } + return true; + }); + + // When selected award changes in modal, auto-populate value and defaults + const handleAwardSelect = (awardIdStr: string) => { + setSelectedAwardId(awardIdStr); + setCreateApiError(null); + const award = awards.find( + (a) => + String(a.id) === awardIdStr || + toolsService.encodeIdSync(a.id) === awardIdStr + ); + if (award) { + if (award.amount && award.amount > 0) { + setTotalValueInput(award.amount); + } + } + }; + + // Selected award metadata preview + const activeSelectedAward = awards.find( + (a) => + String(a.id) === selectedAwardId || + toolsService.encodeIdSync(a.id) === selectedAwardId + ); + const activeSelectedOrg = getOrganisationForAward(activeSelectedAward); + const activeSelectedProg = getProgrammeForAward(activeSelectedAward); + const activeSelectedProj = getProjectForAward(activeSelectedAward, activeSelectedProg); + + // Mutations + const createContractMutation = useMutation({ + mutationFn: async () => { + setCreateApiError(null); + if (!selectedAwardId) { + throw new Error('Please select an approved award from the list.'); + } + if (!totalValueInput || totalValueInput <= 0) { + throw new Error('Please enter a valid total contract value greater than 0.'); + } + if (!contractPdfFile) { + throw new Error('Please upload a PDF document of the contract.'); + } + + // Step 1: Create contract via POST /v1/api/grants/contracts (awardId will be hashed by service) + const createdContract = await createContract({ + awardId: selectedAwardId, + totalValue: Number(totalValueInput), + effectiveAt: new Date(effectiveAtInput).toISOString(), + expiresAt: new Date(expiresAtInput).toISOString(), + }); + + // Target contractId for upload + const contractIdToUploadAgainst = + createdContract?.id ?? (createdContract as any)?.contractId ?? selectedAwardId; + + // Step 2: Upload PDF document to POST /v1/api/grants/contracts/{contractId} + if (contractPdfFile && contractIdToUploadAgainst) { + await uploadContractFile(contractIdToUploadAgainst, contractPdfFile); + } + + return createdContract; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['contracts'] }); + queryClient.invalidateQueries({ queryKey: ['awards'] }); + queryClient.invalidateQueries({ queryKey: ['dashboard'] }); + success( + 'Contract Created & Document Uploaded', + 'The contract record has been registered and the PDF document securely stored in S3.' + ); + setCreateModalOpen(false); + resetCreateForm(); + }, + onError: (err: unknown) => { + setCreateApiError(err); + toastError('Contract Creation Failed', err); + }, + }); + + const uploadFileMutation = useMutation({ + mutationFn: async () => { + setUploadApiError(null); + if (!contractForFileUpload) throw new Error('No contract selected for upload.'); + if (!selectedUploadFile) throw new Error('Please select a PDF document to upload.'); + + await uploadContractFile(contractForFileUpload.id, selectedUploadFile); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['contracts'] }); + success('Contract Document Updated', 'New contract PDF document uploaded successfully.'); + setContractForFileUpload(null); + setSelectedUploadFile(null); + }, + onError: (err: unknown) => { + setUploadApiError(err); + toastError('Document Upload Failed', err); + }, + }); + + const updateStatusMutation = useMutation({ + mutationFn: async () => { + setUpdateApiError(null); + if (!contractToUpdate) throw new Error('No contract selected.'); + await updateContract({ + contractId: contractToUpdate.id, + status: newStatus, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['contracts'] }); + queryClient.invalidateQueries({ queryKey: ['dashboard'] }); + success('Contract Status Updated', 'The contract lifecycle status has been updated.'); + setContractToUpdate(null); + }, + onError: (err: unknown) => { + setUpdateApiError(err); + toastError('Status Update Failed', err); + }, + }); + + const resetCreateForm = () => { + setSelectedAwardId(''); + setTotalValueInput(0); + setContractPdfFile(null); + setCreateApiError(null); + }; + + // Secure download link handler (15-minute on-demand link) + const handleDownloadSecureLink = async (contract: Contract) => { + setDownloadingContractId(contract.id); + try { + const url = await getContractSecureLink(contract.id); + if (url && typeof url === 'string' && url.trim() !== '') { + const link = document.createElement('a'); + link.href = url.trim(); + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + success( + '15-Minute Secure Link Generated', + 'A time-limited download URL has been generated on-demand and opened.' + ); + } else { + toastError('Download Link Unavailable', 'The server returned an empty secure download URL.'); + } + } catch (err: unknown) { + toastError('Failed to Retrieve Secure Link', err); + } finally { + setDownloadingContractId(null); + } + }; + + // Filter and search contracts + const filteredContracts = contracts.filter((c) => { + // Status filter + if (statusFilter !== 'all') { + const statusNum = Number(c.status); + if (statusNum !== Number(statusFilter)) return false; + } + + // Search filter + if (searchTerm.trim()) { + const q = searchTerm.toLowerCase(); + const cId = String(c.id).toLowerCase(); + const aId = String(c.awardId).toLowerCase(); + + const award = getAwardForContract(c); + const org = getOrganisationForAward(award); + const prog = getProgrammeForAward(award); + const proj = getProjectForAward(award, prog); + + const orgName = org?.name?.toLowerCase() || ''; + const progName = prog?.name?.toLowerCase() || ''; + const projName = proj?.name?.toLowerCase() || ''; + + if ( + !cId.includes(q) && + !aId.includes(q) && + !orgName.includes(q) && + !progName.includes(q) && + !projName.includes(q) + ) { + return false; + } + } + + return true; + }); + + // Calculate summary metrics + const totalContractCount = contracts.length; + const activeContractsValue = contracts + .filter((c) => Number(c.status) === ContractStatus.Active) + .reduce((sum, c) => sum + (Number(c.totalValue) || 0), 0); + const pendingContractsCount = contracts.filter( + (c) => + Number(c.status) === ContractStatus.Draft || + Number(c.status) === ContractStatus.PendingApproval + ).length; + + return ( +
+ {/* Summary Cards */} +
+
+
+ + Total Contracts + +
+ +
+
+
+ {loadingContracts ? '...' : totalContractCount} +
+

+ Linked to approved grant funding awards +

+
+ +
+
+ + Active Value (ZAR) + +
+ +
+
+
+ {loadingContracts ? '...' : formatZAR(activeContractsValue)} +
+

+ Legally executed active commitments +

+
+ +
+
+ + In Review / Draft + +
+ +
+
+
+ {loadingContracts ? '...' : pendingContractsCount} +
+

+ Pending legal review or signature +

+
+ +
+
+ + Approved Awards Available + +
+ +
+
+
+ {loadingAwards + ? '...' + : awards.filter((a) => Number(a.status) === ApprovalStatus.Approved).length} +
+

+ Ready to establish contractual legal binding +

+
+
+ + {/* Action and Filter Bar */} +
+
+ {/* Search */} +
+ + setSearchTerm(e.target.value)} + className="w-full bg-[#151b2c] border border-slate-700/80 rounded-xl pl-9 pr-3 py-1.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-blue-500" + /> +
+ + {/* Status Filter */} +
+ + +
+ + +
+ + +
+ + {/* Contracts Table */} +
+
+

+ + Contract Registry & Associated Programme Hierarchy +

+ + Showing {filteredContracts.length} of {contracts.length} contracts + +
+ + {loadingContracts ? ( +
+ + Loading contractual records and resolving Award links... +
+ ) : filteredContracts.length === 0 ? ( +
+ +

No contracts found

+

+ {searchTerm || statusFilter !== 'all' + ? 'No contracts match the current search filters. Try resetting the filters.' + : 'No contracts have been created yet. Click "Create Contract & Upload PDF" to link an approved award.'} +

+ {!(searchTerm || statusFilter !== 'all') && ( + + )} +
+ ) : ( +
+ + + + + + + + + + + + + + + {filteredContracts.map((contract) => { + const award = getAwardForContract(contract); + const org = getOrganisationForAward(award); + const prog = getProgrammeForAward(award); + const proj = getProjectForAward(award, prog); + const statusBadge = getContractStatusBadge(contract.status); + const isDownloading = downloadingContractId === contract.id; + + return ( + + {/* Contract ID */} + + + {/* Linked Award */} + + + {/* Programme & Project */} + + + {/* Organisation */} + + + {/* Validity Period */} + + + {/* Status */} + + + {/* Document / File S3 Download */} + + + {/* Actions */} + + + ); + })} + +
Contract / RefAward & AmountProgramme & ProjectOrganisationValidity PeriodStatusDocument / S3Actions
+
+ #{contract.id} +
+
+ Hash: {toolsService.encodeIdSync(contract.id)} +
+
+ {formatZAR(contract.totalValue)} +
+
+ {award ? ( +
+
+ Award #{award.id} + {award.referenceNo && ( + + ({award.referenceNo}) + + )} +
+
+ Funded: {formatZAR(award.amount)} +
+ {award.status && ( + + {getApprovalStatusBadge(award.status).label} + + )} +
+ ) : ( + + Award #{contract.awardId} + + )} +
+
+ + + {prog?.name || 'Programme Linked'} + +
+
+ + + {proj?.name || (award?.projectId ? `Project #${award.projectId}` : 'Project Scope')} + +
+
+
+ + + {org?.name || 'Recipient Organisation'} + +
+ {org?.registrationNo && ( +
+ Reg: {org.registrationNo} +
+ )} +
+
+ + {formatDate(contract.effectiveAt)} +
+
+ to {formatDate(contract.expiresAt)} +
+
+ + {statusBadge.label} + + +
+ + + +
+
+ + 15-min secure link +
+
+ +
+
+ )} +
+ + {/* ======================================================== */} + {/* MODAL 1: Create Contract & Initial PDF Upload Workflow */} + {/* ======================================================== */} + setCreateModalOpen(false)} + title="Create Legal Contract & Attach PDF" + > +
{ + e.preventDefault(); + createContractMutation.mutate(); + }} + className="space-y-4" + > + {/* ProblemDetails Error Alert */} + {createApiError && ( + setCreateApiError(null)} + title="Contract Creation Rejected" + /> + )} + + {/* Award Selection Dropdown */} +
+
+ + +
+ + +

+ The endpoint expects the award identifier hash; selecting an award links Programme, Project, and Organisation. +

+
+ + {/* Associated Context Preview */} + {activeSelectedAward && ( +
+
+ + Linked Award Hierarchy +
+
+
+ Recipient Organisation: + + {activeSelectedOrg?.name || 'Unknown'} + +
+
+ Funding Programme: + + {activeSelectedProg?.name || 'Programme Attached'} + +
+
+ Associated Project: + + {activeSelectedProj?.name || (activeSelectedAward.projectId ? `Project #${activeSelectedAward.projectId}` : 'Portfolio Project')} + +
+
+ Approved Award Amount: + + {formatZAR(activeSelectedAward.amount)} + +
+
+
+ )} + + {/* Total Value */} +
+ + setTotalValueInput(Number(e.target.value))} + placeholder="e.g. 500000" + className="w-full bg-[#151b2c] border border-slate-700 rounded-xl px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500" + required + /> +
+ + {/* Dates */} +
+
+ + setEffectiveAtInput(e.target.value)} + className="w-full bg-[#151b2c] border border-slate-700 rounded-xl px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500" + required + /> +
+
+ + setExpiresAtInput(e.target.value)} + className="w-full bg-[#151b2c] border border-slate-700 rounded-xl px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500" + required + /> +
+
+ + {/* Mandatory Contract PDF File Upload */} +
+ +
+ {contractPdfFile ? ( +
+
+ +
+

+ {contractPdfFile.name} +

+

+ {(contractPdfFile.size / 1024).toFixed(1)} KB • application/pdf +

+
+
+ +
+ ) : ( + + )} +
+

+ Upon contract creation, the PDF is immediately uploaded to S3 and linked to this contract record. +

+
+ + {/* Modal Actions */} +
+ + +
+ +
+ + {/* ======================================================== */} + {/* MODAL 2: Replace / Upload New Contract PDF File */} + {/* ======================================================== */} + setContractForFileUpload(null)} + title={`Upload New Contract Document (Contract #${contractForFileUpload?.id})`} + > +
{ + e.preventDefault(); + uploadFileMutation.mutate(); + }} + className="space-y-4" + > + {uploadApiError && ( + setUploadApiError(null)} + title="File Upload Failed" + /> + )} + +

+ Contracts can always upload a new PDF file if there was an issue or amendment with the previous document. +

+ +
+ +
+ {selectedUploadFile ? ( +
+
+ +
+

+ {selectedUploadFile.name} +

+

+ {(selectedUploadFile.size / 1024).toFixed(1)} KB • application/pdf +

+
+
+ +
+ ) : ( + + )} +
+
+ +
+ + +
+ +
+ + {/* ======================================================== */} + {/* MODAL 3: Update Contract Status */} + {/* ======================================================== */} + setContractToUpdate(null)} + title={`Update Contract Status (Contract #${contractToUpdate?.id})`} + > +
{ + e.preventDefault(); + updateStatusMutation.mutate(); + }} + className="space-y-4" + > + {updateApiError && ( + setUpdateApiError(null)} + title="Status Transition Failed" + /> + )} + +

+ Like any other data element, contracts can be updated and their status changed across the lifecycle. +

+ +
+ + +
+ +
+ + +
+ +
+
+ ); +}; diff --git a/PostFundManagement.ConsoleUI/src/components/ui/BrandLogo.tsx b/PostFundManagement.ConsoleUI/src/components/ui/BrandLogo.tsx index 3d53a33..9018f96 100644 --- a/PostFundManagement.ConsoleUI/src/components/ui/BrandLogo.tsx +++ b/PostFundManagement.ConsoleUI/src/components/ui/BrandLogo.tsx @@ -17,12 +17,7 @@ const sizeMap = { }; /** - * Modern, stylized SVG brand emblem representing Post Fund Management (PFM). - * Features an isometric kinetic nexus with three interlocking governance facets: - * 1. Top Canopy: Governance, Strategy & Portfolios (Radiant Indigo / Violet) - * 2. Left Vault: Capital, Grants & Disbursement Assurance (Electric Sapphire) - * 3. Right Wing: Milestones, Evidence & Impact Delivery (Cyan to Emerald) - * 4. Center Core: Luminous Floating Diamond Milestone Star + * SVG brand logo component for Post Fund Management (PFM). */ export const BrandLogo: React.FC = ({ size = 'md', diff --git a/PostFundManagement.ConsoleUI/src/components/ui/HelpTooltip.tsx b/PostFundManagement.ConsoleUI/src/components/ui/HelpTooltip.tsx new file mode 100644 index 0000000..dfcd092 --- /dev/null +++ b/PostFundManagement.ConsoleUI/src/components/ui/HelpTooltip.tsx @@ -0,0 +1,125 @@ +import React, { useState, useRef, useEffect, ReactNode } from 'react'; +import { Info, X } from 'lucide-react'; + +export interface HelpTooltipProps { + /** Concise context-sensitive explanation of the section, control, metric, or dialog */ + content: ReactNode; + /** Optional title for the tooltip popover */ + title?: string; + /** Custom alignment: top, bottom, left, right (defaults to top) */ + placement?: 'top' | 'bottom' | 'left' | 'right'; + /** Size of the (i) icon: sm (13px), md (15px), lg (18px) */ + size?: 'sm' | 'md' | 'lg'; + /** Additional styling classes for the container or trigger button */ + className?: string; + /** Accessible label or ARIA label */ + ariaLabel?: string; +} + +export const HelpTooltip: React.FC = ({ + content, + title, + placement = 'top', + size = 'md', + className = '', + ariaLabel = 'Information and guidance', +}) => { + const [isOpen, setIsOpen] = useState(false); + const containerRef = useRef(null); + + // Close on Escape or click outside + useEffect(() => { + if (!isOpen) return; + + const handleOutsideClick = (e: MouseEvent | TouchEvent) => { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setIsOpen(false); + } + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleOutsideClick); + document.addEventListener('touchstart', handleOutsideClick); + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('mousedown', handleOutsideClick); + document.removeEventListener('touchstart', handleOutsideClick); + document.removeEventListener('keydown', handleKeyDown); + }; + }, [isOpen]); + + const sizeClasses = { + sm: 'w-3.5 h-3.5 text-[11px]', + md: 'w-4 h-4 text-xs', + lg: 'w-5 h-5 text-sm', + }[size]; + + const iconSizes = { + sm: 'w-3 h-3', + md: 'w-3.5 h-3.5', + lg: 'w-4 h-4', + }[size]; + + const placementClasses = { + top: 'bottom-full left-1/2 -translate-x-1/2 mb-2', + bottom: 'top-full left-1/2 -translate-x-1/2 mt-2', + left: 'right-full top-1/2 -translate-y-1/2 mr-2', + right: 'left-full top-1/2 -translate-y-1/2 ml-2', + }[placement]; + + return ( +
+ + + {isOpen && ( +
e.stopPropagation()} + className={`absolute ${placementClasses} z-50 w-72 max-w-[calc(100vw-32px)] p-3 rounded-xl bg-[#0c121e] border border-blue-500/30 text-slate-200 shadow-2xl backdrop-blur-md animate-in fade-in zoom-in-95 duration-150`} + > +
+
+ + + {title || 'Guidance & Information'} + +
+ +
+ +
+ {content} +
+
+ )} +
+ ); +}; diff --git a/PostFundManagement.ConsoleUI/src/components/ui/Modal.tsx b/PostFundManagement.ConsoleUI/src/components/ui/Modal.tsx index a12097b..cfad194 100644 --- a/PostFundManagement.ConsoleUI/src/components/ui/Modal.tsx +++ b/PostFundManagement.ConsoleUI/src/components/ui/Modal.tsx @@ -1,11 +1,13 @@ import React, { ReactNode, useEffect } from 'react'; import { X } from 'lucide-react'; +import { HelpTooltip } from './HelpTooltip'; interface ModalProps { isOpen: boolean; onClose: () => void; title: string; subtitle?: string; + helpText?: ReactNode; children: ReactNode; maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl'; } @@ -15,6 +17,7 @@ export const Modal: React.FC = ({ onClose, title, subtitle, + helpText, children, maxWidth = 'lg', }) => { @@ -56,7 +59,17 @@ export const Modal: React.FC = ({ >
-

{title}

+
+

{title}

+ {helpText && ( + + )} +
{subtitle &&

{subtitle}

}
+ )} +
+
+ ); +}; diff --git a/PostFundManagement.ConsoleUI/src/components/ui/RiskHeatmapMatrix.tsx b/PostFundManagement.ConsoleUI/src/components/ui/RiskHeatmapMatrix.tsx index 57b9857..c276fd5 100644 --- a/PostFundManagement.ConsoleUI/src/components/ui/RiskHeatmapMatrix.tsx +++ b/PostFundManagement.ConsoleUI/src/components/ui/RiskHeatmapMatrix.tsx @@ -2,6 +2,7 @@ import React, { useState, useMemo } from 'react'; import { Risk, Project } from '../../types'; import { getUserEmail } from '../../lib/formatters'; import { AlertTriangle, ShieldAlert, CheckCircle2, ChevronRight, X, Info } from 'lucide-react'; +import { HelpTooltip } from './HelpTooltip'; interface RiskHeatmapMatrixProps { risks: Risk[]; @@ -168,7 +169,15 @@ export const RiskHeatmapMatrix: React.FC = ({ {/* Header & Controls */}
- {title &&

{title}

} +
+ {title &&

{title}

} + +
{subtitle &&

{subtitle}

}
@@ -200,6 +209,12 @@ export const RiskHeatmapMatrix: React.FC = ({ 3×3 Summary
+
diff --git a/PostFundManagement.ConsoleUI/src/components/ui/Toast.tsx b/PostFundManagement.ConsoleUI/src/components/ui/Toast.tsx index a07afbd..e4a741c 100644 --- a/PostFundManagement.ConsoleUI/src/components/ui/Toast.tsx +++ b/PostFundManagement.ConsoleUI/src/components/ui/Toast.tsx @@ -25,6 +25,7 @@ export interface ToastItem { categoryLabel?: string; technicalDetails?: string; fieldErrors?: Array<{ field: string; message: string }>; + errorsList?: string[]; } export interface ToastOptions { @@ -34,6 +35,7 @@ export interface ToastOptions { categoryLabel?: string; technicalDetails?: string; fieldErrors?: Array<{ field: string; message: string }>; + errorsList?: string[]; } interface ToastContextType { @@ -153,11 +155,21 @@ const ToastCard: React.FC<{ )}
- {toast.message && ( + {/* Display errors directly if multiple, or single message directly */} + {toast.errorsList && toast.errorsList.length > 1 ? ( +
    + {toast.errorsList.map((err, idx) => ( +
  • + • + {err} +
  • + ))} +
+ ) : toast.message ? (

{toast.message}

- )} + ) : null} {/* Optional collapsible technical reference */} {toast.technicalDetails && ( @@ -255,6 +267,7 @@ export const ToastProvider: React.FC<{ children: ReactNode }> = ({ children }) = categoryLabel: options?.categoryLabel, technicalDetails: options?.technicalDetails, fieldErrors: options?.fieldErrors, + errorsList: options?.errorsList, }, ]); }; @@ -302,6 +315,7 @@ export const ToastProvider: React.FC<{ children: ReactNode }> = ({ children }) = categoryLabel: options?.categoryLabel || parsed.categoryLabel, technicalDetails: parsed.technicalDetails || options?.technicalDetails, fieldErrors: parsed.fieldErrors || options?.fieldErrors, + errorsList: parsed.errorsList || options?.errorsList, }); }; diff --git a/PostFundManagement.ConsoleUI/src/config.ts b/PostFundManagement.ConsoleUI/src/config.ts index b7ad2ca..c3f2ab1 100644 --- a/PostFundManagement.ConsoleUI/src/config.ts +++ b/PostFundManagement.ConsoleUI/src/config.ts @@ -65,13 +65,33 @@ function pickFirstNonEmpty(...candidates: any[]): string | undefined { } /** - * Normalizes an API base URL ensuring proper protocol and single trailing slash. + * Normalizes an API base URL ensuring HTTPS protocol by default and a single trailing slash. */ function normalizeBaseUrl(url: string): string { - const trimmed = url.trim(); + let trimmed = url.trim(); + // Ensure HTTPS is default protocol if protocol is omitted + if (!/^https?:\/\//i.test(trimmed)) { + trimmed = `https://${trimmed}`; + } else if (trimmed.startsWith('http://') && !trimmed.includes('localhost') && !trimmed.includes('127.0.0.1')) { + // Upgrade remote HTTP URLs to HTTPS by default + trimmed = trimmed.replace(/^http:\/\//i, 'https://'); + } return trimmed.endsWith('/') ? trimmed : `${trimmed}/`; } +/** + * Normalizes an OIDC/STS Authority URL ensuring HTTPS protocol by default. + */ +function normalizeAuthorityUrl(url: string): string { + let trimmed = url.trim(); + if (!/^https?:\/\//i.test(trimmed)) { + trimmed = `https://${trimmed}`; + } else if (trimmed.startsWith('http://') && !trimmed.includes('localhost') && !trimmed.includes('127.0.0.1')) { + trimmed = trimmed.replace(/^http:\/\//i, 'https://'); + } + return trimmed.replace(/\/+$/, ''); +} + /** * Reads any runtime config object injected into window. * Supports: window.__RUNTIME_CONFIG__, window.APP_CONFIG, window._env_ @@ -268,7 +288,7 @@ function resolveConfig(fetchedJson?: Record): AppConfig { return { SecuritySettings: { - Authority: authority, + Authority: normalizeAuthorityUrl(authority), Audience: audience, ClientId: clientId, ClientSecret: clientSecret, diff --git a/PostFundManagement.ConsoleUI/src/pages/ExecutiveDashboard.tsx b/PostFundManagement.ConsoleUI/src/pages/ExecutiveDashboard.tsx index 20b32e5..18b6db2 100644 --- a/PostFundManagement.ConsoleUI/src/pages/ExecutiveDashboard.tsx +++ b/PostFundManagement.ConsoleUI/src/pages/ExecutiveDashboard.tsx @@ -28,6 +28,7 @@ import { import { formatCompactZAR, formatZAR, getIndustrySectorLabel } from '../lib/formatters'; import { ApprovalStatus } from '../types'; import { RiskHeatmapMatrix } from '../components/ui/RiskHeatmapMatrix'; +import { HelpTooltip } from '../components/ui/HelpTooltip'; export const ExecutiveDashboard: React.FC = () => { const queryClient = useQueryClient(); @@ -35,7 +36,7 @@ export const ExecutiveDashboard: React.FC = () => { const [selectedSector, setSelectedSector] = useState('All'); const [selectedPeriod, setSelectedPeriod] = useState('FY 2026/27'); - // Real Queries from OpenAPI Endpoints + // Data queries const { data: dashboard, isLoading: loadingDashboard, refetch: refetchDashboard, isFetching } = useQuery({ queryKey: ['dashboard'], queryFn: getDashboard, @@ -109,7 +110,7 @@ export const ExecutiveDashboard: React.FC = () => { return risks.filter((r) => projectIds.has(String(r.projectId))); }, [risks, filteredProjects, selectedProgramme, selectedSector]); - // Headline metrics sourced directly from live API models with real filtering + // Headline portfolio metrics calculation const portfolioValue = useMemo(() => { if (selectedProgramme !== 'All' || selectedSector !== 'All') { const projectIds = new Set(filteredProjects.map((p) => String(p.id))); @@ -176,7 +177,7 @@ export const ExecutiveDashboard: React.FC = () => { return Math.round((onTrack / targetProjects.length) * 100); }, [dashboard, filteredProjects, projects]); - // Dynamically calculate At Risk count directly from live registered risks + // Calculate at-risk projects count based on risk scores const atRiskCount = useMemo(() => { const targetRisks = filteredRisks; const highRisks = targetRisks.filter((r) => { @@ -198,7 +199,7 @@ export const ExecutiveDashboard: React.FC = () => { return sumActual > 0 ? sumActual : 0; }, [dashboard, indicators]); - // Real sector breakdown calculated dynamically from actual project records + // Sector allocation breakdown const sectorData = useMemo(() => { const targetProjects = filteredProjects.length > 0 ? filteredProjects : projects; if (targetProjects.length === 0) return []; @@ -224,7 +225,7 @@ export const ExecutiveDashboard: React.FC = () => { }); }, [projects, filteredProjects, portfolioValue]); - // Quarterly disbursements distribution computed from real disbursement items + // Quarterly disbursements distribution const quarterlyTrend = useMemo(() => { if (!disbursements || disbursements.length === 0) { return [ @@ -260,7 +261,7 @@ export const ExecutiveDashboard: React.FC = () => { })); }, [disbursements]); - // Attention Projects derived directly from live project data & risk scores + // Projects requiring management attention based on risk profile const attentionProjects = useMemo(() => { const targetProjects = filteredProjects.length > 0 ? filteredProjects : projects; if (targetProjects.length === 0) return []; @@ -323,6 +324,12 @@ export const ExecutiveDashboard: React.FC = () => {

Executive Portfolio Dashboard

+ Live Portfolio Feed @@ -339,41 +346,65 @@ export const ExecutiveDashboard: React.FC = () => { Filter:
- +
+ + +
- +
+ + +
- +
+ + +
@@ -494,13 +560,27 @@ export const GovernancePage: React.FC = () => { - +
+ + +
); @@ -544,6 +624,18 @@ export const GovernancePage: React.FC = () => { Owner: {getUserEmail(p.ownedBy)}
+