Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
RolePlayScorecardController
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 index
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 show
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 update
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 reset
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Http\Controllers\v2\RolePlay;
4
5use App\Http\Controllers\Controller;
6use App\Http\Models\UserRolePlaySettings;
7use App\Http\Requests\v2\RolePlay\UpdateScorecardRequest;
8use App\Http\Services\ScorecardResolverService;
9use Illuminate\Http\JsonResponse;
10use Illuminate\Http\Request;
11
12/**
13 * RolePlay Scorecard Controller
14 *
15 * Manages per-user scorecard configuration overrides for roleplay sessions.
16 * Supports viewing resolved scorecards (with the full user -> company -> system
17 * fallback chain) and saving/resetting user-level overrides per call type.
18 *
19 * When a company has forced a scorecard (`is_forced = true`), the user cannot
20 * save an override and the endpoint returns 403.
21 */
22class RolePlayScorecardController extends Controller
23{
24    /**
25     * @param ScorecardResolverService $scorecardResolver Service for resolving the effective scorecard
26     */
27    public function __construct(
28        private readonly ScorecardResolverService $scorecardResolver
29    ) {}
30
31    /**
32     * List resolved scorecards for ALL call types for the authenticated user.
33     *
34     * Returns the effective scorecard for every known call type, applying
35     * the resolution chain: user override -> company default -> system default.
36     *
37     * @param Request $request
38     * @return JsonResponse
39     *
40     * @response 200 {
41     *   "result": {
42     *     "data": [
43     *       {
44     *         "call_type": "cold-call",
45     *         "scorecard": [...],
46     *         "source": "system",
47     *         "locked": false
48     *       },
49     *       {
50     *         "call_type": "discovery-call",
51     *         "scorecard": [...],
52     *         "source": "company",
53     *         "locked": true
54     *       }
55     *     ]
56     *   }
57     * }
58     */
59    public function index(Request $request): JsonResponse
60    {
61        $user = $request->user();
62
63        $resolved = $this->scorecardResolver->resolveAll($user->id);
64
65        // Transform keyed map to array of objects with call_type included
66        $data = [];
67        foreach ($resolved as $callType => $entry) {
68            $data[] = array_merge(['call_type' => $callType], $entry);
69        }
70
71        return response()->json(['data' => $data]);
72    }
73
74    /**
75     * Get the resolved scorecard for a specific call type.
76     *
77     * Returns the effective scorecard after applying the resolution chain:
78     * user override -> company default -> system default.
79     *
80     * @param Request $request
81     * @param string $callType The call type identifier (e.g., 'cold-call', 'discovery-call')
82     * @return JsonResponse
83     *
84     * @response 200 {
85     *   "result": {
86     *     "data": {
87     *       "call_type": "cold-call",
88     *       "scorecard": [...],
89     *       "source": "user",
90     *       "locked": false
91     *     }
92     *   }
93     * }
94     */
95    public function show(Request $request, string $callType): JsonResponse
96    {
97        $user = $request->user();
98
99        $resolved = $this->scorecardResolver->resolve($user->id, $callType);
100
101        return response()->json([
102            'data' => array_merge(['call_type' => $callType], $resolved),
103        ]);
104    }
105
106    /**
107     * Save a user-level scorecard override for a specific call type.
108     *
109     * Stores the provided scorecard in the user's settings under
110     * `scorecard_config[$callType]`. If the company has forced this
111     * call type's scorecard, returns 403.
112     *
113     * @param UpdateScorecardRequest $request Validated scorecard data
114     * @param string $callType The call type identifier (e.g., 'cold-call', 'discovery-call')
115     * @return JsonResponse
116     *
117     * @response 200 {
118     *   "result": {
119     *     "data": {
120     *       "call_type": "cold-call",
121     *       "scorecard": [...],
122     *       "source": "user",
123     *       "locked": false
124     *     }
125     *   }
126     * }
127     * @response 403 {"error": "This scorecard is locked by your company and cannot be modified."}
128     */
129    public function update(UpdateScorecardRequest $request, string $callType): JsonResponse
130    {
131        $user = $request->user();
132
133        // Check if the company has forced the scorecard for this call type
134        $resolved = $this->scorecardResolver->resolve($user->id, $callType);
135        if ($resolved['locked']) {
136            return response()->json([
137                'error' => 'This scorecard is locked by your company and cannot be modified.',
138            ], 403);
139        }
140
141        // Save the user-level override
142        $settings = UserRolePlaySettings::forUser($user->id);
143        $config = $settings->scorecard_config ?? [];
144        $config[$callType] = $request->input('scorecard');
145        $settings->update(['scorecard_config' => $config]);
146
147        return response()->json([
148            'data' => [
149                'call_type' => $callType,
150                'scorecard' => $request->input('scorecard'),
151                'source'    => 'user',
152                'locked'    => false,
153            ],
154        ]);
155    }
156
157    /**
158     * Remove the user-level scorecard override for a specific call type.
159     *
160     * Clears the user's custom scorecard for the given call type, causing
161     * subsequent resolution to fall back to the company or system default.
162     * Returns the resolved scorecard after the reset.
163     *
164     * @param Request $request
165     * @param string $callType The call type identifier (e.g., 'cold-call', 'discovery-call')
166     * @return JsonResponse
167     *
168     * @response 200 {
169     *   "result": {
170     *     "data": {
171     *       "call_type": "cold-call",
172     *       "scorecard": [...],
173     *       "source": "company",
174     *       "locked": false
175     *     }
176     *   }
177     * }
178     */
179    public function reset(Request $request, string $callType): JsonResponse
180    {
181        $user = $request->user();
182
183        // Remove the user override for this call type
184        $settings = UserRolePlaySettings::forUser($user->id);
185        $config = $settings->scorecard_config ?? [];
186        unset($config[$callType]);
187        $settings->update(['scorecard_config' => $config]);
188
189        // Re-resolve to get the fallback scorecard
190        $resolved = $this->scorecardResolver->resolve($user->id, $callType);
191
192        return response()->json([
193            'data' => array_merge(['call_type' => $callType], $resolved),
194        ]);
195    }
196}