297 lines
12 KiB
PHP
297 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Action;
|
|
use App\Models\ArchiveSetting;
|
|
use App\Models\Decision;
|
|
use App\Models\EmailTemplate;
|
|
use App\Models\Segment;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Inertia\Inertia;
|
|
|
|
class WorkflowController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
return Inertia::render('Settings/Workflow/Index', [
|
|
'actions' => Action::query()->with(['decisions', 'segment'])->withCount('activities')->orderBy('id')->get(),
|
|
'decisions' => Decision::query()->with(['actions', 'events'])->withCount('activities')->orderBy('id')->get(),
|
|
'segments' => Segment::query()->get(),
|
|
'email_templates' => EmailTemplate::query()->where('active', true)->get(['id', 'name', 'entity_types']),
|
|
'events' => \App\Models\Event::query()->orderBy('name')->get(['id', 'name', 'key', 'description', 'active']),
|
|
'archive_settings' => ArchiveSetting::query()->where('enabled', true)->orderBy('id')->get(['id', 'name']),
|
|
]);
|
|
}
|
|
|
|
public function storeAction(Request $request)
|
|
{
|
|
$attributes = $request->validate([
|
|
'name' => 'required|string|max:50',
|
|
'color_tag' => 'nullable|string|max:25',
|
|
'segment_id' => 'nullable|integer|exists:segments,id',
|
|
'decisions' => 'nullable|array',
|
|
'decisions.*.id' => 'required_with:decisions.*|integer|exists:decisions,id',
|
|
'decisions.*.name' => 'required_with:decisions.*|string|max:50',
|
|
]);
|
|
|
|
$decisionIds = collect($attributes['decisions'] ?? [])->pluck('id')->toArray();
|
|
|
|
\DB::transaction(function () use ($attributes, $decisionIds) {
|
|
/** @var \App\Models\Action $row */
|
|
$row = Action::create([
|
|
'name' => $attributes['name'],
|
|
'color_tag' => $attributes['color_tag'] ?? null,
|
|
'segment_id' => $attributes['segment_id'] ?? null,
|
|
]);
|
|
|
|
if (! empty($decisionIds)) {
|
|
$row->decisions()->sync($decisionIds);
|
|
}
|
|
});
|
|
|
|
return to_route('settings.workflow')->with('success', 'Action created successfully!');
|
|
}
|
|
|
|
public function updateAction(int $id, Request $request)
|
|
{
|
|
$row = Action::findOrFail($id);
|
|
|
|
$attributes = $request->validate([
|
|
'name' => 'required|string|max:50',
|
|
'color_tag' => 'nullable|string|max:25',
|
|
'segment_id' => 'nullable|integer|exists:segments,id',
|
|
'decisions' => 'nullable|array',
|
|
'decisions.*.id' => 'required_with:decisions.*|integer|exists:decisions,id',
|
|
'decisions.*.name' => 'required_with:decisions.*|string|max:50',
|
|
]);
|
|
|
|
$decisionIds = collect($attributes['decisions'] ?? [])->pluck('id')->toArray();
|
|
|
|
\DB::transaction(function () use ($attributes, $decisionIds, $row) {
|
|
$row->update([
|
|
'name' => $attributes['name'],
|
|
'color_tag' => $attributes['color_tag'],
|
|
'segment_id' => $attributes['segment_id'] ?? null,
|
|
]);
|
|
$row->decisions()->sync($decisionIds);
|
|
});
|
|
|
|
return to_route('settings.workflow')->with('success', 'Update successful!');
|
|
}
|
|
|
|
public function storeDecision(Request $request)
|
|
{
|
|
$attributes = $request->validate([
|
|
'name' => 'required|string|max:50',
|
|
'color_tag' => 'nullable|string|max:25',
|
|
'auto_mail' => 'sometimes|boolean',
|
|
'email_template_id' => 'nullable|integer|exists:email_templates,id',
|
|
'actions' => 'nullable|array',
|
|
'actions.*.id' => 'required_with:actions.*|integer|exists:actions,id',
|
|
'actions.*.name' => 'required_with:actions.*|string|max:50',
|
|
'events' => 'nullable|array',
|
|
'events.*.id' => 'required_with:events.*|integer|exists:events,id',
|
|
'events.*.active' => 'sometimes|boolean',
|
|
'events.*.run_order' => 'nullable|integer',
|
|
'events.*.config' => 'nullable|array',
|
|
]);
|
|
|
|
$actionIds = collect($attributes['actions'] ?? [])->pluck('id')->toArray();
|
|
$eventsPayload = collect($attributes['events'] ?? []);
|
|
|
|
// Extra server-side validation for event-specific config keys
|
|
$validationErrors = [];
|
|
foreach ($eventsPayload as $i => $ev) {
|
|
$idEv = isset($ev['id']) ? (int) $ev['id'] : null;
|
|
if (! $idEv) {
|
|
continue;
|
|
}
|
|
$eventModel = \App\Models\Event::find($idEv);
|
|
$key = $eventModel?->key ?? ($ev['key'] ?? null);
|
|
if ($key === 'add_segment') {
|
|
$seg = $ev['config']['segment_id'] ?? null;
|
|
if (empty($seg) || ! Segment::where('id', $seg)->exists()) {
|
|
$validationErrors["events.$i.config.segment_id"] = 'Please select a valid segment for the add_segment event.';
|
|
}
|
|
} elseif ($key === 'archive_contract') {
|
|
$as = $ev['config']['archive_setting_id'] ?? null;
|
|
if (empty($as) || ! ArchiveSetting::where('id', $as)->exists()) {
|
|
$validationErrors["events.$i.config.archive_setting_id"] = 'Please select a valid archive setting for the archive_contract event.';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! empty($validationErrors)) {
|
|
throw ValidationException::withMessages($validationErrors);
|
|
}
|
|
|
|
\DB::transaction(function () use ($attributes, $actionIds, $eventsPayload) {
|
|
/** @var \App\Models\Decision $row */
|
|
$row = Decision::create([
|
|
'name' => $attributes['name'],
|
|
'color_tag' => $attributes['color_tag'] ?? null,
|
|
'auto_mail' => (bool) ($attributes['auto_mail'] ?? false),
|
|
'email_template_id' => $attributes['email_template_id'] ?? null,
|
|
]);
|
|
|
|
if (! empty($actionIds)) {
|
|
$row->actions()->sync($actionIds);
|
|
}
|
|
|
|
// Attach decision events with pivot attributes
|
|
if ($eventsPayload->isNotEmpty()) {
|
|
$sync = [];
|
|
foreach ($eventsPayload as $ev) {
|
|
$id = (int) ($ev['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
continue;
|
|
}
|
|
$cfg = $ev['config'] ?? null;
|
|
if (is_array($cfg)) {
|
|
$cfg = json_encode($cfg);
|
|
} elseif (is_string($cfg)) {
|
|
$trim = trim($cfg);
|
|
$cfg = $trim === '' ? null : $cfg;
|
|
} else {
|
|
$cfg = null;
|
|
}
|
|
$sync[$id] = [
|
|
'active' => (bool) ($ev['active'] ?? true),
|
|
'run_order' => isset($ev['run_order']) ? (int) $ev['run_order'] : null,
|
|
'config' => $cfg,
|
|
];
|
|
}
|
|
$row->events()->sync($sync);
|
|
}
|
|
});
|
|
|
|
return to_route('settings.workflow')->with('success', 'Decision created successfully!');
|
|
}
|
|
|
|
public function updateDecision(int $id, Request $request)
|
|
{
|
|
$row = Decision::findOrFail($id);
|
|
|
|
$attributes = $request->validate([
|
|
'name' => 'required|string|max:50',
|
|
'color_tag' => 'nullable|string|max:25',
|
|
'auto_mail' => 'sometimes|boolean',
|
|
'email_template_id' => 'nullable|integer|exists:email_templates,id',
|
|
'actions' => 'nullable|array',
|
|
'actions.*.id' => 'required_with:actions.*|integer|exists:actions,id',
|
|
'actions.*.name' => 'required_with:actions.*|string|max:50',
|
|
'events' => 'nullable|array',
|
|
'events.*.id' => 'required_with:events.*|integer|exists:events,id',
|
|
'events.*.active' => 'sometimes|boolean',
|
|
'events.*.run_order' => 'nullable|integer',
|
|
'events.*.config' => 'nullable|array',
|
|
]);
|
|
|
|
$actionIds = collect($attributes['actions'] ?? [])->pluck('id')->toArray();
|
|
$eventsPayload = collect($attributes['events'] ?? []);
|
|
|
|
// Extra server-side validation for event-specific config keys
|
|
$validationErrors = [];
|
|
foreach ($eventsPayload as $i => $ev) {
|
|
$idEv = isset($ev['id']) ? (int) $ev['id'] : null;
|
|
if (! $idEv) {
|
|
continue;
|
|
}
|
|
$eventModel = \App\Models\Event::find($idEv);
|
|
$key = $eventModel?->key ?? ($ev['key'] ?? null);
|
|
if ($key === 'add_segment') {
|
|
$seg = $ev['config']['segment_id'] ?? null;
|
|
if (empty($seg) || ! Segment::where('id', $seg)->exists()) {
|
|
$validationErrors["events.$i.config.segment_id"] = 'Please select a valid segment for the add_segment event.';
|
|
}
|
|
} elseif ($key === 'archive_contract') {
|
|
$as = $ev['config']['archive_setting_id'] ?? null;
|
|
if (empty($as) || ! ArchiveSetting::where('id', $as)->exists()) {
|
|
$validationErrors["events.$i.config.archive_setting_id"] = 'Please select a valid archive setting for the archive_contract event.';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! empty($validationErrors)) {
|
|
throw ValidationException::withMessages($validationErrors);
|
|
}
|
|
|
|
\DB::transaction(function () use ($attributes, $actionIds, $eventsPayload, $row) {
|
|
$row->update([
|
|
'name' => $attributes['name'],
|
|
'color_tag' => $attributes['color_tag'] ?? null,
|
|
'auto_mail' => (bool) ($attributes['auto_mail'] ?? false),
|
|
'email_template_id' => $attributes['email_template_id'] ?? null,
|
|
]);
|
|
$row->actions()->sync($actionIds);
|
|
|
|
// Sync decision events with pivot attributes
|
|
if ($eventsPayload->isNotEmpty()) {
|
|
$sync = [];
|
|
foreach ($eventsPayload as $ev) {
|
|
$id = (int) ($ev['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
continue;
|
|
}
|
|
$cfg = $ev['config'] ?? null;
|
|
// ensure string JSON stored; accept already-JSON strings
|
|
if (is_array($cfg)) {
|
|
$cfg = json_encode($cfg);
|
|
} elseif (is_string($cfg)) {
|
|
$trim = trim($cfg);
|
|
// If not valid JSON, keep raw string (handler side can parse/ignore)
|
|
$cfg = $trim === '' ? null : $cfg;
|
|
} else {
|
|
$cfg = null;
|
|
}
|
|
$sync[$id] = [
|
|
'active' => (bool) ($ev['active'] ?? true),
|
|
'run_order' => isset($ev['run_order']) ? (int) $ev['run_order'] : null,
|
|
'config' => $cfg,
|
|
];
|
|
}
|
|
$row->events()->sync($sync);
|
|
} else {
|
|
// If empty provided explicitly, detach all to reflect UI intent
|
|
if (array_key_exists('events', $attributes)) {
|
|
$row->events()->detach();
|
|
}
|
|
}
|
|
});
|
|
|
|
return to_route('settings.workflow')->with('success', 'Decision updated successfully!');
|
|
}
|
|
|
|
public function destroyAction(int $id)
|
|
{
|
|
$row = Action::findOrFail($id);
|
|
if ($row->activities()->exists()) {
|
|
return back()->with('error', 'Cannot delete action because dependent activities exist.');
|
|
}
|
|
|
|
\DB::transaction(function () use ($row) {
|
|
$row->decisions()->detach();
|
|
$row->delete();
|
|
});
|
|
|
|
return back()->with('success', 'Action deleted successfully!');
|
|
}
|
|
|
|
public function destroyDecision(int $id)
|
|
{
|
|
$row = Decision::findOrFail($id);
|
|
if ($row->activities()->exists()) {
|
|
return back()->with('error', 'Cannot delete decision because dependent activities exist.');
|
|
}
|
|
|
|
\DB::transaction(function () use ($row) {
|
|
$row->actions()->detach();
|
|
$row->delete();
|
|
});
|
|
|
|
return back()->with('success', 'Decision deleted successfully!');
|
|
}
|
|
}
|