Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 155
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
RoleConfigController
0.00% covered (danger)
0.00%
0 / 155
0.00% covered (danger)
0.00%
0 / 6
992
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getRoles
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 getRolesMap
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 updateRoles
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 1
210
 seedRoles
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
42
 deleteRole
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace BuyerKiosk\TeamMember\Controllers;
4
5use BuyerKiosk\TeamMember\Services\RoleConfigService;
6
7/**
8 * RoleConfigController - API endpoints for role configuration
9 *
10 * Handles CRUD operations for store-specific role configuration.
11 * All endpoints require store access and appropriate permissions.
12 *
13 * @package BuyerKiosk\TeamMember\Controllers
14 */
15class RoleConfigController
16{
17    /**
18     * @var \Slim\Slim Slim application instance
19     */
20    private $app;
21
22    /**
23     * @var RoleConfigService Role configuration service
24     */
25    private RoleConfigService $roleConfigService;
26
27    /**
28     * Constructor
29     *
30     * @param \Slim\Slim $app Slim application instance
31     * @param RoleConfigService $roleConfigService Role config service
32     */
33    public function __construct($app, RoleConfigService $roleConfigService)
34    {
35        $this->app = $app;
36        $this->roleConfigService = $roleConfigService;
37    }
38
39    /**
40     * GET /:typeNum/api/roles
41     * Get all roles for the store
42     */
43    public function getRoles(): void
44    {
45        $this->app->response->headers->set('Content-Type', 'application/json');
46
47        try {
48            $roles = $this->roleConfigService->toArray();
49            $hasCustom = $this->roleConfigService->hasCustomRoles();
50
51            echo json_encode([
52                'success' => true,
53                'data' => [
54                    'roles' => $roles,
55                    'isCustom' => $hasCustom,
56                ],
57            ]);
58        } catch (\Exception $e) {
59            $this->app->halt(500, json_encode([
60                'success' => false,
61                'error' => 'server_error',
62                'message' => 'Failed to load roles',
63            ]));
64        }
65    }
66
67    /**
68     * GET /:typeNum/api/roles/map
69     * Get roles as a map (optimized for JavaScript consumption)
70     */
71    public function getRolesMap(): void
72    {
73        $this->app->response->headers->set('Content-Type', 'application/json');
74
75        try {
76            $rolesMap = $this->roleConfigService->toMap();
77
78            echo json_encode([
79                'success' => true,
80                'data' => $rolesMap,
81            ]);
82        } catch (\Exception $e) {
83            $this->app->halt(500, json_encode([
84                'success' => false,
85                'error' => 'server_error',
86                'message' => 'Failed to load roles',
87            ]));
88        }
89    }
90
91    /**
92     * PUT /:typeNum/api/roles
93     * Update/create multiple roles at once
94     *
95     * Request body:
96     * {
97     *   "roles": [
98     *     {"id": 4, "name": "Buyer", "color": "#ffc107", "sortOrder": 3}
99     *   ]
100     * }
101     */
102    public function updateRoles(): void
103    {
104        $this->app->response->headers->set('Content-Type', 'application/json');
105
106        // Check permission
107        if (!$this->app->user->checkAccess('uri_employees')) {
108            $this->app->halt(403, json_encode([
109                'success' => false,
110                'error' => 'forbidden',
111                'message' => 'You do not have permission to manage roles',
112            ]));
113            return;
114        }
115
116        // Validate CSRF token
117        $data = json_decode($this->app->request->getBody(), true);
118        if (!$data) {
119            $data = $this->app->request->post();
120        }
121
122        if (!isset($data['csrf_token']) || !\NoCSRF::check('csrf_token', $data, false)) {
123            $this->app->halt(403, json_encode([
124                'success' => false,
125                'error' => 'csrf_error',
126                'message' => 'Invalid or missing CSRF token',
127            ]));
128            return;
129        }
130
131        if (empty($data['roles']) || !is_array($data['roles'])) {
132            $this->app->halt(400, json_encode([
133                'success' => false,
134                'error' => 'validation_error',
135                'message' => 'Missing roles array',
136            ]));
137            return;
138        }
139
140        try {
141            $updated = 0;
142            $errors = [];
143
144            foreach ($data['roles'] as $role) {
145                if (!isset($role['id']) || !isset($role['name']) || !isset($role['color'])) {
146                    $errors[] = "Role missing required fields: id, name, color";
147                    continue;
148                }
149
150                $result = $this->roleConfigService->saveRole(
151                    (int) $role['id'],
152                    $role['name'],
153                    $role['color'],
154                    (int) ($role['sortOrder'] ?? 0)
155                );
156
157                if ($result) {
158                    $updated++;
159                } else {
160                    $errors[] = "Failed to save role ID {$role['id']}";
161                }
162            }
163
164            echo json_encode([
165                'success' => empty($errors),
166                'data' => [
167                    'updated' => $updated,
168                    'errors' => $errors,
169                    'roles' => $this->roleConfigService->toArray(),
170                    'csrf_token' => \NoCSRF::generate('csrf_token'), // Fresh token for next request
171                ],
172            ]);
173        } catch (\InvalidArgumentException $e) {
174            $this->app->halt(400, json_encode([
175                'success' => false,
176                'error' => 'validation_error',
177                'message' => $e->getMessage(),
178            ]));
179        } catch (\Exception $e) {
180            $this->app->halt(500, json_encode([
181                'success' => false,
182                'error' => 'server_error',
183                'message' => 'Failed to update roles',
184            ]));
185        }
186    }
187
188    /**
189     * POST /:typeNum/api/roles/seed
190     * Initialize store with default roles
191     */
192    public function seedRoles(): void
193    {
194        $this->app->response->headers->set('Content-Type', 'application/json');
195
196        // Check permission
197        if (!$this->app->user->checkAccess('uri_employees')) {
198            $this->app->halt(403, json_encode([
199                'success' => false,
200                'error' => 'forbidden',
201                'message' => 'You do not have permission to manage roles',
202            ]));
203            return;
204        }
205
206        // Validate CSRF token
207        $data = json_decode($this->app->request->getBody(), true);
208        if (!$data) {
209            $data = $this->app->request->post();
210        }
211
212        if (!isset($data['csrf_token']) || !\NoCSRF::check('csrf_token', $data, false)) {
213            $this->app->halt(403, json_encode([
214                'success' => false,
215                'error' => 'csrf_error',
216                'message' => 'Invalid or missing CSRF token',
217            ]));
218            return;
219        }
220
221        try {
222            $created = $this->roleConfigService->seedDefaultRoles();
223
224            echo json_encode([
225                'success' => true,
226                'data' => [
227                    'created' => $created,
228                    'roles' => $this->roleConfigService->toArray(),
229                ],
230            ]);
231        } catch (\Exception $e) {
232            $this->app->halt(500, json_encode([
233                'success' => false,
234                'error' => 'server_error',
235                'message' => 'Failed to seed roles',
236            ]));
237        }
238    }
239
240    /**
241     * DELETE /:typeNum/api/roles/:roleId
242     * Deactivate a role (soft delete)
243     */
244    public function deleteRole(int $roleId): void
245    {
246        $this->app->response->headers->set('Content-Type', 'application/json');
247
248        // Check permission
249        if (!$this->app->user->checkAccess('uri_employees')) {
250            $this->app->halt(403, json_encode([
251                'success' => false,
252                'error' => 'forbidden',
253                'message' => 'You do not have permission to manage roles',
254            ]));
255            return;
256        }
257
258        // Validate CSRF token
259        $data = json_decode($this->app->request->getBody(), true);
260        if (!$data) {
261            $data = $this->app->request->post();
262        }
263
264        if (!isset($data['csrf_token']) || !\NoCSRF::check('csrf_token', $data, false)) {
265            $this->app->halt(403, json_encode([
266                'success' => false,
267                'error' => 'csrf_error',
268                'message' => 'Invalid or missing CSRF token',
269            ]));
270            return;
271        }
272
273        try {
274            $result = $this->roleConfigService->deactivateRole($roleId);
275
276            echo json_encode([
277                'success' => $result,
278                'data' => [
279                    'roleId' => $roleId,
280                    'roles' => $this->roleConfigService->toArray(),
281                ],
282            ]);
283        } catch (\Exception $e) {
284            $this->app->halt(500, json_encode([
285                'success' => false,
286                'error' => 'server_error',
287                'message' => 'Failed to delete role',
288            ]));
289        }
290    }
291}