Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
51.43% covered (warning)
51.43%
18 / 35
42.86% covered (danger)
42.86%
3 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
RolePlayPersonaTemplatesController
51.43% covered (warning)
51.43%
18 / 35
42.86% covered (danger)
42.86%
3 / 7
28.50
0.00% covered (danger)
0.00%
0 / 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
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 store
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 update
42.86% covered (danger)
42.86%
3 / 7
0.00% covered (danger)
0.00%
0 / 1
2.75
 destroy
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 materialize
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
2.09
1<?php
2
3namespace App\Http\Controllers\v2\RolePlay;
4
5use App\Http\Controllers\Controller;
6use App\Http\Models\RolePlayPersonaTemplate;
7use App\Http\Requests\v2\RolePlay\StoreRolePlayPersonaTemplateRequest;
8use App\Http\Requests\v2\RolePlay\UpdateRolePlayPersonaTemplateRequest;
9use App\Http\Resources\v2\RolePlayPersonaTemplateResource;
10use App\Http\Services\RolePlayPersonaTemplateService;
11use Illuminate\Auth\Access\AuthorizationException;
12use Illuminate\Http\JsonResponse;
13use Illuminate\Http\Request;
14
15/**
16 * Controller for the RolePlay Persona Templates feature.
17 *
18 * Templates are a reusable Persona Details + Product Details bundle that
19 * users hydrate into new personas without copy-pasting. They never store
20 * customer profiles — fresh ICPs are generated when a template is used.
21 *
22 * Visibility:
23 *  - personal: only the owner sees and manages it
24 *  - company: visible to everyone in the company; only Global Admins can write
25 */
26class RolePlayPersonaTemplatesController extends Controller
27{
28    public function __construct(
29        private readonly RolePlayPersonaTemplateService $service,
30    ) {}
31
32    /**
33     * List templates visible to the authenticated user.
34     *
35     * @response 200 {"result": {"data": [{"id": "...", "name": "..."}]}}
36     */
37    public function index(Request $request)
38    {
39        $templates = $this->service->listVisibleTo($request->user());
40
41        return RolePlayPersonaTemplateResource::collection($templates);
42    }
43
44    /**
45     * Show a single template by ID.
46     *
47     * @response 200 {"result": {"id": "...", "name": "..."}}
48     * @response 403 {"error": "You do not have access to this template."}
49     */
50    public function show(Request $request, RolePlayPersonaTemplate $template): JsonResponse
51    {
52        try {
53            $this->service->assertCanRead($request->user(), $template);
54        } catch (AuthorizationException $e) {
55            return response()->json(['error' => $e->getMessage()], 403);
56        }
57
58        return response()->json([
59            'status' => 'success',
60            'data' => new RolePlayPersonaTemplateResource($template),
61        ]);
62    }
63
64    /**
65     * Create a new template.
66     *
67     * @response 201 {"result": {"id": "...", "name": "..."}}
68     * @response 403 {"error": "Only company Global Admins can manage company-visibility templates."}
69     */
70    public function store(StoreRolePlayPersonaTemplateRequest $request): JsonResponse
71    {
72        try {
73            $template = $this->service->create($request->user(), $request->validated());
74        } catch (AuthorizationException $e) {
75            return response()->json(['error' => $e->getMessage()], 403);
76        }
77
78        return response()->json([
79            'status' => 'success',
80            'data' => new RolePlayPersonaTemplateResource($template),
81        ], 201);
82    }
83
84    /**
85     * Update an existing template.
86     *
87     * @response 200 {"result": {"id": "...", "name": "..."}}
88     * @response 403 {"error": "..."}
89     */
90    public function update(
91        UpdateRolePlayPersonaTemplateRequest $request,
92        RolePlayPersonaTemplate $template
93    ): JsonResponse {
94        try {
95            $template = $this->service->update($request->user(), $template, $request->validated());
96        } catch (AuthorizationException $e) {
97            return response()->json(['error' => $e->getMessage()], 403);
98        }
99
100        return response()->json([
101            'status' => 'success',
102            'data' => new RolePlayPersonaTemplateResource($template),
103        ]);
104    }
105
106    /**
107     * Delete a template.
108     *
109     * @response 200 {"result": {"status": "success"}}
110     * @response 403 {"error": "..."}
111     */
112    public function destroy(Request $request, RolePlayPersonaTemplate $template): JsonResponse
113    {
114        try {
115            $this->service->delete($request->user(), $template);
116        } catch (AuthorizationException $e) {
117            return response()->json(['error' => $e->getMessage()], 403);
118        }
119
120        return response()->json(['status' => 'success']);
121    }
122
123    /**
124     * Materialize a template into a draft persona payload.
125     *
126     * Returns the Persona Details + Product Details fields ready for the
127     * frontend to hydrate a new persona form. customer_profiles is always
128     * empty so the user must regenerate fresh ICPs against the new context.
129     *
130     * @response 200 {"result": {"name": "...", "type": "...", "customer_profiles": []}}
131     * @response 403 {"error": "..."}
132     */
133    public function materialize(Request $request, RolePlayPersonaTemplate $template): JsonResponse
134    {
135        try {
136            $payload = $this->service->materialize($request->user(), $template);
137        } catch (AuthorizationException $e) {
138            return response()->json(['error' => $e->getMessage()], 403);
139        }
140
141        return response()->json([
142            'status' => 'success',
143            'data' => $payload,
144        ]);
145    }
146}