Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
CompanyRolePlayScorecardController
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 show
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 update
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 destroy
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace App\Http\Controllers\v2\Company;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\v2\Company\UpdateCompanyScorecardRequest;
7use App\Http\Services\CompanyRolePlayScorecardService;
8use Illuminate\Http\JsonResponse;
9use Illuminate\Http\Request;
10
11/**
12 * Company RolePlay Scorecard Controller
13 *
14 * Manages company-level scorecard configurations for roleplay sessions.
15 * Company admins can define custom scorecards per call type that serve as
16 * defaults for all users within the company. When `is_forced` is true,
17 * users are prevented from overriding the scorecard.
18 *
19 * The authenticated user must belong to a company (have a `company_id`).
20 * Routes are protected by the `ensure.company.admin` middleware.
21 */
22class CompanyRolePlayScorecardController extends Controller
23{
24    public function __construct(
25        private readonly CompanyRolePlayScorecardService $scorecardService,
26    ) {}
27
28    /**
29     * List all company scorecard configurations.
30     *
31     * Returns all scorecard configurations defined for the authenticated
32     * user's company, across all call types.
33     *
34     * @response 200 {
35     *   "result": {
36     *     "data": [
37     *       {
38     *         "id": "...",
39     *         "call_type": "cold-call",
40     *         "scorecard": [...],
41     *         "is_forced": false,
42     *         "created_by": "...",
43     *         "created_at": 1700000000,
44     *         "updated_at": 1700000000
45     *       }
46     *     ]
47     *   }
48     * }
49     */
50    public function index(Request $request): JsonResponse
51    {
52        $scorecards = $this->scorecardService->listForCompany($request->user()->company_id);
53
54        return response()->json(['data' => $scorecards]);
55    }
56
57    /**
58     * Get the company scorecard configuration for a specific call type.
59     *
60     * @param  string  $callType  The call type identifier (e.g., 'cold-call', 'discovery-call')
61     *
62     * @response 200 {
63     *   "result": {
64     *     "data": {
65     *       "id": "...",
66     *       "call_type": "cold-call",
67     *       "scorecard": [...],
68     *       "is_forced": false,
69     *       "created_by": "...",
70     *       "created_at": 1700000000,
71     *       "updated_at": 1700000000
72     *     }
73     *   }
74     * }
75     * @response 404 {"status": "error", "message": "Scorecard not found for this call type."}
76     */
77    public function show(Request $request, string $callType): JsonResponse
78    {
79        $scorecard = $this->scorecardService->findForCompanyAndCallType(
80            $request->user()->company_id,
81            $callType,
82        );
83
84        if (! $scorecard) {
85            return response()->json([
86                'status' => 'error',
87                'message' => 'Scorecard not found for this call type.',
88            ], 404);
89        }
90
91        return response()->json(['data' => $scorecard]);
92    }
93
94    /**
95     * Create or update a company scorecard configuration for a specific call type.
96     *
97     * Uses upsert semantics: if no record exists for the given company + call type,
98     * a new one is created. Otherwise, the existing record is updated.
99     *
100     * @param  UpdateCompanyScorecardRequest  $request  Validated scorecard data
101     * @param  string  $callType  The call type identifier (e.g., 'cold-call', 'discovery-call')
102     *
103     * @response 200 {
104     *   "result": {
105     *     "data": {
106     *       "id": "...",
107     *       "call_type": "cold-call",
108     *       "scorecard": [...],
109     *       "is_forced": false,
110     *       "created_by": "...",
111     *       "created_at": 1700000000,
112     *       "updated_at": 1700000000
113     *     }
114     *   }
115     * }
116     */
117    public function update(UpdateCompanyScorecardRequest $request, string $callType): JsonResponse
118    {
119        $user = $request->user();
120
121        $scorecard = $this->scorecardService->upsertForCompanyAndCallType(
122            $user->company_id,
123            $callType,
124            (array) $request->input('scorecard'),
125            (bool) $request->input('is_forced', false),
126            $user->id,
127        );
128
129        return response()->json(['data' => $scorecard]);
130    }
131
132    /**
133     * Delete the company scorecard configuration for a specific call type.
134     *
135     * Removes the company-level scorecard override. Users will then fall back
136     * to the system default scorecard for this call type.
137     *
138     * @param  string  $callType  The call type identifier (e.g., 'cold-call', 'discovery-call')
139     *
140     * @response 200 {"status": "success"}
141     * @response 404 {"status": "error", "message": "Scorecard not found for this call type."}
142     */
143    public function destroy(Request $request, string $callType): JsonResponse
144    {
145        $deleted = $this->scorecardService->deleteForCompanyAndCallType(
146            $request->user()->company_id,
147            $callType,
148        );
149
150        if (! $deleted) {
151            return response()->json([
152                'status' => 'error',
153                'message' => 'Scorecard not found for this call type.',
154            ], 404);
155        }
156
157        return response()->json(['status' => 'success']);
158    }
159}