diff --git a/frontend/src/lib/api/generated/sdk.gen.ts b/frontend/src/lib/api/generated/sdk.gen.ts index a0eb5e7..652775e 100644 --- a/frontend/src/lib/api/generated/sdk.gen.ts +++ b/frontend/src/lib/api/generated/sdk.gen.ts @@ -3,7 +3,7 @@ import { type Client, type Options as Options2, type TDataShape, urlSearchParamsBodySerializer } from './client'; import { client } from './client.gen'; -import type { AdminActivateUserData, AdminActivateUserErrors, AdminActivateUserResponses, AdminAddOrganizationMemberData, AdminAddOrganizationMemberErrors, AdminAddOrganizationMemberResponses, AdminBulkUserActionData, AdminBulkUserActionErrors, AdminBulkUserActionResponses, AdminCreateOrganizationData, AdminCreateOrganizationErrors, AdminCreateOrganizationResponses, AdminCreateUserData, AdminCreateUserErrors, AdminCreateUserResponses, AdminDeactivateUserData, AdminDeactivateUserErrors, AdminDeactivateUserResponses, AdminDeleteOrganizationData, AdminDeleteOrganizationErrors, AdminDeleteOrganizationResponses, AdminDeleteUserData, AdminDeleteUserErrors, AdminDeleteUserResponses, AdminGetOrganizationData, AdminGetOrganizationErrors, AdminGetOrganizationResponses, AdminGetStatsData, AdminGetStatsResponses, AdminGetUserData, AdminGetUserErrors, AdminGetUserResponses, AdminListOrganizationMembersData, AdminListOrganizationMembersErrors, AdminListOrganizationMembersResponses, AdminListOrganizationsData, AdminListOrganizationsErrors, AdminListOrganizationsResponses, AdminListSessionsData, AdminListSessionsErrors, AdminListSessionsResponses, AdminListUsersData, AdminListUsersErrors, AdminListUsersResponses, AdminRemoveOrganizationMemberData, AdminRemoveOrganizationMemberErrors, AdminRemoveOrganizationMemberResponses, AdminUpdateOrganizationData, AdminUpdateOrganizationErrors, AdminUpdateOrganizationResponses, AdminUpdateUserData, AdminUpdateUserErrors, AdminUpdateUserResponses, ChangeCurrentUserPasswordData, ChangeCurrentUserPasswordErrors, ChangeCurrentUserPasswordResponses, CleanupExpiredSessionsData, CleanupExpiredSessionsResponses, ConfirmPasswordResetData, ConfirmPasswordResetErrors, ConfirmPasswordResetResponses, DeleteOauthClientData, DeleteOauthClientErrors, DeleteOauthClientResponses, DeleteUserData, DeleteUserErrors, DeleteUserResponses, GetCurrentUserProfileData, GetCurrentUserProfileResponses, GetMyOrganizationsData, GetMyOrganizationsErrors, GetMyOrganizationsResponses, GetOauthAuthorizationUrlData, GetOauthAuthorizationUrlErrors, GetOauthAuthorizationUrlResponses, GetOauthServerMetadataData, GetOauthServerMetadataResponses, GetOrganizationData, GetOrganizationErrors, GetOrganizationMembersData, GetOrganizationMembersErrors, GetOrganizationMembersResponses, GetOrganizationResponses, GetUserByIdData, GetUserByIdErrors, GetUserByIdResponses, HandleOauthCallbackData, HandleOauthCallbackErrors, HandleOauthCallbackResponses, HealthCheckData, HealthCheckResponses, ListMyOauthConsentsData, ListMyOauthConsentsResponses, ListMySessionsData, ListMySessionsResponses, ListOauthAccountsData, ListOauthAccountsResponses, ListOauthClientsData, ListOauthClientsResponses, ListOauthProvidersData, ListOauthProvidersResponses, ListUsersData, ListUsersErrors, ListUsersResponses, LoginData, LoginErrors, LoginOauthData, LoginOauthErrors, LoginOauthResponses, LoginResponses, LogoutAllData, LogoutAllResponses, LogoutData, LogoutErrors, LogoutResponses, OauthProviderAuthorizeData, OauthProviderAuthorizeErrors, OauthProviderAuthorizeResponses, OauthProviderConsentData, OauthProviderConsentErrors, OauthProviderConsentResponses, OauthProviderIntrospectData, OauthProviderIntrospectErrors, OauthProviderIntrospectResponses, OauthProviderRevokeData, OauthProviderRevokeErrors, OauthProviderRevokeResponses, OauthProviderTokenData, OauthProviderTokenErrors, OauthProviderTokenResponses, RefreshTokenData, RefreshTokenErrors, RefreshTokenResponses, RegisterData, RegisterErrors, RegisterOauthClientData, RegisterOauthClientErrors, RegisterOauthClientResponses, RegisterResponses, RequestPasswordResetData, RequestPasswordResetErrors, RequestPasswordResetResponses, RevokeMyOauthConsentData, RevokeMyOauthConsentErrors, RevokeMyOauthConsentResponses, RevokeSessionData, RevokeSessionErrors, RevokeSessionResponses, RootGetData, RootGetResponses, SendTestEventData, SendTestEventErrors, SendTestEventResponses, StartOauthLinkData, StartOauthLinkErrors, StartOauthLinkResponses, StreamProjectEventsData, StreamProjectEventsErrors, StreamProjectEventsResponses, UnlinkOauthAccountData, UnlinkOauthAccountErrors, UnlinkOauthAccountResponses, UpdateCurrentUserData, UpdateCurrentUserErrors, UpdateCurrentUserResponses, UpdateOrganizationData, UpdateOrganizationErrors, UpdateOrganizationResponses, UpdateUserData, UpdateUserErrors, UpdateUserResponses } from './types.gen'; +import type { AddIssueToSprintData, AddIssueToSprintErrors, AddIssueToSprintResponses, AdminActivateUserData, AdminActivateUserErrors, AdminActivateUserResponses, AdminAddOrganizationMemberData, AdminAddOrganizationMemberErrors, AdminAddOrganizationMemberResponses, AdminBulkUserActionData, AdminBulkUserActionErrors, AdminBulkUserActionResponses, AdminCreateOrganizationData, AdminCreateOrganizationErrors, AdminCreateOrganizationResponses, AdminCreateUserData, AdminCreateUserErrors, AdminCreateUserResponses, AdminDeactivateUserData, AdminDeactivateUserErrors, AdminDeactivateUserResponses, AdminDeleteOrganizationData, AdminDeleteOrganizationErrors, AdminDeleteOrganizationResponses, AdminDeleteUserData, AdminDeleteUserErrors, AdminDeleteUserResponses, AdminGetOrganizationData, AdminGetOrganizationErrors, AdminGetOrganizationResponses, AdminGetStatsData, AdminGetStatsResponses, AdminGetUserData, AdminGetUserErrors, AdminGetUserResponses, AdminListOrganizationMembersData, AdminListOrganizationMembersErrors, AdminListOrganizationMembersResponses, AdminListOrganizationsData, AdminListOrganizationsErrors, AdminListOrganizationsResponses, AdminListSessionsData, AdminListSessionsErrors, AdminListSessionsResponses, AdminListUsersData, AdminListUsersErrors, AdminListUsersResponses, AdminRemoveOrganizationMemberData, AdminRemoveOrganizationMemberErrors, AdminRemoveOrganizationMemberResponses, AdminUpdateOrganizationData, AdminUpdateOrganizationErrors, AdminUpdateOrganizationResponses, AdminUpdateUserData, AdminUpdateUserErrors, AdminUpdateUserResponses, ArchiveProjectData, ArchiveProjectErrors, ArchiveProjectResponses, AssignIssueData, AssignIssueErrors, AssignIssueResponses, CancelSprintData, CancelSprintErrors, CancelSprintResponses, ChangeCurrentUserPasswordData, ChangeCurrentUserPasswordErrors, ChangeCurrentUserPasswordResponses, CleanupExpiredSessionsData, CleanupExpiredSessionsResponses, CompleteSprintData, CompleteSprintErrors, CompleteSprintResponses, ConfirmPasswordResetData, ConfirmPasswordResetErrors, ConfirmPasswordResetResponses, CreateAgentTypeData, CreateAgentTypeErrors, CreateAgentTypeResponses, CreateIssueData, CreateIssueErrors, CreateIssueResponses, CreateProjectData, CreateProjectErrors, CreateProjectResponses, CreateSprintData, CreateSprintErrors, CreateSprintResponses, DeactivateAgentTypeData, DeactivateAgentTypeErrors, DeactivateAgentTypeResponses, DeleteIssueData, DeleteIssueErrors, DeleteIssueResponses, DeleteOauthClientData, DeleteOauthClientErrors, DeleteOauthClientResponses, DeleteSprintData, DeleteSprintErrors, DeleteSprintResponses, DeleteUserData, DeleteUserErrors, DeleteUserResponses, GetActiveSprintData, GetActiveSprintErrors, GetActiveSprintResponses, GetAgentData, GetAgentErrors, GetAgentMetricsData, GetAgentMetricsErrors, GetAgentMetricsResponses, GetAgentResponses, GetAgentTypeBySlugData, GetAgentTypeBySlugErrors, GetAgentTypeBySlugResponses, GetAgentTypeData, GetAgentTypeErrors, GetAgentTypeResponses, GetCurrentUserProfileData, GetCurrentUserProfileResponses, GetIssueData, GetIssueErrors, GetIssueResponses, GetIssueStatsData, GetIssueStatsErrors, GetIssueStatsResponses, GetMyOrganizationsData, GetMyOrganizationsErrors, GetMyOrganizationsResponses, GetOauthAuthorizationUrlData, GetOauthAuthorizationUrlErrors, GetOauthAuthorizationUrlResponses, GetOauthServerMetadataData, GetOauthServerMetadataResponses, GetOrganizationData, GetOrganizationErrors, GetOrganizationMembersData, GetOrganizationMembersErrors, GetOrganizationMembersResponses, GetOrganizationResponses, GetProjectAgentMetricsData, GetProjectAgentMetricsErrors, GetProjectAgentMetricsResponses, GetProjectBySlugData, GetProjectBySlugErrors, GetProjectBySlugResponses, GetProjectData, GetProjectErrors, GetProjectResponses, GetProjectVelocityData, GetProjectVelocityErrors, GetProjectVelocityResponses, GetSprintData, GetSprintErrors, GetSprintIssuesData, GetSprintIssuesErrors, GetSprintIssuesResponses, GetSprintResponses, GetUserByIdData, GetUserByIdErrors, GetUserByIdResponses, HandleOauthCallbackData, HandleOauthCallbackErrors, HandleOauthCallbackResponses, HealthCheckData, HealthCheckResponses, ListAgentTypesData, ListAgentTypesErrors, ListAgentTypesResponses, ListIssuesData, ListIssuesErrors, ListIssuesResponses, ListMyOauthConsentsData, ListMyOauthConsentsResponses, ListMySessionsData, ListMySessionsResponses, ListOauthAccountsData, ListOauthAccountsResponses, ListOauthClientsData, ListOauthClientsResponses, ListOauthProvidersData, ListOauthProvidersResponses, ListProjectAgentsData, ListProjectAgentsErrors, ListProjectAgentsResponses, ListProjectsData, ListProjectsErrors, ListProjectsResponses, ListSprintsData, ListSprintsErrors, ListSprintsResponses, ListUsersData, ListUsersErrors, ListUsersResponses, LoginData, LoginErrors, LoginOauthData, LoginOauthErrors, LoginOauthResponses, LoginResponses, LogoutAllData, LogoutAllResponses, LogoutData, LogoutErrors, LogoutResponses, OauthProviderAuthorizeData, OauthProviderAuthorizeErrors, OauthProviderAuthorizeResponses, OauthProviderConsentData, OauthProviderConsentErrors, OauthProviderConsentResponses, OauthProviderIntrospectData, OauthProviderIntrospectErrors, OauthProviderIntrospectResponses, OauthProviderRevokeData, OauthProviderRevokeErrors, OauthProviderRevokeResponses, OauthProviderTokenData, OauthProviderTokenErrors, OauthProviderTokenResponses, PauseAgentData, PauseAgentErrors, PauseAgentResponses, PauseProjectData, PauseProjectErrors, PauseProjectResponses, RefreshTokenData, RefreshTokenErrors, RefreshTokenResponses, RegisterData, RegisterErrors, RegisterOauthClientData, RegisterOauthClientErrors, RegisterOauthClientResponses, RegisterResponses, RemoveIssueFromSprintData, RemoveIssueFromSprintErrors, RemoveIssueFromSprintResponses, RequestPasswordResetData, RequestPasswordResetErrors, RequestPasswordResetResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, ResumeProjectData, ResumeProjectErrors, ResumeProjectResponses, RevokeMyOauthConsentData, RevokeMyOauthConsentErrors, RevokeMyOauthConsentResponses, RevokeSessionData, RevokeSessionErrors, RevokeSessionResponses, RootGetData, RootGetResponses, SendTestEventData, SendTestEventErrors, SendTestEventResponses, SpawnAgentData, SpawnAgentErrors, SpawnAgentResponses, StartOauthLinkData, StartOauthLinkErrors, StartOauthLinkResponses, StartSprintData, StartSprintErrors, StartSprintResponses, StreamProjectEventsData, StreamProjectEventsErrors, StreamProjectEventsResponses, SyncIssueData, SyncIssueErrors, SyncIssueResponses, TerminateAgentData, TerminateAgentErrors, TerminateAgentResponses, UnassignIssueData, UnassignIssueErrors, UnassignIssueResponses, UnlinkOauthAccountData, UnlinkOauthAccountErrors, UnlinkOauthAccountResponses, UpdateAgentData, UpdateAgentErrors, UpdateAgentResponses, UpdateAgentTypeData, UpdateAgentTypeErrors, UpdateAgentTypeResponses, UpdateCurrentUserData, UpdateCurrentUserErrors, UpdateCurrentUserResponses, UpdateIssueData, UpdateIssueErrors, UpdateIssueResponses, UpdateOrganizationData, UpdateOrganizationErrors, UpdateOrganizationResponses, UpdateProjectData, UpdateProjectErrors, UpdateProjectResponses, UpdateSprintData, UpdateSprintErrors, UpdateSprintResponses, UpdateUserData, UpdateUserErrors, UpdateUserResponses } from './types.gen'; export type Options = Options2 & { /** @@ -1293,9 +1293,16 @@ export const getOrganizationMembers = (opt * * Stream real-time events for a project via Server-Sent Events (SSE). * - * **Authentication**: Required (Bearer token) + * **Authentication**: Required (Bearer token OR query parameter) * **Authorization**: Must have access to the project * + * **Authentication Methods**: + * - Bearer token in Authorization header (preferred) + * - Query parameter `token` (for EventSource compatibility) + * + * Note: EventSource API doesn't support custom headers, so the query parameter + * option is provided for browser-based SSE clients. + * * **SSE Event Format**: * ``` * event: agent.status_changed @@ -1320,12 +1327,6 @@ export const getOrganizationMembers = (opt export const streamProjectEvents = (options: Options) => { return (options.client ?? client).sse.get({ responseType: 'text', - security: [ - { - scheme: 'bearer', - type: 'http' - } - ], url: '/api/v1/projects/{project_id}/events/stream', ...options }); @@ -1356,6 +1357,1072 @@ export const sendTestEvent = (options: Opt }); }; +/** + * List Projects + * + * List projects for the current user with filtering and pagination. + * + * Regular users see only their own projects. + * Superusers can see all projects by setting `all_projects=true`. + * + * **Rate Limit**: 30 requests/minute + */ +export const listProjects = (options?: Options) => { + return (options?.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects', + ...options + }); +}; + +/** + * Create Project + * + * Create a new project for the current user. + * + * The project will be owned by the authenticated user. + * A unique slug is required for URL-friendly project identification. + * + * **Rate Limit**: 10 requests/minute + */ +export const createProject = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Archive Project + * + * Archive a project (soft delete). + * + * Only the project owner or a superuser can archive a project. + * Archived projects are not deleted but are no longer accessible for active work. + * + * **Rate Limit**: 10 requests/minute + */ +export const archiveProject = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}', + ...options + }); +}; + +/** + * Get Project + * + * Get detailed information about a specific project. + * + * Users can only access their own projects unless they are superusers. + * + * **Rate Limit**: 60 requests/minute + */ +export const getProject = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}', + ...options + }); +}; + +/** + * Update Project + * + * Update an existing project. + * + * Only the project owner or a superuser can update a project. + * Only provided fields will be updated. + * + * **Rate Limit**: 20 requests/minute + */ +export const updateProject = (options: Options) => { + return (options.client ?? client).patch({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Project by Slug + * + * Get detailed information about a project by its slug. + * + * Users can only access their own projects unless they are superusers. + * + * **Rate Limit**: 60 requests/minute + */ +export const getProjectBySlug = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/slug/{slug}', + ...options + }); +}; + +/** + * Pause Project + * + * Pause an active project. + * + * Only ACTIVE projects can be paused. + * Only the project owner or a superuser can pause a project. + * + * **Rate Limit**: 10 requests/minute + */ +export const pauseProject = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/pause', + ...options + }); +}; + +/** + * Resume Project + * + * Resume a paused project. + * + * Only PAUSED projects can be resumed. + * Only the project owner or a superuser can resume a project. + * + * **Rate Limit**: 10 requests/minute + */ +export const resumeProject = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/resume', + ...options + }); +}; + +/** + * List Agent Types + * + * Get paginated list of active agent types + */ +export const listAgentTypes = (options?: Options) => { + return (options?.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types', + ...options + }); +}; + +/** + * Create Agent Type + * + * Create a new agent type configuration (admin only) + */ +export const createAgentType = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Deactivate Agent Type + * + * Deactivate an agent type (soft delete, admin only) + */ +export const deactivateAgentType = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types/{agent_type_id}', + ...options + }); +}; + +/** + * Get Agent Type + * + * Get agent type details by ID + */ +export const getAgentType = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types/{agent_type_id}', + ...options + }); +}; + +/** + * Update Agent Type + * + * Update an existing agent type configuration (admin only) + */ +export const updateAgentType = (options: Options) => { + return (options.client ?? client).patch({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types/{agent_type_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Agent Type by Slug + * + * Get agent type details by slug + */ +export const getAgentTypeBySlug = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/agent-types/slug/{slug}', + ...options + }); +}; + +/** + * List Issues + * + * Get paginated list of issues in a project with filtering + */ +export const listIssues = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues', + ...options + }); +}; + +/** + * Create Issue + * + * Create a new issue in a project + */ +export const createIssue = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Issue Statistics + * + * Get aggregated issue statistics for a project + */ +export const getIssueStats = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/stats', + ...options + }); +}; + +/** + * Delete Issue + * + * Delete an issue permanently + */ +export const deleteIssue = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}', + ...options + }); +}; + +/** + * Get Issue + * + * Get detailed information about a specific issue + */ +export const getIssue = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}', + ...options + }); +}; + +/** + * Update Issue + * + * Update an existing issue + */ +export const updateIssue = (options: Options) => { + return (options.client ?? client).patch({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Assign Issue + * + * Assign an issue to an agent or human + */ +export const assignIssue = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}/assign', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Unassign Issue + * + * Remove agent/human assignment from an issue. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * This clears both agent and human assignee fields. + * + * **Rate Limit**: 60 requests/minute + */ +export const unassignIssue = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}/assignment', + ...options + }); +}; + +/** + * Trigger Issue Sync + * + * Trigger synchronization with external issue tracker + */ +export const syncIssue = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/issues/{issue_id}/sync', + ...options + }); +}; + +/** + * List Project Agents + * + * List all agent instances in a project with optional filtering. + */ +export const listProjectAgents = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents', + ...options + }); +}; + +/** + * Spawn Agent Instance + * + * Spawn a new agent instance in a project. Requires project ownership or superuser. + */ +export const spawnAgent = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Project Agent Metrics + * + * Get aggregated usage metrics for all agents in a project. + */ +export const getProjectAgentMetrics = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/metrics', + ...options + }); +}; + +/** + * Terminate Agent + * + * Terminate an agent instance, permanently stopping it. + */ +export const terminateAgent = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}', + ...options + }); +}; + +/** + * Get Agent Details + * + * Get detailed information about a specific agent instance. + */ +export const getAgent = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}', + ...options + }); +}; + +/** + * Update Agent + * + * Update an agent instance's configuration and state. + */ +export const updateAgent = (options: Options) => { + return (options.client ?? client).patch({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Pause Agent + * + * Pause an agent instance, temporarily stopping its work. + */ +export const pauseAgent = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}/pause', + ...options + }); +}; + +/** + * Resume Agent + * + * Resume a paused agent instance. + */ +export const resumeAgent = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}/resume', + ...options + }); +}; + +/** + * Get Agent Metrics + * + * Get usage metrics for a specific agent instance. + */ +export const getAgentMetrics = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/agents/{agent_id}/metrics', + ...options + }); +}; + +/** + * List Sprints + * + * List all sprints for a project with pagination. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Filtering**: By status + * + * **Rate Limit**: 60 requests/minute + */ +export const listSprints = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints', + ...options + }); +}; + +/** + * Create Sprint + * + * Create a new sprint for a project. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Sprint number is auto-generated if not provided + * - End date must be after start date + * - Sprint is created in PLANNED status by default + * + * **Rate Limit**: 30 requests/minute + */ +export const createSprint = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Active Sprint + * + * Get the currently active sprint for a project. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * Returns null if no active sprint exists. + * + * **Rate Limit**: 60 requests/minute + */ +export const getActiveSprint = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/active', + ...options + }); +}; + +/** + * Get Project Velocity + * + * Get velocity metrics for completed sprints in the project. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * Returns velocity data for the last N completed sprints (default 5). + * Useful for capacity planning and sprint estimation. + * + * **Rate Limit**: 60 requests/minute + */ +export const getProjectVelocity = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/velocity', + ...options + }); +}; + +/** + * Delete Sprint + * + * Delete a sprint permanently. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Only PLANNED or CANCELLED sprints can be deleted + * - ACTIVE or COMPLETED sprints must be cancelled first + * - Issues in the sprint will have their sprint_id set to NULL + * + * **Rate Limit**: 10 requests/minute + */ +export const deleteSprint = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}', + ...options + }); +}; + +/** + * Get Sprint Details + * + * Get detailed information about a specific sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * Includes issue counts and project information. + * + * **Rate Limit**: 60 requests/minute + */ +export const getSprint = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}', + ...options + }); +}; + +/** + * Update Sprint + * + * Update sprint information. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Cannot modify COMPLETED sprints + * - Cannot modify CANCELLED sprints + * - End date must remain after start date + * + * **Rate Limit**: 30 requests/minute + */ +export const updateSprint = (options: Options) => { + return (options.client ?? client).patch({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Start Sprint + * + * Start a planned sprint, making it the active sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Only PLANNED sprints can be started + * - Only one active sprint per project allowed + * - Calculates planned points from assigned issues + * + * **Rate Limit**: 10 requests/minute + */ +export const startSprint = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/start', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Complete Sprint + * + * Complete an active sprint and calculate velocity. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Only ACTIVE sprints can be completed + * - Velocity (completed story points) is auto-calculated + * - Incomplete issues remain in the sprint but can be moved + * + * **Rate Limit**: 10 requests/minute + */ +export const completeSprint = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/complete', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Cancel Sprint + * + * Cancel a planned or active sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Only PLANNED or ACTIVE sprints can be cancelled + * - Issues in the sprint are NOT automatically removed + * - Cancelled sprints cannot be reactivated + * + * **Rate Limit**: 10 requests/minute + */ +export const cancelSprint = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/cancel', + ...options + }); +}; + +/** + * Remove Issue from Sprint + * + * Remove an issue from a sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Issue must currently be in this sprint + * - Cannot modify COMPLETED sprints (use cancel first) + * + * **Rate Limit**: 30 requests/minute + */ +export const removeIssueFromSprint = (options: Options) => { + return (options.client ?? client).delete({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues', + ...options + }); +}; + +/** + * Get Sprint Issues + * + * Get all issues assigned to a sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * Issues are returned sorted by priority (highest first) then creation date. + * + * **Rate Limit**: 60 requests/minute + */ +export const getSprintIssues = (options: Options) => { + return (options.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues', + ...options + }); +}; + +/** + * Add Issue to Sprint + * + * Add an existing issue to a sprint. + * + * **Authentication**: Required (Bearer token) + * **Authorization**: Project owner or superuser + * + * **Business Rules**: + * - Issue must belong to the same project + * - Cannot add issues to COMPLETED or CANCELLED sprints + * + * **Rate Limit**: 30 requests/minute + */ +export const addIssueToSprint = (options: Options) => { + return (options.client ?? client).post({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues', + ...options + }); +}; + /** * OAuth Server Metadata * diff --git a/frontend/src/lib/api/generated/types.gen.ts b/frontend/src/lib/api/generated/types.gen.ts index 198ee95..d5a426b 100644 --- a/frontend/src/lib/api/generated/types.gen.ts +++ b/frontend/src/lib/api/generated/types.gen.ts @@ -115,6 +115,419 @@ export type AdminStatsResponse = { user_status: Array; }; +/** + * AgentInstanceCreate + * + * Schema for creating a new agent instance. + */ +export type AgentInstanceCreate = { + /** + * Agent Type Id + */ + agent_type_id: string; + /** + * Project Id + */ + project_id: string; + /** + * Name + */ + name: string; + status?: AgentStatus; + /** + * Current Task + */ + current_task?: string | null; + /** + * Short Term Memory + */ + short_term_memory?: { + [key: string]: unknown; + }; + /** + * Long Term Memory Ref + */ + long_term_memory_ref?: string | null; + /** + * Session Id + */ + session_id?: string | null; +}; + +/** + * AgentInstanceMetrics + * + * Schema for agent instance metrics summary. + */ +export type AgentInstanceMetrics = { + /** + * Total Instances + */ + total_instances: number; + /** + * Active Instances + */ + active_instances: number; + /** + * Idle Instances + */ + idle_instances: number; + /** + * Total Tasks Completed + */ + total_tasks_completed: number; + /** + * Total Tokens Used + */ + total_tokens_used: number; + /** + * Total Cost Incurred + */ + total_cost_incurred: string; +}; + +/** + * AgentInstanceResponse + * + * Schema for agent instance API responses. + */ +export type AgentInstanceResponse = { + /** + * Id + */ + id: string; + /** + * Agent Type Id + */ + agent_type_id: string; + /** + * Project Id + */ + project_id: string; + /** + * Name + */ + name: string; + status: AgentStatus; + /** + * Current Task + */ + current_task?: string | null; + /** + * Short Term Memory + */ + short_term_memory?: { + [key: string]: unknown; + }; + /** + * Long Term Memory Ref + */ + long_term_memory_ref?: string | null; + /** + * Session Id + */ + session_id?: string | null; + /** + * Last Activity At + */ + last_activity_at?: string | null; + /** + * Terminated At + */ + terminated_at?: string | null; + /** + * Tasks Completed + */ + tasks_completed?: number; + /** + * Tokens Used + */ + tokens_used?: number; + /** + * Cost Incurred + */ + cost_incurred?: string; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; + /** + * Agent Type Name + */ + agent_type_name?: string | null; + /** + * Agent Type Slug + */ + agent_type_slug?: string | null; + /** + * Project Name + */ + project_name?: string | null; + /** + * Project Slug + */ + project_slug?: string | null; + /** + * Assigned Issues Count + */ + assigned_issues_count?: number | null; +}; + +/** + * AgentInstanceUpdate + * + * Schema for updating an agent instance. + */ +export type AgentInstanceUpdate = { + status?: AgentStatus | null; + /** + * Current Task + */ + current_task?: string | null; + /** + * Short Term Memory + */ + short_term_memory?: { + [key: string]: unknown; + } | null; + /** + * Long Term Memory Ref + */ + long_term_memory_ref?: string | null; + /** + * Session Id + */ + session_id?: string | null; + /** + * Last Activity At + */ + last_activity_at?: string | null; + /** + * Tasks Completed + */ + tasks_completed?: number | null; + /** + * Tokens Used + */ + tokens_used?: number | null; + /** + * Cost Incurred + */ + cost_incurred?: number | string | null; +}; + +/** + * AgentStatus + * + * Current operational status of an agent instance. + * + * IDLE: Agent is available but not currently working + * WORKING: Agent is actively processing a task + * WAITING: Agent is waiting for external input or approval + * PAUSED: Agent has been manually paused + * TERMINATED: Agent instance has been shut down + */ +export type AgentStatus = 'idle' | 'working' | 'waiting' | 'paused' | 'terminated'; + +/** + * AgentTypeCreate + * + * Schema for creating a new agent type. + */ +export type AgentTypeCreate = { + /** + * Name + */ + name: string; + /** + * Slug + */ + slug: string; + /** + * Description + */ + description?: string | null; + /** + * Expertise + */ + expertise?: Array; + /** + * Personality Prompt + */ + personality_prompt: string; + /** + * Primary Model + */ + primary_model: string; + /** + * Fallback Models + */ + fallback_models?: Array; + /** + * Model Params + */ + model_params?: { + [key: string]: unknown; + }; + /** + * Mcp Servers + */ + mcp_servers?: Array; + /** + * Tool Permissions + */ + tool_permissions?: { + [key: string]: unknown; + }; + /** + * Is Active + */ + is_active?: boolean; +}; + +/** + * AgentTypeResponse + * + * Schema for agent type API responses. + */ +export type AgentTypeResponse = { + /** + * Name + */ + name: string; + /** + * Slug + */ + slug?: string | null; + /** + * Description + */ + description?: string | null; + /** + * Expertise + */ + expertise?: Array; + /** + * Personality Prompt + */ + personality_prompt: string; + /** + * Primary Model + */ + primary_model: string; + /** + * Fallback Models + */ + fallback_models?: Array; + /** + * Model Params + */ + model_params?: { + [key: string]: unknown; + }; + /** + * Mcp Servers + */ + mcp_servers?: Array; + /** + * Tool Permissions + */ + tool_permissions?: { + [key: string]: unknown; + }; + /** + * Is Active + */ + is_active?: boolean; + /** + * Id + */ + id: string; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; + /** + * Instance Count + */ + instance_count?: number | null; +}; + +/** + * AgentTypeUpdate + * + * Schema for updating an agent type. + */ +export type AgentTypeUpdate = { + /** + * Name + */ + name?: string | null; + /** + * Slug + */ + slug?: string | null; + /** + * Description + */ + description?: string | null; + /** + * Expertise + */ + expertise?: Array | null; + /** + * Personality Prompt + */ + personality_prompt?: string | null; + /** + * Primary Model + */ + primary_model?: string | null; + /** + * Fallback Models + */ + fallback_models?: Array | null; + /** + * Model Params + */ + model_params?: { + [key: string]: unknown; + } | null; + /** + * Mcp Servers + */ + mcp_servers?: Array | null; + /** + * Tool Permissions + */ + tool_permissions?: { + [key: string]: unknown; + } | null; + /** + * Is Active + */ + is_active?: boolean | null; +}; + +/** + * AutonomyLevel + * + * Defines how much control the human has over agent actions. + * + * FULL_CONTROL: Human must approve every agent action + * MILESTONE: Human approves at sprint boundaries and major decisions + * AUTONOMOUS: Agents work independently, only escalating critical issues + */ +export type AutonomyLevel = 'full_control' | 'milestone' | 'autonomous'; + /** * Body_login_oauth */ @@ -406,6 +819,289 @@ export type HttpValidationError = { detail?: Array; }; +/** + * IssueAssign + * + * Schema for assigning an issue. + */ +export type IssueAssign = { + /** + * Assigned Agent Id + */ + assigned_agent_id?: string | null; + /** + * Human Assignee + */ + human_assignee?: string | null; +}; + +/** + * IssueCreate + * + * Schema for creating a new issue. + */ +export type IssueCreate = { + /** + * Title + */ + title: string; + /** + * Body + */ + body?: string; + status?: IssueStatus; + priority?: IssuePriority; + /** + * Labels + */ + labels?: Array; + /** + * Story Points + */ + story_points?: number | null; + /** + * Project Id + */ + project_id: string; + /** + * Assigned Agent Id + */ + assigned_agent_id?: string | null; + /** + * Human Assignee + */ + human_assignee?: string | null; + /** + * Sprint Id + */ + sprint_id?: string | null; + /** + * External Tracker Type + */ + external_tracker_type?: 'gitea' | 'github' | 'gitlab' | null; + /** + * External Issue Id + */ + external_issue_id?: string | null; + /** + * Remote Url + */ + remote_url?: string | null; + /** + * External Issue Number + */ + external_issue_number?: number | null; +}; + +/** + * IssuePriority + * + * Issue priority levels. + * + * LOW: Nice to have, can be deferred + * MEDIUM: Standard priority, should be done + * HIGH: Important, should be prioritized + * CRITICAL: Must be done immediately, blocking other work + */ +export type IssuePriority = 'low' | 'medium' | 'high' | 'critical'; + +/** + * IssueResponse + * + * Schema for issue API responses. + */ +export type IssueResponse = { + /** + * Id + */ + id: string; + /** + * Project Id + */ + project_id: string; + /** + * Title + */ + title: string; + /** + * Body + */ + body: string; + status: IssueStatus; + priority: IssuePriority; + /** + * Labels + */ + labels?: Array; + /** + * Assigned Agent Id + */ + assigned_agent_id?: string | null; + /** + * Human Assignee + */ + human_assignee?: string | null; + /** + * Sprint Id + */ + sprint_id?: string | null; + /** + * Story Points + */ + story_points?: number | null; + /** + * External Tracker Type + */ + external_tracker_type?: string | null; + /** + * External Issue Id + */ + external_issue_id?: string | null; + /** + * Remote Url + */ + remote_url?: string | null; + /** + * External Issue Number + */ + external_issue_number?: number | null; + sync_status?: SyncStatus; + /** + * Last Synced At + */ + last_synced_at?: string | null; + /** + * External Updated At + */ + external_updated_at?: string | null; + /** + * Closed At + */ + closed_at?: string | null; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; + /** + * Project Name + */ + project_name?: string | null; + /** + * Project Slug + */ + project_slug?: string | null; + /** + * Sprint Name + */ + sprint_name?: string | null; + /** + * Assigned Agent Type Name + */ + assigned_agent_type_name?: string | null; +}; + +/** + * IssueStats + * + * Schema for issue statistics. + */ +export type IssueStats = { + /** + * Total + */ + total: number; + /** + * Open + */ + open: number; + /** + * In Progress + */ + in_progress: number; + /** + * In Review + */ + in_review: number; + /** + * Blocked + */ + blocked: number; + /** + * Closed + */ + closed: number; + /** + * By Priority + */ + by_priority: { + [key: string]: number; + }; + /** + * Total Story Points + */ + total_story_points?: number | null; + /** + * Completed Story Points + */ + completed_story_points?: number | null; +}; + +/** + * IssueStatus + * + * Issue workflow status. + * + * OPEN: Issue is ready to be worked on + * IN_PROGRESS: Agent or human is actively working on the issue + * IN_REVIEW: Work is complete, awaiting review + * BLOCKED: Issue cannot proceed due to dependencies or blockers + * CLOSED: Issue has been completed or cancelled + */ +export type IssueStatus = 'open' | 'in_progress' | 'in_review' | 'blocked' | 'closed'; + +/** + * IssueUpdate + * + * Schema for updating an issue. + */ +export type IssueUpdate = { + /** + * Title + */ + title?: string | null; + /** + * Body + */ + body?: string | null; + status?: IssueStatus | null; + priority?: IssuePriority | null; + /** + * Labels + */ + labels?: Array | null; + /** + * Assigned Agent Id + */ + assigned_agent_id?: string | null; + /** + * Human Assignee + */ + human_assignee?: string | null; + /** + * Sprint Id + */ + sprint_id?: string | null; + /** + * Story Points + */ + story_points?: number | null; + sync_status?: SyncStatus | null; +}; + /** * LoginRequest */ @@ -1034,6 +1730,54 @@ export type PaginatedResponseAdminSessionResponse = { pagination: PaginationMeta; }; +/** + * PaginatedResponse[AgentInstanceResponse] + */ +export type PaginatedResponseAgentInstanceResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + +/** + * PaginatedResponse[AgentTypeResponse] + */ +export type PaginatedResponseAgentTypeResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + +/** + * PaginatedResponse[IssueResponse] + */ +export type PaginatedResponseIssueResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + /** * PaginatedResponse[OrganizationMemberResponse] */ @@ -1066,6 +1810,38 @@ export type PaginatedResponseOrganizationResponse = { pagination: PaginationMeta; }; +/** + * PaginatedResponse[ProjectResponse] + */ +export type PaginatedResponseProjectResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + +/** + * PaginatedResponse[SprintResponse] + */ +export type PaginatedResponseSprintResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + /** * PaginatedResponse[UserResponse] */ @@ -1176,6 +1952,138 @@ export type PasswordResetRequest = { email: string; }; +/** + * ProjectCreate + * + * Schema for creating a new project. + */ +export type ProjectCreate = { + /** + * Name + */ + name: string; + /** + * Slug + */ + slug: string; + /** + * Description + */ + description?: string | null; + autonomy_level?: AutonomyLevel; + status?: ProjectStatus; + /** + * Settings + */ + settings?: { + [key: string]: unknown; + }; + /** + * Owner Id + */ + owner_id?: string | null; +}; + +/** + * ProjectResponse + * + * Schema for project API responses. + */ +export type ProjectResponse = { + /** + * Name + */ + name: string; + /** + * Slug + */ + slug?: string | null; + /** + * Description + */ + description?: string | null; + autonomy_level?: AutonomyLevel; + status?: ProjectStatus; + /** + * Settings + */ + settings?: { + [key: string]: unknown; + }; + /** + * Id + */ + id: string; + /** + * Owner Id + */ + owner_id?: string | null; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; + /** + * Agent Count + */ + agent_count?: number | null; + /** + * Issue Count + */ + issue_count?: number | null; + /** + * Active Sprint Name + */ + active_sprint_name?: string | null; +}; + +/** + * ProjectStatus + * + * Project lifecycle status. + * + * ACTIVE: Project is actively being worked on + * PAUSED: Project is temporarily on hold + * COMPLETED: Project has been delivered successfully + * ARCHIVED: Project is no longer accessible for work + */ +export type ProjectStatus = 'active' | 'paused' | 'completed' | 'archived'; + +/** + * ProjectUpdate + * + * Schema for updating a project. + * + * Note: owner_id is intentionally excluded to prevent IDOR vulnerabilities. + * Project ownership transfer should be done via a dedicated endpoint with + * proper authorization checks. + */ +export type ProjectUpdate = { + /** + * Name + */ + name?: string | null; + /** + * Slug + */ + slug?: string | null; + /** + * Description + */ + description?: string | null; + autonomy_level?: AutonomyLevel | null; + status?: ProjectStatus | null; + /** + * Settings + */ + settings?: { + [key: string]: unknown; + } | null; +}; + /** * RefreshTokenRequest */ @@ -1281,6 +2189,234 @@ export type SessionResponse = { */ export type SortOrder = 'asc' | 'desc'; +/** + * SprintComplete + * + * Schema for completing a sprint. + */ +export type SprintComplete = { + /** + * Velocity + */ + velocity?: number | null; + /** + * Notes + */ + notes?: string | null; +}; + +/** + * SprintCreate + * + * Schema for creating a new sprint. + */ +export type SprintCreate = { + /** + * Name + */ + name: string; + /** + * Number + */ + number: number; + /** + * Goal + */ + goal?: string | null; + /** + * Start Date + */ + start_date: string; + /** + * End Date + */ + end_date: string; + status?: SprintStatus; + /** + * Planned Points + */ + planned_points?: number | null; + /** + * Velocity + */ + velocity?: number | null; + /** + * Project Id + */ + project_id: string; +}; + +/** + * SprintResponse + * + * Schema for sprint API responses. + */ +export type SprintResponse = { + /** + * Name + */ + name: string; + /** + * Number + */ + number: number; + /** + * Goal + */ + goal?: string | null; + /** + * Start Date + */ + start_date: string; + /** + * End Date + */ + end_date: string; + status?: SprintStatus; + /** + * Planned Points + */ + planned_points?: number | null; + /** + * Velocity + */ + velocity?: number | null; + /** + * Id + */ + id: string; + /** + * Project Id + */ + project_id: string; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; + /** + * Project Name + */ + project_name?: string | null; + /** + * Project Slug + */ + project_slug?: string | null; + /** + * Issue Count + */ + issue_count?: number | null; + /** + * Open Issues + */ + open_issues?: number | null; + /** + * Completed Issues + */ + completed_issues?: number | null; +}; + +/** + * SprintStart + * + * Schema for starting a sprint. + */ +export type SprintStart = { + /** + * Start Date + */ + start_date?: string | null; +}; + +/** + * SprintStatus + * + * Sprint lifecycle status. + * + * PLANNED: Sprint has been created but not started + * ACTIVE: Sprint is currently in progress + * IN_REVIEW: Sprint work is done, demo/review pending + * COMPLETED: Sprint has been finished successfully + * CANCELLED: Sprint was cancelled before completion + */ +export type SprintStatus = 'planned' | 'active' | 'in_review' | 'completed' | 'cancelled'; + +/** + * SprintUpdate + * + * Schema for updating a sprint. + */ +export type SprintUpdate = { + /** + * Name + */ + name?: string | null; + /** + * Goal + */ + goal?: string | null; + /** + * Start Date + */ + start_date?: string | null; + /** + * End Date + */ + end_date?: string | null; + status?: SprintStatus | null; + /** + * Planned Points + */ + planned_points?: number | null; + /** + * Velocity + */ + velocity?: number | null; +}; + +/** + * SprintVelocity + * + * Schema for sprint velocity metrics. + */ +export type SprintVelocity = { + /** + * Sprint Number + */ + sprint_number: number; + /** + * Sprint Name + */ + sprint_name: string; + /** + * Planned Points + */ + planned_points: number | null; + /** + * Velocity + */ + velocity: number | null; + /** + * Velocity Ratio + */ + velocity_ratio: number | null; +}; + +/** + * SyncStatus + * + * External issue tracker synchronization status. + * + * SYNCED: Local and remote are in sync + * PENDING: Local changes waiting to be pushed + * CONFLICT: Merge conflict between local and remote + * ERROR: Synchronization failed due to an error + */ +export type SyncStatus = 'synced' | 'pending' | 'conflict' | 'error'; + /** * Token */ @@ -3189,6 +4325,10 @@ export type GetOrganizationMembersResponse = GetOrganizationMembersResponses[key export type StreamProjectEventsData = { body?: never; headers?: { + /** + * Authorization + */ + Authorization?: string | null; /** * Last-Event-Id */ @@ -3200,7 +4340,14 @@ export type StreamProjectEventsData = { */ project_id: string; }; - query?: never; + query?: { + /** + * Token + * + * Auth token (for EventSource compatibility) + */ + token?: string | null; + }; url: '/api/v1/projects/{project_id}/events/stream'; }; @@ -3274,6 +4421,1599 @@ export type SendTestEventResponses = { export type SendTestEventResponse = SendTestEventResponses[keyof SendTestEventResponses]; +export type ListProjectsData = { + body?: never; + path?: never; + query?: { + /** + * Status + * + * Filter by project status + */ + status?: ProjectStatus | null; + /** + * Search + * + * Search by name, slug, or description + */ + search?: string | null; + /** + * All Projects + * + * Show all projects (superuser only) + */ + all_projects?: boolean; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/projects'; +}; + +export type ListProjectsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ListProjectsError = ListProjectsErrors[keyof ListProjectsErrors]; + +export type ListProjectsResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseProjectResponse; +}; + +export type ListProjectsResponse = ListProjectsResponses[keyof ListProjectsResponses]; + +export type CreateProjectData = { + body: ProjectCreate; + path?: never; + query?: never; + url: '/api/v1/projects'; +}; + +export type CreateProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateProjectError = CreateProjectErrors[keyof CreateProjectErrors]; + +export type CreateProjectResponses = { + /** + * Successful Response + */ + 201: ProjectResponse; +}; + +export type CreateProjectResponse = CreateProjectResponses[keyof CreateProjectResponses]; + +export type ArchiveProjectData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}'; +}; + +export type ArchiveProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ArchiveProjectError = ArchiveProjectErrors[keyof ArchiveProjectErrors]; + +export type ArchiveProjectResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type ArchiveProjectResponse = ArchiveProjectResponses[keyof ArchiveProjectResponses]; + +export type GetProjectData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}'; +}; + +export type GetProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetProjectError = GetProjectErrors[keyof GetProjectErrors]; + +export type GetProjectResponses = { + /** + * Successful Response + */ + 200: ProjectResponse; +}; + +export type GetProjectResponse = GetProjectResponses[keyof GetProjectResponses]; + +export type UpdateProjectData = { + body: ProjectUpdate; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}'; +}; + +export type UpdateProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateProjectError = UpdateProjectErrors[keyof UpdateProjectErrors]; + +export type UpdateProjectResponses = { + /** + * Successful Response + */ + 200: ProjectResponse; +}; + +export type UpdateProjectResponse = UpdateProjectResponses[keyof UpdateProjectResponses]; + +export type GetProjectBySlugData = { + body?: never; + path: { + /** + * Slug + */ + slug: string; + }; + query?: never; + url: '/api/v1/projects/slug/{slug}'; +}; + +export type GetProjectBySlugErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetProjectBySlugError = GetProjectBySlugErrors[keyof GetProjectBySlugErrors]; + +export type GetProjectBySlugResponses = { + /** + * Successful Response + */ + 200: ProjectResponse; +}; + +export type GetProjectBySlugResponse = GetProjectBySlugResponses[keyof GetProjectBySlugResponses]; + +export type PauseProjectData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/pause'; +}; + +export type PauseProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type PauseProjectError = PauseProjectErrors[keyof PauseProjectErrors]; + +export type PauseProjectResponses = { + /** + * Successful Response + */ + 200: ProjectResponse; +}; + +export type PauseProjectResponse = PauseProjectResponses[keyof PauseProjectResponses]; + +export type ResumeProjectData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/resume'; +}; + +export type ResumeProjectErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ResumeProjectError = ResumeProjectErrors[keyof ResumeProjectErrors]; + +export type ResumeProjectResponses = { + /** + * Successful Response + */ + 200: ProjectResponse; +}; + +export type ResumeProjectResponse = ResumeProjectResponses[keyof ResumeProjectResponses]; + +export type ListAgentTypesData = { + body?: never; + path?: never; + query?: { + /** + * Is Active + * + * Filter by active status + */ + is_active?: boolean; + /** + * Search + * + * Search by name, slug, description + */ + search?: string | null; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/agent-types'; +}; + +export type ListAgentTypesErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ListAgentTypesError = ListAgentTypesErrors[keyof ListAgentTypesErrors]; + +export type ListAgentTypesResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseAgentTypeResponse; +}; + +export type ListAgentTypesResponse = ListAgentTypesResponses[keyof ListAgentTypesResponses]; + +export type CreateAgentTypeData = { + body: AgentTypeCreate; + path?: never; + query?: never; + url: '/api/v1/agent-types'; +}; + +export type CreateAgentTypeErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateAgentTypeError = CreateAgentTypeErrors[keyof CreateAgentTypeErrors]; + +export type CreateAgentTypeResponses = { + /** + * Successful Response + */ + 201: AgentTypeResponse; +}; + +export type CreateAgentTypeResponse = CreateAgentTypeResponses[keyof CreateAgentTypeResponses]; + +export type DeactivateAgentTypeData = { + body?: never; + path: { + /** + * Agent Type Id + */ + agent_type_id: string; + }; + query?: never; + url: '/api/v1/agent-types/{agent_type_id}'; +}; + +export type DeactivateAgentTypeErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeactivateAgentTypeError = DeactivateAgentTypeErrors[keyof DeactivateAgentTypeErrors]; + +export type DeactivateAgentTypeResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type DeactivateAgentTypeResponse = DeactivateAgentTypeResponses[keyof DeactivateAgentTypeResponses]; + +export type GetAgentTypeData = { + body?: never; + path: { + /** + * Agent Type Id + */ + agent_type_id: string; + }; + query?: never; + url: '/api/v1/agent-types/{agent_type_id}'; +}; + +export type GetAgentTypeErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetAgentTypeError = GetAgentTypeErrors[keyof GetAgentTypeErrors]; + +export type GetAgentTypeResponses = { + /** + * Successful Response + */ + 200: AgentTypeResponse; +}; + +export type GetAgentTypeResponse = GetAgentTypeResponses[keyof GetAgentTypeResponses]; + +export type UpdateAgentTypeData = { + body: AgentTypeUpdate; + path: { + /** + * Agent Type Id + */ + agent_type_id: string; + }; + query?: never; + url: '/api/v1/agent-types/{agent_type_id}'; +}; + +export type UpdateAgentTypeErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateAgentTypeError = UpdateAgentTypeErrors[keyof UpdateAgentTypeErrors]; + +export type UpdateAgentTypeResponses = { + /** + * Successful Response + */ + 200: AgentTypeResponse; +}; + +export type UpdateAgentTypeResponse = UpdateAgentTypeResponses[keyof UpdateAgentTypeResponses]; + +export type GetAgentTypeBySlugData = { + body?: never; + path: { + /** + * Slug + */ + slug: string; + }; + query?: never; + url: '/api/v1/agent-types/slug/{slug}'; +}; + +export type GetAgentTypeBySlugErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetAgentTypeBySlugError = GetAgentTypeBySlugErrors[keyof GetAgentTypeBySlugErrors]; + +export type GetAgentTypeBySlugResponses = { + /** + * Successful Response + */ + 200: AgentTypeResponse; +}; + +export type GetAgentTypeBySlugResponse = GetAgentTypeBySlugResponses[keyof GetAgentTypeBySlugResponses]; + +export type ListIssuesData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: { + /** + * Status + * + * Filter by issue status + */ + status?: IssueStatus | null; + /** + * Priority + * + * Filter by priority + */ + priority?: IssuePriority | null; + /** + * Labels + * + * Filter by labels (comma-separated) + */ + labels?: Array | null; + /** + * Sprint Id + * + * Filter by sprint ID + */ + sprint_id?: string | null; + /** + * Assigned Agent Id + * + * Filter by assigned agent ID + */ + assigned_agent_id?: string | null; + /** + * Sync Status + * + * Filter by sync status + */ + sync_status?: SyncStatus | null; + /** + * Search + * + * Search in title and body + */ + search?: string | null; + /** + * Sort By + * + * Field to sort by (created_at, updated_at, priority, status, title) + */ + sort_by?: string; + /** + * Sort order + */ + sort_order?: SortOrder; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/projects/{project_id}/issues'; +}; + +export type ListIssuesErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ListIssuesError = ListIssuesErrors[keyof ListIssuesErrors]; + +export type ListIssuesResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseIssueResponse; +}; + +export type ListIssuesResponse = ListIssuesResponses[keyof ListIssuesResponses]; + +export type CreateIssueData = { + body: IssueCreate; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues'; +}; + +export type CreateIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateIssueError = CreateIssueErrors[keyof CreateIssueErrors]; + +export type CreateIssueResponses = { + /** + * Successful Response + */ + 201: IssueResponse; +}; + +export type CreateIssueResponse = CreateIssueResponses[keyof CreateIssueResponses]; + +export type GetIssueStatsData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/stats'; +}; + +export type GetIssueStatsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetIssueStatsError = GetIssueStatsErrors[keyof GetIssueStatsErrors]; + +export type GetIssueStatsResponses = { + /** + * Successful Response + */ + 200: IssueStats; +}; + +export type GetIssueStatsResponse = GetIssueStatsResponses[keyof GetIssueStatsResponses]; + +export type DeleteIssueData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}'; +}; + +export type DeleteIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteIssueError = DeleteIssueErrors[keyof DeleteIssueErrors]; + +export type DeleteIssueResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type DeleteIssueResponse = DeleteIssueResponses[keyof DeleteIssueResponses]; + +export type GetIssueData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}'; +}; + +export type GetIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetIssueError = GetIssueErrors[keyof GetIssueErrors]; + +export type GetIssueResponses = { + /** + * Successful Response + */ + 200: IssueResponse; +}; + +export type GetIssueResponse = GetIssueResponses[keyof GetIssueResponses]; + +export type UpdateIssueData = { + body: IssueUpdate; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}'; +}; + +export type UpdateIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateIssueError = UpdateIssueErrors[keyof UpdateIssueErrors]; + +export type UpdateIssueResponses = { + /** + * Successful Response + */ + 200: IssueResponse; +}; + +export type UpdateIssueResponse = UpdateIssueResponses[keyof UpdateIssueResponses]; + +export type AssignIssueData = { + body: IssueAssign; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}/assign'; +}; + +export type AssignIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type AssignIssueError = AssignIssueErrors[keyof AssignIssueErrors]; + +export type AssignIssueResponses = { + /** + * Successful Response + */ + 200: IssueResponse; +}; + +export type AssignIssueResponse = AssignIssueResponses[keyof AssignIssueResponses]; + +export type UnassignIssueData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}/assignment'; +}; + +export type UnassignIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UnassignIssueError = UnassignIssueErrors[keyof UnassignIssueErrors]; + +export type UnassignIssueResponses = { + /** + * Successful Response + */ + 200: IssueResponse; +}; + +export type UnassignIssueResponse = UnassignIssueResponses[keyof UnassignIssueResponses]; + +export type SyncIssueData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Issue Id + */ + issue_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/issues/{issue_id}/sync'; +}; + +export type SyncIssueErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type SyncIssueError = SyncIssueErrors[keyof SyncIssueErrors]; + +export type SyncIssueResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type SyncIssueResponse = SyncIssueResponses[keyof SyncIssueResponses]; + +export type ListProjectAgentsData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: { + /** + * Status + * + * Filter by agent status + */ + status?: AgentStatus | null; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/projects/{project_id}/agents'; +}; + +export type ListProjectAgentsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ListProjectAgentsError = ListProjectAgentsErrors[keyof ListProjectAgentsErrors]; + +export type ListProjectAgentsResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseAgentInstanceResponse; +}; + +export type ListProjectAgentsResponse = ListProjectAgentsResponses[keyof ListProjectAgentsResponses]; + +export type SpawnAgentData = { + body: AgentInstanceCreate; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents'; +}; + +export type SpawnAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type SpawnAgentError = SpawnAgentErrors[keyof SpawnAgentErrors]; + +export type SpawnAgentResponses = { + /** + * Successful Response + */ + 201: AgentInstanceResponse; +}; + +export type SpawnAgentResponse = SpawnAgentResponses[keyof SpawnAgentResponses]; + +export type GetProjectAgentMetricsData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/metrics'; +}; + +export type GetProjectAgentMetricsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetProjectAgentMetricsError = GetProjectAgentMetricsErrors[keyof GetProjectAgentMetricsErrors]; + +export type GetProjectAgentMetricsResponses = { + /** + * Successful Response + */ + 200: AgentInstanceMetrics; +}; + +export type GetProjectAgentMetricsResponse = GetProjectAgentMetricsResponses[keyof GetProjectAgentMetricsResponses]; + +export type TerminateAgentData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}'; +}; + +export type TerminateAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type TerminateAgentError = TerminateAgentErrors[keyof TerminateAgentErrors]; + +export type TerminateAgentResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type TerminateAgentResponse = TerminateAgentResponses[keyof TerminateAgentResponses]; + +export type GetAgentData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}'; +}; + +export type GetAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetAgentError = GetAgentErrors[keyof GetAgentErrors]; + +export type GetAgentResponses = { + /** + * Successful Response + */ + 200: AgentInstanceResponse; +}; + +export type GetAgentResponse = GetAgentResponses[keyof GetAgentResponses]; + +export type UpdateAgentData = { + body: AgentInstanceUpdate; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}'; +}; + +export type UpdateAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateAgentError = UpdateAgentErrors[keyof UpdateAgentErrors]; + +export type UpdateAgentResponses = { + /** + * Successful Response + */ + 200: AgentInstanceResponse; +}; + +export type UpdateAgentResponse = UpdateAgentResponses[keyof UpdateAgentResponses]; + +export type PauseAgentData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}/pause'; +}; + +export type PauseAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type PauseAgentError = PauseAgentErrors[keyof PauseAgentErrors]; + +export type PauseAgentResponses = { + /** + * Successful Response + */ + 200: AgentInstanceResponse; +}; + +export type PauseAgentResponse = PauseAgentResponses[keyof PauseAgentResponses]; + +export type ResumeAgentData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}/resume'; +}; + +export type ResumeAgentErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ResumeAgentError = ResumeAgentErrors[keyof ResumeAgentErrors]; + +export type ResumeAgentResponses = { + /** + * Successful Response + */ + 200: AgentInstanceResponse; +}; + +export type ResumeAgentResponse = ResumeAgentResponses[keyof ResumeAgentResponses]; + +export type GetAgentMetricsData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Agent Id + */ + agent_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/agents/{agent_id}/metrics'; +}; + +export type GetAgentMetricsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetAgentMetricsError = GetAgentMetricsErrors[keyof GetAgentMetricsErrors]; + +export type GetAgentMetricsResponses = { + /** + * Successful Response + */ + 200: AgentInstanceMetrics; +}; + +export type GetAgentMetricsResponse = GetAgentMetricsResponses[keyof GetAgentMetricsResponses]; + +export type ListSprintsData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: { + /** + * Status + * + * Filter by sprint status + */ + status?: SprintStatus | null; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/projects/{project_id}/sprints'; +}; + +export type ListSprintsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ListSprintsError = ListSprintsErrors[keyof ListSprintsErrors]; + +export type ListSprintsResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseSprintResponse; +}; + +export type ListSprintsResponse = ListSprintsResponses[keyof ListSprintsResponses]; + +export type CreateSprintData = { + body: SprintCreate; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints'; +}; + +export type CreateSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateSprintError = CreateSprintErrors[keyof CreateSprintErrors]; + +export type CreateSprintResponses = { + /** + * Successful Response + */ + 201: SprintResponse; +}; + +export type CreateSprintResponse = CreateSprintResponses[keyof CreateSprintResponses]; + +export type GetActiveSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/active'; +}; + +export type GetActiveSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetActiveSprintError = GetActiveSprintErrors[keyof GetActiveSprintErrors]; + +export type GetActiveSprintResponses = { + /** + * Response Get Active Sprint + * + * Successful Response + */ + 200: SprintResponse | null; +}; + +export type GetActiveSprintResponse = GetActiveSprintResponses[keyof GetActiveSprintResponses]; + +export type GetProjectVelocityData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + }; + query?: { + /** + * Limit + * + * Number of completed sprints to include + */ + limit?: number; + }; + url: '/api/v1/projects/{project_id}/sprints/velocity'; +}; + +export type GetProjectVelocityErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetProjectVelocityError = GetProjectVelocityErrors[keyof GetProjectVelocityErrors]; + +export type GetProjectVelocityResponses = { + /** + * Response Get Project Velocity + * + * Successful Response + */ + 200: Array; +}; + +export type GetProjectVelocityResponse = GetProjectVelocityResponses[keyof GetProjectVelocityResponses]; + +export type DeleteSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}'; +}; + +export type DeleteSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteSprintError = DeleteSprintErrors[keyof DeleteSprintErrors]; + +export type DeleteSprintResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type DeleteSprintResponse = DeleteSprintResponses[keyof DeleteSprintResponses]; + +export type GetSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}'; +}; + +export type GetSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetSprintError = GetSprintErrors[keyof GetSprintErrors]; + +export type GetSprintResponses = { + /** + * Successful Response + */ + 200: SprintResponse; +}; + +export type GetSprintResponse = GetSprintResponses[keyof GetSprintResponses]; + +export type UpdateSprintData = { + body: SprintUpdate; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}'; +}; + +export type UpdateSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateSprintError = UpdateSprintErrors[keyof UpdateSprintErrors]; + +export type UpdateSprintResponses = { + /** + * Successful Response + */ + 200: SprintResponse; +}; + +export type UpdateSprintResponse = UpdateSprintResponses[keyof UpdateSprintResponses]; + +export type StartSprintData = { + /** + * Sprint Start + */ + body?: SprintStart | null; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/start'; +}; + +export type StartSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type StartSprintError = StartSprintErrors[keyof StartSprintErrors]; + +export type StartSprintResponses = { + /** + * Successful Response + */ + 200: SprintResponse; +}; + +export type StartSprintResponse = StartSprintResponses[keyof StartSprintResponses]; + +export type CompleteSprintData = { + /** + * Sprint Complete + */ + body?: SprintComplete | null; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/complete'; +}; + +export type CompleteSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CompleteSprintError = CompleteSprintErrors[keyof CompleteSprintErrors]; + +export type CompleteSprintResponses = { + /** + * Successful Response + */ + 200: SprintResponse; +}; + +export type CompleteSprintResponse = CompleteSprintResponses[keyof CompleteSprintResponses]; + +export type CancelSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: never; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/cancel'; +}; + +export type CancelSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CancelSprintError = CancelSprintErrors[keyof CancelSprintErrors]; + +export type CancelSprintResponses = { + /** + * Successful Response + */ + 200: SprintResponse; +}; + +export type CancelSprintResponse = CancelSprintResponses[keyof CancelSprintResponses]; + +export type RemoveIssueFromSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query: { + /** + * Issue Id + * + * ID of the issue to remove from the sprint + */ + issue_id: string; + }; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues'; +}; + +export type RemoveIssueFromSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type RemoveIssueFromSprintError = RemoveIssueFromSprintErrors[keyof RemoveIssueFromSprintErrors]; + +export type RemoveIssueFromSprintResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type RemoveIssueFromSprintResponse = RemoveIssueFromSprintResponses[keyof RemoveIssueFromSprintResponses]; + +export type GetSprintIssuesData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query?: { + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues'; +}; + +export type GetSprintIssuesErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type GetSprintIssuesError = GetSprintIssuesErrors[keyof GetSprintIssuesErrors]; + +export type GetSprintIssuesResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseIssueResponse; +}; + +export type GetSprintIssuesResponse = GetSprintIssuesResponses[keyof GetSprintIssuesResponses]; + +export type AddIssueToSprintData = { + body?: never; + path: { + /** + * Project Id + */ + project_id: string; + /** + * Sprint Id + */ + sprint_id: string; + }; + query: { + /** + * Issue Id + * + * ID of the issue to add to the sprint + */ + issue_id: string; + }; + url: '/api/v1/projects/{project_id}/sprints/{sprint_id}/issues'; +}; + +export type AddIssueToSprintErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type AddIssueToSprintError = AddIssueToSprintErrors[keyof AddIssueToSprintErrors]; + +export type AddIssueToSprintResponses = { + /** + * Successful Response + */ + 200: MessageResponse; +}; + +export type AddIssueToSprintResponse = AddIssueToSprintResponses[keyof AddIssueToSprintResponses]; + export type GetOauthServerMetadataData = { body?: never; path?: never; diff --git a/frontend/src/lib/api/hooks/useProjects.ts b/frontend/src/lib/api/hooks/useProjects.ts index 38d6d24..671c9a2 100644 --- a/frontend/src/lib/api/hooks/useProjects.ts +++ b/frontend/src/lib/api/hooks/useProjects.ts @@ -4,12 +4,14 @@ * Provides data for the projects list page with filtering, * sorting, and pagination. * - * Uses mock data until backend endpoints are available. + * Uses SDK to call API (intercepted by MSW in demo mode). * * @see Issue #54 */ import { useQuery } from '@tanstack/react-query'; +import { listProjects as listProjectsApi } from '@/lib/api/generated'; +import type { ProjectStatus as ApiProjectStatus, ProjectResponse } from '@/lib/api/generated'; import type { ProjectStatus } from '@/components/projects/types'; // ============================================================================ @@ -19,6 +21,7 @@ import type { ProjectStatus } from '@/components/projects/types'; export interface ProjectListItem { id: string; name: string; + slug: string; description?: string; status: ProjectStatus; complexity: 'low' | 'medium' | 'high'; @@ -33,6 +36,7 @@ export interface ProjectListItem { name: string; }; tags?: string[]; + autonomyLevel: string; } export interface ProjectsListParams { @@ -56,134 +60,64 @@ export interface ProjectsListResponse { } // ============================================================================ -// Mock Data +// Helpers // ============================================================================ -const mockProjects: ProjectListItem[] = [ - { - id: 'proj-001', - name: 'E-Commerce Platform Redesign', - description: - 'Complete redesign of the e-commerce platform with modern UI/UX and improved checkout flow', - status: 'active', - complexity: 'high', - progress: 67, - openIssues: 12, - activeAgents: 4, - currentSprint: 'Sprint 3', - lastActivity: '2 minutes ago', - createdAt: '2025-11-15T10:00:00Z', - owner: { id: 'user-001', name: 'Felipe Cardoso' }, - tags: ['e-commerce', 'frontend', 'ux'], - }, - { - id: 'proj-002', - name: 'Mobile Banking App', - description: - 'Native mobile app for banking services with biometric authentication and real-time notifications', - status: 'active', - complexity: 'high', - progress: 45, - openIssues: 8, - activeAgents: 5, - currentSprint: 'Sprint 2', - lastActivity: '15 minutes ago', - createdAt: '2025-11-20T09:00:00Z', - owner: { id: 'user-001', name: 'Felipe Cardoso' }, - tags: ['mobile', 'fintech', 'security'], - }, - { - id: 'proj-003', - name: 'Internal HR Portal', - description: - 'Employee self-service portal for HR operations including leave requests and performance reviews', - status: 'paused', - complexity: 'medium', - progress: 23, - openIssues: 5, - activeAgents: 0, - currentSprint: 'Sprint 1', - lastActivity: '2 days ago', - createdAt: '2025-10-01T08:00:00Z', - owner: { id: 'user-002', name: 'Maria Santos' }, - tags: ['internal', 'hr', 'portal'], - }, - { - id: 'proj-004', - name: 'API Gateway Modernization', - description: - 'Migrate legacy API gateway to cloud-native architecture with improved rate limiting and caching', - status: 'active', - complexity: 'high', - progress: 82, - openIssues: 3, - activeAgents: 2, - currentSprint: 'Sprint 4', - lastActivity: '1 hour ago', - createdAt: '2025-12-01T11:00:00Z', - owner: { id: 'user-001', name: 'Felipe Cardoso' }, - tags: ['api', 'cloud', 'infrastructure'], - }, - { - id: 'proj-005', - name: 'Customer Analytics Dashboard', - description: - 'Real-time analytics dashboard for customer behavior insights with ML-powered predictions', - status: 'completed', - complexity: 'medium', - progress: 100, - openIssues: 0, - activeAgents: 0, - lastActivity: '2 weeks ago', - createdAt: '2025-09-01T10:00:00Z', - owner: { id: 'user-003', name: 'Alex Johnson' }, - tags: ['analytics', 'ml', 'dashboard'], - }, - { - id: 'proj-006', - name: 'DevOps Pipeline Automation', - description: 'Automate CI/CD pipelines with AI-assisted deployments and rollback capabilities', - status: 'active', - complexity: 'medium', - progress: 35, - openIssues: 6, - activeAgents: 3, - currentSprint: 'Sprint 1', - lastActivity: '30 minutes ago', - createdAt: '2025-12-10T14:00:00Z', - owner: { id: 'user-001', name: 'Felipe Cardoso' }, - tags: ['devops', 'automation', 'ci-cd'], - }, - { - id: 'proj-007', - name: 'Inventory Management System', - description: 'Warehouse inventory tracking with barcode scanning and automated reordering', - status: 'archived', - complexity: 'low', - progress: 100, - openIssues: 0, - activeAgents: 0, - lastActivity: '1 month ago', - createdAt: '2025-06-15T08:00:00Z', - owner: { id: 'user-002', name: 'Maria Santos' }, - tags: ['inventory', 'warehouse', 'logistics'], - }, - { - id: 'proj-008', - name: 'Customer Support Chatbot', - description: 'AI-powered chatbot for 24/7 customer support with sentiment analysis', - status: 'active', - complexity: 'medium', - progress: 58, - openIssues: 4, - activeAgents: 2, - currentSprint: 'Sprint 2', - lastActivity: '45 minutes ago', - createdAt: '2025-12-05T09:00:00Z', - owner: { id: 'user-003', name: 'Alex Johnson' }, - tags: ['ai', 'chatbot', 'support'], - }, -]; +/** + * Maps API ProjectResponse to our ProjectListItem format + */ +function mapProjectResponse(project: ProjectResponse & Record): ProjectListItem { + // Map complexity from mock data format to UI format + const rawComplexity = project.complexity as string; + let complexity: 'low' | 'medium' | 'high' = 'medium'; + if (rawComplexity === 'script' || rawComplexity === 'simple' || rawComplexity === 'low') { + complexity = 'low'; + } else if (rawComplexity === 'complex' || rawComplexity === 'high') { + complexity = 'high'; + } + + return { + id: project.id, + name: project.name, + slug: project.slug || project.name.toLowerCase().replace(/\s+/g, '-'), + description: project.description || undefined, + status: project.status as ProjectStatus, + complexity, + progress: (project.progress as number) || 0, + openIssues: (project.openIssues as number) || project.issue_count || 0, + activeAgents: (project.activeAgents as number) || project.agent_count || 0, + currentSprint: project.active_sprint_name || undefined, + lastActivity: (project.lastActivity as string) || formatRelativeTime(project.updated_at), + createdAt: project.created_at, + owner: { + id: project.owner_id || 'unknown', + name: (project.ownerName as string) || 'Unknown', + }, + tags: (project.tags as string[]) || [], + autonomyLevel: project.autonomy_level || 'milestone', + }; +} + +/** + * Format a date string as relative time (e.g., "2 minutes ago") + */ +function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMins / 60); + const diffDays = Math.floor(diffHours / 24); + const diffWeeks = Math.floor(diffDays / 7); + const diffMonths = Math.floor(diffDays / 30); + + if (diffMins < 1) return 'Just now'; + if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`; + if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; + if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; + if (diffWeeks < 4) return `${diffWeeks} week${diffWeeks > 1 ? 's' : ''} ago`; + return `${diffMonths} month${diffMonths > 1 ? 's' : ''} ago`; +} // ============================================================================ // Hook @@ -206,35 +140,33 @@ export function useProjects(params: ProjectsListParams = {}) { return useQuery({ queryKey: ['projects', { search, status, complexity, sortBy, sortOrder, page, limit }], queryFn: async () => { - // Simulate network delay - await new Promise((resolve) => setTimeout(resolve, 400)); + // Call API via SDK (MSW intercepts in demo mode) + const response = await listProjectsApi({ + query: { + status: status !== 'all' ? (status as ApiProjectStatus) : undefined, + search: search || undefined, + page, + limit, + }, + }); - // Filter projects - let filtered = [...mockProjects]; - - // Search filter - if (search) { - const searchLower = search.toLowerCase(); - filtered = filtered.filter( - (p) => - p.name.toLowerCase().includes(searchLower) || - p.description?.toLowerCase().includes(searchLower) || - p.tags?.some((t) => t.toLowerCase().includes(searchLower)) - ); + if (response.error) { + throw new Error('Failed to fetch projects'); } - // Status filter - if (status !== 'all') { - filtered = filtered.filter((p) => p.status === status); - } + // Get raw response data + const apiData = response.data; + let projects = apiData.data.map((p) => + mapProjectResponse(p as ProjectResponse & Record) + ); - // Complexity filter + // Client-side complexity filter (not supported by API) if (complexity !== 'all') { - filtered = filtered.filter((p) => p.complexity === complexity); + projects = projects.filter((p) => p.complexity === complexity); } - // Sort - filtered.sort((a, b) => { + // Client-side sorting + projects.sort((a, b) => { let comparison = 0; switch (sortBy) { case 'name': @@ -254,15 +186,12 @@ export function useProjects(params: ProjectsListParams = {}) { return sortOrder === 'asc' ? comparison : -comparison; }); - // Pagination - const total = filtered.length; + // Calculate pagination + const total = apiData.pagination.total; const totalPages = Math.ceil(total / limit); - const start = (page - 1) * limit; - const end = start + limit; - const paginatedData = filtered.slice(start, end); return { - data: paginatedData, + data: projects, pagination: { page, limit, diff --git a/frontend/src/mocks/data/projects.ts b/frontend/src/mocks/data/projects.ts new file mode 100644 index 0000000..fc297d5 --- /dev/null +++ b/frontend/src/mocks/data/projects.ts @@ -0,0 +1,266 @@ +/** + * Mock Project Data for Demo Mode + * + * Sample projects used by MSW handlers in demo mode. + */ + +import type { ProjectResponse, ProjectStatus } from '@/lib/api/generated'; + +export interface ProjectListItem extends ProjectResponse { + // Extended UI fields (computed/stored separately in real app) + complexity?: 'script' | 'simple' | 'medium' | 'complex'; + progress?: number; + openIssues?: number; + activeAgents?: number; + lastActivity?: string; + tags?: string[]; + ownerName?: string; +} + +export const sampleProjects: ProjectListItem[] = [ + { + id: 'proj-001', + name: 'E-Commerce Platform Redesign', + slug: 'ecommerce-redesign', + description: 'Complete redesign of the e-commerce platform with modern UI/UX', + autonomy_level: 'milestone', + status: 'active', + settings: {}, + owner_id: 'user-001', + created_at: '2025-11-15T10:00:00Z', + updated_at: new Date(Date.now() - 2 * 60 * 1000).toISOString(), + agent_count: 5, + issue_count: 70, + active_sprint_name: 'Sprint 3', + // Extended fields + complexity: 'complex', + progress: 67, + openIssues: 12, + activeAgents: 4, + lastActivity: '2 minutes ago', + tags: ['e-commerce', 'frontend', 'ux'], + ownerName: 'Felipe Cardoso', + }, + { + id: 'proj-002', + name: 'Mobile Banking App', + slug: 'mobile-banking', + description: 'Native mobile app for banking services with biometric authentication', + autonomy_level: 'full_control', + status: 'active', + settings: {}, + owner_id: 'user-001', + created_at: '2025-11-20T09:00:00Z', + updated_at: new Date(Date.now() - 15 * 60 * 1000).toISOString(), + agent_count: 5, + issue_count: 45, + active_sprint_name: 'Sprint 2', + complexity: 'complex', + progress: 45, + openIssues: 8, + activeAgents: 5, + lastActivity: '15 minutes ago', + tags: ['mobile', 'fintech', 'security'], + ownerName: 'Felipe Cardoso', + }, + { + id: 'proj-003', + name: 'Internal HR Portal', + slug: 'hr-portal', + description: 'Employee self-service portal for HR operations', + autonomy_level: 'milestone', + status: 'paused', + settings: {}, + owner_id: 'user-002', + created_at: '2025-10-01T08:00:00Z', + updated_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), + agent_count: 3, + issue_count: 25, + active_sprint_name: 'Sprint 1', + complexity: 'medium', + progress: 23, + openIssues: 5, + activeAgents: 0, + lastActivity: '2 days ago', + tags: ['internal', 'hr', 'portal'], + ownerName: 'Maria Santos', + }, + { + id: 'proj-004', + name: 'API Gateway Modernization', + slug: 'api-gateway', + description: 'Migrate legacy API gateway to cloud-native architecture', + autonomy_level: 'autonomous', + status: 'active', + settings: {}, + owner_id: 'user-001', + created_at: '2025-12-01T11:00:00Z', + updated_at: new Date(Date.now() - 60 * 60 * 1000).toISOString(), + agent_count: 3, + issue_count: 40, + active_sprint_name: 'Sprint 4', + complexity: 'complex', + progress: 82, + openIssues: 3, + activeAgents: 2, + lastActivity: '1 hour ago', + tags: ['api', 'cloud', 'infrastructure'], + ownerName: 'Felipe Cardoso', + }, + { + id: 'proj-005', + name: 'Customer Analytics Dashboard', + slug: 'analytics-dashboard', + description: 'Real-time analytics dashboard for customer behavior insights', + autonomy_level: 'milestone', + status: 'completed', + settings: {}, + owner_id: 'user-003', + created_at: '2025-09-01T10:00:00Z', + updated_at: new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString(), + agent_count: 0, + issue_count: 50, + active_sprint_name: null, + complexity: 'medium', + progress: 100, + openIssues: 0, + activeAgents: 0, + lastActivity: '2 weeks ago', + tags: ['analytics', 'ml', 'dashboard'], + ownerName: 'Alex Johnson', + }, + { + id: 'proj-006', + name: 'DevOps Pipeline Automation', + slug: 'devops-automation', + description: 'Automate CI/CD pipelines with AI-assisted deployments', + autonomy_level: 'milestone', + status: 'active', + settings: {}, + owner_id: 'user-001', + created_at: '2025-12-10T14:00:00Z', + updated_at: new Date(Date.now() - 30 * 60 * 1000).toISOString(), + agent_count: 4, + issue_count: 30, + active_sprint_name: 'Sprint 1', + complexity: 'medium', + progress: 35, + openIssues: 6, + activeAgents: 3, + lastActivity: '30 minutes ago', + tags: ['devops', 'automation', 'ci-cd'], + ownerName: 'Felipe Cardoso', + }, + { + id: 'proj-007', + name: 'Inventory Management System', + slug: 'inventory-system', + description: 'Warehouse inventory tracking with barcode scanning', + autonomy_level: 'full_control', + status: 'archived', + settings: {}, + owner_id: 'user-002', + created_at: '2025-06-15T08:00:00Z', + updated_at: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(), + agent_count: 0, + issue_count: 80, + active_sprint_name: null, + complexity: 'simple', + progress: 100, + openIssues: 0, + activeAgents: 0, + lastActivity: '1 month ago', + tags: ['inventory', 'warehouse', 'logistics'], + ownerName: 'Maria Santos', + }, + { + id: 'proj-008', + name: 'Customer Support Chatbot', + slug: 'support-chatbot', + description: 'AI-powered chatbot for 24/7 customer support', + autonomy_level: 'autonomous', + status: 'active', + settings: {}, + owner_id: 'user-003', + created_at: '2025-12-05T09:00:00Z', + updated_at: new Date(Date.now() - 45 * 60 * 1000).toISOString(), + agent_count: 3, + issue_count: 35, + active_sprint_name: 'Sprint 2', + complexity: 'medium', + progress: 58, + openIssues: 4, + activeAgents: 2, + lastActivity: '45 minutes ago', + tags: ['ai', 'chatbot', 'support'], + ownerName: 'Alex Johnson', + }, +]; + +// In-memory store for demo mode (allows create/update/delete) +let projectsStore = [...sampleProjects]; + +export function getProjects(): ProjectListItem[] { + return projectsStore; +} + +export function getProjectById(id: string): ProjectListItem | undefined { + return projectsStore.find((p) => p.id === id); +} + +export function getProjectBySlug(slug: string): ProjectListItem | undefined { + return projectsStore.find((p) => p.slug === slug); +} + +export function createProject(data: Partial): ProjectListItem { + const newProject: ProjectListItem = { + id: `proj-${Date.now()}`, + name: data.name || 'New Project', + slug: data.slug || data.name?.toLowerCase().replace(/\s+/g, '-') || `project-${Date.now()}`, + description: data.description || null, + autonomy_level: data.autonomy_level || 'milestone', + status: 'active', + settings: data.settings || {}, + owner_id: data.owner_id || 'user-001', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + agent_count: 0, + issue_count: 0, + active_sprint_name: null, + complexity: data.complexity || 'medium', + progress: 0, + openIssues: 0, + activeAgents: 0, + lastActivity: 'Just now', + tags: data.tags || [], + ownerName: 'Demo User', + }; + projectsStore.unshift(newProject); + return newProject; +} + +export function updateProject( + id: string, + data: Partial +): ProjectListItem | undefined { + const index = projectsStore.findIndex((p) => p.id === id); + if (index === -1) return undefined; + + projectsStore[index] = { + ...projectsStore[index], + ...data, + updated_at: new Date().toISOString(), + }; + return projectsStore[index]; +} + +export function deleteProject(id: string): boolean { + const index = projectsStore.findIndex((p) => p.id === id); + if (index === -1) return false; + projectsStore.splice(index, 1); + return true; +} + +export function resetProjects(): void { + projectsStore = [...sampleProjects]; +} diff --git a/frontend/src/mocks/handlers/generated.ts b/frontend/src/mocks/handlers/generated.ts index f5040cc..cca3f1b 100644 --- a/frontend/src/mocks/handlers/generated.ts +++ b/frontend/src/mocks/handlers/generated.ts @@ -8,7 +8,7 @@ * * For custom handler behavior, use src/mocks/handlers/overrides.ts * - * Generated: 2025-12-30T02:14:59.598Z + * Generated: 2026-01-03T01:13:34.961Z */ import { http, HttpResponse, delay } from 'msw'; @@ -603,4 +603,544 @@ export const generatedHandlers = [ message: 'Operation successful' }); }), + + /** + * Create Project + */ + http.post(`${API_BASE_URL}/api/v1/projects`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * List Projects + */ + http.get(`${API_BASE_URL}/api/v1/projects`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Project + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Update Project + */ + http.patch(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Archive Project + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Project by Slug + */ + http.get(`${API_BASE_URL}/api/v1/projects/slug/:slug`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Pause Project + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/pause`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Resume Project + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/resume`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Create Agent Type + */ + http.post(`${API_BASE_URL}/api/v1/agent-types`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * List Agent Types + */ + http.get(`${API_BASE_URL}/api/v1/agent-types`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Update Agent Type + */ + http.patch(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Deactivate Agent Type + */ + http.delete(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Agent Type + */ + http.get(`${API_BASE_URL}/api/v1/agent-types/:agent_type_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Agent Type by Slug + */ + http.get(`${API_BASE_URL}/api/v1/agent-types/slug/:slug`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Create Issue + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * List Issues + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Issue Statistics + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues/stats`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Issue + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Update Issue + */ + http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Delete Issue + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Assign Issue + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/assign`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Unassign Issue + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/assignment`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Trigger Issue Sync + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/issues/:issue_id/sync`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Spawn Agent Instance + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * List Project Agents + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Project Agent Metrics + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/metrics`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Agent Details + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Update Agent + */ + http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Terminate Agent + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Pause Agent + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/pause`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Resume Agent + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/resume`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Agent Metrics + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/agents/:agent_id/metrics`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Create Sprint + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * List Sprints + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Active Sprint + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/active`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Project Velocity + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/velocity`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Sprint Details + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Update Sprint + */ + http.patch(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Delete Sprint + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Start Sprint + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/start`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Complete Sprint + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/complete`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Cancel Sprint + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/cancel`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Get Sprint Issues + */ + http.get(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Add Issue to Sprint + */ + http.post(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), + + /** + * Remove Issue from Sprint + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:project_id/sprints/:sprint_id/issues`, async ({ request, params }) => { + await delay(NETWORK_DELAY); + + return HttpResponse.json({ + success: true, + message: 'Operation successful' + }); + }), ]; diff --git a/frontend/src/mocks/handlers/overrides.ts b/frontend/src/mocks/handlers/overrides.ts index 8fcb500..79d5f4c 100644 --- a/frontend/src/mocks/handlers/overrides.ts +++ b/frontend/src/mocks/handlers/overrides.ts @@ -14,6 +14,14 @@ import { http, HttpResponse, delay } from 'msw'; import { generateMockToken } from '../utils/tokens'; import { validateCredentials, setCurrentUser, currentUser } from '../data/users'; +import { + getProjects, + getProjectById, + getProjectBySlug, + createProject, + updateProject, + deleteProject, +} from '../data/projects'; import config from '@/config/app.config'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000'; @@ -182,4 +190,164 @@ export const overrideHandlers = [ accounts: [], }); }), + + // ============================================ + // PROJECT HANDLERS + // ============================================ + + /** + * List Projects + * Returns paginated list of projects with filtering support + */ + http.get(`${API_BASE_URL}/api/v1/projects`, async ({ request }) => { + await delay(NETWORK_DELAY); + + const url = new URL(request.url); + const status = url.searchParams.get('status'); + const search = url.searchParams.get('search'); + const skip = parseInt(url.searchParams.get('skip') || '0'); + const limit = parseInt(url.searchParams.get('limit') || '20'); + + let projects = getProjects(); + + // Filter by status + if (status && status !== 'all') { + projects = projects.filter((p) => p.status === status); + } + + // Filter by search term + if (search) { + const searchLower = search.toLowerCase(); + projects = projects.filter( + (p) => + p.name.toLowerCase().includes(searchLower) || + p.description?.toLowerCase().includes(searchLower) + ); + } + + const total = projects.length; + const paginatedProjects = projects.slice(skip, skip + limit); + const totalPages = Math.ceil(total / limit); + + return HttpResponse.json({ + data: paginatedProjects, + pagination: { + total, + page: Math.floor(skip / limit) + 1, + limit, + total_pages: totalPages, + has_next: skip + limit < total, + has_prev: skip > 0, + }, + }); + }), + + /** + * Create Project + */ + http.post(`${API_BASE_URL}/api/v1/projects`, async ({ request }) => { + await delay(NETWORK_DELAY); + + const body = (await request.json()) as any; + const newProject = createProject(body); + + return HttpResponse.json(newProject, { status: 201 }); + }), + + /** + * Get Project by ID + */ + http.get(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params }) => { + await delay(NETWORK_DELAY); + + const { projectId } = params; + const project = getProjectById(projectId as string); + + if (!project) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json(project); + }), + + /** + * Get Project by Slug + */ + http.get(`${API_BASE_URL}/api/v1/projects/slug/:slug`, async ({ params }) => { + await delay(NETWORK_DELAY); + + const { slug } = params; + const project = getProjectBySlug(slug as string); + + if (!project) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json(project); + }), + + /** + * Update Project + */ + http.patch(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params, request }) => { + await delay(NETWORK_DELAY); + + const { projectId } = params; + const body = (await request.json()) as any; + const updated = updateProject(projectId as string, body); + + if (!updated) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json(updated); + }), + + /** + * Archive (Delete) Project + */ + http.delete(`${API_BASE_URL}/api/v1/projects/:projectId`, async ({ params }) => { + await delay(NETWORK_DELAY); + + const { projectId } = params; + const deleted = deleteProject(projectId as string); + + if (!deleted) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json({ message: 'Project archived successfully' }); + }), + + /** + * Pause Project + */ + http.post(`${API_BASE_URL}/api/v1/projects/:projectId/pause`, async ({ params }) => { + await delay(NETWORK_DELAY); + + const { projectId } = params; + const updated = updateProject(projectId as string, { status: 'paused' as any }); + + if (!updated) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json(updated); + }), + + /** + * Resume Project + */ + http.post(`${API_BASE_URL}/api/v1/projects/:projectId/resume`, async ({ params }) => { + await delay(NETWORK_DELAY); + + const { projectId } = params; + const updated = updateProject(projectId as string, { status: 'active' as any }); + + if (!updated) { + return HttpResponse.json({ detail: 'Project not found' }, { status: 404 }); + } + + return HttpResponse.json(updated); + }), ]; diff --git a/frontend/tests/components/projects/ProjectCard.test.tsx b/frontend/tests/components/projects/ProjectCard.test.tsx index 8243382..435a052 100644 --- a/frontend/tests/components/projects/ProjectCard.test.tsx +++ b/frontend/tests/components/projects/ProjectCard.test.tsx @@ -10,6 +10,7 @@ describe('ProjectCard', () => { const mockProject: ProjectListItem = { id: 'proj-1', name: 'Test Project', + slug: 'test-project', description: 'This is a test project description', status: 'active', complexity: 'medium', @@ -21,6 +22,7 @@ describe('ProjectCard', () => { createdAt: '2025-01-01T00:00:00Z', owner: { id: 'user-1', name: 'Test User' }, tags: ['frontend', 'react', 'typescript'], + autonomyLevel: 'milestone', }; it('renders project name', () => { diff --git a/frontend/tests/components/projects/ProjectsGrid.test.tsx b/frontend/tests/components/projects/ProjectsGrid.test.tsx index 25afa7c..8669a6f 100644 --- a/frontend/tests/components/projects/ProjectsGrid.test.tsx +++ b/frontend/tests/components/projects/ProjectsGrid.test.tsx @@ -18,6 +18,7 @@ describe('ProjectsGrid', () => { { id: 'proj-1', name: 'Project One', + slug: 'project-one', description: 'First project', status: 'active', complexity: 'medium', @@ -27,10 +28,12 @@ describe('ProjectsGrid', () => { lastActivity: '5 min ago', createdAt: '2025-01-01T00:00:00Z', owner: { id: 'user-1', name: 'User One' }, + autonomyLevel: 'milestone', }, { id: 'proj-2', name: 'Project Two', + slug: 'project-two', description: 'Second project', status: 'paused', complexity: 'high', @@ -40,6 +43,7 @@ describe('ProjectsGrid', () => { lastActivity: '1 day ago', createdAt: '2025-01-02T00:00:00Z', owner: { id: 'user-2', name: 'User Two' }, + autonomyLevel: 'full_control', }, ];