| @@ -1,10 +1,12 @@ | ||
| 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; | |
| 7 | 9 | use FluentBoards\App\Http\Controllers\LabelController; |
| 8 | 10 | use FluentBoards\App\Http\Controllers\MCPSettingsController; |
| 9 | 11 | use FluentBoards\App\Http\Controllers\NotificationController; |
| 10 | 12 | use FluentBoards\App\Http\Controllers\OptionsController; |
| @@ -33,9 +35,9 @@ | ||
| 33 | 35 | }); |
| 34 | 36 | |
| 35 | 37 | $router->withPolicy('BoardUserPolicy')->group(function ($router) { |
| 36 | 38 | |
| 37 | - $router->get('/quick-search', [OptionsController::class, 'quickSearch']); | |
| 39 | + $router->get('/global-search', [OptionsController::class, 'globalSearch']); | |
| 38 | 40 | |
| 39 | 41 | $router->get('/member-associated-users/{id}', [UserController::class, 'memberAssociatedTaskUsers']); |
| 40 | 42 | $router->get('ajax-options', [OptionsController::class, 'selectorOptions']); |
| 41 | 43 | $router->post('update-global-notification-settings', [OptionsController::class, 'updateGlobalNotificationSettings']); |
| @@ -41,10 +43,13 @@ | ||
| 41 | 43 | $router->post('update-global-notification-settings', [OptionsController::class, 'updateGlobalNotificationSettings']); |
| 42 | 44 | $router->get('get-global-notification-settings', [OptionsController::class, 'getGlobalNotificationSettings']); |
| 43 | 45 | $router->put('update-dashboard-view-settings', [OptionsController::class, 'updateDashboardViewSettings']); |
| 44 | 46 | $router->get('get-dashboard-view-settings', [OptionsController::class, 'getDashboardViewSettings']); |
| 45 | - $router->get('projects/reports', [ReportController::class, 'getBoardReports']); | |
| 46 | 47 | $router->get('reports/timesheet', [ReportController::class, 'getTimeSheetReport']); |
| 48 | + $router->get('reports/overview', [ReportController::class, 'getOverviewReport']); | |
| 49 | + $router->get('reports/tasks', [ReportController::class, 'getTasksReport']); | |
| 50 | + $router->get('reports/activity', [ReportController::class, 'getActivityReport']); | |
| 51 | + $router->get('reports/roadmap', [ReportController::class, 'getRoadmapReport']); | |
| 47 | 52 | |
| 48 | 53 | }); |
| 49 | 54 | |
| 50 | 55 | $router->prefix('projects')->withPolicy('AuthPolicy')->group(function ($router) { |
| @@ -50,11 +55,12 @@ | ||
| 50 | 55 | $router->prefix('projects')->withPolicy('AuthPolicy')->group(function ($router) { |
| 51 | 56 | $router->get('/', [BoardController::class, 'getBoards']); |
| 52 | 57 | $router->post('/', [BoardController::class, 'create']); |
| 53 | 58 | $router->get('/get-default-board-colors', [BoardController::class, 'getBoardDefaultBackgroundColors']); |
| 59 | + // 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 | 60 | $router->get('/list-of-boards', [BoardController::class, 'getBoardsList']); // it is using for to get all boards by user |
| 55 | 61 | $router->get('/user-accessible-boards', [BoardController::class, 'getOnlyBoardsByUser']); |
| 56 | - $router->get('/crm-associated-boards/{id}', [BoardController::class, 'getAssociatedBoards'])->int('associated_id'); | |
| 62 | + $router->get('/crm-associated-boards/{associated_id}', [BoardController::class, 'getAssociatedBoards'])->int('associated_id'); | |
| 57 | 63 | $router->get('/currencies', [BoardController::class, 'getCurrencies']); |
| 58 | 64 | $router->get('/user-admin-in-boards', [BoardController::class, 'getUsersOfBoards']); |
| 59 | 65 | $router->get('/recent-boards', [BoardController::class, 'getRecentBoards']); |
| 60 | 66 | $router->get('/pinned-boards', [BoardController::class, 'getPinnedBoards']); |
| @@ -89,8 +95,9 @@ | ||
| 89 | 95 | $router->get('/assignees', [BoardController::class, 'getAssigneesByBoard'])->int('board_id'); |
| 90 | 96 | $router->get('/activities', [BoardController::class, 'getActivities'])->int('board_id'); |
| 91 | 97 | |
| 92 | 98 | $router->put('/stage-move-all-task', [BoardController::class, 'moveAllTasks'])->int('board_id'); |
| 99 | + // 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 | 100 | $router->post('/stage-create', [BoardController::class, 'createStage'])->int('board_id'); |
| 94 | 101 | $router->put('/stage/{stage_id}/sort-task', [StageController::class, 'sortStageTasks'])->int('board_id')->int('stage_id'); |
| 95 | 102 | $router->put('/stage/{stage_id}/archive-all-task', [BoardController::class, 'archiveAllTasksInStage'])->int('board_id')->int('stage_id'); |
| 96 | 103 | $router->put('/re-position-stages', [BoardController::class, 'repositionStages'])->int('board_id'); |
| @@ -107,9 +114,8 @@ | ||
| 107 | 114 | $router->get('/stage-task-available-positions/{stage_id}', [BoardController::class, 'getStageTaskAvailablePositions'])->int('board_id')->int('stage_id'); |
| 108 | 115 | |
| 109 | 116 | $router->post('/crm-contact', [BoardController::class, 'updateAssociateCrmContact'])->int('board_id'); |
| 110 | 117 | $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 | 118 | |
| 113 | 119 | $router->get('/notification-settings', [NotificationController::class, 'getBoardNotificationSettings'])->int('board_id'); |
| 114 | 120 | $router->put('/update-notification-settings', [NotificationController::class, 'updateBoardNotificationSettings'])->int('board_id'); |
| 115 | 121 | |
| @@ -120,10 +126,8 @@ | ||
| 120 | 126 | $router->post('/upload/background-image', [BoardController::class, 'uploadBoardBackground'])->int('board_id'); |
| 121 | 127 | $router->put('/archive-board', [BoardController::class, 'archiveBoard'])->int('board_id'); |
| 122 | 128 | $router->put('/restore-board', [BoardController::class, 'restoreBoard'])->int('board_id'); |
| 123 | 129 | $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 | 130 | $router->get('/public-access-settings', [BoardController::class, 'getPublicAccessSettings'])->int('board_id'); |
| 127 | 131 | $router->put('/toggle-public-access', [BoardController::class, 'togglePublicAccess'])->int('board_id'); |
| 128 | 132 | |
| 129 | 133 | |
| @@ -134,13 +138,17 @@ | ||
| 134 | 138 | $router->get('/filtered', [TaskController::class, 'getFilteredBoardTasks'])->int('board_id'); |
| 135 | 139 | $router->get('/table', [TaskController::class, 'getTableTasks'])->int('board_id'); |
| 136 | 140 | $router->get('/by-stage', [TaskController::class, 'getTasksByBoardStage'])->int('board_id'); |
| 137 | 141 | $router->get('/stage-page', [TaskController::class, 'getStageTasksPage'])->int('board_id'); |
| 142 | + // 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 | 143 | $router->post('/', [TaskController::class, 'create']); |
| 139 | 144 | $router->post('/create-task-from-image', [TaskController::class, 'createTaskFromImage'])->int('board_id'); |
| 140 | 145 | $router->get('/archived', [TaskController::class, 'getArchivedTasks'])->int('board_id'); |
| 141 | 146 | $router->get('/{task_id}', [TaskController::class, 'find'])->int('board_id')->int('task_id')->int('task_id'); |
| 142 | 147 | $router->put('/{task_id}', [TaskController::class, 'updateTaskProperties'])->int('board_id')->int('task_id'); |
| 148 | + $router->delete('/{task_id}/support-ticket', [TaskController::class, 'removeSupportTicketLink']) | |
| 149 | + ->int('board_id') | |
| 150 | + ->int('task_id'); | |
| 143 | 151 | $router->post('/{task_id}/dates', [TaskController::class, 'updateTaskDates'])->int('board_id')->int('task_id'); |
| 144 | 152 | $router->put('/{task_id}/move-task', [TaskController::class, 'moveTask'])->int('board_id')->int('task_id'); |
| 145 | 153 | $router->post('/bulk-actions', [TaskController::class, 'bulkActions'])->int('board_id'); |
| 146 | 154 | $router->post('/update-cover-photo/{task_id}', [TaskController::class, 'updateTaskCoverPhoto'])->int('task_id'); |
| @@ -148,8 +156,14 @@ | ||
| 148 | 156 | $router->delete('/{task_id}', [TaskController::class, 'deleteTask'])->int('board_id')->int('task_id'); |
| 149 | 157 | $router->put('/{task_id}/move-to-next-stage', [TaskController::class, 'moveTaskToNextStage'])->int('board_id')->int('task_id'); |
| 150 | 158 | $router->put('/{task_id}/pin', [TaskController::class, 'toggleTaskPinned'])->int('board_id')->int('task_id'); |
| 151 | 159 | |
| 160 | + // AI task intelligence (summarize / subtasks / suggestions) — board-scoped. | |
| 161 | + $router->post('/{task_id}/ai-assist', [AiController::class, 'taskAssist'])->int('board_id')->int('task_id'); | |
| 162 | + // Transactional batch apply for AI label/priority suggestions (all-or-nothing). | |
| 163 | + // Bulk subtask creation lives with the other subtask APIs in Fluent Boards Pro. | |
| 164 | + $router->post('/{task_id}/ai-apply-suggestions', [AiController::class, 'applySuggestions'])->int('board_id')->int('task_id'); | |
| 165 | + | |
| 152 | 166 | // Comments Routes Area |
| 153 | 167 | $router->get('/{task_id}/comments', [CommentController::class, 'getComments'])->int('board_id')->int('task_id'); |
| 154 | 168 | $router->post('/{task_id}/comments', [CommentController::class, 'create'])->int('board_id')->int('task_id'); |
| 155 | 169 | $router->put('/comments/{comment_id}', [CommentController::class, 'update'])->int('board_id')->int('comment_id'); |
| @@ -188,10 +202,32 @@ | ||
| 188 | 202 | $router->get('/mcp/config-snippet', [MCPSettingsController::class, 'getConfigSnippet']); |
| 189 | 203 | |
| 190 | 204 | $router->get('pages', [OptionsController::class, 'getPages']); |
| 191 | 205 | |
| 206 | + $router->prefix('folders')->group(function ($router) { | |
| 207 | + $router->get('/', [FolderController::class, 'getFolders']); | |
| 208 | + $router->post('/', [FolderController::class, 'createFolder']); | |
| 209 | + $router->prefix('/{folder_id}')->group(function ($router) { | |
| 210 | + $router->get('/', [FolderController::class, 'getFolderById'])->int('folder_id'); | |
| 211 | + $router->post('/add-board', [FolderController::class, 'addBoardToFolder'])->int('folder_id'); | |
| 212 | + $router->post('/remove-board', [FolderController::class, 'removeBoardFromFolder'])->int('folder_id'); | |
| 213 | + $router->put('/', [FolderController::class, 'updateFolder'])->int('folder_id'); | |
| 214 | + $router->delete('/', [FolderController::class, 'deleteFolder'])->int('folder_id'); | |
| 215 | + }); | |
| 216 | + }); | |
| 217 | + | |
| 192 | 218 | }); |
| 193 | 219 | |
| 220 | +// AI writing assistant. Settings routes are admin-only; `generate` is open to | |
| 221 | +// any authenticated user (enforced per-method inside AiPolicy). | |
| 222 | +$router->prefix('ai')->withPolicy('AiPolicy')->group(function ($router) { | |
| 223 | + $router->get('/settings', [AiController::class, 'getSettings']); | |
| 224 | + $router->post('/settings', [AiController::class, 'saveSettings']); | |
| 225 | + $router->post('/models', [AiController::class, 'getModels']); | |
| 226 | + $router->post('/test', [AiController::class, 'testConnection']); | |
| 227 | + $router->post('/generate', [AiController::class, 'generate']); | |
| 228 | +}); | |
| 229 | + | |
| 194 | 230 | $router->prefix('webhooks')->withPolicy('WebhookPolicy')->group(function ($router) { |
| 195 | 231 | $router->get('/', [WebhookController::class, 'index']); |
| 196 | 232 | $router->post('/', [WebhookController::class, 'create']); |
| 197 | 233 | $router->put('/{id}', [WebhookController::class, 'update'])->int('id'); |
| @@ -210,9 +246,11 @@ | ||
| 210 | 246 | $router->prefix('member/{id}')->withPolicy('UserPolicy')->group(function ($router) { |
| 211 | 247 | $router->get('/', [UserController::class, 'getMemberInfo']); |
| 212 | 248 | $router->get('/projects', [UserController::class, 'getMemberBoards']); |
| 213 | 249 | $router->get('/tasks', [UserController::class, 'getMemberAssociatedTasks']); |
| 250 | + $router->get('/task-counts', [UserController::class, 'getMemberTaskCounts']); | |
| 214 | 251 | $router->get('/activities', [UserController::class, 'getMemberRelatedAcitivies']); |
| 252 | + $router->get('/stats', [UserController::class, 'getMemberStats']); | |
| 215 | 253 | }); |
| 216 | 254 | |
| 217 | 255 | /* |
| 218 | 256 | * TODO: I guess we can minimize the number of routes. and Backend code needs to be refactored |
| @@ -238,9 +276,9 @@ | ||
| 238 | 276 | }); |
| 239 | 277 | |
| 240 | 278 | // User utility routes |
| 241 | 279 | $router->withPolicy('UserPolicy')->group(function ($router) { |
| 242 | - $router->get('/quick-search', [OptionsController::class, 'quickSearch']); | |
| 280 | + $router->get('/global-search', [OptionsController::class, 'globalSearch']); | |
| 243 | 281 | }); |
| 244 | 282 | |
| 245 | 283 | |
| 246 | 284 | $router->prefix('options')->withPolicy('AuthPolicy')->group(function ($router) { |