style updates
This commit is contained in:
@@ -42,6 +42,7 @@ class AuditTrailController extends Controller
|
|||||||
* Show the specified resource.
|
* Show the specified resource.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
|
*
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function show($id)
|
public function show($id)
|
||||||
@@ -53,6 +54,7 @@ class AuditTrailController extends Controller
|
|||||||
* Show the form for editing the specified resource.
|
* Show the form for editing the specified resource.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
|
*
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
@@ -64,6 +66,7 @@ class AuditTrailController extends Controller
|
|||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
|
*
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, $id)
|
public function update(Request $request, $id)
|
||||||
@@ -75,6 +78,7 @@ class AuditTrailController extends Controller
|
|||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
|
*
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function destroy($id)
|
public function destroy($id)
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ class AuditTrailServiceProvider extends ServiceProvider
|
|||||||
module_path(ModuleInfo::name(), 'Config/config.php') => config_path(ModuleInfo::nameLower() . '.php'),
|
module_path(ModuleInfo::name(), 'Config/config.php') => config_path(ModuleInfo::nameLower() . '.php'),
|
||||||
], 'config');
|
], 'config');
|
||||||
$this->mergeConfigFrom(
|
$this->mergeConfigFrom(
|
||||||
module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower()
|
module_path(ModuleInfo::name(), 'Config/config.php'),
|
||||||
|
ModuleInfo::nameLower(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ class IsoCountriesSeeder extends Seeder
|
|||||||
'iso_status' => 'official',
|
'iso_status' => 'official',
|
||||||
'source' => 'bundled-iso-3166-1',
|
'source' => 'bundled-iso-3166-1',
|
||||||
'source_version' => '2026-06-15',
|
'source_version' => '2026-06-15',
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class IsoCountrySubdivisionsSeeder extends Seeder
|
|||||||
'last_synced_at' => $now,
|
'last_synced_at' => $now,
|
||||||
'source' => 'bundled-bootstrap',
|
'source' => 'bundled-bootstrap',
|
||||||
'source_version' => '2026-06-15',
|
'source_version' => '2026-06-15',
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
|
||||||
use Modules\Countries\Http\Controllers\CountriesController;
|
use Modules\Countries\Http\Controllers\CountriesController;
|
||||||
use Modules\Countries\Http\Controllers\CountrySyncController;
|
use Modules\Countries\Http\Controllers\CountrySyncController;
|
||||||
use Modules\Countries\Http\Controllers\ReferenceDataController;
|
use Modules\Countries\Http\Controllers\ReferenceDataController;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class DocumentationCategory extends Model
|
|||||||
* Active category scope.
|
* Active category scope.
|
||||||
*
|
*
|
||||||
* @param Builder<DocumentationCategory> $query
|
* @param Builder<DocumentationCategory> $query
|
||||||
|
*
|
||||||
* @return Builder<DocumentationCategory>
|
* @return Builder<DocumentationCategory>
|
||||||
*/
|
*/
|
||||||
public function scopeActive(Builder $query): Builder
|
public function scopeActive(Builder $query): Builder
|
||||||
@@ -78,6 +79,7 @@ class DocumentationCategory extends Model
|
|||||||
* Ordered category scope.
|
* Ordered category scope.
|
||||||
*
|
*
|
||||||
* @param Builder<DocumentationCategory> $query
|
* @param Builder<DocumentationCategory> $query
|
||||||
|
*
|
||||||
* @return Builder<DocumentationCategory>
|
* @return Builder<DocumentationCategory>
|
||||||
*/
|
*/
|
||||||
public function scopeOrdered(Builder $query): Builder
|
public function scopeOrdered(Builder $query): Builder
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class DocumentationDocument extends Model
|
|||||||
* Published document scope.
|
* Published document scope.
|
||||||
*
|
*
|
||||||
* @param Builder<DocumentationDocument> $query
|
* @param Builder<DocumentationDocument> $query
|
||||||
|
*
|
||||||
* @return Builder<DocumentationDocument>
|
* @return Builder<DocumentationDocument>
|
||||||
*/
|
*/
|
||||||
public function scopePublished(Builder $query): Builder
|
public function scopePublished(Builder $query): Builder
|
||||||
@@ -113,6 +114,7 @@ class DocumentationDocument extends Model
|
|||||||
* Featured document scope.
|
* Featured document scope.
|
||||||
*
|
*
|
||||||
* @param Builder<DocumentationDocument> $query
|
* @param Builder<DocumentationDocument> $query
|
||||||
|
*
|
||||||
* @return Builder<DocumentationDocument>
|
* @return Builder<DocumentationDocument>
|
||||||
*/
|
*/
|
||||||
public function scopeFeatured(Builder $query): Builder
|
public function scopeFeatured(Builder $query): Builder
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ final class DocumentationServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
$this->mergeConfigFrom(
|
$this->mergeConfigFrom(
|
||||||
module_path('Documentation', 'Config/config.php'),
|
module_path('Documentation', 'Config/config.php'),
|
||||||
'documentation'
|
'documentation',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class DocumentationModuleTest extends TestCase
|
|||||||
* Add a CSRF token for projects that keep web middleware enabled during tests.
|
* Add a CSRF token for projects that keep web middleware enabled during tests.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function csrfPayload(array $payload): array
|
private function csrfPayload(array $payload): array
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ return new class extends Migration
|
|||||||
'sort_order' => ($index + 1) * 10,
|
'sort_order' => ($index + 1) * 10,
|
||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
] as $row) {
|
] as $row) {
|
||||||
Category::query()->updateOrCreate(
|
Category::query()->updateOrCreate(
|
||||||
['company_id' => null, 'slug' => Str::slug($row['name'])],
|
['company_id' => null, 'slug' => Str::slug($row['name'])],
|
||||||
$row + ['company_id' => null, 'slug' => Str::slug($row['name'])]
|
$row + ['company_id' => null, 'slug' => Str::slug($row['name'])],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'color' => 'blue',
|
'color' => 'blue',
|
||||||
'description' => 'Default support team for triage, ownership and ticket routing.',
|
'description' => 'Default support team for triage, ownership and ticket routing.',
|
||||||
'is_default' => true,
|
'is_default' => true,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Project::query()->updateOrCreate(
|
Project::query()->updateOrCreate(
|
||||||
@@ -132,7 +132,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'color' => 'indigo',
|
'color' => 'indigo',
|
||||||
'description' => 'Default project for operational helpdesk tickets.',
|
'description' => 'Default project for operational helpdesk tickets.',
|
||||||
'is_default' => true,
|
'is_default' => true,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'description' => 'Default Helpdesk workflow board covering new work through closure.',
|
'description' => 'Default Helpdesk workflow board covering new work through closure.',
|
||||||
'is_default' => true,
|
'is_default' => true,
|
||||||
'sort_order' => 1,
|
'sort_order' => 1,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
$statuses = Status::query()
|
$statuses = Status::query()
|
||||||
@@ -181,7 +181,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
] as $template) {
|
] as $template) {
|
||||||
WorkflowTemplate::query()->updateOrCreate(
|
WorkflowTemplate::query()->updateOrCreate(
|
||||||
['name' => $template['name'], 'event' => $template['event']],
|
['name' => $template['name'], 'event' => $template['event']],
|
||||||
$template + ['text_body' => null, 'cc' => null, 'bcc' => null, 'is_active' => true]
|
$template + ['text_body' => null, 'cc' => null, 'bcc' => null, 'is_active' => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'business_hours_only' => false,
|
'business_hours_only' => false,
|
||||||
'is_default' => true,
|
'is_default' => true,
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
CannedResponse::query()->updateOrCreate(
|
CannedResponse::query()->updateOrCreate(
|
||||||
@@ -223,7 +223,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'body' => '<p>Hello {{ requester.name }},</p><p>Thank you for the update on {{ ticket.reference }}. I am reviewing this now and will come back with the next action shortly.</p><p>Regards,<br>{{ agent.name }}</p>',
|
'body' => '<p>Hello {{ requester.name }},</p><p>Thank you for the update on {{ ticket.reference }}. I am reviewing this now and will come back with the next action shortly.</p><p>Regards,<br>{{ agent.name }}</p>',
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
'sort_order' => 10,
|
'sort_order' => 10,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
ChecklistTemplate::query()->updateOrCreate(
|
ChecklistTemplate::query()->updateOrCreate(
|
||||||
@@ -236,7 +236,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'slug' => 'bug-triage-checklist',
|
'slug' => 'bug-triage-checklist',
|
||||||
'items' => ['Confirm reproduction steps', 'Check recent deployment/build', 'Identify affected tenant/company', 'Record fix or workaround'],
|
'items' => ['Confirm reproduction steps', 'Check recent deployment/build', 'Identify affected tenant/company', 'Record fix or workaround'],
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ([
|
foreach ([
|
||||||
@@ -245,7 +245,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
] as $tag) {
|
] as $tag) {
|
||||||
Tag::query()->updateOrCreate(
|
Tag::query()->updateOrCreate(
|
||||||
['company_id' => null, 'slug' => $tag['slug']],
|
['company_id' => null, 'slug' => $tag['slug']],
|
||||||
$tag + ['company_id' => null]
|
$tag + ['company_id' => null],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
|
|||||||
'release_channel' => 'core',
|
'release_channel' => 'core',
|
||||||
'description' => 'Default service record for WMS platform support tickets.',
|
'description' => 'Default service record for WMS platform support tickets.',
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ final class HelpdeskLivewireAssetsTest extends TestCase
|
|||||||
self::assertLessThan(
|
self::assertLessThan(
|
||||||
strpos($layout, '@fluxScripts'),
|
strpos($layout, '@fluxScripts'),
|
||||||
strpos($layout, '@livewireScripts'),
|
strpos($layout, '@livewireScripts'),
|
||||||
'Livewire scripts must be loaded before Flux scripts so settings CRUD actions fire in the browser.'
|
'Livewire scripts must be loaded before Flux scripts so settings CRUD actions fire in the browser.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Modules\Helpdesk\Http\Controllers;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Modules\Helpdesk\Models\Ticket;
|
use Modules\Helpdesk\Models\Ticket;
|
||||||
@@ -34,7 +35,7 @@ final class TicketAttachmentStoreController extends Controller
|
|||||||
|
|
||||||
$uploadedFiles = $request->file('attachments', []);
|
$uploadedFiles = $request->file('attachments', []);
|
||||||
$uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles];
|
$uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles];
|
||||||
$uploadedFiles = array_values(array_filter($uploadedFiles, static fn ($file): bool => $file instanceof \Illuminate\Http\UploadedFile && $file->isValid()));
|
$uploadedFiles = array_values(array_filter($uploadedFiles, static fn ($file): bool => $file instanceof UploadedFile && $file->isValid()));
|
||||||
|
|
||||||
$attachments->storeMany($ticket, $uploadedFiles, (int) $user->id);
|
$attachments->storeMany($ticket, $uploadedFiles, (int) $user->id);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Modules\Helpdesk\Http\Controllers;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -74,7 +75,7 @@ final class TicketCommentStoreController extends Controller
|
|||||||
|
|
||||||
$uploadedFiles = $request->file('attachments', []);
|
$uploadedFiles = $request->file('attachments', []);
|
||||||
$uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles];
|
$uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles];
|
||||||
$uploadedFiles = array_values(array_filter($uploadedFiles, static fn ($file): bool => $file instanceof \Illuminate\Http\UploadedFile && $file->isValid()));
|
$uploadedFiles = array_values(array_filter($uploadedFiles, static fn ($file): bool => $file instanceof UploadedFile && $file->isValid()));
|
||||||
|
|
||||||
$tickets->comment(
|
$tickets->comment(
|
||||||
ticket: $ticket,
|
ticket: $ticket,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ final class TicketControlsController extends Controller
|
|||||||
(bool) ($validated['closure_internal'] ?? false),
|
(bool) ($validated['closure_internal'] ?? false),
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
'Closure comment'
|
'Closure comment',
|
||||||
);
|
);
|
||||||
|
|
||||||
$fresh = $this->freshTicketForAction($ticket);
|
$fresh = $this->freshTicketForAction($ticket);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ final class KanbanBoard extends Component
|
|||||||
$workflow->dispatch(
|
$workflow->dispatch(
|
||||||
$freshTicket,
|
$freshTicket,
|
||||||
$status->is_closed ? 'ticket.closed' : 'ticket.updated',
|
$status->is_closed ? 'ticket.closed' : 'ticket.updated',
|
||||||
Auth::user()
|
Auth::user(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$message = 'Ticket ' . $freshTicket->reference . ' moved from ' . $oldStatusName . ' to ' . $status->name . '.';
|
$message = 'Ticket ' . $freshTicket->reference . ' moved from ' . $oldStatusName . ' to ' . $status->name . '.';
|
||||||
@@ -241,7 +241,7 @@ final class KanbanBoard extends Component
|
|||||||
{
|
{
|
||||||
DB::table('helpdesk_kanban_board_statuses')->updateOrInsert(
|
DB::table('helpdesk_kanban_board_statuses')->updateOrInsert(
|
||||||
['board_id' => $boardId, 'status_id' => $statusId],
|
['board_id' => $boardId, 'status_id' => $statusId],
|
||||||
['sort_order' => 999, 'updated_at' => now(), 'created_at' => now()]
|
['sort_order' => 999, 'updated_at' => now(), 'created_at' => now()],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ final class KanbanBoard extends Component
|
|||||||
'sort_order' => ($index + 1) * 10,
|
'sort_order' => ($index + 1) * 10,
|
||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Modules\Helpdesk\Livewire;
|
|||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -824,9 +826,11 @@ final class SettingsManager extends Component
|
|||||||
* which made every new Helpdesk settings CRUD form appear to do nothing.
|
* which made every new Helpdesk settings CRUD form appear to do nothing.
|
||||||
*
|
*
|
||||||
* @template TModel of \Illuminate\Database\Eloquent\Model
|
* @template TModel of \Illuminate\Database\Eloquent\Model
|
||||||
|
*
|
||||||
* @param class-string<TModel> $modelClass
|
* @param class-string<TModel> $modelClass
|
||||||
* @param int|string|null $id
|
* @param int|string|null $id
|
||||||
* @param array<string, mixed> $data
|
* @param array<string, mixed> $data
|
||||||
|
*
|
||||||
* @return TModel
|
* @return TModel
|
||||||
*/
|
*/
|
||||||
private function persistSettingModel(string $modelClass, int|string|null $id, array $data): object
|
private function persistSettingModel(string $modelClass, int|string|null $id, array $data): object
|
||||||
@@ -847,7 +851,7 @@ final class SettingsManager extends Component
|
|||||||
$model = $modelClass::query()->create($data);
|
$model = $modelClass::query()->create($data);
|
||||||
|
|
||||||
return $model;
|
return $model;
|
||||||
} catch (\Illuminate\Database\QueryException $exception) {
|
} catch (QueryException $exception) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'settings' => 'The record could not be saved. Check the slug is unique for this section/company and try again.',
|
'settings' => 'The record could not be saved. Check the slug is unique for this section/company and try again.',
|
||||||
]);
|
]);
|
||||||
@@ -857,7 +861,7 @@ final class SettingsManager extends Component
|
|||||||
/**
|
/**
|
||||||
* Clear the default flag on every other record without generating invalid null-id SQL.
|
* Clear the default flag on every other record without generating invalid null-id SQL.
|
||||||
*
|
*
|
||||||
* @param class-string<\Illuminate\Database\Eloquent\Model> $modelClass
|
* @param class-string<Model> $modelClass
|
||||||
*/
|
*/
|
||||||
private function clearOtherDefaults(string $modelClass, int|string|null $currentId, int|string|null $companyId = null): void
|
private function clearOtherDefaults(string $modelClass, int|string|null $currentId, int|string|null $companyId = null): void
|
||||||
{
|
{
|
||||||
@@ -867,7 +871,7 @@ final class SettingsManager extends Component
|
|||||||
$query->whereKeyNot((int) $currentId);
|
$query->whereKeyNot((int) $currentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Schema::hasColumn((new $modelClass())->getTable(), 'company_id')) {
|
if (Schema::hasColumn((new $modelClass)->getTable(), 'company_id')) {
|
||||||
$query->where('company_id', filled($companyId) ? (int) $companyId : null);
|
$query->where('company_id', filled($companyId) ? (int) $companyId : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,6 +911,7 @@ final class SettingsManager extends Component
|
|||||||
* Remove is_default before save when the database has not been migrated yet.
|
* Remove is_default before save when the database has not been migrated yet.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $data
|
* @param array<string, mixed> $data
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function guardDefaultColumn(string $table, array $data): array
|
private function guardDefaultColumn(string $table, array $data): array
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ final class TicketCreate extends Component
|
|||||||
|
|
||||||
public bool $isSaving = false;
|
public bool $isSaving = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validation rules are intentionally evaluated only when the user submits
|
* Validation rules are intentionally evaluated only when the user submits
|
||||||
* the form, not while rich-text/select widgets are synchronising.
|
* the form, not while rich-text/select widgets are synchronising.
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ final class TicketShow extends Component
|
|||||||
$this->dispatch('helpdesk-reply-added');
|
$this->dispatch('helpdesk-reply-added');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh ticket aggregates and the time-log modal after the timer component starts or stops.
|
* Refresh ticket aggregates and the time-log modal after the timer component starts or stops.
|
||||||
*/
|
*/
|
||||||
@@ -211,7 +210,7 @@ final class TicketShow extends Component
|
|||||||
$this->closureInternal,
|
$this->closureInternal,
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
'Closure comment'
|
'Closure comment',
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->reset(['closureComment', 'closureInternal']);
|
$this->reset(['closureComment', 'closureInternal']);
|
||||||
@@ -398,7 +397,6 @@ final class TicketShow extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first-response SLA headline for the SLA panel.
|
* Get the first-response SLA headline for the SLA panel.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Modules\Helpdesk\Models;
|
namespace Modules\Helpdesk\Models;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -23,10 +24,10 @@ final class Category extends Model
|
|||||||
/** @var array<string, string> */
|
/** @var array<string, string> */
|
||||||
protected $casts = ['company_id' => 'integer', 'is_default' => 'boolean'];
|
protected $casts = ['company_id' => 'integer', 'is_default' => 'boolean'];
|
||||||
|
|
||||||
/** @return BelongsTo<\App\Models\Company, Category> */
|
/** @return BelongsTo<Company, Category> */
|
||||||
public function company(): BelongsTo
|
public function company(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(\App\Models\Company::class, 'company_id');
|
return $this->belongsTo(Company::class, 'company_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return HasMany<Ticket> */
|
/** @return HasMany<Ticket> */
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||||||
namespace Modules\Helpdesk\Models;
|
namespace Modules\Helpdesk\Models;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
|
|
||||||
final class Comment extends Model
|
final class Comment extends Model
|
||||||
{
|
{
|
||||||
@@ -89,13 +89,10 @@ final class Comment extends Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param Builder<Comment> $query */
|
/** @param Builder<Comment> $query */
|
||||||
|
|
||||||
public function scopeActive(Builder $query): Builder
|
public function scopeActive(Builder $query): Builder
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
return $query->whereNull('tombstoned_at');
|
return $query->whereNull('tombstoned_at');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Modules\Helpdesk\Models;
|
namespace Modules\Helpdesk\Models;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -23,10 +24,10 @@ final class Project extends Model
|
|||||||
/** @var array<string, string> */
|
/** @var array<string, string> */
|
||||||
protected $casts = ['company_id' => 'integer', 'team_id' => 'integer', 'archived_at' => 'datetime', 'is_default' => 'boolean'];
|
protected $casts = ['company_id' => 'integer', 'team_id' => 'integer', 'archived_at' => 'datetime', 'is_default' => 'boolean'];
|
||||||
|
|
||||||
/** @return BelongsTo<\App\Models\Company, Project> */
|
/** @return BelongsTo<Company, Project> */
|
||||||
public function company(): BelongsTo
|
public function company(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(\App\Models\Company::class, 'company_id');
|
return $this->belongsTo(Company::class, 'company_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return BelongsTo<Team, Project> */
|
/** @return BelongsTo<Team, Project> */
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Modules\Helpdesk\Models;
|
namespace Modules\Helpdesk\Models;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -27,10 +28,10 @@ final class Team extends Model
|
|||||||
'is_default' => 'boolean',
|
'is_default' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @return BelongsTo<\App\Models\Company, Team> */
|
/** @return BelongsTo<Company, Team> */
|
||||||
public function company(): BelongsTo
|
public function company(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(\App\Models\Company::class, 'company_id');
|
return $this->belongsTo(Company::class, 'company_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return HasMany<Project> */
|
/** @return HasMany<Project> */
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class HelpdeskServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
$this->mergeConfigFrom(
|
$this->mergeConfigFrom(
|
||||||
module_path(ModuleInfo::name(), 'Config/config.php'),
|
module_path(ModuleInfo::name(), 'Config/config.php'),
|
||||||
ModuleInfo::nameLower()
|
ModuleInfo::nameLower(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Modules\Helpdesk\Http\Controllers\AttachmentDownloadController;
|
use Modules\Helpdesk\Http\Controllers\AttachmentDownloadController;
|
||||||
use Modules\Helpdesk\Http\Controllers\HelpdeskController;
|
use Modules\Helpdesk\Http\Controllers\HelpdeskController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketControlsController;
|
use Modules\Helpdesk\Http\Controllers\TicketAttachmentStoreController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketCommentDeleteController;
|
use Modules\Helpdesk\Http\Controllers\TicketCommentDeleteController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketCommentStoreController;
|
use Modules\Helpdesk\Http\Controllers\TicketCommentStoreController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketAttachmentStoreController;
|
use Modules\Helpdesk\Http\Controllers\TicketControlsController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketStoreController;
|
use Modules\Helpdesk\Http\Controllers\TicketStoreController;
|
||||||
use Modules\Helpdesk\Http\Controllers\TicketTimeLogController;
|
use Modules\Helpdesk\Http\Controllers\TicketTimeLogController;
|
||||||
use Modules\Helpdesk\Http\Controllers\WorkflowOpenController;
|
use Modules\Helpdesk\Http\Controllers\WorkflowOpenController;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ final class AgentAssignmentService
|
|||||||
->selectRaw('count(*)')
|
->selectRaw('count(*)')
|
||||||
->whereColumn('helpdesk_tickets.agent_id', 'users.id')
|
->whereColumn('helpdesk_tickets.agent_id', 'users.id')
|
||||||
->whereNull('helpdesk_tickets.completed_at'),
|
->whereNull('helpdesk_tickets.completed_at'),
|
||||||
'open_helpdesk_tickets_count'
|
'open_helpdesk_tickets_count',
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($categoryId !== null) {
|
if ($categoryId !== null) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ final class GitWebhookIngestionService
|
|||||||
* Persist supported Git provider commits and link them to matching Helpdesk ticket references.
|
* Persist supported Git provider commits and link them to matching Helpdesk ticket references.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array{status: string, message: string, commit_count: int, linked_count: int, ignored_count: int}
|
* @return array{status: string, message: string, commit_count: int, linked_count: int, ignored_count: int}
|
||||||
*/
|
*/
|
||||||
public function ingest(GitRepository $repository, array $payload, string $provider, ?string $deliveryId, ?string $event): array
|
public function ingest(GitRepository $repository, array $payload, string $provider, ?string $deliveryId, ?string $event): array
|
||||||
@@ -76,7 +77,7 @@ final class GitWebhookIngestionService
|
|||||||
'message' => $message,
|
'message' => $message,
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
'committed_at' => $committedAt,
|
'committed_at' => $committedAt,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
$commitCount++;
|
$commitCount++;
|
||||||
@@ -171,6 +172,7 @@ final class GitWebhookIngestionService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array<int, array<string, mixed>>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
private function commitsFromPayload(array $payload): array
|
private function commitsFromPayload(array $payload): array
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ declare(strict_types=1);
|
|||||||
namespace Modules\Helpdesk\Services;
|
namespace Modules\Helpdesk\Services;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Modules\Helpdesk\Models\SlaPolicy;
|
use Illuminate\Support\Carbon;
|
||||||
use Modules\Helpdesk\Models\Comment;
|
use Modules\Helpdesk\Models\Comment;
|
||||||
|
use Modules\Helpdesk\Models\SlaPolicy;
|
||||||
use Modules\Helpdesk\Models\Ticket;
|
use Modules\Helpdesk\Models\Ticket;
|
||||||
use Modules\Helpdesk\Models\TicketSlaEvent;
|
use Modules\Helpdesk\Models\TicketSlaEvent;
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ final class SlaService
|
|||||||
|
|
||||||
private function markFirstResponse(Ticket $ticket, mixed $respondedAt, ?User $actor = null): void
|
private function markFirstResponse(Ticket $ticket, mixed $respondedAt, ?User $actor = null): void
|
||||||
{
|
{
|
||||||
$respondedAt = $respondedAt instanceof \Illuminate\Support\Carbon
|
$respondedAt = $respondedAt instanceof Carbon
|
||||||
? $respondedAt
|
? $respondedAt
|
||||||
: now();
|
: now();
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
|||||||
namespace Modules\Helpdesk\Services;
|
namespace Modules\Helpdesk\Services;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\UploadedFile;
|
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Modules\Helpdesk\Tests\Browser;
|
namespace Modules\Helpdesk\Tests\Browser;
|
||||||
|
|
||||||
use Laravel\Dusk\Browser;
|
use Laravel\Dusk\Browser;
|
||||||
use Modules\Helpdesk\Models\CannedResponse;
|
|
||||||
use Modules\Helpdesk\Models\Comment;
|
use Modules\Helpdesk\Models\Comment;
|
||||||
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
|
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
|
||||||
use Tests\DuskTestCase;
|
use Tests\DuskTestCase;
|
||||||
@@ -69,7 +68,6 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
|
|||||||
'is_internal' => false,
|
'is_internal' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
Comment::query()->create([
|
Comment::query()->create([
|
||||||
'ticket_id' => $ticket->id,
|
'ticket_id' => $ticket->id,
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ final class HelpdeskLivewireAssetsTest extends TestCase
|
|||||||
self::assertLessThan(
|
self::assertLessThan(
|
||||||
strpos($layout, '@fluxScripts'),
|
strpos($layout, '@fluxScripts'),
|
||||||
strpos($layout, '@livewireScripts'),
|
strpos($layout, '@livewireScripts'),
|
||||||
'Livewire scripts must be loaded before Flux scripts so settings CRUD actions fire in the browser.'
|
'Livewire scripts must be loaded before Flux scripts so settings CRUD actions fire in the browser.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ final class SettingsCrudTest extends TestCase
|
|||||||
$checklist = ChecklistTemplate::query()->where('slug', 'release-checklist')->firstOrFail();
|
$checklist = ChecklistTemplate::query()->where('slug', 'release-checklist')->firstOrFail();
|
||||||
Livewire::test(SettingsManager::class)
|
Livewire::test(SettingsManager::class)
|
||||||
->call('editChecklistTemplate', $checklist->id)
|
->call('editChecklistTemplate', $checklist->id)
|
||||||
->set('checklistTemplateForm.items_text', "Updated item")
|
->set('checklistTemplateForm.items_text', 'Updated item')
|
||||||
->call('saveChecklistTemplate')
|
->call('saveChecklistTemplate')
|
||||||
->assertHasNoErrors();
|
->assertHasNoErrors();
|
||||||
$this->assertDatabaseHas('helpdesk_checklist_templates', ['id' => $checklist->id]);
|
$this->assertDatabaseHas('helpdesk_checklist_templates', ['id' => $checklist->id]);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Modules\Helpdesk\Tests\Feature;
|
namespace Modules\Helpdesk\Tests\Feature;
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@@ -13,6 +13,7 @@ use Livewire\Livewire;
|
|||||||
use Modules\Helpdesk\Livewire\TicketShow;
|
use Modules\Helpdesk\Livewire\TicketShow;
|
||||||
use Modules\Helpdesk\Models\Attachment;
|
use Modules\Helpdesk\Models\Attachment;
|
||||||
use Modules\Helpdesk\Models\Comment;
|
use Modules\Helpdesk\Models\Comment;
|
||||||
|
use Modules\Helpdesk\Services\TicketService;
|
||||||
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
|
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@@ -69,7 +70,6 @@ final class TicketConversationTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function test_comment_post_endpoint_creates_lotus_identity_and_search_text(): void
|
public function test_comment_post_endpoint_creates_lotus_identity_and_search_text(): void
|
||||||
{
|
{
|
||||||
$fixtures = $this->seedLookupData();
|
$fixtures = $this->seedLookupData();
|
||||||
@@ -98,7 +98,7 @@ final class TicketConversationTest extends TestCase
|
|||||||
$fixtures = $this->seedLookupData();
|
$fixtures = $this->seedLookupData();
|
||||||
$owner = $this->user();
|
$owner = $this->user();
|
||||||
$ticket = $this->ticket(['owner' => $owner, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
$ticket = $this->ticket(['owner' => $owner, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
||||||
$parent = app(\Modules\Helpdesk\Services\TicketService::class)->comment($ticket, $owner, '<p>Parent response</p>');
|
$parent = app(TicketService::class)->comment($ticket, $owner, '<p>Parent response</p>');
|
||||||
|
|
||||||
$this->actingAs($owner)
|
$this->actingAs($owner)
|
||||||
->post(route('helpdesk.tickets.comments.store', $ticket), [
|
->post(route('helpdesk.tickets.comments.store', $ticket), [
|
||||||
@@ -115,8 +115,6 @@ final class TicketConversationTest extends TestCase
|
|||||||
self::assertSame('RE: Parent response', $reply->subject);
|
self::assertSame('RE: Parent response', $reply->subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function test_existing_ticket_accepts_multiple_supporting_file_attachments(): void
|
public function test_existing_ticket_accepts_multiple_supporting_file_attachments(): void
|
||||||
{
|
{
|
||||||
Storage::fake((string) config('helpdesk.disk', 'public'));
|
Storage::fake((string) config('helpdesk.disk', 'public'));
|
||||||
@@ -187,7 +185,7 @@ final class TicketConversationTest extends TestCase
|
|||||||
$admin = $this->user(['company_id' => $company->id, 'is_platform_admin' => true]);
|
$admin = $this->user(['company_id' => $company->id, 'is_platform_admin' => true]);
|
||||||
$owner = $this->user(['company_id' => $company->id]);
|
$owner = $this->user(['company_id' => $company->id]);
|
||||||
$ticket = $this->ticket(['owner' => $owner, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
$ticket = $this->ticket(['owner' => $owner, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
||||||
$comment = app(\Modules\Helpdesk\Services\TicketService::class)->comment($ticket, $owner, '<p>Delete me</p>');
|
$comment = app(TicketService::class)->comment($ticket, $owner, '<p>Delete me</p>');
|
||||||
|
|
||||||
$this->actingAs($admin)
|
$this->actingAs($admin)
|
||||||
->delete(route('helpdesk.tickets.comments.destroy', [$ticket, $comment]))
|
->delete(route('helpdesk.tickets.comments.destroy', [$ticket, $comment]))
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ final class TicketCrudTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function test_ticket_create_does_not_validate_before_submit(): void
|
public function test_ticket_create_does_not_validate_before_submit(): void
|
||||||
{
|
{
|
||||||
$this->seedLookupData();
|
$this->seedLookupData();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ final class TicketTimerButtonSizeFilesTest extends TestCase
|
|||||||
self::assertStringNotContainsString('px-3 py-1.5 text-xs', $view);
|
self::assertStringNotContainsString('px-3 py-1.5 text-xs', $view);
|
||||||
self::assertStringNotContainsString('ticket-timer.js', $view);
|
self::assertStringNotContainsString('ticket-timer.js', $view);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure the active timer action also remains readable and single-line.
|
* Ensure the active timer action also remains readable and single-line.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class TicketTimerFilesTest extends TestCase
|
|||||||
$this->assertStringStartsWith('<div', $view);
|
$this->assertStringStartsWith('<div', $view);
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
str_starts_with($view, '<style'),
|
str_starts_with($view, '<style'),
|
||||||
'The ticket timer Livewire view must not start with a <style> tag.'
|
'The ticket timer Livewire view must not start with a <style> tag.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class StripeWebhookController extends Controller
|
|||||||
'description' => 'Stripe payment received',
|
'description' => 'Stripe payment received',
|
||||||
'paid_at' => now(),
|
'paid_at' => now(),
|
||||||
'metadata' => $intent,
|
'metadata' => $intent,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class PlatformSeeder extends Seeder
|
|||||||
'timezone' => 'Europe/London',
|
'timezone' => 'Europe/London',
|
||||||
'is_system_company' => true,
|
'is_system_company' => true,
|
||||||
'billing_email' => 'mark.gregory@mpba.co.uk',
|
'billing_email' => 'mark.gregory@mpba.co.uk',
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
$user = User::updateOrCreate(
|
$user = User::updateOrCreate(
|
||||||
@@ -55,7 +55,7 @@ class PlatformSeeder extends Seeder
|
|||||||
'is_company_admin' => true,
|
'is_company_admin' => true,
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
$user->assignRole($role);
|
$user->assignRole($role);
|
||||||
$warehouse = Warehouse::firstOrNew([
|
$warehouse = Warehouse::firstOrNew([
|
||||||
@@ -72,7 +72,7 @@ class PlatformSeeder extends Seeder
|
|||||||
|
|
||||||
WarehouseValidationRule::updateOrCreate(
|
WarehouseValidationRule::updateOrCreate(
|
||||||
['warehouse_id' => $warehouse->id, 'rule_key' => 'stock.location.required'],
|
['warehouse_id' => $warehouse->id, 'rule_key' => 'stock.location.required'],
|
||||||
['name' => 'Stock location required', 'severity' => 'error', 'is_active' => true, 'configuration' => ['required' => true]]
|
['name' => 'Stock location required', 'severity' => 'error', 'is_active' => true, 'configuration' => ['required' => true]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ final class PricingDatabaseSeeder extends Seeder
|
|||||||
|
|
||||||
DB::table('platform_prices')->updateOrInsert(
|
DB::table('platform_prices')->updateOrInsert(
|
||||||
['code' => $plan['code']],
|
['code' => $plan['code']],
|
||||||
$payload
|
$payload,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ class PricingServiceProvider extends ServiceProvider
|
|||||||
module_path(ModuleInfo::name(), 'Config/config.php') => config_path(ModuleInfo::nameLower() . '.php'),
|
module_path(ModuleInfo::name(), 'Config/config.php') => config_path(ModuleInfo::nameLower() . '.php'),
|
||||||
], 'config');
|
], 'config');
|
||||||
$this->mergeConfigFrom(
|
$this->mergeConfigFrom(
|
||||||
module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower()
|
module_path(ModuleInfo::name(), 'Config/config.php'),
|
||||||
|
ModuleInfo::nameLower(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
|
|||||||
use Modules\Warehouse\app\Models\Warehouse;
|
use Modules\Warehouse\app\Models\Warehouse;
|
||||||
use Modules\Warehouse\app\Models\WarehouseValidationRule;
|
use Modules\Warehouse\app\Models\WarehouseValidationRule;
|
||||||
|
|
||||||
|
|
||||||
class WarehouseDashboardController extends Controller
|
class WarehouseDashboardController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Warehouse extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'settings' => 'array'
|
'settings' => 'array',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
app()->isProduction(),
|
app()->isProduction(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Password::defaults(fn (): ?Password => app()->isProduction()
|
Password::defaults(
|
||||||
|
fn (): ?Password => app()->isProduction()
|
||||||
? Password::min(12)
|
? Password::min(12)
|
||||||
->mixedCase()
|
->mixedCase()
|
||||||
->letters()
|
->letters()
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ final class DashboardChartService
|
|||||||
FROM EWDAT.ICM688M2
|
FROM EWDAT.ICM688M2
|
||||||
WHERE A5PICKCMTS >= CURRENT DATE - 7 DAYS
|
WHERE A5PICKCMTS >= CURRENT DATE - 7 DAYS
|
||||||
GROUP BY DATE(A5PICKCMTS)
|
GROUP BY DATE(A5PICKCMTS)
|
||||||
ORDER BY PICK_DATE'
|
ORDER BY PICK_DATE',
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
@@ -112,7 +112,7 @@ final class DashboardChartService
|
|||||||
AND SHDATA LIKE 'Success%'
|
AND SHDATA LIKE 'Success%'
|
||||||
AND SHDT >= VARCHAR_FORMAT(CURRENT DATE - 7 DAYS, 'YYYYMMDD')
|
AND SHDT >= VARCHAR_FORMAT(CURRENT DATE - 7 DAYS, 'YYYYMMDD')
|
||||||
GROUP BY SHDT
|
GROUP BY SHDT
|
||||||
ORDER BY SHDT"
|
ORDER BY SHDT",
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
@@ -143,7 +143,7 @@ final class DashboardChartService
|
|||||||
WHERE SHDT >= VARCHAR_FORMAT(CURRENT DATE - 30 DAYS, 'YYYYMMDD')
|
WHERE SHDT >= VARCHAR_FORMAT(CURRENT DATE - 30 DAYS, 'YYYYMMDD')
|
||||||
GROUP BY TRIM(SHSOURCE)
|
GROUP BY TRIM(SHSOURCE)
|
||||||
ORDER BY TOTAL DESC
|
ORDER BY TOTAL DESC
|
||||||
FETCH FIRST 5 ROWS ONLY"
|
FETCH FIRST 5 ROWS ONLY",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! empty($rows)) {
|
if (! empty($rows)) {
|
||||||
@@ -176,7 +176,7 @@ final class DashboardChartService
|
|||||||
WHERE AT9QTY <= AT9WRN
|
WHERE AT9QTY <= AT9WRN
|
||||||
GROUP BY TRIM(AT9WHS)
|
GROUP BY TRIM(AT9WHS)
|
||||||
ORDER BY TOTAL DESC
|
ORDER BY TOTAL DESC
|
||||||
FETCH FIRST 6 ROWS ONLY'
|
FETCH FIRST 6 ROWS ONLY',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! empty($rows)) {
|
if (! empty($rows)) {
|
||||||
@@ -209,7 +209,7 @@ final class DashboardChartService
|
|||||||
SUM(A5PACKED) AS PACKED_QTY,
|
SUM(A5PACKED) AS PACKED_QTY,
|
||||||
SUM(A5RSVQTY - A5PACKED) AS REMAINING_QTY
|
SUM(A5RSVQTY - A5PACKED) AS REMAINING_QTY
|
||||||
FROM EWDAT.ICM688M2
|
FROM EWDAT.ICM688M2
|
||||||
WHERE A5RSVQTY > 0'
|
WHERE A5RSVQTY > 0',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! empty($rows)) {
|
if (! empty($rows)) {
|
||||||
@@ -261,6 +261,7 @@ final class DashboardChartService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, string> $labels
|
* @param array<int, string> $labels
|
||||||
|
*
|
||||||
* @return array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
private function emptySeries(array $labels): array
|
private function emptySeries(array $labels): array
|
||||||
@@ -289,6 +290,7 @@ final class DashboardChartService
|
|||||||
/**
|
/**
|
||||||
* @param array<int, string> $labels
|
* @param array<int, string> $labels
|
||||||
* @param array<int, int> $data
|
* @param array<int, int> $data
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function lineChart(array $labels, array $data, string $label): array
|
private function lineChart(array $labels, array $data, string $label): array
|
||||||
@@ -307,6 +309,7 @@ final class DashboardChartService
|
|||||||
/**
|
/**
|
||||||
* @param array<int, string> $labels
|
* @param array<int, string> $labels
|
||||||
* @param array<int, int> $data
|
* @param array<int, int> $data
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function barChart(array $labels, array $data, string $label): array
|
private function barChart(array $labels, array $data, string $label): array
|
||||||
@@ -324,6 +327,7 @@ final class DashboardChartService
|
|||||||
/**
|
/**
|
||||||
* @param array<int, string> $labels
|
* @param array<int, string> $labels
|
||||||
* @param array<int, int> $data
|
* @param array<int, int> $data
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function doughnutChart(array $labels, array $data, string $label): array
|
private function doughnutChart(array $labels, array $data, string $label): array
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
headers: Request::HEADER_X_FORWARDED_FOR
|
headers: Request::HEADER_X_FORWARDED_FOR
|
||||||
| Request::HEADER_X_FORWARDED_HOST
|
| Request::HEADER_X_FORWARDED_HOST
|
||||||
| Request::HEADER_X_FORWARDED_PORT
|
| Request::HEADER_X_FORWARDED_PORT
|
||||||
| Request::HEADER_X_FORWARDED_PROTO
|
| Request::HEADER_X_FORWARDED_PROTO,
|
||||||
);
|
);
|
||||||
|
|
||||||
$middleware->alias([
|
$middleware->alias([
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ return [
|
|||||||
|
|
||||||
'prefix' => env(
|
'prefix' => env(
|
||||||
'HORIZON_PREFIX',
|
'HORIZON_PREFIX',
|
||||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
|
Str::slug(env('APP_NAME', 'laravel'), '_') . '_horizon:',
|
||||||
),
|
),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,3 +1,60 @@
|
|||||||
{
|
{
|
||||||
"preset": "laravel"
|
"preset": "laravel",
|
||||||
|
"exclude": [
|
||||||
|
"bootstrap/cache",
|
||||||
|
"storage",
|
||||||
|
"public/build",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"notName": [
|
||||||
|
"_ide_helper.php",
|
||||||
|
"_ide_helper_models.php",
|
||||||
|
".phpstorm.meta.php"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"Pint/phpdoc_type_annotations_only": false,
|
||||||
|
"no_superfluous_phpdoc_tags": false,
|
||||||
|
"array_syntax": {
|
||||||
|
"syntax": "short"
|
||||||
|
},
|
||||||
|
"binary_operator_spaces": {
|
||||||
|
"default": "single_space"
|
||||||
|
},
|
||||||
|
"cast_spaces": {
|
||||||
|
"space": "single"
|
||||||
|
},
|
||||||
|
"concat_space": {
|
||||||
|
"spacing": "one"
|
||||||
|
},
|
||||||
|
"method_argument_space": {
|
||||||
|
"on_multiline": "ensure_fully_multiline",
|
||||||
|
"attribute_placement": "standalone"
|
||||||
|
},
|
||||||
|
"no_unused_imports": true,
|
||||||
|
"ordered_imports": {
|
||||||
|
"sort_algorithm": "alpha"
|
||||||
|
},
|
||||||
|
"phpdoc_align": {
|
||||||
|
"align": "vertical"
|
||||||
|
},
|
||||||
|
"phpdoc_order": {
|
||||||
|
"order": [
|
||||||
|
"param",
|
||||||
|
"return",
|
||||||
|
"throws"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"phpdoc_separation": true,
|
||||||
|
"phpdoc_trim": true,
|
||||||
|
"single_import_per_statement": true,
|
||||||
|
"single_line_after_imports": true,
|
||||||
|
"single_line_comment_spacing": true,
|
||||||
|
"trailing_comma_in_multiline": {
|
||||||
|
"elements": [
|
||||||
|
"arrays",
|
||||||
|
"arguments",
|
||||||
|
"parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class AuthBrowserTest extends DuskTestCase
|
|||||||
|
|
||||||
$browser->waitUntil(
|
$browser->waitUntil(
|
||||||
'window.location.pathname === "/" || window.location.pathname === "/login" || ! document.querySelector(".wms-app-shell")',
|
'window.location.pathname === "/" || window.location.pathname === "/login" || ! document.querySelector(".wms-app-shell")',
|
||||||
15
|
15,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ if (link) {
|
|||||||
window.location.href = expectedPath;
|
window.location.href = expectedPath;
|
||||||
}
|
}
|
||||||
JS,
|
JS,
|
||||||
json_encode($path, JSON_THROW_ON_ERROR)
|
json_encode($path, JSON_THROW_ON_ERROR),
|
||||||
));
|
));
|
||||||
|
|
||||||
$browser->waitForLocation($path, 15);
|
$browser->waitForLocation($path, 15);
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ trait InteractsWithWmsData
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, string> $features
|
* @param array<int, string> $features
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function platformPriceAttributes(
|
private function platformPriceAttributes(
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace Tests\Browser;
|
namespace Tests\Browser;
|
||||||
|
|
||||||
use Laravel\Dusk\Browser;
|
use Laravel\Dusk\Browser;
|
||||||
use Modules\Warehouse\app\Models\WarehouseValidationRule;
|
|
||||||
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
|
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
|
||||||
use Tests\Browser\Concerns\InteractsWithWmsData;
|
use Tests\Browser\Concerns\InteractsWithWmsData;
|
||||||
use Tests\DuskTestCase;
|
use Tests\DuskTestCase;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ abstract class DuskTestCase extends BaseTestCase
|
|||||||
*/
|
*/
|
||||||
protected function driver(): RemoteWebDriver
|
protected function driver(): RemoteWebDriver
|
||||||
{
|
{
|
||||||
$options = (new ChromeOptions())->addArguments([
|
$options = (new ChromeOptions)->addArguments([
|
||||||
'--disable-gpu',
|
'--disable-gpu',
|
||||||
'--headless=new',
|
'--headless=new',
|
||||||
'--window-size=1920,1080',
|
'--window-size=1920,1080',
|
||||||
@@ -56,8 +56,8 @@ abstract class DuskTestCase extends BaseTestCase
|
|||||||
$_ENV['DUSK_DRIVER_URL'] ?? 'http://127.0.0.1:9515',
|
$_ENV['DUSK_DRIVER_URL'] ?? 'http://127.0.0.1:9515',
|
||||||
DesiredCapabilities::chrome()->setCapability(
|
DesiredCapabilities::chrome()->setCapability(
|
||||||
ChromeOptions::CAPABILITY,
|
ChromeOptions::CAPABILITY,
|
||||||
$options
|
$options,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,12 +191,11 @@ abstract class DuskTestCase extends BaseTestCase
|
|||||||
&& $path !== $expected
|
&& $path !== $expected
|
||||||
&& ! str_ends_with($path, '.zip')
|
&& ! str_ends_with($path, '.zip')
|
||||||
&& ! str_ends_with($path, '.txt');
|
&& ! str_ends_with($path, '.txt');
|
||||||
}
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
usort($candidates, static function (string $left, string $right): int {
|
usort($candidates, static function (string $left, string $right): int {
|
||||||
$score = static fn (string $path): int =>
|
$score = static fn (string $path): int => str_contains($path, 'mac-arm64') ? 0
|
||||||
str_contains($path, 'mac-arm64') ? 0
|
|
||||||
: (str_contains($path, 'mac-arm') ? 1
|
: (str_contains($path, 'mac-arm') ? 1
|
||||||
: (str_contains($path, 'mac') ? 2 : 3));
|
: (str_contains($path, 'mac') ? 2 : 3));
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ class PlatformAdministrationTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $overrides
|
* @param array<string, mixed> $overrides
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function pricePayload(array $overrides = []): array
|
private function pricePayload(array $overrides = []): array
|
||||||
@@ -267,6 +268,7 @@ class PlatformAdministrationTest extends TestCase
|
|||||||
* Return payload with a valid test CSRF token.
|
* Return payload with a valid test CSRF token.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function csrfPayload(array $payload = []): array
|
private function csrfPayload(array $payload = []): array
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ class PlatformPriceCrudTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $overrides
|
* @param array<string, mixed> $overrides
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function pricePayload(array $overrides = []): array
|
private function pricePayload(array $overrides = []): array
|
||||||
@@ -302,6 +303,7 @@ class PlatformPriceCrudTest extends TestCase
|
|||||||
* Return payload with a valid test CSRF token.
|
* Return payload with a valid test CSRF token.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function csrfPayload(array $payload = []): array
|
private function csrfPayload(array $payload = []): array
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ class WarehouseCrudTest extends TestCase
|
|||||||
* Return payload with a valid test CSRF token.
|
* Return payload with a valid test CSRF token.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $payload
|
* @param array<string, mixed> $payload
|
||||||
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private function csrfPayload(array $payload = []): array
|
private function csrfPayload(array $payload = []): array
|
||||||
|
|||||||
+4
-3
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
|
use App\Http\Middleware\VerifyCsrfToken;
|
||||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
use Illuminate\Testing\TestResponse;
|
use Illuminate\Testing\TestResponse;
|
||||||
@@ -30,9 +31,9 @@ abstract class TestCase extends BaseTestCase
|
|||||||
$this->withoutMiddleware(ValidateCsrfToken::class);
|
$this->withoutMiddleware(ValidateCsrfToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists(\App\Http\Middleware\VerifyCsrfToken::class)) {
|
if (class_exists(VerifyCsrfToken::class)) {
|
||||||
\App\Http\Middleware\VerifyCsrfToken::except(['*']);
|
VerifyCsrfToken::except(['*']);
|
||||||
$this->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);
|
$this->withoutMiddleware(VerifyCsrfToken::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user