| @@ -1,10 +1,13 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | //if accessed directly exit |
| 3 | 3 | if (!defined('ABSPATH')) exit; |
| 4 | 4 | |
| 5 | +use FluentBoards\App\Http\Controllers\AiController; | |
| 5 | 6 | use FluentBoards\App\Http\Controllers\BoardController; |
| 6 | 7 | use FluentBoards\App\Http\Controllers\CommentController; |
| 8 | +use FluentBoards\App\Http\Controllers\FolderController; | |
| 9 | +use FluentBoards\App\Http\Controllers\ImportController; | |
| 7 | 10 | use FluentBoards\App\Http\Controllers\LabelController; |
| 8 | 11 | use FluentBoards\App\Http\Controllers\MCPSettingsController; |
| 9 | 12 | use FluentBoards\App\Http\Controllers\NotificationController; |
| 10 | 13 | use FluentBoards\App\Http\Controllers\OptionsController; |
| @@ -33,9 +36,9 @@ | ||
| 33 | 36 | }); |
| 34 | 37 | |
| 35 | 38 | $router->withPolicy('BoardUserPolicy')->group(function ($router) { |
| 36 | 39 | |
| 37 | - $router->get('/quick-search', [OptionsController::class, 'quickSearch']); | |
| 40 | + $router->get('/global-search', [OptionsController::class, 'globalSearch']); | |
| 38 | 41 | |
| 39 | 42 | $router->get('/member-associated-users/{id}', [UserController::class, 'memberAssociatedTaskUsers']); |
| 40 | 43 | $router->get('ajax-options', [OptionsController::class, 'selectorOptions']); |
| 41 | 44 | $router->post('update-global-notification-settings', [OptionsController::class, 'updateGlobalNotificationSettings']); |
| @@ -41,10 +44,13 @@ | ||
| 41 | 44 | $router->post('update-global-notification-settings', [OptionsController::class, 'updateGlobalNotificationSettings']); |
| 42 | 45 | $router->get('get-global-notification-settings', [OptionsController::class, 'getGlobalNotificationSettings']); |
| 43 | 46 | $router->put('update-dashboard-view-settings', [OptionsController::class, 'updateDashboardViewSettings']); |
| 44 | 47 | $router->get('get-dashboard-view-settings', [OptionsController::class, 'getDashboardViewSettings']); |
| 45 | - $router->get('projects/reports', [ReportController::class, 'getBoardReports']); | |
| 46 | 48 | $router->get('reports/timesheet', [ReportController::class, 'getTimeSheetReport']); |
| 49 | + $router->get('reports/overview', [ReportController::class, 'getOverviewReport']); | |
| 50 | + $router->get('reports/tasks', [ReportController::class, 'getTasksReport']); | |
| 51 | + $router->get('reports/activity', [ReportController::class, 'getActivityReport']); | |
| 52 | + $router->get('reports/roadmap', [ReportController::class, 'getRoadmapReport']); | |
| 47 | 53 | |
| 48 | 54 | }); |
| 49 | 55 | |
| 50 | 56 | $router->prefix('projects')->withPolicy('AuthPolicy')->group(function ($router) { |
| @@ -50,11 +56,12 @@ | ||
| 50 | 56 | $router->prefix('projects')->withPolicy('AuthPolicy')->group(function ($router) { |
| 51 | 57 | $router->get('/', [BoardController::class, 'getBoards']); |
| 52 | 58 | $router->post('/', [BoardController::class, 'create']); |
| 53 | 59 | $router->get('/get-default-board-colors', [BoardController::class, 'getBoardDefaultBackgroundColors']); |
| 60 | + // NOTE: The global "Add" (board/stage/task) drawer was removed from the v2 UI. This endpoint is NOT dedicated to that feature — it remains in active use by other components, so it must stay. | |
| 54 | 61 | $router->get('/list-of-boards', [BoardController::class, 'getBoardsList']); // it is using for to get all boards by user |
| 55 | 62 | $router->get('/user-accessible-boards', [BoardController::class, 'getOnlyBoardsByUser']); |
| 56 | - $router->get('/crm-associated-boards/{id}', [BoardController::class, 'getAssociatedBoards'])->int('associated_id'); | |
| 63 | + $router->get('/crm-associated-boards/{associated_id}', [BoardController::class, 'getAssociatedBoards'])->int('associated_id'); | |
| 57 | 64 | $router->get('/currencies', [BoardController::class, 'getCurrencies']); |
| 58 | 65 | $router->get('/user-admin-in-boards', [BoardController::class, 'getUsersOfBoards']); |
| 59 | 66 | $router->get('/recent-boards', [BoardController::class, 'getRecentBoards']); |
| 60 | 67 | $router->get('/pinned-boards', [BoardController::class, 'getPinnedBoards']); |
| @@ -89,8 +96,9 @@ | ||
| 89 | 96 | $router->get('/assignees', [BoardController::class, 'getAssigneesByBoard'])->int('board_id'); |
| 90 | 97 | $router->get('/activities', [BoardController::class, 'getActivities'])->int('board_id'); |
| 91 | 98 | |
| 92 | 99 | $router->put('/stage-move-all-task', [BoardController::class, 'moveAllTasks'])->int('board_id'); |
| 100 | + // NOTE: The global "Add" drawer was removed from the v2 UI. This stage-create endpoint is shared (in-board stage creation still uses it), so it must stay. | |
| 93 | 101 | $router->post('/stage-create', [BoardController::class, 'createStage'])->int('board_id'); |
| 94 | 102 | $router->put('/stage/{stage_id}/sort-task', [StageController::class, 'sortStageTasks'])->int('board_id')->int('stage_id'); |
| 95 | 103 | $router->put('/stage/{stage_id}/archive-all-task', [BoardController::class, 'archiveAllTasksInStage'])->int('board_id')->int('stage_id'); |
| 96 | 104 | $router->put('/re-position-stages', [BoardController::class, 'repositionStages'])->int('board_id'); |
| @@ -107,9 +115,8 @@ | ||
| 107 | 115 | $router->get('/stage-task-available-positions/{stage_id}', [BoardController::class, 'getStageTaskAvailablePositions'])->int('board_id')->int('stage_id'); |
| 108 | 116 | |
| 109 | 117 | $router->post('/crm-contact', [BoardController::class, 'updateAssociateCrmContact'])->int('board_id'); |
| 110 | 118 | $router->get('/crm-contacts', [BoardController::class, 'getAssociateCrmContacts'])->int('board_id'); |
| 111 | - $router->delete('/crm-contact/{contact_id}', [BoardController::class, 'deleteAssociateCrmContact'])->int('board_id')->int('contact_id'); | |
| 112 | 119 | |
| 113 | 120 | $router->get('/notification-settings', [NotificationController::class, 'getBoardNotificationSettings'])->int('board_id'); |
| 114 | 121 | $router->put('/update-notification-settings', [NotificationController::class, 'updateBoardNotificationSettings'])->int('board_id'); |
| 115 | 122 | |
| @@ -120,10 +127,8 @@ | ||
| 120 | 127 | $router->post('/upload/background-image', [BoardController::class, 'uploadBoardBackground'])->int('board_id'); |
| 121 | 128 | $router->put('/archive-board', [BoardController::class, 'archiveBoard'])->int('board_id'); |
| 122 | 129 | $router->put('/restore-board', [BoardController::class, 'restoreBoard'])->int('board_id'); |
| 123 | 130 | $router->get('/board-menu-items', [BoardController::class, 'getBoardMenuItems'])->int('board_id'); |
| 124 | - $router->get('/stage-wise-reports', [ReportController::class, 'getStageWiseBoardReports'])->int('board_id'); | |
| 125 | - | |
| 126 | 131 | $router->get('/public-access-settings', [BoardController::class, 'getPublicAccessSettings'])->int('board_id'); |
| 127 | 132 | $router->put('/toggle-public-access', [BoardController::class, 'togglePublicAccess'])->int('board_id'); |
| 128 | 133 | |
| 129 | 134 | |
| @@ -134,13 +139,17 @@ | ||
| 134 | 139 | $router->get('/filtered', [TaskController::class, 'getFilteredBoardTasks'])->int('board_id'); |
| 135 | 140 | $router->get('/table', [TaskController::class, 'getTableTasks'])->int('board_id'); |
| 136 | 141 | $router->get('/by-stage', [TaskController::class, 'getTasksByBoardStage'])->int('board_id'); |
| 137 | 142 | $router->get('/stage-page', [TaskController::class, 'getStageTasksPage'])->int('board_id'); |
| 143 | + // NOTE: The global "Add" drawer was removed from the v2 UI. This is the primary task-create endpoint used throughout the app, so it must stay. | |
| 138 | 144 | $router->post('/', [TaskController::class, 'create']); |
| 139 | 145 | $router->post('/create-task-from-image', [TaskController::class, 'createTaskFromImage'])->int('board_id'); |
| 140 | 146 | $router->get('/archived', [TaskController::class, 'getArchivedTasks'])->int('board_id'); |
| 141 | 147 | $router->get('/{task_id}', [TaskController::class, 'find'])->int('board_id')->int('task_id')->int('task_id'); |
| 142 | 148 | $router->put('/{task_id}', [TaskController::class, 'updateTaskProperties'])->int('board_id')->int('task_id'); |
| 149 | + $router->delete('/{task_id}/support-ticket', [TaskController::class, 'removeSupportTicketLink']) | |
| 150 | + ->int('board_id') | |
| 151 | + ->int('task_id'); | |
| 143 | 152 | $router->post('/{task_id}/dates', [TaskController::class, 'updateTaskDates'])->int('board_id')->int('task_id'); |
| 144 | 153 | $router->put('/{task_id}/move-task', [TaskController::class, 'moveTask'])->int('board_id')->int('task_id'); |
| 145 | 154 | $router->post('/bulk-actions', [TaskController::class, 'bulkActions'])->int('board_id'); |
| 146 | 155 | $router->post('/update-cover-photo/{task_id}', [TaskController::class, 'updateTaskCoverPhoto'])->int('task_id'); |
| @@ -148,8 +157,14 @@ | ||
| 148 | 157 | $router->delete('/{task_id}', [TaskController::class, 'deleteTask'])->int('board_id')->int('task_id'); |
| 149 | 158 | $router->put('/{task_id}/move-to-next-stage', [TaskController::class, 'moveTaskToNextStage'])->int('board_id')->int('task_id'); |
| 150 | 159 | $router->put('/{task_id}/pin', [TaskController::class, 'toggleTaskPinned'])->int('board_id')->int('task_id'); |
| 151 | 160 | |
| 161 | + // AI task intelligence (summarize / subtasks / suggestions) — board-scoped. | |
| 162 | + $router->post('/{task_id}/ai-assist', [AiController::class, 'taskAssist'])->int('board_id')->int('task_id'); | |
| 163 | + // Transactional batch apply for AI label/priority suggestions (all-or-nothing). | |
| 164 | + // Bulk subtask creation lives with the other subtask APIs in Fluent Boards Pro. | |
| 165 | + $router->post('/{task_id}/ai-apply-suggestions', [AiController::class, 'applySuggestions'])->int('board_id')->int('task_id'); | |
| 166 | + | |
| 152 | 167 | // Comments Routes Area |
| 153 | 168 | $router->get('/{task_id}/comments', [CommentController::class, 'getComments'])->int('board_id')->int('task_id'); |
| 154 | 169 | $router->post('/{task_id}/comments', [CommentController::class, 'create'])->int('board_id')->int('task_id'); |
| 155 | 170 | $router->put('/comments/{comment_id}', [CommentController::class, 'update'])->int('board_id')->int('comment_id'); |
| @@ -183,15 +198,37 @@ | ||
| 183 | 198 | $router->post('/general-settings', [OptionsController::class, 'saveGeneralSettings']); |
| 184 | 199 | |
| 185 | 200 | $router->get('/mcp/status', [MCPSettingsController::class, 'status']); |
| 186 | 201 | $router->post('/mcp/toggle', [MCPSettingsController::class, 'toggle']); |
| 202 | + $router->post('/mcp/member-access', [MCPSettingsController::class, 'toggleMemberAccess']); | |
| 187 | 203 | $router->post('/mcp/install-adapter', [MCPSettingsController::class, 'installAdapter']); |
| 188 | - $router->get('/mcp/config-snippet', [MCPSettingsController::class, 'getConfigSnippet']); | |
| 189 | 204 | |
| 190 | 205 | $router->get('pages', [OptionsController::class, 'getPages']); |
| 191 | 206 | |
| 207 | + $router->prefix('folders')->group(function ($router) { | |
| 208 | + $router->get('/', [FolderController::class, 'getFolders']); | |
| 209 | + $router->post('/', [FolderController::class, 'createFolder']); | |
| 210 | + $router->prefix('/{folder_id}')->group(function ($router) { | |
| 211 | + $router->get('/', [FolderController::class, 'getFolderById'])->int('folder_id'); | |
| 212 | + $router->post('/add-board', [FolderController::class, 'addBoardToFolder'])->int('folder_id'); | |
| 213 | + $router->post('/remove-board', [FolderController::class, 'removeBoardFromFolder'])->int('folder_id'); | |
| 214 | + $router->put('/', [FolderController::class, 'updateFolder'])->int('folder_id'); | |
| 215 | + $router->delete('/', [FolderController::class, 'deleteFolder'])->int('folder_id'); | |
| 216 | + }); | |
| 217 | + }); | |
| 218 | + | |
| 192 | 219 | }); |
| 193 | 220 | |
| 221 | +// AI writing assistant. Settings routes are admin-only; `generate` is open to | |
| 222 | +// any authenticated user (enforced per-method inside AiPolicy). | |
| 223 | +$router->prefix('ai')->withPolicy('AiPolicy')->group(function ($router) { | |
| 224 | + $router->get('/settings', [AiController::class, 'getSettings']); | |
| 225 | + $router->post('/settings', [AiController::class, 'saveSettings']); | |
| 226 | + $router->post('/models', [AiController::class, 'getModels']); | |
| 227 | + $router->post('/test', [AiController::class, 'testConnection']); | |
| 228 | + $router->post('/generate', [AiController::class, 'generate']); | |
| 229 | +}); | |
| 230 | + | |
| 194 | 231 | $router->prefix('webhooks')->withPolicy('WebhookPolicy')->group(function ($router) { |
| 195 | 232 | $router->get('/', [WebhookController::class, 'index']); |
| 196 | 233 | $router->post('/', [WebhookController::class, 'create']); |
| 197 | 234 | $router->put('/{id}', [WebhookController::class, 'update'])->int('id'); |
| @@ -210,9 +247,16 @@ | ||
| 210 | 247 | $router->prefix('member/{id}')->withPolicy('UserPolicy')->group(function ($router) { |
| 211 | 248 | $router->get('/', [UserController::class, 'getMemberInfo']); |
| 212 | 249 | $router->get('/projects', [UserController::class, 'getMemberBoards']); |
| 213 | 250 | $router->get('/tasks', [UserController::class, 'getMemberAssociatedTasks']); |
| 251 | + $router->get('/task-counts', [UserController::class, 'getMemberTaskCounts']); | |
| 214 | 252 | $router->get('/activities', [UserController::class, 'getMemberRelatedAcitivies']); |
| 253 | + $router->get('/stats', [UserController::class, 'getMemberStats']); | |
| 254 | + $router->post('/display-name', [UserController::class, 'updateDisplayName']); | |
| 255 | + $router->post('/photo', [UserController::class, 'updateProfilePhoto']); | |
| 256 | + $router->get('/mcp-connections', [UserController::class, 'getMcpConnections']); | |
| 257 | + $router->post('/mcp-connections', [UserController::class, 'createMcpConnection']); | |
| 258 | + $router->delete('/mcp-connections/{uuid}', [UserController::class, 'revokeMcpConnection']); | |
| 215 | 259 | }); |
| 216 | 260 | |
| 217 | 261 | /* |
| 218 | 262 | * TODO: I guess we can minimize the number of routes. and Backend code needs to be refactored |
| @@ -238,9 +282,9 @@ | ||
| 238 | 282 | }); |
| 239 | 283 | |
| 240 | 284 | // User utility routes |
| 241 | 285 | $router->withPolicy('UserPolicy')->group(function ($router) { |
| 242 | - $router->get('/quick-search', [OptionsController::class, 'quickSearch']); | |
| 286 | + $router->get('/global-search', [OptionsController::class, 'globalSearch']); | |
| 243 | 287 | }); |
| 244 | 288 | |
| 245 | 289 | |
| 246 | 290 | $router->prefix('options')->withPolicy('AuthPolicy')->group(function ($router) { |
| @@ -245,5 +289,12 @@ | ||
| 245 | 289 | |
| 246 | 290 | $router->prefix('options')->withPolicy('AuthPolicy')->group(function ($router) { |
| 247 | 291 | $router->get('members', [OptionsController::class, 'getBoardMembers']); |
| 248 | 292 | $router->get('projects', [OptionsController::class, 'getBoards']); |
| 293 | +}); | |
| 294 | + | |
| 295 | +// Native board imports are available without the Pro addon. | |
| 296 | +$router->prefix('fluent-boards-import')->withPolicy('AdminPolicy')->group(function ($router) { | |
| 297 | + $router->post('import-file', [ImportController::class, 'importFile']); | |
| 298 | + $router->post('upload-json-chunk', [ImportController::class, 'uploadJsonChunk']); | |
| 299 | + $router->post('process-json-import', [ImportController::class, 'processJsonImport']); | |
| 249 | 300 | }); |